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.
- package/README.md +110 -141
- package/docs/README.md +34 -230
- package/docs/_cover.md +14 -0
- package/docs/_sidebar.md +44 -12
- package/docs/_topbar.md +5 -0
- package/docs/api/doCount.md +109 -0
- package/docs/api/doCreate.md +132 -0
- package/docs/api/doDelete.md +101 -0
- package/docs/api/doRead.md +122 -0
- package/docs/api/doReads.md +136 -0
- package/docs/api/doUndelete.md +98 -0
- package/docs/api/doUpdate.md +129 -0
- package/docs/api/getRoleName.md +84 -0
- package/docs/api/loadFromPackage.md +153 -0
- package/docs/api/marshalRecordFromSourceToObject.md +92 -0
- package/docs/api/query.md +133 -0
- package/docs/api/rawQueries.md +197 -0
- package/docs/api/reference.md +117 -0
- package/docs/api/setAuthorizer.md +103 -0
- package/docs/api/setDefault.md +90 -0
- package/docs/api/setDefaultIdentifier.md +84 -0
- package/docs/api/setDomain.md +56 -0
- package/docs/api/setIDUser.md +91 -0
- package/docs/api/setJsonSchema.md +92 -0
- package/docs/api/setProvider.md +87 -0
- package/docs/api/setSchema.md +107 -0
- package/docs/api/setScope.md +68 -0
- package/docs/api/validateObject.md +119 -0
- package/docs/architecture.md +316 -0
- package/docs/audit-tracking.md +226 -0
- package/docs/configuration.md +317 -0
- package/docs/providers/meadow-endpoints.md +306 -0
- package/docs/providers/mongodb.md +319 -0
- package/docs/providers/postgresql.md +312 -0
- package/docs/providers/rocksdb.md +297 -0
- package/docs/query-dsl.md +269 -0
- package/docs/quick-start.md +384 -0
- package/docs/raw-queries.md +193 -0
- package/docs/retold-catalog.json +61 -1
- package/docs/retold-keyword-index.json +15860 -4839
- package/docs/soft-deletes.md +224 -0
- package/package.json +44 -13
- package/scripts/bookstore-seed-postgresql.sql +135 -0
- package/scripts/dgraph-test-db.sh +144 -0
- package/scripts/meadow-test-cleanup.sh +5 -1
- package/scripts/mongodb-test-db.sh +98 -0
- package/scripts/postgresql-test-db.sh +124 -0
- package/scripts/solr-test-db.sh +135 -0
- package/source/Meadow.js +5 -0
- package/source/providers/Meadow-Provider-DGraph.js +679 -0
- package/source/providers/Meadow-Provider-MeadowEndpoints.js +1 -1
- package/source/providers/Meadow-Provider-MongoDB.js +527 -0
- package/source/providers/Meadow-Provider-PostgreSQL.js +361 -0
- package/source/providers/Meadow-Provider-RocksDB.js +1300 -0
- package/source/providers/Meadow-Provider-Solr.js +726 -0
- package/test/Meadow-Provider-DGraph_tests.js +741 -0
- package/test/Meadow-Provider-MongoDB_tests.js +661 -0
- package/test/Meadow-Provider-PostgreSQL_tests.js +787 -0
- package/test/Meadow-Provider-RocksDB_tests.js +887 -0
- package/test/Meadow-Provider-SQLiteBrowser-Headless_tests.js +657 -0
- package/test/Meadow-Provider-SQLiteBrowser_tests.js +895 -0
- 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();
|