meadow 2.0.23 → 2.0.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +110 -141
  2. package/docs/README.md +34 -230
  3. package/docs/_cover.md +14 -0
  4. package/docs/_sidebar.md +44 -12
  5. package/docs/_topbar.md +5 -0
  6. package/docs/api/doCount.md +109 -0
  7. package/docs/api/doCreate.md +132 -0
  8. package/docs/api/doDelete.md +101 -0
  9. package/docs/api/doRead.md +122 -0
  10. package/docs/api/doReads.md +136 -0
  11. package/docs/api/doUndelete.md +98 -0
  12. package/docs/api/doUpdate.md +129 -0
  13. package/docs/api/getRoleName.md +84 -0
  14. package/docs/api/loadFromPackage.md +153 -0
  15. package/docs/api/marshalRecordFromSourceToObject.md +92 -0
  16. package/docs/api/query.md +133 -0
  17. package/docs/api/rawQueries.md +197 -0
  18. package/docs/api/reference.md +117 -0
  19. package/docs/api/setAuthorizer.md +103 -0
  20. package/docs/api/setDefault.md +90 -0
  21. package/docs/api/setDefaultIdentifier.md +84 -0
  22. package/docs/api/setDomain.md +56 -0
  23. package/docs/api/setIDUser.md +91 -0
  24. package/docs/api/setJsonSchema.md +92 -0
  25. package/docs/api/setProvider.md +87 -0
  26. package/docs/api/setSchema.md +107 -0
  27. package/docs/api/setScope.md +68 -0
  28. package/docs/api/validateObject.md +119 -0
  29. package/docs/architecture.md +316 -0
  30. package/docs/audit-tracking.md +226 -0
  31. package/docs/configuration.md +317 -0
  32. package/docs/providers/meadow-endpoints.md +306 -0
  33. package/docs/providers/mongodb.md +319 -0
  34. package/docs/providers/postgresql.md +312 -0
  35. package/docs/providers/rocksdb.md +297 -0
  36. package/docs/query-dsl.md +269 -0
  37. package/docs/quick-start.md +384 -0
  38. package/docs/raw-queries.md +193 -0
  39. package/docs/retold-catalog.json +61 -1
  40. package/docs/retold-keyword-index.json +15860 -4839
  41. package/docs/soft-deletes.md +224 -0
  42. package/package.json +44 -13
  43. package/scripts/bookstore-seed-postgresql.sql +135 -0
  44. package/scripts/dgraph-test-db.sh +144 -0
  45. package/scripts/meadow-test-cleanup.sh +5 -1
  46. package/scripts/mongodb-test-db.sh +98 -0
  47. package/scripts/postgresql-test-db.sh +124 -0
  48. package/scripts/solr-test-db.sh +135 -0
  49. package/source/Meadow.js +5 -0
  50. package/source/providers/Meadow-Provider-DGraph.js +679 -0
  51. package/source/providers/Meadow-Provider-MeadowEndpoints.js +1 -1
  52. package/source/providers/Meadow-Provider-MongoDB.js +527 -0
  53. package/source/providers/Meadow-Provider-PostgreSQL.js +361 -0
  54. package/source/providers/Meadow-Provider-RocksDB.js +1300 -0
  55. package/source/providers/Meadow-Provider-Solr.js +726 -0
  56. package/test/Meadow-Provider-DGraph_tests.js +741 -0
  57. package/test/Meadow-Provider-MongoDB_tests.js +661 -0
  58. package/test/Meadow-Provider-PostgreSQL_tests.js +787 -0
  59. package/test/Meadow-Provider-RocksDB_tests.js +887 -0
  60. package/test/Meadow-Provider-SQLiteBrowser-Headless_tests.js +657 -0
  61. package/test/Meadow-Provider-SQLiteBrowser_tests.js +895 -0
  62. package/test/Meadow-Provider-Solr_tests.js +679 -0
@@ -0,0 +1,726 @@
1
+ // ##### Part of the **[retold](https://stevenvelozo.github.io/retold/)** system
2
+ /**
3
+ * @license MIT
4
+ * @author <steven@velozo.com>
5
+ */
6
+ var MeadowProvider = function ()
7
+ {
8
+ function createNew(pFable)
9
+ {
10
+ // If a valid Fable object isn't passed in, return a constructor
11
+ if (typeof (pFable) !== 'object')
12
+ {
13
+ return { new: createNew };
14
+ }
15
+ var _Fable = pFable;
16
+ var _GlobalLogLevel = 0;
17
+ if (_Fable.settings.Solr)
18
+ {
19
+ _GlobalLogLevel = _Fable.settings.Solr.GlobalLogLevel || 0;
20
+ }
21
+
22
+ /**
23
+ * Get the Solr client instance from the connection service.
24
+ */
25
+ var getClient = function ()
26
+ {
27
+ if (typeof (_Fable.MeadowSolrProvider) == 'object' && _Fable.MeadowSolrProvider.connected)
28
+ {
29
+ return _Fable.MeadowSolrProvider.pool;
30
+ }
31
+ return false;
32
+ };
33
+
34
+ var getProvider = function ()
35
+ {
36
+ if (typeof (_Fable.MeadowSolrProvider) == 'object')
37
+ {
38
+ return _Fable.MeadowSolrProvider;
39
+ }
40
+ return false;
41
+ };
42
+
43
+ /**
44
+ * Deep-walk an object and replace $$NOW sentinels with ISO date strings.
45
+ *
46
+ * @param {Object} pObj Object to process (mutated in place)
47
+ * @return {Object} The same object with sentinels replaced
48
+ */
49
+ var replaceSentinels = function (pObj)
50
+ {
51
+ if (typeof pObj !== 'object' || pObj === null)
52
+ {
53
+ return pObj;
54
+ }
55
+
56
+ for (var tmpKey in pObj)
57
+ {
58
+ if (!pObj.hasOwnProperty(tmpKey))
59
+ {
60
+ continue;
61
+ }
62
+
63
+ if (pObj[tmpKey] === '$$NOW')
64
+ {
65
+ pObj[tmpKey] = new Date().toISOString();
66
+ }
67
+ else if (typeof pObj[tmpKey] === 'object' && pObj[tmpKey] !== null)
68
+ {
69
+ replaceSentinels(pObj[tmpKey]);
70
+ }
71
+ }
72
+ return pObj;
73
+ };
74
+
75
+ /**
76
+ * Get the next auto-increment sequence value for a collection/column pair.
77
+ * Uses a _meadow_counters Solr collection with search + update.
78
+ *
79
+ * @param {Object} pClient Solr client instance
80
+ * @param {String} pScope Collection name
81
+ * @param {String} pIDColumn Identity column name
82
+ * @param {Function} fCallback (error, nextSequenceValue)
83
+ */
84
+ var getNextSequence = function (pClient, pScope, pIDColumn, fCallback)
85
+ {
86
+ var tmpCounterKey = pScope + '.' + pIDColumn;
87
+
88
+ // Search for existing counter document
89
+ var tmpSearchQuery = pClient.query().q('id:"' + tmpCounterKey + '"').rows(1);
90
+ pClient.search(tmpSearchQuery, function (pSearchError, pSearchResult)
91
+ {
92
+ if (pSearchError)
93
+ {
94
+ return fCallback(pSearchError);
95
+ }
96
+
97
+ var tmpNextSeq = 1;
98
+ if (pSearchResult && pSearchResult.response &&
99
+ pSearchResult.response.docs && pSearchResult.response.docs.length > 0)
100
+ {
101
+ tmpNextSeq = (pSearchResult.response.docs[0].seq || 0) + 1;
102
+ }
103
+
104
+ // Upsert the counter document
105
+ var tmpCounterDoc = { id: tmpCounterKey, seq: tmpNextSeq };
106
+ pClient.add(tmpCounterDoc, function (pAddError)
107
+ {
108
+ if (pAddError)
109
+ {
110
+ return fCallback(pAddError);
111
+ }
112
+ pClient.commit(function (pCommitError)
113
+ {
114
+ if (pCommitError)
115
+ {
116
+ return fCallback(pCommitError);
117
+ }
118
+ return fCallback(null, tmpNextSeq);
119
+ });
120
+ });
121
+ });
122
+ };
123
+
124
+ // The Meadow marshaller also passes in the Schema as the third parameter
125
+ var marshalRecordFromSourceToObject = function (pObject, pRecord)
126
+ {
127
+ for (var tmpColumn in pRecord)
128
+ {
129
+ // Skip Solr's internal _version_ field
130
+ if (tmpColumn === '_version_')
131
+ {
132
+ continue;
133
+ }
134
+ // Solr multi-valued fields return arrays; unwrap single-element arrays
135
+ var tmpValue = pRecord[tmpColumn];
136
+ if (Array.isArray(tmpValue) && tmpValue.length === 1)
137
+ {
138
+ tmpValue = tmpValue[0];
139
+ }
140
+ pObject[tmpColumn] = tmpValue;
141
+ }
142
+ };
143
+
144
+ var Create = function (pQuery, fCallback)
145
+ {
146
+ var tmpResult = pQuery.parameters.result;
147
+
148
+ pQuery.setDialect('Solr').buildCreateQuery();
149
+
150
+ if (pQuery.logLevel > 0 ||
151
+ _GlobalLogLevel > 0)
152
+ {
153
+ _Fable.log.trace(pQuery.query.body, pQuery.query.parameters);
154
+ }
155
+
156
+ var tmpClient = getClient();
157
+ if (!tmpClient)
158
+ {
159
+ tmpResult.error = new Error('No Solr connection available.');
160
+ tmpResult.executed = true;
161
+ return fCallback();
162
+ }
163
+
164
+ var tmpOp = pQuery.query.parameters.solrOperation;
165
+ if (!tmpOp)
166
+ {
167
+ tmpResult.error = new Error('No Solr operation generated.');
168
+ tmpResult.executed = true;
169
+ return fCallback();
170
+ }
171
+
172
+ var tmpDocument = replaceSentinels(JSON.parse(JSON.stringify(tmpOp.document)));
173
+
174
+ // Check for $$AUTOINCREMENT sentinel
175
+ var tmpAutoIncrementColumn = false;
176
+ for (var tmpKey in tmpDocument)
177
+ {
178
+ if (tmpDocument[tmpKey] === '$$AUTOINCREMENT')
179
+ {
180
+ tmpAutoIncrementColumn = tmpKey;
181
+ break;
182
+ }
183
+ }
184
+
185
+ if (tmpAutoIncrementColumn)
186
+ {
187
+ // Get next sequence, then insert
188
+ getNextSequence(tmpClient, tmpOp.collection, tmpAutoIncrementColumn, function (pSeqError, pSeqValue)
189
+ {
190
+ if (pSeqError)
191
+ {
192
+ tmpResult.error = pSeqError;
193
+ tmpResult.value = false;
194
+ tmpResult.executed = true;
195
+ return fCallback();
196
+ }
197
+
198
+ tmpDocument[tmpAutoIncrementColumn] = pSeqValue;
199
+
200
+ tmpClient.add(tmpDocument, function (pAddError)
201
+ {
202
+ if (pAddError)
203
+ {
204
+ tmpResult.error = pAddError;
205
+ tmpResult.value = false;
206
+ tmpResult.executed = true;
207
+ return fCallback();
208
+ }
209
+ tmpClient.commit(function (pCommitError)
210
+ {
211
+ if (pCommitError)
212
+ {
213
+ tmpResult.error = pCommitError;
214
+ tmpResult.value = false;
215
+ tmpResult.executed = true;
216
+ return fCallback();
217
+ }
218
+ tmpResult.error = null;
219
+ tmpResult.value = pSeqValue;
220
+ tmpResult.executed = true;
221
+ return fCallback();
222
+ });
223
+ });
224
+ });
225
+ }
226
+ else
227
+ {
228
+ tmpClient.add(tmpDocument, function (pAddError, pAddResult)
229
+ {
230
+ if (pAddError)
231
+ {
232
+ tmpResult.error = pAddError;
233
+ tmpResult.value = false;
234
+ tmpResult.executed = true;
235
+ return fCallback();
236
+ }
237
+ tmpClient.commit(function (pCommitError)
238
+ {
239
+ if (pCommitError)
240
+ {
241
+ tmpResult.error = pCommitError;
242
+ tmpResult.value = false;
243
+ tmpResult.executed = true;
244
+ return fCallback();
245
+ }
246
+ tmpResult.error = null;
247
+ tmpResult.value = tmpDocument.id || true;
248
+ tmpResult.executed = true;
249
+ return fCallback();
250
+ });
251
+ });
252
+ }
253
+ };
254
+
255
+ var Read = function (pQuery, fCallback)
256
+ {
257
+ var tmpResult = pQuery.parameters.result;
258
+
259
+ pQuery.setDialect('Solr').buildReadQuery();
260
+
261
+ if (pQuery.logLevel > 0 ||
262
+ _GlobalLogLevel > 0)
263
+ {
264
+ _Fable.log.trace(pQuery.query.body, pQuery.query.parameters);
265
+ }
266
+
267
+ var tmpClient = getClient();
268
+ if (!tmpClient)
269
+ {
270
+ tmpResult.error = new Error('No Solr connection available.');
271
+ tmpResult.executed = true;
272
+ return fCallback();
273
+ }
274
+
275
+ var tmpOp = pQuery.query.parameters.solrOperation;
276
+
277
+ var tmpSearchQuery = tmpClient.query().q(tmpOp.query || '*:*');
278
+
279
+ if (tmpOp.filterQuery)
280
+ {
281
+ tmpSearchQuery.parameters.push('fq=' + encodeURIComponent(tmpOp.filterQuery));
282
+ }
283
+ if (tmpOp.fields)
284
+ {
285
+ tmpSearchQuery = tmpSearchQuery.fl(tmpOp.fields);
286
+ }
287
+ if (tmpOp.sort)
288
+ {
289
+ tmpSearchQuery.parameters.push('sort=' + encodeURIComponent(tmpOp.sort));
290
+ }
291
+ if (typeof tmpOp.rows !== 'undefined')
292
+ {
293
+ tmpSearchQuery = tmpSearchQuery.rows(tmpOp.rows);
294
+ }
295
+ if (tmpOp.start)
296
+ {
297
+ tmpSearchQuery = tmpSearchQuery.start(tmpOp.start);
298
+ }
299
+
300
+ tmpClient.search(tmpSearchQuery, function (pSearchError, pSearchResult)
301
+ {
302
+ if (pSearchError)
303
+ {
304
+ tmpResult.error = pSearchError;
305
+ tmpResult.value = false;
306
+ tmpResult.executed = true;
307
+ return fCallback();
308
+ }
309
+
310
+ tmpResult.error = null;
311
+ tmpResult.value = (pSearchResult && pSearchResult.response) ? pSearchResult.response.docs : [];
312
+ tmpResult.executed = true;
313
+ return fCallback();
314
+ });
315
+ };
316
+
317
+ var Update = function (pQuery, fCallback)
318
+ {
319
+ var tmpResult = pQuery.parameters.result;
320
+
321
+ pQuery.setDialect('Solr').buildUpdateQuery();
322
+
323
+ if (pQuery.logLevel > 0 ||
324
+ _GlobalLogLevel > 0)
325
+ {
326
+ _Fable.log.trace(pQuery.query.body, pQuery.query.parameters);
327
+ }
328
+
329
+ var tmpClient = getClient();
330
+ if (!tmpClient)
331
+ {
332
+ tmpResult.error = new Error('No Solr connection available.');
333
+ tmpResult.executed = true;
334
+ return fCallback();
335
+ }
336
+
337
+ var tmpOp = pQuery.query.parameters.solrOperation;
338
+ if (!tmpOp)
339
+ {
340
+ tmpResult.error = new Error('No Solr operation generated.');
341
+ tmpResult.executed = true;
342
+ return fCallback();
343
+ }
344
+
345
+ // First, search for matching documents to get their IDs
346
+ var tmpSearchQuery = tmpClient.query().q(tmpOp.filterQuery || '*:*').fl('id').rows(1000000);
347
+
348
+ tmpClient.search(tmpSearchQuery, function (pSearchError, pSearchResult)
349
+ {
350
+ if (pSearchError)
351
+ {
352
+ tmpResult.error = pSearchError;
353
+ tmpResult.value = false;
354
+ tmpResult.executed = true;
355
+ return fCallback();
356
+ }
357
+
358
+ var tmpDocs = (pSearchResult && pSearchResult.response) ? pSearchResult.response.docs : [];
359
+ if (tmpDocs.length === 0)
360
+ {
361
+ tmpResult.error = null;
362
+ tmpResult.value = 0;
363
+ tmpResult.executed = true;
364
+ return fCallback();
365
+ }
366
+
367
+ var tmpUpdate = replaceSentinels(JSON.parse(JSON.stringify(tmpOp.update)));
368
+
369
+ // Build atomic update documents for each matching doc
370
+ var tmpUpdateDocs = [];
371
+ for (var i = 0; i < tmpDocs.length; i++)
372
+ {
373
+ var tmpUpdateDoc = { id: tmpDocs[i].id };
374
+ for (var tmpField in tmpUpdate)
375
+ {
376
+ tmpUpdateDoc[tmpField] = tmpUpdate[tmpField];
377
+ }
378
+ tmpUpdateDocs.push(tmpUpdateDoc);
379
+ }
380
+
381
+ tmpClient.add(tmpUpdateDocs, function (pUpdateError)
382
+ {
383
+ if (pUpdateError)
384
+ {
385
+ tmpResult.error = pUpdateError;
386
+ tmpResult.value = false;
387
+ tmpResult.executed = true;
388
+ return fCallback();
389
+ }
390
+ tmpClient.commit(function (pCommitError)
391
+ {
392
+ if (pCommitError)
393
+ {
394
+ tmpResult.error = pCommitError;
395
+ tmpResult.value = false;
396
+ tmpResult.executed = true;
397
+ return fCallback();
398
+ }
399
+ tmpResult.error = null;
400
+ tmpResult.value = tmpUpdateDocs;
401
+ tmpResult.executed = true;
402
+ return fCallback();
403
+ });
404
+ });
405
+ });
406
+ };
407
+
408
+ var Delete = function (pQuery, fCallback)
409
+ {
410
+ var tmpResult = pQuery.parameters.result;
411
+
412
+ pQuery.setDialect('Solr').buildDeleteQuery();
413
+
414
+ if (pQuery.logLevel > 0 ||
415
+ _GlobalLogLevel > 0)
416
+ {
417
+ _Fable.log.trace(pQuery.query.body, pQuery.query.parameters);
418
+ }
419
+
420
+ var tmpClient = getClient();
421
+ if (!tmpClient)
422
+ {
423
+ tmpResult.error = new Error('No Solr connection available.');
424
+ tmpResult.executed = true;
425
+ return fCallback();
426
+ }
427
+
428
+ var tmpOp = pQuery.query.parameters.solrOperation;
429
+
430
+ if (tmpOp.operation === 'atomicUpdate')
431
+ {
432
+ // Soft delete - search for IDs then apply atomic update
433
+ var tmpSearchQuery = tmpClient.query().q(tmpOp.filterQuery || '*:*').fl('id').rows(1000000);
434
+
435
+ tmpClient.search(tmpSearchQuery, function (pSearchError, pSearchResult)
436
+ {
437
+ if (pSearchError)
438
+ {
439
+ tmpResult.error = pSearchError;
440
+ tmpResult.value = false;
441
+ tmpResult.executed = true;
442
+ return fCallback();
443
+ }
444
+
445
+ var tmpDocs = (pSearchResult && pSearchResult.response) ? pSearchResult.response.docs : [];
446
+ if (tmpDocs.length === 0)
447
+ {
448
+ tmpResult.error = null;
449
+ tmpResult.value = 0;
450
+ tmpResult.executed = true;
451
+ return fCallback();
452
+ }
453
+
454
+ var tmpUpdate = replaceSentinels(JSON.parse(JSON.stringify(tmpOp.update)));
455
+
456
+ var tmpUpdateDocs = [];
457
+ for (var i = 0; i < tmpDocs.length; i++)
458
+ {
459
+ var tmpUpdateDoc = { id: tmpDocs[i].id };
460
+ for (var tmpField in tmpUpdate)
461
+ {
462
+ tmpUpdateDoc[tmpField] = tmpUpdate[tmpField];
463
+ }
464
+ tmpUpdateDocs.push(tmpUpdateDoc);
465
+ }
466
+
467
+ tmpClient.add(tmpUpdateDocs, function (pUpdateError)
468
+ {
469
+ if (pUpdateError)
470
+ {
471
+ tmpResult.error = pUpdateError;
472
+ tmpResult.value = false;
473
+ tmpResult.executed = true;
474
+ return fCallback();
475
+ }
476
+ tmpClient.commit(function (pCommitError)
477
+ {
478
+ if (pCommitError)
479
+ {
480
+ tmpResult.error = pCommitError;
481
+ tmpResult.value = false;
482
+ tmpResult.executed = true;
483
+ return fCallback();
484
+ }
485
+ tmpResult.error = null;
486
+ tmpResult.value = tmpDocs.length;
487
+ tmpResult.executed = true;
488
+ return fCallback();
489
+ });
490
+ });
491
+ });
492
+ }
493
+ else
494
+ {
495
+ // Hard delete
496
+ // First count how many docs match, then delete
497
+ var tmpCountQuery = tmpClient.query().q(tmpOp.filterQuery || '*:*').rows(0);
498
+ tmpClient.search(tmpCountQuery, function (pCountError, pCountResult)
499
+ {
500
+ var tmpDeleteCount = 0;
501
+ if (!pCountError && pCountResult && pCountResult.response)
502
+ {
503
+ tmpDeleteCount = pCountResult.response.numFound || 0;
504
+ }
505
+
506
+ tmpClient.deleteByQuery(tmpOp.filterQuery || '*:*', function (pDeleteError)
507
+ {
508
+ if (pDeleteError)
509
+ {
510
+ tmpResult.error = pDeleteError;
511
+ tmpResult.value = false;
512
+ tmpResult.executed = true;
513
+ return fCallback();
514
+ }
515
+ tmpClient.commit(function (pCommitError)
516
+ {
517
+ if (pCommitError)
518
+ {
519
+ tmpResult.error = pCommitError;
520
+ tmpResult.value = false;
521
+ tmpResult.executed = true;
522
+ return fCallback();
523
+ }
524
+ tmpResult.error = null;
525
+ tmpResult.value = tmpDeleteCount;
526
+ tmpResult.executed = true;
527
+ return fCallback();
528
+ });
529
+ });
530
+ });
531
+ }
532
+ };
533
+
534
+ var Undelete = function (pQuery, fCallback)
535
+ {
536
+ var tmpResult = pQuery.parameters.result;
537
+
538
+ pQuery.setDialect('Solr').buildUndeleteQuery();
539
+
540
+ if (pQuery.logLevel > 0 ||
541
+ _GlobalLogLevel > 0)
542
+ {
543
+ _Fable.log.trace(pQuery.query.body, pQuery.query.parameters);
544
+ }
545
+
546
+ var tmpClient = getClient();
547
+ if (!tmpClient)
548
+ {
549
+ tmpResult.error = new Error('No Solr connection available.');
550
+ tmpResult.executed = true;
551
+ return fCallback();
552
+ }
553
+
554
+ var tmpOp = pQuery.query.parameters.solrOperation;
555
+ if (!tmpOp || tmpOp.operation === 'noop')
556
+ {
557
+ tmpResult.error = null;
558
+ tmpResult.value = 0;
559
+ tmpResult.executed = true;
560
+ return fCallback();
561
+ }
562
+
563
+ // Search for matching documents
564
+ var tmpSearchQuery = tmpClient.query().q(tmpOp.filterQuery || '*:*').fl('id').rows(1000000);
565
+
566
+ tmpClient.search(tmpSearchQuery, function (pSearchError, pSearchResult)
567
+ {
568
+ if (pSearchError)
569
+ {
570
+ tmpResult.error = pSearchError;
571
+ tmpResult.value = false;
572
+ tmpResult.executed = true;
573
+ return fCallback();
574
+ }
575
+
576
+ var tmpDocs = (pSearchResult && pSearchResult.response) ? pSearchResult.response.docs : [];
577
+ if (tmpDocs.length === 0)
578
+ {
579
+ tmpResult.error = null;
580
+ tmpResult.value = 0;
581
+ tmpResult.executed = true;
582
+ return fCallback();
583
+ }
584
+
585
+ var tmpUpdate = replaceSentinels(JSON.parse(JSON.stringify(tmpOp.update)));
586
+
587
+ var tmpUpdateDocs = [];
588
+ for (var i = 0; i < tmpDocs.length; i++)
589
+ {
590
+ var tmpUpdateDoc = { id: tmpDocs[i].id };
591
+ for (var tmpField in tmpUpdate)
592
+ {
593
+ tmpUpdateDoc[tmpField] = tmpUpdate[tmpField];
594
+ }
595
+ tmpUpdateDocs.push(tmpUpdateDoc);
596
+ }
597
+
598
+ tmpClient.add(tmpUpdateDocs, function (pUpdateError)
599
+ {
600
+ if (pUpdateError)
601
+ {
602
+ tmpResult.error = pUpdateError;
603
+ tmpResult.value = false;
604
+ tmpResult.executed = true;
605
+ return fCallback();
606
+ }
607
+ tmpClient.commit(function (pCommitError)
608
+ {
609
+ if (pCommitError)
610
+ {
611
+ tmpResult.error = pCommitError;
612
+ tmpResult.value = false;
613
+ tmpResult.executed = true;
614
+ return fCallback();
615
+ }
616
+ tmpResult.error = null;
617
+ tmpResult.value = tmpDocs.length;
618
+ tmpResult.executed = true;
619
+ return fCallback();
620
+ });
621
+ });
622
+ });
623
+ };
624
+
625
+ var Count = function (pQuery, fCallback)
626
+ {
627
+ var tmpResult = pQuery.parameters.result;
628
+
629
+ pQuery.setDialect('Solr').buildCountQuery();
630
+
631
+ if (pQuery.logLevel > 0 ||
632
+ _GlobalLogLevel > 0)
633
+ {
634
+ _Fable.log.trace(pQuery.query.body, pQuery.query.parameters);
635
+ }
636
+
637
+ var tmpClient = getClient();
638
+ if (!tmpClient)
639
+ {
640
+ tmpResult.error = new Error('No Solr connection available.');
641
+ tmpResult.executed = true;
642
+ return fCallback();
643
+ }
644
+
645
+ var tmpOp = pQuery.query.parameters.solrOperation;
646
+
647
+ var tmpSearchQuery = tmpClient.query().q(tmpOp.query || '*:*').rows(0);
648
+
649
+ if (tmpOp.filterQuery)
650
+ {
651
+ tmpSearchQuery.parameters.push('fq=' + encodeURIComponent(tmpOp.filterQuery));
652
+ }
653
+
654
+ if (tmpOp.distinct && tmpOp.distinctFields && tmpOp.distinctFields.length > 0)
655
+ {
656
+ // For distinct count, we need to use faceting or grouping
657
+ // Use JSON facet API for distinct count
658
+ var tmpFacetFields = {};
659
+ for (var i = 0; i < tmpOp.distinctFields.length; i++)
660
+ {
661
+ tmpFacetFields[tmpOp.distinctFields[i]] = '$' + tmpOp.distinctFields[i];
662
+ }
663
+
664
+ // Use group.field for distinct counting
665
+ tmpSearchQuery = tmpSearchQuery.groupBy(tmpOp.distinctFields[0]).groupNGroups(true);
666
+ }
667
+
668
+ tmpClient.search(tmpSearchQuery, function (pSearchError, pSearchResult)
669
+ {
670
+ if (pSearchError)
671
+ {
672
+ tmpResult.error = pSearchError;
673
+ tmpResult.value = false;
674
+ tmpResult.executed = true;
675
+ return fCallback();
676
+ }
677
+
678
+ tmpResult.error = null;
679
+
680
+ if (tmpOp.distinct && pSearchResult && pSearchResult.grouped)
681
+ {
682
+ // Get ngroups from grouped response
683
+ var tmpGroupKeys = Object.keys(pSearchResult.grouped);
684
+ if (tmpGroupKeys.length > 0)
685
+ {
686
+ tmpResult.value = pSearchResult.grouped[tmpGroupKeys[0]].ngroups || 0;
687
+ }
688
+ else
689
+ {
690
+ tmpResult.value = 0;
691
+ }
692
+ }
693
+ else
694
+ {
695
+ tmpResult.value = (pSearchResult && pSearchResult.response) ? pSearchResult.response.numFound : 0;
696
+ }
697
+ tmpResult.executed = true;
698
+ return fCallback();
699
+ });
700
+ };
701
+
702
+ var tmpNewProvider = (
703
+ {
704
+ marshalRecordFromSourceToObject: marshalRecordFromSourceToObject,
705
+
706
+ Create: Create,
707
+ Read: Read,
708
+ Update: Update,
709
+ Delete: Delete,
710
+ Undelete: Undelete,
711
+ Count: Count,
712
+
713
+ getProvider: getProvider,
714
+ providerCreatesSupported: true,
715
+
716
+ new: createNew
717
+ });
718
+
719
+
720
+ return tmpNewProvider;
721
+ }
722
+
723
+ return createNew();
724
+ };
725
+
726
+ module.exports = new MeadowProvider();