@storecraft/database-mongodb 1.0.21 → 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 CHANGED
@@ -45,16 +45,15 @@ const app = new App()
45
45
  }
46
46
  )
47
47
  )
48
+ .init();
48
49
 
49
- await app.init();
50
- await migrateToLatest(app.db, false);
51
- // create if not exists
52
- await app.vectorstore.createVectorIndex(false, false);
50
+ await migrateToLatest(app.__show_me_everything.db, false);
51
+ await app.__show_me_everything.vector_store.createVectorIndex();
53
52
 
54
- const server = http.createServer(app.handler).listen(
53
+ http.createServer(app.handler).listen(
55
54
  8000,
56
55
  () => {
57
- console.log(`Server is running on http://localhost:8000`);
56
+ app.print_banner('http://localhost:8000');
58
57
  }
59
58
  );
60
59
 
package/index.js CHANGED
@@ -19,6 +19,7 @@ import { impl as storefronts } from './src/con.storefronts.js';
19
19
  import { impl as tags } from './src/con.tags.js';
20
20
  import { impl as templates } from './src/con.templates.js';
21
21
  import { impl as search } from './src/con.search.js';
22
+ import { impl as chats } from './src/con.chats.js';
22
23
  export { migrateToLatest } from './migrate.js';
23
24
  export { MongoVectorStore } from './vector-store/index.js';
24
25
 
@@ -28,7 +29,7 @@ export { MongoVectorStore } from './vector-store/index.js';
28
29
  export class MongoDB {
29
30
 
30
31
  /** @satisfies {ENV<Config>} */
31
- static EnvConfig = /** @type{const} */ ({
32
+ static EnvConfig = /** @type {const} */ ({
32
33
  db_name: 'MONGODB_NAME',
33
34
  url: 'MONGODB_URL',
34
35
  });
@@ -73,8 +74,8 @@ export class MongoDB {
73
74
 
74
75
  const c = this.#config;
75
76
 
76
- c.db_name ??= app.platform.env[MongoDB.EnvConfig.db_name];
77
- c.url ??= app.platform.env[MongoDB.EnvConfig.url] ?? 'main';
77
+ c.db_name ??= app.env[MongoDB.EnvConfig.db_name];
78
+ c.url ??= app.env[MongoDB.EnvConfig.url] ?? 'main';
78
79
 
79
80
  if(!this.config.db_name || !this.config.url) {
80
81
  throw new Error(
@@ -98,6 +99,7 @@ export class MongoDB {
98
99
  shipping_methods: shipping(this),
99
100
  templates: templates(this),
100
101
  search: search(this),
102
+ chats: chats(this),
101
103
  }
102
104
 
103
105
  this.#is_ready = true;
@@ -0,0 +1,57 @@
1
+ import { Db, MongoClient } from 'mongodb';
2
+
3
+ const collections = [
4
+ 'chats'
5
+ ];
6
+
7
+ /**
8
+ * @param {Db} db
9
+ * @param {MongoClient} client
10
+ */
11
+ export async function up(db, client) {
12
+
13
+ const session = client.startSession();
14
+ try {
15
+ await session.withTransaction(async ($session) => {
16
+ for (const collection_name of collections) {
17
+
18
+ await db.createCollection(collection_name, {session: $session})
19
+ await db.collection(collection_name).dropIndexes({ session: $session });
20
+
21
+ await db.collection(collection_name).createIndexes(
22
+ [
23
+ {
24
+ key: { handle: 1 }, name: 'handle+',
25
+ background: false, unique: true, sparse: true
26
+ },
27
+ {
28
+ key: { updated_at: 1, _id: 1 }, name: '(updated_at+, _id+)',
29
+ background: false, sparse: true
30
+ },
31
+ {
32
+ key: { updated_at: -1, _id: -1 }, name: '(updated_at-, _id-)',
33
+ background: false, sparse: true
34
+ },
35
+ {
36
+ key: { search: 1 }, name: '(search+)',
37
+ background: false, sparse: true
38
+ },
39
+ ], {
40
+ session: $session
41
+ }
42
+ )
43
+ }
44
+
45
+ });
46
+ } finally {
47
+ await session.endSession();
48
+ }
49
+ }
50
+
51
+ /**
52
+ *
53
+ * @param {Db} db
54
+ * @param {MongoClient} client
55
+ */
56
+ export async function down(db, client) {
57
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@storecraft/database-mongodb",
3
- "version": "1.0.21",
3
+ "version": "1.2.5",
4
4
  "description": "Storecraft database driver for mongodb on node / bun / deno platform",
5
5
  "license": "MIT",
6
6
  "author": "Tomer Shalev (https://github.com/store-craft)",
@@ -36,7 +36,6 @@
36
36
  "database-mongodb:docker-compose-up": "docker compose -f ./tests/docker-compose.yml up -d ",
37
37
  "database-mongodb:test": "node ./tests/runner.test.js",
38
38
  "test": "npm run database-mongodb:test",
39
- "prepublishOnly": "npm version patch --force",
40
39
  "sc-publish": "npm publish"
41
40
  },
42
41
  "dependencies": {
@@ -0,0 +1,119 @@
1
+ /**
2
+ * @import { db_chats as db_col } from '@storecraft/core/database'
3
+ * @import { WithRelations } from './utils.types.js'
4
+ */
5
+ import { Collection } from 'mongodb'
6
+ import { MongoDB } from '../index.js'
7
+ import { count_regular, get_regular, list_regular } from './con.shared.js'
8
+ import { handle_or_id, to_objid } from './utils.funcs.js';
9
+ import {
10
+ add_search_terms_relation_on, delete_me,
11
+ save_me,
12
+ } from './utils.relations.js';
13
+
14
+ /**
15
+ * @param {MongoDB} d
16
+ * @returns {Collection<WithRelations<db_col["$type_get"]>>}
17
+ */
18
+ const col = (d) => d.collection('chats');
19
+
20
+ /**
21
+ * @param {MongoDB} driver
22
+ * @returns {db_col["upsert"]}
23
+ */
24
+ const upsert = (driver) => {
25
+ return async (data, search_terms=[]) => {
26
+ data = {...data};
27
+ const objid = to_objid(data.id);
28
+ const session = driver.mongo_client.startSession();
29
+
30
+ try {
31
+ await session.withTransaction(
32
+ async () => {
33
+
34
+ // SEARCH
35
+ add_search_terms_relation_on(data, search_terms);
36
+
37
+ // SAVE ME
38
+ await save_me(driver, 'chats', objid, data, session);
39
+
40
+ }
41
+ );
42
+ } catch(e) {
43
+ console.log(e);
44
+ return false;
45
+ } finally {
46
+ await session.endSession();
47
+ }
48
+
49
+ return true;
50
+ }
51
+
52
+ }
53
+ /**
54
+ * @param {MongoDB} driver
55
+ */
56
+ const get = (driver) => get_regular(driver, col(driver));
57
+
58
+ /**
59
+ * @param {MongoDB} driver
60
+ * @returns {db_col["remove"]}
61
+ */
62
+ const remove = (driver) => {
63
+ return async (id_or_handle) => {
64
+ const item = await col(driver).findOne(handle_or_id(id_or_handle));
65
+
66
+ if(!item) return;
67
+
68
+ const objid = to_objid(item.id)
69
+ const session = driver.mongo_client.startSession();
70
+
71
+ try {
72
+ await session.withTransaction(
73
+ async () => {
74
+
75
+ // DELETE ME
76
+ await delete_me(
77
+ driver, 'chats', objid, session
78
+ );
79
+
80
+ }
81
+ );
82
+ } catch(e) {
83
+ console.log(e);
84
+ return false;
85
+ } finally {
86
+ await session.endSession();
87
+ }
88
+
89
+ return true;
90
+ }
91
+
92
+ }
93
+
94
+
95
+ /**
96
+ * @param {MongoDB} driver
97
+ */
98
+ const list = (driver) => list_regular(driver, col(driver));
99
+
100
+ /**
101
+ * @param {MongoDB} driver
102
+ */
103
+ const count = (driver) => count_regular(driver, col(driver));
104
+
105
+ /**
106
+ * @param {MongoDB} driver
107
+ * @return {db_col & { _col: ReturnType<col>}}
108
+ */
109
+ export const impl = (driver) => {
110
+
111
+ return {
112
+ _col: col(driver),
113
+ get: get(driver),
114
+ upsert: upsert(driver),
115
+ remove: remove(driver),
116
+ list: list(driver),
117
+ count: count(driver),
118
+ }
119
+ }
package/src/con.search.js CHANGED
@@ -20,7 +20,8 @@ const tables = [
20
20
  'notifications',
21
21
  'discounts',
22
22
  'orders',
23
- 'templates'
23
+ 'templates',
24
+ 'chats'
24
25
  ]
25
26
 
26
27
  /**
@@ -40,7 +41,23 @@ const prefix_to_resource = {
40
41
  'tag': 'tags',
41
42
  'template': 'templates',
42
43
  'post': 'posts',
43
-
44
+ 'chat': 'chats',
45
+ }
46
+
47
+ const resource_to_props = {
48
+ 'auth_users': ['id', 'handle'],
49
+ 'collections': ['id', 'handle'],
50
+ 'customers': ['id', 'handle'],
51
+ 'discounts': ['id', 'handle', 'title'],
52
+ 'images': ['id', 'handle', 'name'],
53
+ 'orders': ['id'],
54
+ 'products': ['id', 'handle', 'title'],
55
+ 'shipping_methods': ['id', 'handle', 'title'],
56
+ 'storefronts': ['id', 'handle', 'title'],
57
+ 'tags': ['id', 'handle'],
58
+ 'templates': ['id', 'handle', 'title'],
59
+ 'posts': ['id', 'handle', 'title'],
60
+ 'chats': ['id', 'customer_email', 'customer_id'],
44
61
  }
45
62
 
46
63
  /**
@@ -87,10 +104,15 @@ export const quicksearch = (driver) => {
87
104
  },
88
105
  {
89
106
  $project: {
90
- title: 1,
91
- handle: 1,
92
- // '_relations.search': 1,
93
107
  id: 1,
108
+ handle: 1,
109
+
110
+ title: 1,
111
+ name: 1,
112
+
113
+ customer_email: 1,
114
+ customer_id: 1,
115
+
94
116
  _id: 0
95
117
  }
96
118
  }
@@ -8,7 +8,7 @@ import { api } from '@storecraft/core/test-runner';
8
8
  // Main MongoDB test suite with the core test-runner for api layer
9
9
  //
10
10
 
11
- export const create_app = async () => {
11
+ export const create_app = () => {
12
12
  const app = new App(
13
13
  {
14
14
  auth_admins_emails: ['admin@sc.com'],
@@ -28,13 +28,13 @@ export const create_app = async () => {
28
28
  )
29
29
  )
30
30
 
31
- return app.init()
31
+ return app.init().__show_me_everything.app;
32
32
  }
33
33
 
34
34
  async function test() {
35
- const app = await create_app();
35
+ const app = create_app();
36
36
 
37
- await migrateToLatest(app.db, false);
37
+ await migrateToLatest(app.__show_me_everything.db, false);
38
38
 
39
39
  Object.entries(api).slice(0, -1).forEach(
40
40
  ([name, runner]) => {
@@ -43,7 +43,7 @@ async function test() {
43
43
  );
44
44
 
45
45
  const last_test = Object.values(api).at(-1).create(app);
46
- last_test.after(async ()=>{app.db.disconnect()});
46
+ last_test.after(async ()=>{app.__show_me_everything.db.disconnect()});
47
47
  last_test.run();
48
48
  }
49
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.init();
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 app.db.resources.storefronts.get_default_auto_generated_storefront()
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 app.db.disconnect();
33
+ await db.disconnect();
34
34
  }
35
35
 
36
36
  test();
@@ -15,6 +15,7 @@ mongo_vectorSearch_pipeline,
15
15
  * } from 'mongodb'
16
16
  * @import { ENV } from '@storecraft/core';
17
17
  */
18
+ import { truncate_or_pad_vector } from '@storecraft/core/ai/models/vector-stores/index.js';
18
19
  import { Collection } from 'mongodb';
19
20
  import { MongoClient, ServerApiVersion } from 'mongodb';
20
21
 
@@ -29,13 +30,12 @@ export const DEFAULT_INDEX_NAME = 'vector_store';
29
30
  /**
30
31
  * @description MongoDB Atlas Vector Store
31
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.}
32
- *
33
33
  * @implements {VectorStore}
34
34
  */
35
35
  export class MongoVectorStore {
36
36
 
37
37
  /** @satisfies {ENV<Config>} */
38
- static EnvConfig = /** @type{const} */ ({
38
+ static EnvConfig = /** @type {const} */ ({
39
39
  db_name: 'MONGODB_VECTOR_STORE_DB_NAME',
40
40
  url: 'MONGODB_VECTOR_STORE_URL'
41
41
  });
@@ -94,10 +94,10 @@ export class MongoVectorStore {
94
94
 
95
95
  /** @type {VectorStore["onInit"]} */
96
96
  onInit = (app) => {
97
- this.config.url ??= app.platform.env[MongoVectorStore.EnvConfig.url]
98
- ?? app.platform.env['MONGODB_URL'];
99
- this.config.db_name ??= app.platform.env[MongoVectorStore.EnvConfig.db_name]
100
- ?? app.platform.env['MONGODB_DB_NAME'] ?? 'main';
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';
101
101
  }
102
102
 
103
103
  /** @type {VectorStore["embedder"]} */
@@ -117,7 +117,9 @@ export class MongoVectorStore {
117
117
  (doc, ix) => (
118
118
  {
119
119
  updated_at: new Date().toISOString(),
120
- embedding: vectors[ix],
120
+ embedding: truncate_or_pad_vector(
121
+ vectors[ix], this.config.dimensions
122
+ ),
121
123
  metadata: doc.metadata,
122
124
  pageContent: doc.pageContent,
123
125
  [NAMESPACE_KEY]: doc.namespace,
@@ -164,6 +166,13 @@ export class MongoVectorStore {
164
166
  }
165
167
  );
166
168
 
169
+ if(!result) {
170
+ console.warn(
171
+ 'MongoVectoreStore::upsertDocuments() - no result from embedder'
172
+ );
173
+ return;
174
+ }
175
+
167
176
  const vectors = result.content;
168
177
 
169
178
  return this.upsertVectors(
@@ -247,15 +256,41 @@ export class MongoVectorStore {
247
256
  * @param {boolean} [delete_index_if_exists_before=false]
248
257
  * @returns {Promise<boolean>}
249
258
  */
250
- createVectorIndex = async (disconnect_after_finish=true, delete_index_if_exists_before=false) => {
259
+ createVectorIndex = async (
260
+ disconnect_after_finish=true,
261
+ delete_index_if_exists_before=false
262
+ ) => {
251
263
  if(delete_index_if_exists_before) {
252
264
  await this.deleteVectorIndex();
253
265
  }
254
-
266
+
255
267
  const db = this.client.db(this.config.db_name);
256
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
+
257
290
  // collection name will have the same name as the index
258
291
  await db.createCollection(collection_name);
292
+
293
+
259
294
  const index_result = await db.collection(collection_name).createSearchIndex(
260
295
  {
261
296
  name: this.config.index_name,
@@ -1,24 +0,0 @@
1
- import { test } from 'uvu';
2
- import * as assert from 'uvu/assert';
3
- import { query_vql_to_mongo_filter } from '../src/utils.query.js'
4
-
5
- //
6
- // TODO:
7
- // - [ ] Add tests for query_vql_to_mongo_filter
8
- // although it is tested as a black box in the main test file
9
- //
10
- test('query_vql_to_mongo_filter', async () => {
11
- const vql_ast = {
12
- };
13
-
14
- const mongo = {
15
- };
16
-
17
- // const m1 = query_vql_to_mongo_filter(vql_ast);
18
-
19
- // console.log(JSON.stringify(m1, null, 2))
20
-
21
- // assert.equal(m1, mongo);
22
- });
23
-
24
- test.run();