@storecraft/database-mongodb 1.0.20 → 1.2.5
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 +7 -7
- package/index.js +22 -39
- package/migrate.js +0 -1
- package/migrations/00005_add_chats_collection.js +57 -0
- package/package.json +1 -2
- package/src/con.auth_users.js +3 -15
- package/src/con.chats.js +119 -0
- package/src/con.collections.js +2 -11
- package/src/con.customers.js +5 -14
- package/src/con.discounts.js +6 -11
- package/src/con.discounts.utils.js +2 -3
- package/src/con.images.js +9 -17
- package/src/con.notifications.js +4 -11
- package/src/con.orders.js +4 -7
- package/src/con.posts.js +0 -15
- package/src/con.products.js +1 -9
- package/src/con.search.js +28 -14
- package/src/con.shared.js +23 -45
- package/src/con.shipping.js +0 -7
- package/src/con.storefronts.js +6 -9
- package/src/con.tags.js +7 -11
- package/src/con.templates.js +4 -8
- package/src/utils.funcs.js +5 -6
- package/src/utils.query.js +165 -140
- package/src/utils.query_OLD.js +189 -0
- package/src/utils.relations.js +43 -76
- package/tests/runner.test.js +9 -5
- package/tests/sandbox.js +5 -5
- package/vector-store/index.js +44 -10
- package/tests/query.cursor.test.js +0 -389
- package/tests/query.vql.test.js +0 -71
package/src/utils.relations.js
CHANGED
@@ -1,25 +1,19 @@
|
|
1
1
|
/**
|
2
|
-
* @import { ID } from '@storecraft/core/database'
|
3
2
|
* @import { BaseType } from '@storecraft/core/api'
|
4
3
|
* @import { Relation, WithRelations } from './utils.types.js'
|
5
|
-
* @import { Filter } from 'mongodb'
|
4
|
+
* @import { Collection, Filter } from 'mongodb'
|
6
5
|
*/
|
7
|
-
|
8
6
|
import { ClientSession, ObjectId } from 'mongodb';
|
9
7
|
import { isDef, isUndef, to_objid } from './utils.funcs.js';
|
10
8
|
import { MongoDB } from '../index.js';
|
11
9
|
import { zeroed_relations } from './con.shared.js';
|
12
10
|
|
13
11
|
/**
|
14
|
-
*
|
15
|
-
*
|
16
|
-
*
|
17
|
-
*
|
18
|
-
*
|
19
|
-
*
|
12
|
+
* @description On upsert Create a relation on a given
|
13
|
+
* field that represents a relation. for example, each
|
14
|
+
* product specifies collections it belongs to. Basically
|
15
|
+
* creates an ids array and embedded documents for fast retrival.
|
20
16
|
* @template {BaseType} T
|
21
|
-
*
|
22
|
-
*
|
23
17
|
* @param {MongoDB} driver our driver
|
24
18
|
* @param {T} data data to create the connection from
|
25
19
|
* @param {string} fieldName the field name, that represents
|
@@ -27,8 +21,6 @@ import { zeroed_relations } from './con.shared.js';
|
|
27
21
|
* @param {string} belongsToCollection which collection
|
28
22
|
* does the field relate to
|
29
23
|
* @param {boolean} [reload=false] re-retrive documents ?
|
30
|
-
*
|
31
|
-
*
|
32
24
|
* @returns {Promise<WithRelations<T>>}
|
33
25
|
*/
|
34
26
|
export const create_explicit_relation = async (
|
@@ -50,11 +42,16 @@ export const create_explicit_relation = async (
|
|
50
42
|
/** @type {Relation<any>} */
|
51
43
|
const relation = data_with_rel._relations[fieldName] = {};
|
52
44
|
|
53
|
-
relation.ids = items
|
45
|
+
relation.ids = items
|
46
|
+
.filter(i => isDef(i?.id))
|
47
|
+
.map(c => to_objid(c.id));
|
48
|
+
|
54
49
|
relation.entries = {};
|
55
50
|
|
56
51
|
if(reload) {
|
57
|
-
const entries = await driver
|
52
|
+
const entries = await driver
|
53
|
+
.collection(belongsToCollection)
|
54
|
+
.find(
|
58
55
|
{
|
59
56
|
_id: {
|
60
57
|
$in : relation.ids
|
@@ -76,7 +73,9 @@ export const create_explicit_relation = async (
|
|
76
73
|
|
77
74
|
} else {
|
78
75
|
relation.entries = Object.fromEntries(
|
79
|
-
items.map(
|
76
|
+
items.map(
|
77
|
+
it => [it.id.split('_').at(-1), it]
|
78
|
+
)
|
80
79
|
);
|
81
80
|
}
|
82
81
|
|
@@ -87,17 +86,14 @@ export const create_explicit_relation = async (
|
|
87
86
|
}
|
88
87
|
|
89
88
|
/**
|
90
|
-
* Create a `search` relation on the document (embed it)
|
91
|
-
*
|
89
|
+
* @description Create a `search` relation on the document (embed it)
|
92
90
|
* @template {Object.<string, any>} T
|
93
|
-
*
|
94
91
|
* @param {WithRelations<T>} data
|
95
92
|
* @param {string[]} terms
|
96
|
-
*
|
97
93
|
*/
|
98
94
|
export const add_search_terms_relation_on = (data, terms=[]) => {
|
99
95
|
if(!data)
|
100
|
-
return;
|
96
|
+
return undefined;
|
101
97
|
|
102
98
|
if(!Array.isArray(terms))
|
103
99
|
throw new Error('terms is not an array !');
|
@@ -112,12 +108,9 @@ export const add_search_terms_relation_on = (data, terms=[]) => {
|
|
112
108
|
|
113
109
|
|
114
110
|
/**
|
115
|
-
*
|
116
|
-
* Update an `entry` on all of it's connections in the `relation`.
|
111
|
+
* @description Update an `entry` on all of it's connections in the `relation`.
|
117
112
|
* Suppose, we have a many-to-x relation, then we update `x` on
|
118
113
|
* all of these many connections at once.
|
119
|
-
*
|
120
|
-
*
|
121
114
|
* @param {MongoDB} driver mongodb driver instance
|
122
115
|
* @param {string} collection the collection from which the `relation` is from
|
123
116
|
* @param {string} relation_name the `relation` name
|
@@ -126,7 +119,6 @@ export const add_search_terms_relation_on = (data, terms=[]) => {
|
|
126
119
|
* @param {ClientSession} [session] client `session` for atomicity purposes
|
127
120
|
* @param {string[]} [search_terms_to_add=[]] Extra `search` terms to add
|
128
121
|
* to all the affected connections
|
129
|
-
*
|
130
122
|
*/
|
131
123
|
export const update_entry_on_all_connection_of_relation = (
|
132
124
|
driver, collection, relation_name, entry_objid, entry, session,
|
@@ -155,23 +147,20 @@ export const update_entry_on_all_connection_of_relation = (
|
|
155
147
|
|
156
148
|
|
157
149
|
/**
|
158
|
-
*
|
159
|
-
*
|
160
|
-
* found by `mongodb` filter in the `relation`.
|
150
|
+
* @description Update / Create an `entry` on a specific connection,
|
151
|
+
* that is found by `mongodb` filter in the `relation`.
|
161
152
|
* Suppose, we have a many-to-x relation, then we create a new
|
162
153
|
* connection a-to-x
|
163
|
-
*
|
164
|
-
*
|
165
154
|
* @param {MongoDB} driver mongodb driver instance
|
166
155
|
* @param {string} collection the collection from which the `relation` is from
|
167
156
|
* @param {string} relation_name the `relation` name
|
168
|
-
* @param {import('mongodb').Filter<any>} from_object_filter
|
157
|
+
* @param {import('mongodb').Filter<any>} from_object_filter
|
158
|
+
* the proper `ObjectId` of the from connection
|
169
159
|
* @param {ObjectId} entry_objid the proper `ObjectId` of the entry
|
170
160
|
* @param {object} entry the entry data
|
171
161
|
* @param {ClientSession} [session] client `session` for atomicity purposes
|
172
162
|
* @param {string[]} [search_terms_to_add=[]] Extra `search` terms to add
|
173
163
|
* to the affected connection
|
174
|
-
*
|
175
164
|
*/
|
176
165
|
export const update_specific_connection_of_relation_with_filter = (
|
177
166
|
driver, collection, relation_name, from_object_filter,
|
@@ -198,12 +187,9 @@ export const update_specific_connection_of_relation_with_filter = (
|
|
198
187
|
}
|
199
188
|
|
200
189
|
/**
|
201
|
-
*
|
202
|
-
*
|
203
|
-
*
|
204
|
-
* connection a-to-x
|
205
|
-
*
|
206
|
-
*
|
190
|
+
* @description Update / Create an `entry` on a specific connection
|
191
|
+
* in the `relation`. Suppose, we have a many-to-x relation, then we
|
192
|
+
* create a new connection a-to-x
|
207
193
|
* @param {MongoDB} driver mongodb driver instance
|
208
194
|
* @param {string} collection the collection from which the `relation` is from
|
209
195
|
* @param {string} relation_name the `relation` name
|
@@ -213,7 +199,6 @@ export const update_specific_connection_of_relation_with_filter = (
|
|
213
199
|
* @param {ClientSession} [session] client `session` for atomicity purposes
|
214
200
|
* @param {string[]} [search_terms_to_add=[]] Extra `search` terms to add
|
215
201
|
* to the affected connection
|
216
|
-
*
|
217
202
|
*/
|
218
203
|
export const update_specific_connection_of_relation = (
|
219
204
|
driver, collection, relation_name, from_objid, entry_objid,
|
@@ -233,12 +218,9 @@ export const update_specific_connection_of_relation = (
|
|
233
218
|
|
234
219
|
|
235
220
|
/**
|
236
|
-
*
|
237
|
-
*
|
238
|
-
*
|
239
|
-
* all these many connections at once.
|
240
|
-
*
|
241
|
-
*
|
221
|
+
* @description Remove an `entry` from all of it's connections
|
222
|
+
* in the `relation`. Suppose, we have a many-to-x relation, then
|
223
|
+
* we remove `x` from all these many connections at once.
|
242
224
|
* @param {MongoDB} driver mongodb driver instance
|
243
225
|
* @param {string} collection the collection from which the `relation` is from
|
244
226
|
* @param {string} relation_name the `relation` name
|
@@ -247,13 +229,14 @@ export const update_specific_connection_of_relation = (
|
|
247
229
|
* @param {string[]} [search_terms_to_remove=[]] Extra `search` terms to remove
|
248
230
|
* @param {string[]} [tags_to_remove=[]] Extra `tags` terms to remove
|
249
231
|
* from all the connections
|
250
|
-
*
|
251
232
|
*/
|
252
233
|
export const remove_entry_from_all_connection_of_relation = (
|
253
234
|
driver, collection, relation_name, entry_objid, session,
|
254
235
|
search_terms_to_remove=[], tags_to_remove=[]
|
255
236
|
) => {
|
256
|
-
return driver
|
237
|
+
return /** @type {Collection<{}>} */(driver
|
238
|
+
.collection(collection))
|
239
|
+
.updateMany(
|
257
240
|
{
|
258
241
|
[`_relations.${relation_name}.ids`] : entry_objid
|
259
242
|
},
|
@@ -276,12 +259,9 @@ export const remove_entry_from_all_connection_of_relation = (
|
|
276
259
|
|
277
260
|
|
278
261
|
/**
|
279
|
-
*
|
280
|
-
*
|
281
|
-
*
|
282
|
-
* `a` connection.
|
283
|
-
*
|
284
|
-
*
|
262
|
+
* @description Remove an `entry` from a specific connection in
|
263
|
+
* the `relation`. Suppose, we have a a-to-x relation, then we remove
|
264
|
+
* `x` from `a` connection.
|
285
265
|
* @param {MongoDB} driver mongodb driver instance
|
286
266
|
* @param {string} collection the collection from which the `relation` is from
|
287
267
|
* @param {string} relation_name the `relation` name
|
@@ -290,7 +270,6 @@ export const remove_entry_from_all_connection_of_relation = (
|
|
290
270
|
* @param {ClientSession} [session] client `session` for atomicity purposes
|
291
271
|
* @param {string[]} [search_terms_to_remove=[]] Extra `search` terms to remove
|
292
272
|
* from the affected connection
|
293
|
-
*
|
294
273
|
*/
|
295
274
|
export const remove_specific_connection_of_relation = (
|
296
275
|
driver, collection, relation_name, from_objid, entry_objid, session,
|
@@ -309,31 +288,27 @@ export const remove_specific_connection_of_relation = (
|
|
309
288
|
|
310
289
|
|
311
290
|
/**
|
312
|
-
*
|
313
|
-
* Remove an `entry` from a specific connection in the `relation`.
|
291
|
+
* @description Remove an `entry` from a specific connection in the `relation`.
|
314
292
|
* Suppose, we have a a-to-x relation, then we remove `x` from
|
315
|
-
* `a` connection.
|
316
|
-
*
|
317
|
-
* We locate `a` by a `mongodb` filter
|
318
|
-
*
|
319
|
-
*
|
293
|
+
* `a` connection. We locate `a` by a `mongodb` filter
|
320
294
|
* @param {MongoDB} driver mongodb driver instance
|
321
295
|
* @param {string} collection the collection from which the `relation` is from
|
322
296
|
* @param {string} relation_name the `relation` name
|
323
|
-
* @param {Filter<
|
297
|
+
* @param {Filter<{}>} from_object_filter
|
324
298
|
* `mongodb` Filter to locate the first document, the from part of the connection
|
325
299
|
* @param {ObjectId} entry_objid the proper `ObjectId` of the entry
|
326
300
|
* @param {ClientSession} [session] client `session` for atomicity purposes
|
327
301
|
* @param {string[]} [search_terms_to_remove=[]] Extra `search` terms to remove
|
328
302
|
* from the affected connection
|
329
|
-
*
|
330
303
|
*/
|
331
304
|
export const remove_specific_connection_of_relation_with_filter = (
|
332
305
|
driver, collection, relation_name, from_object_filter, entry_objid, session,
|
333
306
|
search_terms_to_remove
|
334
307
|
) => {
|
335
308
|
|
336
|
-
return driver
|
309
|
+
return /** @type {Collection<{}>} */(driver
|
310
|
+
.collection(collection))
|
311
|
+
.updateOne(
|
337
312
|
from_object_filter,
|
338
313
|
{
|
339
314
|
$pull: {
|
@@ -353,16 +328,12 @@ export const remove_specific_connection_of_relation_with_filter = (
|
|
353
328
|
|
354
329
|
|
355
330
|
/**
|
356
|
-
*
|
357
|
-
* A simple `save` (using **Mongo** `replaceOne`)
|
358
|
-
*
|
359
|
-
*
|
331
|
+
* @description A simple `save` (using **Mongo** `replaceOne`)
|
360
332
|
* @param {MongoDB} driver `mongodb` driver
|
361
333
|
* @param {string} collection the `collection` to save into
|
362
334
|
* @param {ObjectId} object_id the `object id` of the item
|
363
335
|
* @param {object} document the document data
|
364
336
|
* @param {ClientSession} [session] client `session` for atomicity purposes
|
365
|
-
*
|
366
337
|
*/
|
367
338
|
export const save_me = (driver, collection, object_id, document, session) => {
|
368
339
|
return driver.collection(collection).replaceOne(
|
@@ -375,20 +346,16 @@ export const save_me = (driver, collection, object_id, document, session) => {
|
|
375
346
|
upsert: true
|
376
347
|
}
|
377
348
|
);
|
378
|
-
|
379
349
|
}
|
380
350
|
|
381
351
|
|
382
352
|
/**
|
383
|
-
*
|
384
|
-
*
|
385
|
-
*
|
386
|
-
*
|
353
|
+
* @description A simple `delete` (using **Mongo** `deleteOne`)
|
354
|
+
* with `session` transaction
|
387
355
|
* @param {MongoDB} driver `mongodb` driver
|
388
356
|
* @param {string} collection the `collection` to save into
|
389
357
|
* @param {ObjectId} object_id the `object id` of the item
|
390
358
|
* @param {ClientSession} [session] client `session` for atomicity purposes
|
391
|
-
*
|
392
359
|
*/
|
393
360
|
export const delete_me = (driver, collection, object_id, session) => {
|
394
361
|
return driver.collection(collection).deleteOne(
|
package/tests/runner.test.js
CHANGED
@@ -4,7 +4,11 @@ import { MongoDB, migrateToLatest } from '@storecraft/database-mongodb';
|
|
4
4
|
import { NodePlatform } from '@storecraft/core/platform/node';
|
5
5
|
import { api } from '@storecraft/core/test-runner';
|
6
6
|
|
7
|
-
|
7
|
+
//
|
8
|
+
// Main MongoDB test suite with the core test-runner for api layer
|
9
|
+
//
|
10
|
+
|
11
|
+
export const create_app = () => {
|
8
12
|
const app = new App(
|
9
13
|
{
|
10
14
|
auth_admins_emails: ['admin@sc.com'],
|
@@ -24,13 +28,13 @@ export const create_app = async () => {
|
|
24
28
|
)
|
25
29
|
)
|
26
30
|
|
27
|
-
return app.init()
|
31
|
+
return app.init().__show_me_everything.app;
|
28
32
|
}
|
29
33
|
|
30
34
|
async function test() {
|
31
|
-
const app =
|
35
|
+
const app = create_app();
|
32
36
|
|
33
|
-
await migrateToLatest(app.db, false);
|
37
|
+
await migrateToLatest(app.__show_me_everything.db, false);
|
34
38
|
|
35
39
|
Object.entries(api).slice(0, -1).forEach(
|
36
40
|
([name, runner]) => {
|
@@ -39,7 +43,7 @@ async function test() {
|
|
39
43
|
);
|
40
44
|
|
41
45
|
const last_test = Object.values(api).at(-1).create(app);
|
42
|
-
last_test.after(async ()=>{app.db.disconnect()});
|
46
|
+
last_test.after(async ()=>{app.__show_me_everything.db.disconnect()});
|
43
47
|
last_test.run();
|
44
48
|
}
|
45
49
|
|
package/tests/sandbox.js
CHANGED
@@ -16,21 +16,21 @@ export const create_app = async () => {
|
|
16
16
|
)
|
17
17
|
.withPlatform(new NodePlatform())
|
18
18
|
.withDatabase(new MongoDB({ db_name: 'test'}))
|
19
|
+
.init();
|
19
20
|
|
20
|
-
await app.
|
21
|
-
await migrateToLatest(app.db, false);
|
21
|
+
await migrateToLatest(app.__show_me_everything.db, false);
|
22
22
|
return app;
|
23
23
|
}
|
24
24
|
|
25
25
|
|
26
26
|
async function test() {
|
27
27
|
const app = await create_app();
|
28
|
-
|
29
|
-
const sf = await
|
28
|
+
const db = app.__show_me_everything.db;
|
29
|
+
const sf = await db.resources.storefronts.get_default_auto_generated_storefront()
|
30
30
|
|
31
31
|
console.log(JSON.stringify(sf, null, 2));
|
32
32
|
|
33
|
-
await
|
33
|
+
await db.disconnect();
|
34
34
|
}
|
35
35
|
|
36
36
|
test();
|
package/vector-store/index.js
CHANGED
@@ -15,7 +15,7 @@ mongo_vectorSearch_pipeline,
|
|
15
15
|
* } from 'mongodb'
|
16
16
|
* @import { ENV } from '@storecraft/core';
|
17
17
|
*/
|
18
|
-
|
18
|
+
import { truncate_or_pad_vector } from '@storecraft/core/ai/models/vector-stores/index.js';
|
19
19
|
import { Collection } from 'mongodb';
|
20
20
|
import { MongoClient, ServerApiVersion } from 'mongodb';
|
21
21
|
|
@@ -30,13 +30,12 @@ export const DEFAULT_INDEX_NAME = 'vector_store';
|
|
30
30
|
/**
|
31
31
|
* @description MongoDB Atlas Vector Store
|
32
32
|
* {@link https://www.mongodb.com/docs/atlas/atlas-vector-search/vector-search-type/#:~:text=You%20can%20use%20the%20vectorSearch,to%20pre%2Dfilter%20your%20data.}
|
33
|
-
*
|
34
33
|
* @implements {VectorStore}
|
35
34
|
*/
|
36
35
|
export class MongoVectorStore {
|
37
36
|
|
38
37
|
/** @satisfies {ENV<Config>} */
|
39
|
-
static EnvConfig = /** @type{const} */ ({
|
38
|
+
static EnvConfig = /** @type {const} */ ({
|
40
39
|
db_name: 'MONGODB_VECTOR_STORE_DB_NAME',
|
41
40
|
url: 'MONGODB_VECTOR_STORE_URL'
|
42
41
|
});
|
@@ -95,10 +94,10 @@ export class MongoVectorStore {
|
|
95
94
|
|
96
95
|
/** @type {VectorStore["onInit"]} */
|
97
96
|
onInit = (app) => {
|
98
|
-
this.config.url ??= app.
|
99
|
-
?? app.
|
100
|
-
this.config.db_name ??= app.
|
101
|
-
?? app.
|
97
|
+
this.config.url ??= app.env[MongoVectorStore.EnvConfig.url]
|
98
|
+
?? app.env['MONGODB_URL'];
|
99
|
+
this.config.db_name ??= app.env[MongoVectorStore.EnvConfig.db_name]
|
100
|
+
?? app.env['MONGODB_DB_NAME'] ?? 'main';
|
102
101
|
}
|
103
102
|
|
104
103
|
/** @type {VectorStore["embedder"]} */
|
@@ -118,7 +117,9 @@ export class MongoVectorStore {
|
|
118
117
|
(doc, ix) => (
|
119
118
|
{
|
120
119
|
updated_at: new Date().toISOString(),
|
121
|
-
embedding:
|
120
|
+
embedding: truncate_or_pad_vector(
|
121
|
+
vectors[ix], this.config.dimensions
|
122
|
+
),
|
122
123
|
metadata: doc.metadata,
|
123
124
|
pageContent: doc.pageContent,
|
124
125
|
[NAMESPACE_KEY]: doc.namespace,
|
@@ -165,6 +166,13 @@ export class MongoVectorStore {
|
|
165
166
|
}
|
166
167
|
);
|
167
168
|
|
169
|
+
if(!result) {
|
170
|
+
console.warn(
|
171
|
+
'MongoVectoreStore::upsertDocuments() - no result from embedder'
|
172
|
+
);
|
173
|
+
return;
|
174
|
+
}
|
175
|
+
|
168
176
|
const vectors = result.content;
|
169
177
|
|
170
178
|
return this.upsertVectors(
|
@@ -248,15 +256,41 @@ export class MongoVectorStore {
|
|
248
256
|
* @param {boolean} [delete_index_if_exists_before=false]
|
249
257
|
* @returns {Promise<boolean>}
|
250
258
|
*/
|
251
|
-
createVectorIndex = async (
|
259
|
+
createVectorIndex = async (
|
260
|
+
disconnect_after_finish=true,
|
261
|
+
delete_index_if_exists_before=false
|
262
|
+
) => {
|
252
263
|
if(delete_index_if_exists_before) {
|
253
264
|
await this.deleteVectorIndex();
|
254
265
|
}
|
255
|
-
|
266
|
+
|
256
267
|
const db = this.client.db(this.config.db_name);
|
257
268
|
const collection_name = this.config.index_name;
|
269
|
+
|
270
|
+
{ // skip if index already exists
|
271
|
+
const indices = await db
|
272
|
+
.collection(collection_name)
|
273
|
+
.listSearchIndexes()
|
274
|
+
.toArray();
|
275
|
+
|
276
|
+
if(indices?.length) {
|
277
|
+
const index = indices.find(
|
278
|
+
(index) => index.name === this.config.index_name
|
279
|
+
);
|
280
|
+
if(index) {
|
281
|
+
console.log('MongoVectorStore::createVectorIndex - index already exists, skipping');
|
282
|
+
if(disconnect_after_finish)
|
283
|
+
await this.client.close();
|
284
|
+
|
285
|
+
return true;
|
286
|
+
}
|
287
|
+
}
|
288
|
+
}
|
289
|
+
|
258
290
|
// collection name will have the same name as the index
|
259
291
|
await db.createCollection(collection_name);
|
292
|
+
|
293
|
+
|
260
294
|
const index_result = await db.collection(collection_name).createSearchIndex(
|
261
295
|
{
|
262
296
|
name: this.config.index_name,
|