@storecraft/database-mongodb 1.0.14 → 1.0.16
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 +27 -62
- package/{vector-search-extension.js → example-extension/vector-search-extension.js} +4 -1
- package/index.js +5 -0
- package/package.json +2 -1
- package/src/con.auth_users.js +25 -19
- package/src/con.collections.js +70 -6
- package/src/con.customers.js +37 -10
- package/src/con.discounts.js +103 -11
- package/src/con.posts.js +1 -1
- package/src/con.products.js +61 -20
- package/src/con.search.js +4 -4
- package/src/con.shared.js +86 -46
- package/src/con.shipping.js +1 -3
- package/src/con.storefronts.js +113 -94
- package/src/utils.funcs.js +31 -45
- package/src/utils.relations.js +5 -17
- package/src/utils.types.d.ts +12 -0
- package/tests/docker-compose.yml +23 -0
- package/tests/runner.test.js +7 -3
- package/tests/{sandbox.test.js → sandbox.js} +10 -29
- package/vector-store/index.js +22 -8
package/src/utils.funcs.js
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
/**
|
2
|
+
* @import { WithRelations } from './utils.types.js';
|
3
|
+
* @import { Filter } from 'mongodb';
|
4
|
+
*/
|
1
5
|
import { ObjectId } from 'mongodb';
|
2
6
|
|
3
7
|
/** @param {any} v */
|
@@ -14,11 +18,7 @@ export const delete_keys = (...keys) => {
|
|
14
18
|
|
15
19
|
/**
|
16
20
|
* @template T
|
17
|
-
*
|
18
|
-
*
|
19
21
|
* @param {T} o
|
20
|
-
*
|
21
|
-
*
|
22
22
|
* @returns {T}
|
23
23
|
*/
|
24
24
|
return (o) => {
|
@@ -30,16 +30,9 @@ export const delete_keys = (...keys) => {
|
|
30
30
|
|
31
31
|
/**
|
32
32
|
* Sanitize hidden properties in-place
|
33
|
-
*
|
34
|
-
*
|
35
33
|
* @template {object} T
|
36
|
-
*
|
37
|
-
*
|
38
34
|
* @param {T} o
|
39
|
-
*
|
40
|
-
*
|
41
35
|
* @return {Omit<T, '_id' | '_relations'>}
|
42
|
-
*
|
43
36
|
*/
|
44
37
|
export const sanitize_hidden = o => {
|
45
38
|
if(!isDef(o))
|
@@ -53,27 +46,26 @@ export const sanitize_hidden = o => {
|
|
53
46
|
}
|
54
47
|
|
55
48
|
/**
|
56
|
-
*
|
57
|
-
* @template T
|
58
|
-
*
|
59
|
-
*
|
49
|
+
* Sanitize hidden properties in-place recursively
|
50
|
+
* @template {object} T
|
60
51
|
* @param {T} o
|
61
|
-
*
|
62
|
-
*
|
63
|
-
* @returns {T}
|
52
|
+
* @return {Omit<T, '_id' | '_relations'>}
|
64
53
|
*/
|
65
|
-
export const
|
66
|
-
|
54
|
+
export const sanitize_recursively = o => {
|
55
|
+
for(const k of Object.keys(o)) {
|
56
|
+
if(k.startsWith('_')) {
|
57
|
+
delete o[k];
|
58
|
+
} else if(typeof o[k] === 'object') {
|
59
|
+
sanitize_recursively(o[k]);
|
60
|
+
}
|
61
|
+
}
|
62
|
+
return o;
|
67
63
|
}
|
68
64
|
|
69
65
|
/**
|
70
|
-
*
|
71
66
|
* Sanitize the mongo document before sending to client
|
72
|
-
*
|
73
67
|
* @template T
|
74
|
-
*
|
75
|
-
*
|
76
|
-
* @param {T} o
|
68
|
+
* @param {WithRelations<T>} o
|
77
69
|
*/
|
78
70
|
export const sanitize_one = o => {
|
79
71
|
return sanitize_hidden(o)
|
@@ -81,30 +73,31 @@ export const sanitize_one = o => {
|
|
81
73
|
|
82
74
|
/**
|
83
75
|
* Sanitize the mongo document before sending to client
|
84
|
-
*
|
85
76
|
* @template T
|
86
|
-
*
|
87
|
-
*
|
88
|
-
* @param {T[]} o
|
89
|
-
*
|
77
|
+
* @param {WithRelations<T>[]} o
|
90
78
|
*/
|
91
79
|
export const sanitize_array = o => {
|
92
80
|
return o?.map(sanitize_hidden);
|
93
81
|
}
|
94
82
|
|
95
83
|
/**
|
96
|
-
*
|
84
|
+
* @template T
|
85
|
+
* @param {T} o
|
86
|
+
* @returns {T}
|
87
|
+
*/
|
88
|
+
export const delete_id = o => {
|
89
|
+
return delete_keys('_id')(o)
|
90
|
+
}
|
91
|
+
|
92
|
+
/**
|
97
93
|
* @param {string} id
|
98
|
-
*
|
99
94
|
*/
|
100
95
|
export const to_objid = id => {
|
101
96
|
return new ObjectId(id.split('_').at(-1))
|
102
97
|
}
|
103
98
|
|
104
99
|
/**
|
105
|
-
*
|
106
100
|
* @param {string} id
|
107
|
-
*
|
108
101
|
*/
|
109
102
|
export const to_objid_safe = id => {
|
110
103
|
try {
|
@@ -116,27 +109,20 @@ export const to_objid_safe = id => {
|
|
116
109
|
}
|
117
110
|
|
118
111
|
/**
|
112
|
+
* Create a `filter` for `object-id` or `handle`
|
119
113
|
* @template {{handle?: string}} G
|
120
|
-
*
|
121
|
-
*
|
122
114
|
* @param {string} handle_or_id
|
123
|
-
*
|
124
|
-
*
|
125
|
-
* @returns {import('mongodb').Filter<G>}
|
115
|
+
* @returns {Filter<G>}
|
126
116
|
*/
|
127
117
|
export const handle_or_id = (handle_or_id) => {
|
128
|
-
return objid_or_else_filter(handle_or_id);
|
118
|
+
return objid_or_else_filter(handle_or_id, 'handle');
|
129
119
|
}
|
130
120
|
|
131
121
|
|
132
122
|
/**
|
133
123
|
* @template {{handle?: string}} G
|
134
|
-
*
|
135
|
-
*
|
136
124
|
* @param {string} id_or_else
|
137
|
-
*
|
138
|
-
*
|
139
|
-
* @returns {import('mongodb').Filter<G>}
|
125
|
+
* @returns {Filter<G>}
|
140
126
|
*/
|
141
127
|
export const objid_or_else_filter = (id_or_else, else_key='handle') => {
|
142
128
|
try {
|
package/src/utils.relations.js
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
/**
|
2
2
|
* @import { ID } from '@storecraft/core/database'
|
3
3
|
* @import { BaseType } from '@storecraft/core/api'
|
4
|
-
* @import { WithRelations } from './utils.
|
4
|
+
* @import { Relation, WithRelations } from './utils.types.js'
|
5
5
|
* @import { Filter } from 'mongodb'
|
6
6
|
*/
|
7
7
|
|
@@ -10,20 +10,6 @@ import { isDef, isUndef, to_objid } from './utils.funcs.js';
|
|
10
10
|
import { MongoDB } from '../index.js';
|
11
11
|
import { zeroed_relations } from './con.shared.js';
|
12
12
|
|
13
|
-
/**
|
14
|
-
* @template {any} T
|
15
|
-
*
|
16
|
-
* @typedef {Object} Relation
|
17
|
-
* @property {ObjectId[]} [ids]
|
18
|
-
* @property {Record<ID, T>} [entries]
|
19
|
-
*/
|
20
|
-
|
21
|
-
/**
|
22
|
-
* @template {any} T
|
23
|
-
*
|
24
|
-
* @typedef {T & { _relations? : Record<string, Relation<any>> }} WithRelations
|
25
|
-
*/
|
26
|
-
|
27
13
|
/**
|
28
14
|
*
|
29
15
|
* On upsert Create a relation on a given field that represents a relation.
|
@@ -259,12 +245,13 @@ export const update_specific_connection_of_relation = (
|
|
259
245
|
* @param {ObjectId} entry_objid the proper `ObjectId` of the entry
|
260
246
|
* @param {ClientSession} [session] client `session` for atomicity purposes
|
261
247
|
* @param {string[]} [search_terms_to_remove=[]] Extra `search` terms to remove
|
248
|
+
* @param {string[]} [tags_to_remove=[]] Extra `tags` terms to remove
|
262
249
|
* from all the connections
|
263
250
|
*
|
264
251
|
*/
|
265
252
|
export const remove_entry_from_all_connection_of_relation = (
|
266
253
|
driver, collection, relation_name, entry_objid, session,
|
267
|
-
search_terms_to_remove=[]
|
254
|
+
search_terms_to_remove=[], tags_to_remove=[]
|
268
255
|
) => {
|
269
256
|
return driver.collection(collection).updateMany(
|
270
257
|
{
|
@@ -273,7 +260,8 @@ export const remove_entry_from_all_connection_of_relation = (
|
|
273
260
|
{
|
274
261
|
$pull: {
|
275
262
|
[`_relations.${relation_name}.ids`] : entry_objid,
|
276
|
-
'_relations.search': { $in : search_terms_to_remove }
|
263
|
+
'_relations.search': { $in : search_terms_to_remove },
|
264
|
+
'tags': { $in : tags_to_remove },
|
277
265
|
},
|
278
266
|
$unset: {
|
279
267
|
[`_relations.${relation_name}.entries.${entry_objid.toString()}`]: ''
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import type { ID } from "@storecraft/core/database";
|
2
|
+
import type { ObjectId, WithId } from "mongodb";
|
3
|
+
|
4
|
+
export type Relation<T extends any = any> = {
|
5
|
+
ids?: ObjectId[];
|
6
|
+
entries?: Record<ID, T>;
|
7
|
+
}
|
8
|
+
|
9
|
+
export type WithRelations<T extends any = undefined> = {
|
10
|
+
_relations?: Record<string, Relation<any>>
|
11
|
+
_id?: ObjectId;
|
12
|
+
} & T;
|
@@ -0,0 +1,23 @@
|
|
1
|
+
services:
|
2
|
+
mongo1:
|
3
|
+
image: mongo:latest
|
4
|
+
container_name: test-mongodb
|
5
|
+
command: ["--replSet", "rs0", "--bind_ip_all", "--port", "27017"]
|
6
|
+
ports:
|
7
|
+
- 27017:27017
|
8
|
+
extra_hosts:
|
9
|
+
- "host.docker.internal:host-gateway"
|
10
|
+
healthcheck:
|
11
|
+
test: echo "try { rs.status() } catch (err) { rs.initiate({_id:'rs0',members:[{_id:0,host:'host.docker.internal:27017'}]}) }" | mongosh --port 27017 --quiet
|
12
|
+
interval: 5s
|
13
|
+
timeout: 30s
|
14
|
+
start_period: 0s
|
15
|
+
start_interval: 1s
|
16
|
+
retries: 30
|
17
|
+
volumes:
|
18
|
+
- "mongo1_data:/data/db"
|
19
|
+
- "mongo1_config:/data/configdb"
|
20
|
+
|
21
|
+
volumes:
|
22
|
+
mongo1_data:
|
23
|
+
mongo1_config:
|
package/tests/runner.test.js
CHANGED
@@ -9,7 +9,9 @@ export const create_app = async () => {
|
|
9
9
|
{
|
10
10
|
auth_admins_emails: ['admin@sc.com'],
|
11
11
|
auth_secret_access_token: 'auth_secret_access_token',
|
12
|
-
auth_secret_refresh_token: 'auth_secret_refresh_token'
|
12
|
+
auth_secret_refresh_token: 'auth_secret_refresh_token',
|
13
|
+
auth_secret_confirm_email_token: 'auth_secret_confirm_email_token',
|
14
|
+
auth_secret_forgot_password_token: 'auth_secret_forgot_password_token',
|
13
15
|
}
|
14
16
|
)
|
15
17
|
.withPlatform(new NodePlatform())
|
@@ -22,22 +24,24 @@ export const create_app = async () => {
|
|
22
24
|
)
|
23
25
|
)
|
24
26
|
|
25
|
-
return app.init()
|
27
|
+
return app.init()
|
26
28
|
}
|
27
29
|
|
28
30
|
async function test() {
|
29
31
|
const app = await create_app();
|
30
32
|
|
31
33
|
await migrateToLatest(app.db, false);
|
32
|
-
|
34
|
+
|
33
35
|
Object.entries(api).slice(0, -1).forEach(
|
34
36
|
([name, runner]) => {
|
35
37
|
runner.create(app).run();
|
36
38
|
}
|
37
39
|
);
|
40
|
+
|
38
41
|
const last_test = Object.values(api).at(-1).create(app);
|
39
42
|
last_test.after(async ()=>{app.db.disconnect()});
|
40
43
|
last_test.run();
|
41
44
|
}
|
42
45
|
|
46
|
+
// twice
|
43
47
|
test();
|
@@ -1,7 +1,10 @@
|
|
1
|
+
import 'dotenv/config';
|
1
2
|
import { App } from '@storecraft/core';
|
2
3
|
import { MongoDB } from '@storecraft/database-mongodb';
|
3
4
|
import { migrateToLatest } from '@storecraft/database-mongodb/migrate.js';
|
4
5
|
import { NodePlatform } from '@storecraft/core/platform/node';
|
6
|
+
import { expand } from '../src/con.shared.js';
|
7
|
+
import { sanitize_hidden, sanitize_recursively } from '../src/utils.funcs.js';
|
5
8
|
|
6
9
|
export const create_app = async () => {
|
7
10
|
const app = new App(
|
@@ -14,42 +17,20 @@ export const create_app = async () => {
|
|
14
17
|
.withPlatform(new NodePlatform())
|
15
18
|
.withDatabase(new MongoDB({ db_name: 'test'}))
|
16
19
|
|
17
|
-
|
20
|
+
await app.init();
|
21
|
+
await migrateToLatest(app.db, false);
|
22
|
+
return app;
|
18
23
|
}
|
19
24
|
|
20
|
-
/**
|
21
|
-
*
|
22
|
-
* @template R
|
23
|
-
*
|
24
|
-
* @param {(()=>Promise<R>) | (() => R)} fn
|
25
|
-
*/
|
26
|
-
const withTime = async (fn) => {
|
27
|
-
const n1 = Date.now() ;
|
28
|
-
const r = await fn();
|
29
|
-
const delta = Date.now() - n1;
|
30
|
-
console.log(delta);
|
31
|
-
return r;
|
32
|
-
}
|
33
25
|
|
34
26
|
async function test() {
|
35
|
-
const app = await
|
36
|
-
|
37
|
-
await migrateToLatest(app.db, false);
|
38
|
-
|
27
|
+
const app = await create_app();
|
39
28
|
|
40
|
-
const
|
41
|
-
let items = await app.db.resources.search.quicksearch(
|
42
|
-
{
|
43
|
-
vql: '',
|
44
|
-
sortBy: ['updated_at']
|
45
|
-
}
|
46
|
-
);
|
47
|
-
return items;
|
48
|
-
}
|
29
|
+
const sf = await app.db.resources.storefronts.get_default_auto_generated_storefront()
|
49
30
|
|
50
|
-
|
31
|
+
console.log(JSON.stringify(sf, null, 2));
|
51
32
|
|
52
|
-
|
33
|
+
await app.db.disconnect();
|
53
34
|
}
|
54
35
|
|
55
36
|
test();
|
package/vector-store/index.js
CHANGED
@@ -53,21 +53,33 @@ export class MongoVectorStore {
|
|
53
53
|
*/
|
54
54
|
constructor(config) {
|
55
55
|
this.config = {
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
options: config.options ?? {
|
56
|
+
index_name: DEFAULT_INDEX_NAME,
|
57
|
+
similarity: 'cosine',
|
58
|
+
dimensions: 1536,
|
59
|
+
options: {
|
61
60
|
ignoreUndefined: true,
|
62
61
|
serverApi: {
|
63
62
|
version: ServerApiVersion.v1,
|
64
63
|
strict: false,
|
65
64
|
deprecationErrors: true,
|
66
65
|
}
|
67
|
-
}
|
66
|
+
},
|
67
|
+
...config,
|
68
68
|
};
|
69
69
|
}
|
70
70
|
|
71
|
+
get metric() {
|
72
|
+
switch(this.config.similarity) {
|
73
|
+
case 'cosine': return 'cosine';
|
74
|
+
case 'dotProduct': return 'dotproduct';
|
75
|
+
case 'euclidean': return 'euclidean';
|
76
|
+
}
|
77
|
+
};
|
78
|
+
|
79
|
+
get dimensions() {
|
80
|
+
return this.config.dimensions;
|
81
|
+
};
|
82
|
+
|
71
83
|
get client() {
|
72
84
|
if(!this.config.db_name || !this.config.url) {
|
73
85
|
throw new Error('MongoVectorStore::client() - missing url or db_name');
|
@@ -81,8 +93,10 @@ export class MongoVectorStore {
|
|
81
93
|
|
82
94
|
/** @type {VectorStore["onInit"]} */
|
83
95
|
onInit = (app) => {
|
84
|
-
this.config.url ??= app.platform.env[MongoVectorStore.EnvConfig.url
|
85
|
-
|
96
|
+
this.config.url ??= app.platform.env[MongoVectorStore.EnvConfig.url]
|
97
|
+
?? app.platform.env['MONGODB_URL'];
|
98
|
+
this.config.db_name ??= app.platform.env[MongoVectorStore.EnvConfig.db_name]
|
99
|
+
?? app.platform.env['MONGODB_DB_NAME'] ?? 'main';
|
86
100
|
}
|
87
101
|
|
88
102
|
/** @type {VectorStore["embedder"]} */
|