@storecraft/database-mongodb 1.0.15 → 1.0.17

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
@@ -1,4 +1,4 @@
1
- # Storecraft MongoDB driver for Node.js
1
+ # Storecraft MongoDB driver for node / deno / bun
2
2
 
3
3
  <div style="text-align:center">
4
4
  <img src='https://storecraft.app/storecraft-color.svg'
@@ -28,14 +28,27 @@ import { MongoVectorStore } from '@storecraft/database-mongodb/vector-store'
28
28
 
29
29
  const app = new App()
30
30
  .withPlatform(new NodePlatform())
31
- .withDatabase(new MongoDB({}))
31
+ .withDatabase(
32
+ new MongoDB(
33
+ {
34
+ url: process.env.MONGODB_URL,
35
+ db_name: process.env.MONGODB_NAME,
36
+ }
37
+ )
38
+ )
32
39
  .withVectorStore(
33
- new MongoVectorStore({ embedder: new OpenAIEmbedder() })
40
+ new MongoVectorStore(
41
+ {
42
+ embedder: new OpenAIEmbedder(),
43
+ url: process.env.MONGODB_VECTOR_STORE_URL ?? process.env.MONGODB_URL,
44
+ db_name: process.env.MONGODB_VECTOR_STORE_DB_NAME ?? process.env.MONGODB_NAME,
45
+ }
46
+ )
34
47
  )
35
48
 
36
49
  await app.init();
37
50
  await migrateToLatest(app.db, false);
38
- // cerate if not exists
51
+ // create if not exists
39
52
  await app.vectorstore.createVectorIndex(false, false);
40
53
 
41
54
  const server = http.createServer(app.handler).listen(
@@ -48,6 +61,60 @@ const server = http.createServer(app.handler).listen(
48
61
  ```
49
62
 
50
63
 
64
+ Storecraft will search the following `env` variables
65
+
66
+ ```bash
67
+ MONGODB_NAME=main
68
+ MONGODB_URL='mongodb-connection-string'
69
+
70
+ // also, this will default into `MONGODB_NAME`
71
+ MONGODB_VECTOR_STORE_DB_NAME=vector-store
72
+ // also, this will default into `MONGODB_URL`
73
+ MONGODB_VECTOR_STORE_URL='mongodb-connection-string'
74
+ ```
75
+
76
+ So, you can instantiate with empty config
77
+
78
+ ```ts
79
+ .withDatabase(
80
+ new MongoDB()
81
+ )
82
+ .withVectorStore(
83
+ new MongoVectorStore(
84
+ {
85
+ embedder: new OpenAIEmbedder(),
86
+ }
87
+ )
88
+ )
89
+ ```
90
+
91
+
92
+ ## Testing Locally (I recommend to use `Atlas`)
93
+
94
+ 1. First start a `mongo-db` server
95
+ First, make sure you have `docker` installed,
96
+ Then, run
97
+
98
+ ```bash
99
+ npm run database-mongodb:docker-compose-up
100
+ ```
101
+
102
+ 2. create Environment
103
+
104
+ create `.env` file with
105
+
106
+ ```bash
107
+ MONGODB_URL="mongodb://127.0.0.1:27017/?replicaSet=rs0"
108
+ MONGODB_NAME="main"
109
+ ```
110
+
111
+ 3. Run `tests/runner.test.js`
112
+
113
+ ```bash
114
+ npm run database-mongodb:test
115
+ ```
116
+
117
+
51
118
  ```text
52
119
  Author: Tomer Shalev <tomer.shalev@gmail.com>
53
120
  ```
package/index.js CHANGED
@@ -68,6 +68,7 @@ export class MongoDB {
68
68
  constructor(config) {
69
69
  this.#is_ready = false;
70
70
  this.#config = {
71
+ db_name: 'main',
71
72
  options: {
72
73
  ignoreUndefined: true,
73
74
  serverApi: {
@@ -161,6 +162,10 @@ export class MongoDB {
161
162
  return this.#mongo_client;
162
163
  }
163
164
 
165
+ get db() {
166
+ return this.mongo_client.db(this.name);
167
+ }
168
+
164
169
  /**
165
170
  *
166
171
  * @description Get the config object
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@storecraft/database-mongodb",
3
- "version": "1.0.15",
3
+ "version": "1.0.17",
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)",
@@ -33,6 +33,7 @@
33
33
  }
34
34
  },
35
35
  "scripts": {
36
+ "database-mongodb:docker-compose-up": "docker compose -f ./tests/docker-compose.yml up -d ",
36
37
  "database-mongodb:test": "node ./tests/runner.test.js",
37
38
  "test": "npm run database-mongodb:test",
38
39
  "prepublishOnly": "npm version patch --force",
@@ -6,6 +6,7 @@
6
6
  import { MongoDB } from '../index.js'
7
7
  import { Collection } from 'mongodb'
8
8
  import {
9
+ handle_or_id,
9
10
  objid_or_else_filter, sanitize_one, to_objid_safe
10
11
  } from './utils.funcs.js'
11
12
  import {
@@ -62,32 +63,37 @@ const getByEmail = (driver) => {
62
63
 
63
64
  /**
64
65
  * @param {MongoDB} driver
65
- *
66
- *
67
66
  * @returns {db_col["remove"]}
68
67
  */
69
68
  const remove = (driver) => {
70
- return async (id) => {
69
+ return async (id_or_handle) => {
70
+
71
+ const session = driver.mongo_client.startSession();
72
+ try {
73
+ await session.withTransaction(
74
+ async () => {
75
+ const filter = handle_or_id(id_or_handle);
76
+ await col(driver).deleteOne(
77
+ filter,
78
+ { session }
79
+ );
80
+ // customer and auth_user have the same object-id and handle
81
+ // so we can use the same filter
82
+ await driver.resources.customers._col.deleteOne(
83
+ filter,
84
+ { session }
85
+ );
71
86
 
72
- let filter;
73
-
74
- {
75
- if(to_objid_safe(id)) {
76
- filter = {
77
- _id: to_objid_safe(id)
78
- }
79
- } else {
80
- filter = {
81
- email: id
82
87
  }
83
- }
88
+ );
89
+ } catch(e) {
90
+ console.log(e);
91
+ return false;
92
+ } finally {
93
+ await session.endSession();
84
94
  }
85
95
 
86
- const res = await col(driver).deleteOne(
87
- filter
88
- );
89
-
90
- return Boolean(res.deletedCount)
96
+ return true;
91
97
  }
92
98
  }
93
99
 
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @import { db_collections as db_col } from '@storecraft/core/database'
3
3
  * @import { ProductType, VariantType } from '@storecraft/core/api'
4
- * @import { WithRelations } from './utils.relations.js'
4
+ * @import { WithRelations } from './utils.types.js'
5
5
  * @import { Filter } from 'mongodb'
6
6
  */
7
7
 
@@ -27,8 +27,6 @@ const transactionOptions = {
27
27
 
28
28
  /**
29
29
  * @param {MongoDB} d
30
- *
31
- *
32
30
  * @returns {Collection<WithRelations<db_col["$type_get"]>>}
33
31
  */
34
32
  const col = (d) => d.collection('collections');
@@ -169,8 +167,6 @@ const count = (driver) => count_regular(driver, col(driver));
169
167
 
170
168
  /**
171
169
  * @param {MongoDB} driver
172
- *
173
- *
174
170
  * @returns {db_col["list_collection_products"]}
175
171
  */
176
172
  const list_collection_products = (driver) => {
@@ -213,6 +209,72 @@ const list_collection_products = (driver) => {
213
209
  }
214
210
  }
215
211
 
212
+
213
+ /**
214
+ * @param {MongoDB} driver
215
+ * @returns {db_col["count_collection_products"]}
216
+ */
217
+ const count_collection_products = (driver) => {
218
+ return async (handle_or_id, query) => {
219
+
220
+ const {
221
+ filter: filter_query, sort, reverse_sign
222
+ } = query_to_mongo(query);
223
+
224
+ /**
225
+ * @type {Filter<WithRelations<ProductType | VariantType>>
226
+ * }
227
+ */
228
+ const filter = {
229
+ $and: [
230
+ { '_relations.search': `col:${handle_or_id}` },
231
+ ]
232
+ };
233
+
234
+ // add the query filter
235
+ isDef(filter_query) && filter.$and.push(filter_query);
236
+
237
+ const count = await driver.resources.products._col.countDocuments(
238
+ filter
239
+ );
240
+
241
+ return count;
242
+ }
243
+ }
244
+
245
+
246
+
247
+ /**
248
+ * @param {MongoDB} driver
249
+ * @returns {db_col["list_used_products_tags"]}
250
+ */
251
+ const list_used_products_tags = (driver) => {
252
+ return async (handle_or_id) => {
253
+ const items = await driver.resources.products._col.find(
254
+ {
255
+ '_relations.search': `col:${handle_or_id}`
256
+ },
257
+ {
258
+ projection: {
259
+ tags: 1
260
+ }
261
+ }
262
+ ).toArray();
263
+
264
+ const set = (items ?? []).reduce(
265
+ (p, c) => {
266
+ c.tags.forEach(
267
+ (tag) => p.add(tag)
268
+ );
269
+ return p;
270
+ }, new Set()
271
+ )
272
+ // return array from set
273
+ return Array.from(set);
274
+ }
275
+ }
276
+
277
+
216
278
  /**
217
279
  * @param {MongoDB} driver
218
280
  *
@@ -228,6 +290,8 @@ export const impl = (driver) => {
228
290
  remove: remove(driver),
229
291
  list: list(driver),
230
292
  count: count(driver),
231
- list_collection_products: list_collection_products(driver)
293
+ list_collection_products: list_collection_products(driver),
294
+ count_collection_products: count_collection_products(driver),
295
+ list_used_products_tags: list_used_products_tags(driver),
232
296
  }
233
297
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @import { db_customers as db_col } from '@storecraft/core/database'
3
3
  * @import { OrderData } from '@storecraft/core/api'
4
- * @import { WithRelations } from './utils.relations.js'
4
+ * @import { WithRelations } from './utils.types.js'
5
5
  * @import { Filter } from 'mongodb'
6
6
  */
7
7
 
@@ -9,7 +9,7 @@ import { Collection, ObjectId } from 'mongodb'
9
9
  import { MongoDB } from '../index.js'
10
10
  import { count_regular, get_regular, list_regular,
11
11
  upsert_regular } from './con.shared.js'
12
- import { isDef, sanitize_array, to_objid } from './utils.funcs.js'
12
+ import { handle_or_id, isDef, sanitize_array, to_objid } from './utils.funcs.js'
13
13
  import { query_to_mongo } from './utils.query.js';
14
14
 
15
15
  /**
@@ -62,8 +62,6 @@ export const email_or_id = (email_or_id) => {
62
62
 
63
63
  /**
64
64
  * @param {MongoDB} driver
65
- *
66
- *
67
65
  * @returns {db_col["remove"]}
68
66
  */
69
67
  const remove = (driver) => {
@@ -74,7 +72,7 @@ const remove = (driver) => {
74
72
  await session.withTransaction(
75
73
  async () => {
76
74
  const res = await col(driver).findOneAndDelete(
77
- email_or_id(id),
75
+ handle_or_id(id),
78
76
  { session }
79
77
  );
80
78
 
@@ -120,10 +118,10 @@ const list_customer_orders = (driver) => {
120
118
 
121
119
  const { filter: filter_query, sort, reverse_sign } = query_to_mongo(query);
122
120
 
123
- console.log('query', query)
124
- console.log('filter', JSON.stringify(filter_query, null, 2))
125
- console.log('sort', sort)
126
- console.log('expand', query?.expand)
121
+ // console.log('query', query)
122
+ // console.log('filter', JSON.stringify(filter_query, null, 2))
123
+ // console.log('sort', sort)
124
+ // console.log('expand', query?.expand)
127
125
 
128
126
  /** @type {Filter<WithRelations<OrderData>>} */
129
127
  const filter = {
@@ -147,6 +145,34 @@ const list_customer_orders = (driver) => {
147
145
  }
148
146
  }
149
147
 
148
+ /**
149
+ * @param {MongoDB} driver
150
+ * @returns {db_col["count_customer_orders"]}
151
+ */
152
+ const count_customer_orders = (driver) => {
153
+ return async (customer_id, query) => {
154
+
155
+ const { filter: filter_query } = query_to_mongo(query);
156
+
157
+ /** @type {Filter<WithRelations<OrderData>>} */
158
+ const filter = {
159
+ $and: [
160
+ {'_relations.search': `customer:${customer_id}` },
161
+ ]
162
+ };
163
+
164
+ // add the query filter
165
+ isDef(filter_query) && filter.$and.push(filter_query);
166
+
167
+ const count = await driver.resources.orders._col.countDocuments(
168
+ filter
169
+ );
170
+
171
+ return count;
172
+ }
173
+ }
174
+
175
+
150
176
  /**
151
177
  * @param {MongoDB} driver
152
178
  *
@@ -163,6 +189,7 @@ export const impl = (driver) => {
163
189
  remove: remove(driver),
164
190
  list: list(driver),
165
191
  count: count(driver),
166
- list_customer_orders: list_customer_orders(driver)
192
+ list_customer_orders: list_customer_orders(driver),
193
+ count_customer_orders: count_customer_orders(driver),
167
194
  }
168
195
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @import { db_discounts as db_col } from '@storecraft/core/database'
3
3
  * @import { ProductType, VariantType } from '@storecraft/core/api'
4
- * @import { WithRelations } from './utils.relations.js'
4
+ * @import { WithRelations } from './utils.types.js'
5
5
  * @import { Filter } from 'mongodb'
6
6
  */
7
7
 
@@ -23,7 +23,10 @@ import {
23
23
  save_me,
24
24
  update_entry_on_all_connection_of_relation
25
25
  } from './utils.relations.js'
26
-
26
+ import {
27
+ helper_compute_product_extra_search_keywords_because_of_discount_side_effect_for_db,
28
+ helper_compute_product_extra_tags_because_of_discount_side_effect_for_db
29
+ } from '@storecraft/core/database'
27
30
 
28
31
  /**
29
32
  * @param {MongoDB} d
@@ -54,16 +57,27 @@ const upsert = (driver) => {
54
57
  // SEARCH
55
58
  add_search_terms_relation_on(data, search_terms);
56
59
 
57
-
58
60
  ////
59
61
  // PRODUCT --> DISCOUNTS RELATION
60
62
  ////
63
+ const extra_search_for_products =
64
+ helper_compute_product_extra_search_keywords_because_of_discount_side_effect_for_db(
65
+ data
66
+ );
67
+
68
+ const extra_tags_for_products =
69
+ helper_compute_product_extra_tags_because_of_discount_side_effect_for_db(
70
+ data
71
+ );
61
72
 
62
73
  // first remove discount from anywhere
63
74
  await remove_entry_from_all_connection_of_relation(
64
75
  driver, 'products', 'discounts', objid, session,
65
- [
66
- `discount:${data.handle}`, `discount:${data.id}`
76
+ [ // remove search terms
77
+ ...extra_search_for_products
78
+ ],
79
+ [ // remove tags
80
+ ...extra_tags_for_products
67
81
  ]
68
82
  );
69
83
 
@@ -77,8 +91,11 @@ const upsert = (driver) => {
77
91
  $addToSet: {
78
92
  '_relations.discounts.ids': objid,
79
93
  '_relations.search': {
80
- $each : [ `discount:${data.handle}`, `discount:${data.id}` ]
81
- }
94
+ $each : extra_search_for_products
95
+ },
96
+ 'tags': {
97
+ $each : extra_tags_for_products
98
+ }
82
99
  },
83
100
 
84
101
  },
@@ -145,13 +162,27 @@ const remove = (driver) => {
145
162
  try {
146
163
  await session.withTransaction(
147
164
  async () => {
165
+
166
+ const extra_search_for_products =
167
+ helper_compute_product_extra_search_keywords_because_of_discount_side_effect_for_db(
168
+ item
169
+ );
170
+
171
+ const extra_tags_for_products =
172
+ helper_compute_product_extra_tags_because_of_discount_side_effect_for_db(
173
+ item
174
+ );
175
+
148
176
  ////
149
177
  // PRODUCT -> DISCOUNTS RELATION
150
178
  ////
151
179
  await remove_entry_from_all_connection_of_relation(
152
180
  driver, 'products', 'discounts', objid, session,
153
181
  [
154
- `discount:${item.handle}`, `discount:${item.id}`
182
+ ...extra_search_for_products
183
+ ],
184
+ [
185
+ ...extra_tags_for_products
155
186
  ]
156
187
  );
157
188
 
@@ -196,8 +227,6 @@ const count = (driver) => count_regular(driver, col(driver));
196
227
 
197
228
  /**
198
229
  * @param {MongoDB} driver
199
- *
200
- *
201
230
  * @returns {db_col["list_discount_products"]}
202
231
  */
203
232
  const list_discount_products = (driver) => {
@@ -235,6 +264,67 @@ const list_discount_products = (driver) => {
235
264
  }
236
265
  }
237
266
 
267
+
268
+ /**
269
+ * @param {MongoDB} driver
270
+ * @returns {db_col["list_all_discount_products_tags"]}
271
+ */
272
+ const list_all_discount_products_tags = (driver) => {
273
+ return async (handle_or_id) => {
274
+ const items = await driver.resources.products._col.find(
275
+ {
276
+ '_relations.search': `discount:${handle_or_id}`
277
+ },
278
+ {
279
+ projection: {
280
+ tags: 1
281
+ }
282
+ }
283
+ ).toArray();
284
+
285
+ const set = (items ?? []).reduce(
286
+ (p, c) => {
287
+ c.tags.forEach(
288
+ (tag) => p.add(tag)
289
+ );
290
+ return p;
291
+ }, new Set()
292
+ );
293
+
294
+ // return array from set
295
+ return Array.from(set);
296
+ }
297
+ }
298
+
299
+
300
+ /**
301
+ * @param {MongoDB} driver
302
+ * @returns {db_col["count_discount_products"]}
303
+ */
304
+ const count_discount_products = (driver) => {
305
+ return async (handle_or_id, query) => {
306
+
307
+ const { filter: filter_query, sort, reverse_sign } = query_to_mongo(query);
308
+
309
+ /** @type {Filter<WithRelations<ProductType | VariantType>>} */
310
+ const filter = {
311
+ $and: [
312
+ { '_relations.search': `discount:${handle_or_id}` },
313
+ ]
314
+ };
315
+
316
+ // add the query filter
317
+ isDef(filter_query) && filter.$and.push(filter_query);
318
+
319
+ const count = await driver.resources.products._col.countDocuments(
320
+ filter
321
+ );
322
+
323
+ return count;
324
+ }
325
+ }
326
+
327
+
238
328
  /**
239
329
  * @param {MongoDB} driver
240
330
  *
@@ -251,6 +341,8 @@ export const impl = (driver) => {
251
341
  remove: remove(driver),
252
342
  list: list(driver),
253
343
  count: count(driver),
254
- list_discount_products: list_discount_products(driver)
344
+ list_discount_products: list_discount_products(driver),
345
+ list_all_discount_products_tags: list_all_discount_products_tags(driver),
346
+ count_discount_products: count_discount_products(driver)
255
347
  }
256
348
  }
package/src/con.posts.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @import { db_posts as db_col } from '@storecraft/core/database'
3
- * @import { WithRelations } from './utils.relations.js'
3
+ * @import { WithRelations } from './utils.types.js'
4
4
  */
5
5
 
6
6
  import { Collection } from 'mongodb'