1 | #!/usr/local/bin/node
|
2 |
|
3 | const NOPE= function (){};
|
4 |
|
5 | var dyngo= require('./index'),
|
6 | async= require('async'),
|
7 | fs= require('fs'),
|
8 | csv = require('csv'),
|
9 | xlsx = require('./lib/xlsx'),
|
10 | carrier= require('carrier'),
|
11 | util= require('util'),
|
12 | readline= require('readline'),
|
13 | _= require('underscore'),
|
14 | path= require('path').join,
|
15 | colors = require('colors'),
|
16 | GSON = require('gson'),
|
17 | coffee= require('coffee-script'),
|
18 | AWS = require('aws-sdk');
|
19 |
|
20 | var argv = require('optimist').argv;
|
21 |
|
22 | var _history= [];
|
23 |
|
24 | const _json= function (path,content)
|
25 | {
|
26 | try
|
27 | {
|
28 | if (!content)
|
29 | return JSON.parse(fs.readFileSync(path,'utf8'));
|
30 | else
|
31 | {
|
32 | fs.writeFileSync(path,JSON.stringify(content,null,2),'utf8')
|
33 | return { success: function (fn) { process.nextTick(fn); } };
|
34 | }
|
35 | }
|
36 | catch (ex)
|
37 | {
|
38 | console.log((ex+'').red);
|
39 | }
|
40 | },
|
41 | _gson= function (path,content)
|
42 | {
|
43 | try
|
44 | {
|
45 | if (!content)
|
46 | return GSON.parse(fs.readFileSync(path,'utf8'));
|
47 | else
|
48 | {
|
49 | fs.writeFileSync(path,GSON.stringify(content),'utf8')
|
50 | return { success: function (fn) { process.nextTick(fn); } };
|
51 | }
|
52 | }
|
53 | catch (ex)
|
54 | {
|
55 | console.log((ex+'').red);
|
56 | }
|
57 | },
|
58 | _toJSON= function (fields,_transformFnc)
|
59 | {
|
60 | var r= [];
|
61 |
|
62 | this.forEach(function (row,idx)
|
63 | {
|
64 | var obj= {},
|
65 | _val= function (field,value)
|
66 | {
|
67 | var r,
|
68 | path= field.split('.'),
|
69 | current= obj;
|
70 |
|
71 | for (var i=0;i<path.length-1;i++)
|
72 | current= current[path[i]]= current[path[i]] || {};
|
73 |
|
74 | current[path[path.length-1]]= value;
|
75 | };
|
76 |
|
77 | for (var i=0;i<fields.length;i++)
|
78 | _val(fields[i],row[i]);
|
79 |
|
80 | if (_transformFnc)
|
81 | obj= _transformFnc(obj,idx);
|
82 |
|
83 | if (Array.isArray(obj))
|
84 | r.concat(obj);
|
85 | else
|
86 | if (obj)
|
87 | r.push(obj);
|
88 | });
|
89 |
|
90 | return r;
|
91 | },
|
92 | _csv= function (dyn, path, opts, cols, tfnc)
|
93 | {
|
94 | var raster= [],
|
95 | promise= dyn.promise(['end','results']),
|
96 | count= 0;
|
97 |
|
98 | raster.toJSON= _toJSON;
|
99 |
|
100 | csv()
|
101 | .from.path(path, opts)
|
102 | .on('record',function (row,index)
|
103 | {
|
104 | process.stdout.write(('\r'+(count++)).yellow);
|
105 | raster.push(row);
|
106 | })
|
107 | .on('end',function (count)
|
108 | {
|
109 | console.log(('\r'+count).green);
|
110 | promise.trigger.results(raster.toJSON(cols,tfnc));
|
111 | promise.trigger.end();
|
112 | })
|
113 | .on('error', promise.error);
|
114 |
|
115 | return promise;
|
116 | },
|
117 | _xlsx= function (src)
|
118 | {
|
119 | var b64= fs.readFileSync(src, "base64"),
|
120 | workbook= xlsx.decode(b64);
|
121 |
|
122 | workbook.sheet= function (name)
|
123 | {
|
124 | var worksheet;
|
125 |
|
126 | this.worksheets.forEach(function (s)
|
127 | {
|
128 | if (s.name==name)
|
129 | {
|
130 | worksheet= s;
|
131 | return false;
|
132 | }
|
133 | });
|
134 |
|
135 | return worksheet;
|
136 | }
|
137 |
|
138 | workbook.worksheets.forEach(function (s)
|
139 | {
|
140 | s.toJSON= function (fields,_transformFnc)
|
141 | {
|
142 | var r= [];
|
143 |
|
144 | this.data.forEach(function (row,idx)
|
145 | {
|
146 | var obj= {},
|
147 | _nnan= function (v) { return v===0 ? v : (!!v ? v : '') },
|
148 | _value= function (value)
|
149 | {
|
150 | if (value)
|
151 | return _nnan(value.value);
|
152 | else
|
153 | return '';
|
154 | },
|
155 | _val= function (field,value)
|
156 | {
|
157 | var r,
|
158 | path= field.split('.'),
|
159 | current= obj;
|
160 |
|
161 | for (var i=0;i<path.length-1;i++)
|
162 | current= current[path[i]]= current[path[i]] || {};
|
163 |
|
164 | current[path[path.length-1]]= _value(value);
|
165 | };
|
166 |
|
167 | for (var i=0;i<fields.length;i++)
|
168 | _val(fields[i],row[i]);
|
169 |
|
170 | if (_transformFnc)
|
171 | obj= _transformFnc(obj,idx);
|
172 |
|
173 | if (Array.isArray(obj))
|
174 | r.concat(obj);
|
175 | else
|
176 | if (obj)
|
177 | r.push(obj);
|
178 | });
|
179 |
|
180 | return r;
|
181 | }
|
182 | });
|
183 |
|
184 | return workbook;
|
185 | },
|
186 | _eval= function (cmd,db,last,tx)
|
187 | {
|
188 | var __csv= function (path, opts, cols, tfnc) { var args= Array.prototype.slice.apply(arguments); args.unshift(db._dyn); return _csv.apply(null,args); };
|
189 |
|
190 | try
|
191 | {
|
192 | return eval('(function (db,tx,fs,last,_,json,gson,csv,xlsx,argv){ return '+cmd+'; })')(db,tx,fs,last,_,_json,_gson,__csv,_xlsx,argv);
|
193 | }
|
194 | catch (ex)
|
195 | {
|
196 | if (ex instanceof SyntaxError||ex instanceof ReferenceError)
|
197 | return coffee.eval('((global,db,tx,fs,last,_,json,gson,csv,xlsx,argv) -> '+cmd+')')(global,db,tx,fs,last,_,_json,_gson,__csv,_xlsx,argv);
|
198 | else
|
199 | throw ex;
|
200 | }
|
201 | },
|
202 | _dobatch= function (db,lines,done)
|
203 | {
|
204 | var last, tx;
|
205 |
|
206 | return function ()
|
207 | {
|
208 | async.forEachSeries(lines,
|
209 | function (cmd,done)
|
210 | {
|
211 | console.log(cmd);
|
212 |
|
213 | var promise= _eval(cmd,db,last,tx);
|
214 |
|
215 | if (promise==undefined||!(promise.result||promise.success||promise.error||promise.notfound))
|
216 | done();
|
217 | else
|
218 | {
|
219 | if (promise.notfound)
|
220 | promise.notfound(function ()
|
221 | {
|
222 | last= undefined;
|
223 | done();
|
224 | });
|
225 | else
|
226 | if (promise.error)
|
227 | promise.error(function (err)
|
228 | {
|
229 | console.log((err+'').red,err.stack);
|
230 | done();
|
231 | });
|
232 |
|
233 | if (promise.transaction)
|
234 | promise.transaction(function (_tx)
|
235 | {
|
236 | tx= _tx;
|
237 | console.log(('tx: '+tx._id).green);
|
238 | done();
|
239 | });
|
240 | else
|
241 | if (promise.result)
|
242 | promise.result(function (res)
|
243 | {
|
244 | last= res;
|
245 | done();
|
246 | });
|
247 | else
|
248 | if (promise.success)
|
249 | promise.success(function ()
|
250 | {
|
251 | console.log('done!'.green);
|
252 | done();
|
253 | });
|
254 | }
|
255 |
|
256 |
|
257 | },done);
|
258 | };
|
259 | },
|
260 | _doinput= function (db,cb)
|
261 | {
|
262 | var _lines= [];
|
263 |
|
264 | carrier.carry(process.stdin, function (line)
|
265 | {
|
266 | _lines.push(line);
|
267 | },'utf8');
|
268 |
|
269 | process.stdin.on('end',function ()
|
270 | {
|
271 | process.nextTick(function ()
|
272 | {
|
273 | _dobatch(db,_lines,
|
274 | function (err)
|
275 | {
|
276 | if (err)
|
277 | {
|
278 | console.log(err.message.red,err.stack);
|
279 | process.exit(1);
|
280 | }
|
281 | else
|
282 | process.exit(0);
|
283 | })();
|
284 | });
|
285 | });
|
286 |
|
287 | process.stdin.resume();
|
288 |
|
289 |
|
290 | setTimeout(function () { if (_lines.length==0) cb(); },100);
|
291 | },
|
292 | _dorc= function (db,cb)
|
293 | {
|
294 | var rcFile= path(getUserHome(),'.dyngorc'),
|
295 | localRcFile= '.dyngorc',
|
296 | _file= function (f, done)
|
297 | {
|
298 | var _lines= [];
|
299 |
|
300 | if (fs.existsSync(f))
|
301 | {
|
302 | console.log(('executing '+f+'...').green);
|
303 | var rstream= fs.createReadStream(f, { encoding: 'utf8' });
|
304 |
|
305 | rstream.on('end',function ()
|
306 | {
|
307 | process.nextTick(function ()
|
308 | {
|
309 | _dobatch(db,_lines,done)();
|
310 | });
|
311 | });
|
312 |
|
313 | carrier.carry(rstream,function (line)
|
314 | {
|
315 | _lines.push(line);
|
316 | });
|
317 | }
|
318 | else
|
319 | done();
|
320 | };
|
321 |
|
322 | _file(rcFile,function (err)
|
323 | {
|
324 | if (err)
|
325 | {
|
326 | console.log(err.message.red,err.stack);
|
327 | process.exit(1);
|
328 | }
|
329 | else
|
330 | if (localRcFile!=rcFile)
|
331 | _file(localRcFile,function (err)
|
332 | {
|
333 | if (err)
|
334 | {
|
335 | console.log(err.message.red,err.stack);
|
336 | process.exit(1);
|
337 | }
|
338 | else
|
339 | cb();
|
340 | });
|
341 | else
|
342 | cb();
|
343 | });
|
344 | },
|
345 | getUserHome= function()
|
346 | {
|
347 | return process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME'];
|
348 | },
|
349 | getHistory= function()
|
350 | {
|
351 | var historyFile= path(getUserHome(),'.dyngodb_history');
|
352 |
|
353 | try
|
354 | {
|
355 |
|
356 | if (fs.existsSync(historyFile))
|
357 | _history.push
|
358 | .apply(_history,JSON.parse(fs.readFileSync(historyFile,'utf8')));
|
359 |
|
360 | }
|
361 | catch(ex)
|
362 | {}
|
363 |
|
364 | return _history;
|
365 | },
|
366 | saveHistory= function ()
|
367 | {
|
368 | var historyFile= path(getUserHome(),'.dyngodb_history');
|
369 |
|
370 | if (_history&&_history.length>0)
|
371 | fs.writeFileSync(historyFile,JSON.stringify(_history),'utf8');
|
372 | },
|
373 | _collect= function (consume)
|
374 | {
|
375 | return function (cons)
|
376 | {
|
377 | _.keys(cons).forEach(function (table)
|
378 | {
|
379 | var c, tcons= cons[table];
|
380 |
|
381 | if (!(c=consume[table]))
|
382 | c= consume[table]= { read: 0, write: 0 };
|
383 |
|
384 | c.read+= tcons.read;
|
385 | c.write+= tcons.write;
|
386 | });
|
387 | };
|
388 | };
|
389 |
|
390 | process.on('exit', saveHistory);
|
391 | process.on('SIGINT', function () { saveHistory(); process.exit(0); });
|
392 | process.stdin.pause();
|
393 |
|
394 | var args= [function (err,db)
|
395 | {
|
396 | var last, tx;
|
397 |
|
398 | if (err)
|
399 | console.log(err);
|
400 | else
|
401 | {
|
402 |
|
403 | _dorc(db,function ()
|
404 | {
|
405 | _doinput(db,function()
|
406 | {
|
407 | var rl = readline.createInterface
|
408 | ({
|
409 | input: process.stdin,
|
410 | output: process.stdout,
|
411 | completer: function (linePartial, cb)
|
412 | {
|
413 | if (linePartial.indexOf('db.')==0)
|
414 | {
|
415 | var tables= _.collect(_.filter(_.keys(db),
|
416 | function (key) { return key.indexOf(linePartial.replace('db.',''))==0; }),
|
417 | function (res) { return 'db.'+res; });
|
418 | cb(null,[tables, linePartial]);
|
419 | }
|
420 | else
|
421 | cb(null,[[], linePartial]);
|
422 | }
|
423 | });
|
424 |
|
425 | rl.history= getHistory();
|
426 |
|
427 | (function ask()
|
428 | {
|
429 | var _ask= function (fn)
|
430 | {
|
431 | return function ()
|
432 | {
|
433 | var args= arguments;
|
434 | fn.apply(null,args);
|
435 | ask();
|
436 | };
|
437 | },
|
438 | _print= function (obj,cb)
|
439 | {
|
440 | if (obj._old||(obj[0]&&obj[0]._old))
|
441 | db.cleanup(obj).clean(function (obj)
|
442 | {
|
443 | console.log(util.inspect(obj,{ depth: null }));
|
444 | cb();
|
445 | });
|
446 | else
|
447 | {
|
448 | console.log(util.inspect(obj,{ depth: null }));
|
449 | cb();
|
450 | }
|
451 | };
|
452 |
|
453 | rl.question('> ', function (answer)
|
454 | {
|
455 |
|
456 | if (!answer) { ask(); return; };
|
457 |
|
458 | if (answer.indexOf('clean ')==0)
|
459 | {
|
460 | var target= answer.substring(6);
|
461 |
|
462 | answer= 'db.cleanup('+target+')';
|
463 | }
|
464 |
|
465 | if (answer.indexOf('show collections') > -1)
|
466 | {
|
467 | _.filter(_.keys(db),function (key) { return !!db[key].find; }).forEach(function (c) { console.log(c); });
|
468 | ask();
|
469 | return;
|
470 | }
|
471 | else
|
472 | if (answer=='clear')
|
473 | {
|
474 | process.stdout.write('\u001B[2J\u001B[0;0f');
|
475 | ask();
|
476 | return;
|
477 | }
|
478 | else
|
479 | if (answer=='exit')
|
480 | {
|
481 | process.exit(0);
|
482 | return;
|
483 | }
|
484 |
|
485 | try
|
486 | {
|
487 | var time= process.hrtime(),
|
488 | promise= _eval(answer,db,last,tx),
|
489 | end,
|
490 | printed,
|
491 | chunks= 0,
|
492 | consume= { read: 0, write: 0 },
|
493 | _doneres= function () { elapsed(); ask(); },
|
494 | doneres= _.wrap(_doneres,function (done) { if (printed&&end) done(); }),
|
495 | elapsed= function ()
|
496 | {
|
497 | var diff= process.hrtime(time),
|
498 | secs= (diff[0]*1e9+diff[1])/1e9;
|
499 |
|
500 | _.keys(consume).forEach(function (table)
|
501 | {
|
502 | var tcons= consume[table], s= secs<1 ? 1 : secs;
|
503 |
|
504 | if (tcons.read)
|
505 | console.log(('consumed read capacity['+table+']: '+tcons.read+' ('+(tcons.read/s)+' read/sec)').green);
|
506 |
|
507 | if (tcons.write)
|
508 | console.log(('consumed write capacity['+table+']: '+tcons.write+' ('+(tcons.write/s)+' write/sec)').green);
|
509 | });
|
510 |
|
511 | if (chunks) console.log((chunks+' roundtrips').green);
|
512 | console.log((secs+' secs').green);
|
513 | };
|
514 |
|
515 | if (promise==_||promise===false||promise===undefined||promise.createCollection)
|
516 | {
|
517 | _ask(function () { console.log(promise); })();
|
518 | return;
|
519 | }
|
520 |
|
521 | promise= promise || {};
|
522 |
|
523 | if (promise.consumed)
|
524 | promise.consumed(_collect(consume));
|
525 |
|
526 | if (promise.error)
|
527 | promise.error(_ask(function (err)
|
528 | {
|
529 | if (!err) return;
|
530 |
|
531 | if (err.code=='notfound')
|
532 | console.log('no data found'.yellow);
|
533 | else
|
534 | if (err.code=='exists')
|
535 | console.log('The item already exists'.red);
|
536 | else
|
537 | if (err.code=='updatedsinceread')
|
538 | console.log('The item is changed since you read it'.red);
|
539 | else
|
540 | console.log((err+'').red,err.stack);
|
541 | }));
|
542 |
|
543 | if (promise.end)
|
544 | promise.end(function () { end= true; doneres(); });
|
545 |
|
546 | if (promise.count)
|
547 | promise.count(_ask(function (count) { console.log(('\r'+count).green); elapsed(); }));
|
548 |
|
549 | if (promise.transaction)
|
550 | promise.transaction(function (_tx) { tx= _tx; console.log(('tx: '+tx._id).green); elapsed(); ask(); });
|
551 | else
|
552 | if (promise.committed)
|
553 | promise.committed(function () { tx= undefined; console.log('transaction committed'.green); elapsed(); ask(); });
|
554 | else
|
555 | if (promise.rolledback)
|
556 | promise.rolledback(function (competing) { tx= undefined; if (competing) console.log('transaction rolled back'.red); else console.log('transaction rolled back'.green); elapsed(); ask(); });
|
557 | else
|
558 | if (promise.clean)
|
559 | promise.clean(function (obj) { console.log(util.inspect(obj,{ depth: null })); ask(); });
|
560 | else
|
561 | if (promise.result)
|
562 | {
|
563 | last= undefined;
|
564 | promise.result(function (obj) { last= obj; _print(obj,function () { elapsed(); ask(); }); });
|
565 | }
|
566 | else
|
567 | if (promise.results)
|
568 | {
|
569 | last= [];
|
570 | promise.results(function (items)
|
571 | {
|
572 | chunks++;
|
573 | printed= false;
|
574 |
|
575 | last.push.apply(last,items);
|
576 |
|
577 | _print(items,function ()
|
578 | {
|
579 | printed= true;
|
580 | doneres();
|
581 | });
|
582 | });
|
583 | }
|
584 | else
|
585 | if (promise.success)
|
586 | promise.success(_ask(function () { console.log('done!'.green); elapsed(); }));
|
587 | else
|
588 | _ask(function () { console.log(util.inspect(promise,{ depth: null })); })();
|
589 | }
|
590 | catch (ex)
|
591 | {
|
592 | console.log('unknown command'.red,ex,ex.stack);
|
593 | ask();
|
594 | }
|
595 |
|
596 |
|
597 | });
|
598 | })();
|
599 | });
|
600 | });
|
601 | }
|
602 | }];
|
603 |
|
604 | if (argv.local)
|
605 | args.unshift({ dynamo: { endpoint: new AWS.Endpoint('http://localhost:8000') } });
|
606 |
|
607 | dyngo.apply(null,args);
|