@storecraft/database-mongodb 1.0.1
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 +50 -0
- package/db-strategy.md +284 -0
- package/index.js +193 -0
- package/jsconfig.json +14 -0
- package/migrate.js +39 -0
- package/migrations/00000_init_tables.js +60 -0
- package/migrations/00001_seed_email_templates.js +271 -0
- package/package.json +38 -0
- package/src/con.auth_users.js +147 -0
- package/src/con.collections.js +232 -0
- package/src/con.customers.js +172 -0
- package/src/con.discounts.js +261 -0
- package/src/con.discounts.utils.js +137 -0
- package/src/con.images.js +173 -0
- package/src/con.notifications.js +101 -0
- package/src/con.orders.js +61 -0
- package/src/con.posts.js +149 -0
- package/src/con.products.js +537 -0
- package/src/con.search.js +162 -0
- package/src/con.shared.js +333 -0
- package/src/con.shipping.js +153 -0
- package/src/con.storefronts.js +223 -0
- package/src/con.tags.js +62 -0
- package/src/con.templates.js +62 -0
- package/src/utils.funcs.js +152 -0
- package/src/utils.query.js +186 -0
- package/src/utils.relations.js +410 -0
- package/tests/mongo-ping.test.js +34 -0
- package/tests/query.cursor.test.js +389 -0
- package/tests/query.vql.test.js +71 -0
- package/tests/runner.test.js +35 -0
- package/tests/sandbox.test.js +56 -0
- package/types.public.d.ts +22 -0
package/README.md
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# Storecraft MongoDB driver for Node.js
|
2
|
+
|
3
|
+
<div style="text-align:center">
|
4
|
+
<img src='https://storecraft.app/storecraft-color.svg'
|
5
|
+
width='90%'' />
|
6
|
+
</div><hr/><br/>
|
7
|
+
|
8
|
+
Official `mongodb` driver for `StoreCraft` on **Node.js** platforms.
|
9
|
+
|
10
|
+
```bash
|
11
|
+
npm i @storecraft/database-mongodb
|
12
|
+
```
|
13
|
+
|
14
|
+
## usage
|
15
|
+
|
16
|
+
```js
|
17
|
+
import 'dotenv/config';
|
18
|
+
import http from "node:http";
|
19
|
+
import { join } from "node:path";
|
20
|
+
import { homedir } from "node:os";
|
21
|
+
|
22
|
+
import { App } from '@storecraft/core'
|
23
|
+
import { NodePlatform } from '@storecraft/platforms/node';
|
24
|
+
import { MongoDB } from '@storecraft/database-mongodb'
|
25
|
+
import { NodeLocalStorage } from '@storecraft/storage-local/node'
|
26
|
+
|
27
|
+
const app = new App(
|
28
|
+
{
|
29
|
+
auth_admins_emails: ['admin@sc.com'],
|
30
|
+
auth_secret_access_token: 'auth_secret_access_token',
|
31
|
+
auth_secret_refresh_token: 'auth_secret_refresh_token'
|
32
|
+
}
|
33
|
+
)
|
34
|
+
.withPlatform(new NodePlatform())
|
35
|
+
.withDatabase(new MongoDB({ db_name: 'test'}))
|
36
|
+
|
37
|
+
await app.init();
|
38
|
+
|
39
|
+
const server = http.createServer(app.handler).listen(
|
40
|
+
8000,
|
41
|
+
() => {
|
42
|
+
console.log(`Server is running on http://localhost:8000`);
|
43
|
+
}
|
44
|
+
);
|
45
|
+
|
46
|
+
```
|
47
|
+
|
48
|
+
```text
|
49
|
+
Author: Tomer Shalev <tomer.shalev@gmail.com>
|
50
|
+
```
|
package/db-strategy.md
ADDED
@@ -0,0 +1,284 @@
|
|
1
|
+
# MongoDB data modeling
|
2
|
+
|
3
|
+
We embed some relation info in documents to model relations.
|
4
|
+
```js
|
5
|
+
{
|
6
|
+
_relations: {
|
7
|
+
[collection_name]: {
|
8
|
+
ids: [ objectId(0), objectId(1)....],
|
9
|
+
entries: {
|
10
|
+
0: {
|
11
|
+
... data
|
12
|
+
},
|
13
|
+
1: {
|
14
|
+
... more data
|
15
|
+
}
|
16
|
+
}
|
17
|
+
}
|
18
|
+
}
|
19
|
+
}
|
20
|
+
```
|
21
|
+
|
22
|
+
- `ids` array list all the ids of the related documents and
|
23
|
+
help with querying a relation if we need.
|
24
|
+
- `entries` are embedding of these documents keyed by id and value is a document, this
|
25
|
+
is used for super fast read.
|
26
|
+
- We use some of this relations to expand relations and return them to the user,
|
27
|
+
as long these relations are not too big. For example, product will have on avarage 2
|
28
|
+
related colletions, therefore we don't mind embedding the collections inside a product,
|
29
|
+
so when a user wants a product with expanded related collections, he will get them the fastest.
|
30
|
+
- In general we favour FAST READS over FAST WRITES.
|
31
|
+
- so product-->collections, we don't mind expanding.
|
32
|
+
- but, collection-->products, we do mind and will use a different strategy.
|
33
|
+
- In case, we have a large collection, we opt for querying.
|
34
|
+
For example a collection `shirts` can relate to 5000 products, in this case,
|
35
|
+
we offer a way to query the products constrained on collection name, which means, that
|
36
|
+
we don't expand as above and return everything but use a different pathway. We have many ways
|
37
|
+
to achieve this, either by the built in search terms array.
|
38
|
+
|
39
|
+
|
40
|
+
## products --> collections
|
41
|
+
Each product has the following relation:
|
42
|
+
```js
|
43
|
+
product._relations.collections: {
|
44
|
+
// object ids of related collections
|
45
|
+
ids: ObjectId[],
|
46
|
+
// the collections documents
|
47
|
+
entries: Record<ID_STRING, CollectionType>
|
48
|
+
}
|
49
|
+
```
|
50
|
+
|
51
|
+
How connections intentions from product to collections are formed ?
|
52
|
+
Connections are given in the `product.collections = [ { id:'id1' }, ..]` property.
|
53
|
+
We use these as user intention to form a connection in the database. super convenient
|
54
|
+
|
55
|
+
`Product SAVE:`
|
56
|
+
- for each related collection add the entry `_relations.collections.entries[col-id]=col` in the product document.
|
57
|
+
- for each related collection add the collection `ObjectId` to array `_relations.collections.ids` in the product document.
|
58
|
+
|
59
|
+
`Product DELETE:`
|
60
|
+
- Nothing todo
|
61
|
+
|
62
|
+
`Collection SAVE:`
|
63
|
+
- update each related product document with `_relations.collections.entries[col-id] = c`
|
64
|
+
|
65
|
+
`Collection DELETE:`
|
66
|
+
- delete in each related product the entry `_relations.collections.entries[col-id]`
|
67
|
+
- remove in each related product the `ObjectId` from array `_relations.collections.ids`
|
68
|
+
- remove in each related product the `[col:col-handle, col:col-id` from array `search` in the products documents.
|
69
|
+
|
70
|
+
|
71
|
+
## products --> products variants
|
72
|
+
Each product has the following relation:
|
73
|
+
```js
|
74
|
+
product._relations.variants: {
|
75
|
+
// object ids of related collections
|
76
|
+
ids: ObjectId[],
|
77
|
+
// the variants documents
|
78
|
+
entries: Record<ID_STRING, ProductType>
|
79
|
+
}
|
80
|
+
```
|
81
|
+
|
82
|
+
Variant is any product, that has
|
83
|
+
- `parent_handle` property
|
84
|
+
- `parent_id` property
|
85
|
+
- `variant_hint` property
|
86
|
+
|
87
|
+
How connections intentions from product to variants are formed ?
|
88
|
+
As opposed to `products <--> collections`, connections are made implicitly, because of
|
89
|
+
the async nature of connecting and creating variants. In the future, I will add possibility
|
90
|
+
to add variants like collections, i.e, explicitly.
|
91
|
+
- async a product is created with `parent_handle` property set.
|
92
|
+
|
93
|
+
Notes:
|
94
|
+
- Currently, I have no use for the `ids`, but it might change (because each sub variant has one parent).
|
95
|
+
- Variants are always created by a product, that specify he has a parent. i.e, we rely on
|
96
|
+
integrity, so use it with care.
|
97
|
+
|
98
|
+
|
99
|
+
`Variant SAVE`:
|
100
|
+
- save the product ofcourse
|
101
|
+
- use `parent_id` to:
|
102
|
+
- add variant `ObjectId` into the parent's `_relations.variants.ids`
|
103
|
+
- update variant document in the parent's `_relations.collections.entries[variant-id]=variant`
|
104
|
+
|
105
|
+
`Variant DELETE:`
|
106
|
+
- use `parent_id` to:
|
107
|
+
- delete in parent the entry `_relations.variants.entries[variant-id]`
|
108
|
+
- remove in parent the `ObjectId` from array `_relations.variants.ids`
|
109
|
+
|
110
|
+
`Product (parent) SAVE:`
|
111
|
+
- nothing, we don't support explicit relations yet
|
112
|
+
|
113
|
+
`Product (parent) DELETE:`
|
114
|
+
- delete all the children products
|
115
|
+
- delete the parent
|
116
|
+
|
117
|
+
|
118
|
+
## products --> discounts
|
119
|
+
Again, a relation, that connects a collection with many entries (`products`) to one with
|
120
|
+
much fewer (`discounts`). Therefore, we embed documents in.
|
121
|
+
|
122
|
+
Each product has the following relation:
|
123
|
+
```js
|
124
|
+
product._relations.discounts: {
|
125
|
+
// object ids of related discounts
|
126
|
+
ids: ObjectId[],
|
127
|
+
// the variants documents
|
128
|
+
entries: Record<ID_STRING, DiscountType>
|
129
|
+
}
|
130
|
+
```
|
131
|
+
|
132
|
+
Notes:
|
133
|
+
- like `variants`, relation connections are made implictly.
|
134
|
+
- Everytime a discount is saved, it needs to form connection with eligible products.
|
135
|
+
- Everytime a product is saved, it has to remove all of it's discounts and re-test itself.
|
136
|
+
|
137
|
+
|
138
|
+
`Discount SAVE`:
|
139
|
+
- Remove discount info from every related product:
|
140
|
+
- remove discount `ObjectId` from the product's `_relations.discounts.ids` array
|
141
|
+
- unset discount document from the product's `_relations.discounts.entries[discount-id]`
|
142
|
+
- remove [`discount:discount-handle`, `discount:discount-id`] from the product's `search`
|
143
|
+
|
144
|
+
- query the eligible products of the discount:
|
145
|
+
- for each eligible product:
|
146
|
+
- add discount `ObjectId` into the product's `_relations.discounts.ids` array
|
147
|
+
- update discount document in the product's `_relations.discounts.entries[discount-id]=discount`
|
148
|
+
- add [`discount:discount-handle`, `discount:discount-id`] to the product's `search`
|
149
|
+
|
150
|
+
`Discount DELETE`:
|
151
|
+
- Remove discount info from every related product:
|
152
|
+
- remove discount `ObjectId` from the product's `_relations.discounts.ids` array
|
153
|
+
- unset discount document from the product's `_relations.discounts.entries[discount-id]`
|
154
|
+
- remove [`discount:discount-handle`, `discount:discount-id`] from the product's `search`
|
155
|
+
- remove discount document
|
156
|
+
|
157
|
+
|
158
|
+
`Product SAVE:`
|
159
|
+
Product might have changed, therefore it needs to be re-tested for discounts, BUT,
|
160
|
+
it is not OKAY to punish all products saves, usually, it is only required if
|
161
|
+
tags/collections/price changes
|
162
|
+
- delete the product's self relations `_relations.discounts`
|
163
|
+
- get all discounts of type product, which are active and for each:
|
164
|
+
- test eligibility for discount locally,
|
165
|
+
- If eligible:
|
166
|
+
- add discount `ObjectId` into the product's `_relations.discounts.ids` array
|
167
|
+
- update discount document in the product's `_relations.discounts.entries[discount-id]=discount`
|
168
|
+
- add [`discount:discount-handle`, `discount:discount-id`] into the product's `search`
|
169
|
+
|
170
|
+
`Product DELETE:`
|
171
|
+
- Do nothing
|
172
|
+
|
173
|
+
|
174
|
+
## storefronts --> products, collections, discounts, shipping, posts
|
175
|
+
We are going to create explicit connections, (the way `product` connects `collections`).
|
176
|
+
|
177
|
+
Each product has the following relation:
|
178
|
+
```js
|
179
|
+
storefront._relations: {
|
180
|
+
products: {
|
181
|
+
ids: ObjectId[],
|
182
|
+
entries: Record<ID_STRING, ProductType>
|
183
|
+
},
|
184
|
+
collections: {
|
185
|
+
ids: ObjectId[],
|
186
|
+
entries: Record<ID_STRING, CollectionType>
|
187
|
+
},
|
188
|
+
discounts: {
|
189
|
+
ids: ObjectId[],
|
190
|
+
entries: Record<ID_STRING, DiscountType>
|
191
|
+
},
|
192
|
+
shipping: {
|
193
|
+
ids: ObjectId[],
|
194
|
+
entries: Record<ID_STRING, ShippingMethodType>
|
195
|
+
},
|
196
|
+
posts: {
|
197
|
+
ids: ObjectId[],
|
198
|
+
entries: Record<ID_STRING, PostType>
|
199
|
+
},
|
200
|
+
}
|
201
|
+
```
|
202
|
+
|
203
|
+
`Storefront SAVE:`
|
204
|
+
- For Each product in `storefront.products`:
|
205
|
+
- Add `_relations.products.entries[product-id]=product`.
|
206
|
+
- Add `ObjectId` to `_relations.products.ids`.
|
207
|
+
|
208
|
+
- For Each collection in `storefront.collections`:
|
209
|
+
- Add `_relations.collections.entries[collection-id]=collection`.
|
210
|
+
- Add `ObjectId` to `_relations.collections.ids`.
|
211
|
+
|
212
|
+
- For Each discount in `storefront.discounts`:
|
213
|
+
- Add `_relations.discounts.entries[discount-id]=discount`.
|
214
|
+
- Add `ObjectId` to `_relations.discounts.ids`.
|
215
|
+
|
216
|
+
- For Each shipping method in `storefront.shipping_methods`:
|
217
|
+
- Add `_relations.shipping_methods.entries[shipping_method-id]=shipping_method`.
|
218
|
+
- Add `ObjectId` to `_relations.shipping_methods.ids`.
|
219
|
+
|
220
|
+
- For Each post in `storefront.posts`:
|
221
|
+
- Add `_relations.posts.entries[post-id]=post`.
|
222
|
+
- Add `ObjectId` to `_relations.posts.ids`.
|
223
|
+
|
224
|
+
`Storefront DELETE:`
|
225
|
+
- Nothing todo
|
226
|
+
|
227
|
+
`product/collection/discount/shipping/post SAVE:`
|
228
|
+
- update each related `storefront` document with `storefront._relations.products.entries[product-id] = product`
|
229
|
+
- update each related `storefront` document with `storefront._relations.collections.entries[collection-id] = collection`
|
230
|
+
- update each related `storefront` document with `storefront._relations.discounts.entries[discount-id] = discount`
|
231
|
+
- update each related `storefront` document with `storefront._relations.shipping_methods.entries[shipping_method-id] = shipping_method`
|
232
|
+
- update each related `storefront` document with `storefront._relations.posts.entries[post-id] = post`
|
233
|
+
|
234
|
+
|
235
|
+
`product/collection/discount/shipping/post DELETE:`
|
236
|
+
- on `product delete`:
|
237
|
+
- delete in each related `storefront` the entry `storefront._relations.products.entries[product-id]`
|
238
|
+
- remove in each related `storefront` the `ObjectId` from array `storefront._relations.products.ids`
|
239
|
+
- on `collection delete`:
|
240
|
+
- delete in each related `storefront` the entry `storefront._relations.collections.entries[collection-id]`
|
241
|
+
- remove in each related `storefront` the `ObjectId` from array `storefront._relations.collections.ids`
|
242
|
+
- on `discount delete`:
|
243
|
+
- delete in each related `storefront` the entry `storefront._relations.discounts.entries[discount-id]`
|
244
|
+
- remove in each related `storefront` the `ObjectId` from array `storefront._relations.discounts.ids`
|
245
|
+
- on `shipping_method delete`:
|
246
|
+
- delete in each related `storefront` the entry `storefront._relations.shipping_methods.entries[shipping_method-id]`
|
247
|
+
- remove in each related `storefront` the `ObjectId` from array `storefront._relations.shipping_methods.ids`
|
248
|
+
- on `post delete`:
|
249
|
+
- delete in each related `storefront` the entry `storefront._relations.posts.entries[post-id]`
|
250
|
+
- remove in each related `storefront` the `ObjectId` from array `storefront._relations.posts.ids`
|
251
|
+
|
252
|
+
|
253
|
+
|
254
|
+
## images
|
255
|
+
images are immutable, can only be created or deleted. They are not normalized in places used.
|
256
|
+
|
257
|
+
**products/collections/discounts/posts/shipping/storefronts**.{media} --> `image.url`
|
258
|
+
|
259
|
+
Some collections have `media[]` array, and we use it to keep track of used images in
|
260
|
+
the system, it is a soft feature. no hard relations on them.
|
261
|
+
|
262
|
+
`image DELETE`
|
263
|
+
- remove from storage if it's there
|
264
|
+
- for each `products/collections/discounts/posts/shipping/storefronts`, that has image url
|
265
|
+
in it's `media` array, simply remove it.
|
266
|
+
|
267
|
+
`products/collections/discounts/posts/shipping/storefronts GET`:
|
268
|
+
- fetch storage settings
|
269
|
+
- for each media url entry, if it has `storage://`, rewrite it for CDN setting
|
270
|
+
|
271
|
+
`products/collections/discounts/posts/shipping/storefronts SAVE`:
|
272
|
+
- for each media url entry:
|
273
|
+
- upsert `image` record with the url
|
274
|
+
|
275
|
+
## customers --> auth_user
|
276
|
+
they are related through `customer.auth_id` field
|
277
|
+
|
278
|
+
* `au_{id} == cus_{id}`, the postfix is the same
|
279
|
+
|
280
|
+
`customer DELETE:`
|
281
|
+
- delete the auth user references by `customer.auth_id`
|
282
|
+
|
283
|
+
`auth_user DELETE:`
|
284
|
+
- do nothing
|
package/index.js
ADDED
@@ -0,0 +1,193 @@
|
|
1
|
+
import { App } from '@storecraft/core';
|
2
|
+
import { Collection, MongoClient, ServerApiVersion } from 'mongodb';
|
3
|
+
import { impl as auth_users } from './src/con.auth_users.js';
|
4
|
+
import { impl as collections } from './src/con.collections.js';
|
5
|
+
import { impl as customers } from './src/con.customers.js';
|
6
|
+
import { impl as discounts } from './src/con.discounts.js';
|
7
|
+
import { impl as images } from './src/con.images.js';
|
8
|
+
import { impl as notifications } from './src/con.notifications.js';
|
9
|
+
import { impl as orders } from './src/con.orders.js';
|
10
|
+
import { impl as posts } from './src/con.posts.js';
|
11
|
+
import { impl as products } from './src/con.products.js';
|
12
|
+
import { impl as shipping } from './src/con.shipping.js';
|
13
|
+
import { impl as storefronts } from './src/con.storefronts.js';
|
14
|
+
import { impl as tags } from './src/con.tags.js';
|
15
|
+
import { impl as templates } from './src/con.templates.js';
|
16
|
+
import { impl as search } from './src/con.search.js';
|
17
|
+
export { migrateToLatest } from './migrate.js';
|
18
|
+
|
19
|
+
/**
|
20
|
+
* @typedef {Partial<import('./types.public.d.ts').Config>} Config
|
21
|
+
*/
|
22
|
+
|
23
|
+
/**
|
24
|
+
*
|
25
|
+
* @param {string} uri
|
26
|
+
* @param {import('mongodb').MongoClientOptions} [options]
|
27
|
+
*/
|
28
|
+
const connect = async (uri, options) => {
|
29
|
+
|
30
|
+
options = options ?? {
|
31
|
+
ignoreUndefined: true,
|
32
|
+
serverApi: {
|
33
|
+
version: ServerApiVersion.v1,
|
34
|
+
strict: true,
|
35
|
+
deprecationErrors: true,
|
36
|
+
|
37
|
+
}
|
38
|
+
}
|
39
|
+
const client = new MongoClient(uri, options);
|
40
|
+
|
41
|
+
return client.connect();
|
42
|
+
}
|
43
|
+
|
44
|
+
/**
|
45
|
+
* @typedef {import('@storecraft/core/v-database').db_driver} db_driver
|
46
|
+
*
|
47
|
+
*
|
48
|
+
* @implements {db_driver}
|
49
|
+
*/
|
50
|
+
export class MongoDB {
|
51
|
+
|
52
|
+
/**
|
53
|
+
*
|
54
|
+
* @type {boolean}
|
55
|
+
*/
|
56
|
+
#_is_ready;
|
57
|
+
|
58
|
+
/**
|
59
|
+
*
|
60
|
+
* @type {App<any, any, any>}
|
61
|
+
*/
|
62
|
+
#_app;
|
63
|
+
|
64
|
+
/**
|
65
|
+
*
|
66
|
+
* @type {MongoClient}
|
67
|
+
*/
|
68
|
+
#_mongo_client;
|
69
|
+
|
70
|
+
/**
|
71
|
+
*
|
72
|
+
* @type {Config}
|
73
|
+
*/
|
74
|
+
#_config;
|
75
|
+
|
76
|
+
// /** @type {db_driver["resources"]} */
|
77
|
+
// #_resources;
|
78
|
+
|
79
|
+
/**
|
80
|
+
*
|
81
|
+
* @param {Config} [config] config, if undefined,
|
82
|
+
* env variables `MONGODB_URL` will be used for uri upon init later
|
83
|
+
*/
|
84
|
+
constructor(config) {
|
85
|
+
this.#_is_ready = false;
|
86
|
+
this.#_config = config;
|
87
|
+
}
|
88
|
+
|
89
|
+
/**
|
90
|
+
*
|
91
|
+
* @param {App} app
|
92
|
+
*
|
93
|
+
*
|
94
|
+
* @returns {Promise<this>}
|
95
|
+
*/
|
96
|
+
async init(app) {
|
97
|
+
if(this.isReady)
|
98
|
+
return this;
|
99
|
+
const c = this.#_config;
|
100
|
+
this.#_config = {
|
101
|
+
...c,
|
102
|
+
url: c?.url ?? app.platform.env.MONGODB_URL,
|
103
|
+
db_name: c?.db_name ?? app.platform.env.MONGODB_NAME ?? 'main'
|
104
|
+
}
|
105
|
+
|
106
|
+
this.#_mongo_client = await connect(
|
107
|
+
this.config.url,
|
108
|
+
this.config.options
|
109
|
+
);
|
110
|
+
|
111
|
+
this.#_app = app;
|
112
|
+
|
113
|
+
this.resources = {
|
114
|
+
auth_users: auth_users(this),
|
115
|
+
collections: collections(this),
|
116
|
+
customers: customers(this),
|
117
|
+
discounts: discounts(this),
|
118
|
+
images: images(this),
|
119
|
+
notifications: notifications(this),
|
120
|
+
orders: orders(this),
|
121
|
+
posts: posts(this),
|
122
|
+
products: products(this),
|
123
|
+
storefronts: storefronts(this),
|
124
|
+
tags: tags(this),
|
125
|
+
shipping_methods: shipping(this),
|
126
|
+
templates: templates(this),
|
127
|
+
search: search(this),
|
128
|
+
}
|
129
|
+
|
130
|
+
this.#_is_ready = true;
|
131
|
+
|
132
|
+
return this;
|
133
|
+
}
|
134
|
+
|
135
|
+
async disconnect() {
|
136
|
+
await this.mongo_client.close(true);
|
137
|
+
return true;
|
138
|
+
}
|
139
|
+
|
140
|
+
get isReady() { return this.#_is_ready; }
|
141
|
+
|
142
|
+
throwIfNotReady() {
|
143
|
+
if(!this.isReady)
|
144
|
+
throw new Error('Database not ready !!! you need to `.init()` it')
|
145
|
+
}
|
146
|
+
|
147
|
+
/**
|
148
|
+
*
|
149
|
+
* @description database name
|
150
|
+
*/
|
151
|
+
get name () {
|
152
|
+
return this.config.db_name ?? 'main';
|
153
|
+
}
|
154
|
+
|
155
|
+
/**
|
156
|
+
*
|
157
|
+
* @description Get the `storecraft` app
|
158
|
+
*/
|
159
|
+
get app() {
|
160
|
+
return this.#_app;
|
161
|
+
}
|
162
|
+
|
163
|
+
/**
|
164
|
+
*
|
165
|
+
* @description Get the native `mongodb` client
|
166
|
+
*/
|
167
|
+
get mongo_client() {
|
168
|
+
return this.#_mongo_client;
|
169
|
+
}
|
170
|
+
|
171
|
+
/**
|
172
|
+
*
|
173
|
+
* @description Get the config object
|
174
|
+
*/
|
175
|
+
get config() {
|
176
|
+
return this.#_config;
|
177
|
+
}
|
178
|
+
|
179
|
+
/**
|
180
|
+
*
|
181
|
+
* @template {import('@storecraft/core/v-api').BaseType} T
|
182
|
+
*
|
183
|
+
*
|
184
|
+
* @param {string} name
|
185
|
+
*
|
186
|
+
*
|
187
|
+
* @returns {Collection<T>}
|
188
|
+
*/
|
189
|
+
collection(name) {
|
190
|
+
return this.mongo_client.db(this.name).collection(name);
|
191
|
+
}
|
192
|
+
|
193
|
+
}
|
package/jsconfig.json
ADDED
package/migrate.js
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
2
|
+
import { MongoDB } from "./index.js";
|
3
|
+
import { config, up } from 'migrate-mongo';
|
4
|
+
import * as path from 'node:path';
|
5
|
+
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
7
|
+
const __dirname = path.dirname(__filename);
|
8
|
+
|
9
|
+
/**
|
10
|
+
*
|
11
|
+
* @param {MongoDB} driver
|
12
|
+
* @param {boolean} [destroy_db_upon_completion=true]
|
13
|
+
*/
|
14
|
+
export async function migrateToLatest(driver, destroy_db_upon_completion=true) {
|
15
|
+
console.log('Starting Migrations');
|
16
|
+
|
17
|
+
const config_migrate = {
|
18
|
+
migrationsDir: path.join(__dirname, 'migrations'),
|
19
|
+
changelogCollectionName: "migrations",
|
20
|
+
migrationFileExtension: ".js"
|
21
|
+
};
|
22
|
+
|
23
|
+
config.set(config_migrate);
|
24
|
+
|
25
|
+
driver.mongo_client.__db_driver = driver;
|
26
|
+
|
27
|
+
const results = await up(
|
28
|
+
driver.mongo_client.db(driver.name),
|
29
|
+
driver.mongo_client
|
30
|
+
);
|
31
|
+
|
32
|
+
console.log('results: \n', results)
|
33
|
+
|
34
|
+
if(destroy_db_upon_completion) {
|
35
|
+
console.log('disconnecting from mongo');
|
36
|
+
const success = await driver.disconnect();
|
37
|
+
console.log(`- success: ${success}`);
|
38
|
+
}
|
39
|
+
}
|
@@ -0,0 +1,60 @@
|
|
1
|
+
import { Db, MongoClient } from 'mongodb';
|
2
|
+
|
3
|
+
const collections = [
|
4
|
+
'auth_users', 'collections', 'customers', 'discounts',
|
5
|
+
'images', 'notifications', 'orders', 'posts',
|
6
|
+
'products', 'shipping_methods', 'storefronts',
|
7
|
+
'tags', 'templates'
|
8
|
+
];
|
9
|
+
|
10
|
+
/**
|
11
|
+
*
|
12
|
+
* @param {Db} db
|
13
|
+
* @param {MongoClient} client
|
14
|
+
*/
|
15
|
+
export async function up(db, client) {
|
16
|
+
|
17
|
+
const session = client.startSession();
|
18
|
+
try {
|
19
|
+
await session.withTransaction(async () => {
|
20
|
+
for (const collection_name of collections) {
|
21
|
+
|
22
|
+
await db.collection(collection_name).dropIndexes({ session });
|
23
|
+
|
24
|
+
await db.collection(collection_name).createIndexes(
|
25
|
+
[
|
26
|
+
{
|
27
|
+
key: { handle: 1 }, name: 'handle+',
|
28
|
+
background: false, unique: true, sparse: true
|
29
|
+
},
|
30
|
+
{
|
31
|
+
key: { updated_at: 1, _id: 1 }, name: '(updated_at+, _id+)',
|
32
|
+
background: false, sparse: true
|
33
|
+
},
|
34
|
+
{
|
35
|
+
key: { updated_at: -1, _id: -1 }, name: '(updated_at-, _id-)',
|
36
|
+
background: false, sparse: true
|
37
|
+
},
|
38
|
+
{
|
39
|
+
key: { search: 1 }, name: '(search+)',
|
40
|
+
background: false, sparse: true
|
41
|
+
},
|
42
|
+
], {
|
43
|
+
session
|
44
|
+
}
|
45
|
+
)
|
46
|
+
}
|
47
|
+
|
48
|
+
});
|
49
|
+
} finally {
|
50
|
+
await session.endSession();
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
/**
|
55
|
+
*
|
56
|
+
* @param {Db} db
|
57
|
+
* @param {MongoClient} client
|
58
|
+
*/
|
59
|
+
export async function down(db, client) {
|
60
|
+
}
|