monastery 3.0.15 → 3.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/changelog.md +4 -0
- package/docs/model/find.md +23 -21
- package/lib/model-crud.js +28 -10
- package/lib/util.js +4 -0
- package/package.json +3 -3
- package/plugins/images/index.js +3 -2
- package/test/blacklisting.js +11 -3
- package/test/populate.js +12 -8
package/changelog.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [3.0.17](https://github.com/boycce/monastery/compare/3.0.16...3.0.17) (2024-05-02)
|
|
6
|
+
|
|
7
|
+
### [3.0.16](https://github.com/boycce/monastery/compare/3.0.15...3.0.16) (2024-05-02)
|
|
8
|
+
|
|
5
9
|
### [3.0.15](https://github.com/boycce/monastery/compare/3.0.14...3.0.15) (2024-05-01)
|
|
6
10
|
|
|
7
11
|
### [3.0.14](https://github.com/boycce/monastery/compare/3.0.13...3.0.14) (2024-05-01)
|
package/docs/model/find.md
CHANGED
|
@@ -58,30 +58,27 @@ The value of the model reference should be an ID, e.g. `myBook = id`
|
|
|
58
58
|
```js
|
|
59
59
|
{
|
|
60
60
|
fields: {
|
|
61
|
-
myBook: {
|
|
62
|
-
|
|
63
|
-
}
|
|
61
|
+
myBook: { model: 'book' },
|
|
62
|
+
myBooks: [{ model: 'books' }], // you can even populate arrays
|
|
64
63
|
}
|
|
65
64
|
}
|
|
66
65
|
```
|
|
67
66
|
|
|
68
|
-
You are then able to easily populate returned results
|
|
67
|
+
You are then able to easily populate the returned results in the find method:
|
|
69
68
|
|
|
70
69
|
```js
|
|
71
|
-
user.find({ query: {...}, populate: ['myBook'] })
|
|
70
|
+
user.find({ query: {...}, populate: ['myBook', 'myBooks', ...] })
|
|
72
71
|
```
|
|
73
72
|
|
|
74
|
-
You can also populate within embedded document fields.
|
|
75
|
-
you would need to use the [example below](#populate-multiple-documents-into-virtual-fields).
|
|
73
|
+
You can also populate within embedded document fields.
|
|
76
74
|
```js
|
|
77
75
|
user.find({ query: {...}, populate: ['myBooks.book'] })
|
|
78
76
|
```
|
|
79
77
|
|
|
80
78
|
### Custom Populate Query
|
|
81
79
|
|
|
82
|
-
If you would like more control you can either use Mongo's `aggregate` method via [`model._aggregate`](
|
|
83
|
-
populated field still needs to be defined in `definition.fields` if you want to
|
|
84
|
-
and prune any blacklisted fields. See the examples below,
|
|
80
|
+
If you would like more control you can either use Mongo's `aggregate` method via [`model._aggregate`](./rawMethods), or simply pass a MongoDB lookup object to `populate`. When passing a lookup object, the
|
|
81
|
+
populated field still needs to be defined in `definition.fields` if you want Monastery to process any related hooks, field blacklisting and default values. See the example belows,
|
|
85
82
|
|
|
86
83
|
#### Populate a single document
|
|
87
84
|
|
|
@@ -93,37 +90,42 @@ user.find({
|
|
|
93
90
|
populate: [{
|
|
94
91
|
as: 'myBook',
|
|
95
92
|
from: 'book',
|
|
93
|
+
foreignField: '_id',
|
|
96
94
|
localField: 'myBook',
|
|
97
|
-
foreignField: '_id'
|
|
98
95
|
}]
|
|
99
96
|
})
|
|
100
97
|
```
|
|
101
98
|
|
|
102
99
|
#### Populate multiple documents into virtual fields
|
|
103
100
|
|
|
104
|
-
Below populates all books into `user.myBooks` with a `bookOwnerId` equal to the `user._id`. Since `myBooks`
|
|
105
|
-
isn't stored on the user, you will need define it as virtual field
|
|
101
|
+
Below populates all books into `user.myBooks` with a `bookOwnerId` equal to the `user._id`. Since `myBooks` isn't stored on the user, you will need define it as virtual field.
|
|
106
102
|
|
|
107
103
|
```js
|
|
108
|
-
{
|
|
104
|
+
db.model('user', {
|
|
109
105
|
fields: {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
virtual: true
|
|
113
|
-
}],
|
|
106
|
+
name: { type: 'string' },
|
|
107
|
+
myBooks: [{ model: 'book', virtual: true }],
|
|
114
108
|
},
|
|
115
|
-
}
|
|
109
|
+
})
|
|
110
|
+
db.model('book', {
|
|
111
|
+
fields: {
|
|
112
|
+
bookTitle: { type: 'string' },
|
|
113
|
+
bookOwnerId: { model: 'user' },
|
|
114
|
+
},
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
// books and user inserted here...
|
|
116
118
|
|
|
117
119
|
user.find({
|
|
118
120
|
query: {...},
|
|
119
121
|
populate: [{
|
|
120
122
|
as: 'myBooks',
|
|
121
123
|
from: 'book',
|
|
122
|
-
let: {
|
|
124
|
+
let: { userId: '$_id' },
|
|
123
125
|
pipeline: [{
|
|
124
126
|
$match: {
|
|
125
127
|
$expr: {
|
|
126
|
-
$eq: ['$bookOwnerId', '$$
|
|
128
|
+
$eq: ['$bookOwnerId', '$$userId']
|
|
127
129
|
}
|
|
128
130
|
}
|
|
129
131
|
}]
|
package/lib/model-crud.js
CHANGED
|
@@ -141,24 +141,40 @@ Model.prototype.find = async function (opts, _one) {
|
|
|
141
141
|
)
|
|
142
142
|
continue
|
|
143
143
|
}
|
|
144
|
-
|
|
145
|
-
|
|
144
|
+
if (arrayTarget) {
|
|
145
|
+
// Create lookup
|
|
146
|
+
lookups.push({
|
|
147
|
+
$lookup: {
|
|
148
|
+
as: path,
|
|
149
|
+
from: modelName,
|
|
150
|
+
let: { arraypath: '$' + path }, // host array path
|
|
151
|
+
pipeline: [
|
|
152
|
+
// Populating an array doesn't return sorted in the original sort order
|
|
153
|
+
{ $sort: { _id: 1 } },
|
|
154
|
+
{ $match: { $expr: { $in: ['$_id', '$$arraypath'] } } },
|
|
155
|
+
],
|
|
156
|
+
},
|
|
157
|
+
})
|
|
158
|
+
} else {
|
|
159
|
+
// Convert array into a document for non-array targets
|
|
146
160
|
(opts.addFields = opts.addFields || {})[path] = { '$arrayElemAt': [ '$' + path, 0 ] }
|
|
161
|
+
// Create basic lookup
|
|
162
|
+
lookups.push({
|
|
163
|
+
$lookup: {
|
|
164
|
+
as: path,
|
|
165
|
+
from: modelName,
|
|
166
|
+
foreignField: '_id',
|
|
167
|
+
localField: path,
|
|
168
|
+
},
|
|
169
|
+
})
|
|
147
170
|
}
|
|
148
|
-
// Create lookup
|
|
149
|
-
lookups.push({ $lookup: {
|
|
150
|
-
from: modelName,
|
|
151
|
-
localField: path,
|
|
152
|
-
foreignField: '_id',
|
|
153
|
-
as: path,
|
|
154
|
-
}})
|
|
155
171
|
}
|
|
156
172
|
}
|
|
157
173
|
// console.log(1, opts.projection)
|
|
158
174
|
// console.log(2, lookups)
|
|
159
175
|
let aggregate = [
|
|
160
176
|
{ $match: opts.query },
|
|
161
|
-
|
|
177
|
+
{ $sort: opts.sort },
|
|
162
178
|
...(util.isDefined(opts.skip) ? [{ $limit: opts.skip }] : []),
|
|
163
179
|
...(util.isDefined(opts.limit) ? [{ $limit: opts.limit }] : []),
|
|
164
180
|
...lookups,
|
|
@@ -449,6 +465,8 @@ Model.prototype._getProjectionFromProject = function (customProject) {
|
|
|
449
465
|
o[v.replace(/^-/, '')] = v.match(/^-/)? 0 : 1
|
|
450
466
|
return o
|
|
451
467
|
}, {})
|
|
468
|
+
} else if (util.isObject(customProject)) {
|
|
469
|
+
projection = customProject
|
|
452
470
|
}
|
|
453
471
|
return projection
|
|
454
472
|
}
|
package/lib/util.js
CHANGED
|
@@ -402,6 +402,10 @@ module.exports = {
|
|
|
402
402
|
return obj
|
|
403
403
|
},
|
|
404
404
|
|
|
405
|
+
setTimeoutPromise: function(ms) {
|
|
406
|
+
return new Promise(res => setTimeout(res, ms))
|
|
407
|
+
},
|
|
408
|
+
|
|
405
409
|
toArray: function(variable) {
|
|
406
410
|
// converts a variable to an array, if not already so
|
|
407
411
|
if (typeof variable === 'undefined') return []
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "monastery",
|
|
3
3
|
"description": "⛪ A simple, straightforward MongoDB ODM",
|
|
4
4
|
"author": "Ricky Boyce",
|
|
5
|
-
"version": "3.0.
|
|
5
|
+
"version": "3.0.17",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": "github:boycce/monastery",
|
|
8
8
|
"homepage": "https://boycce.github.io/monastery/",
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"@aws-sdk/lib-storage": "3.549.0",
|
|
37
37
|
"@aws-sdk/s3-request-presigner": "3.549.0",
|
|
38
38
|
"debug": "4.3.4",
|
|
39
|
-
"file-type": "
|
|
40
|
-
"mongodb": "
|
|
39
|
+
"file-type": "16.5.4",
|
|
40
|
+
"mongodb": "5.9.2",
|
|
41
41
|
"nanoid": "3.2.0",
|
|
42
42
|
"validator": "13.7.0"
|
|
43
43
|
},
|
package/plugins/images/index.js
CHANGED
|
@@ -50,9 +50,10 @@ let plugin = module.exports = {
|
|
|
50
50
|
// v3 examples: https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/javascript_s3_code_examples.html
|
|
51
51
|
this.getS3Client = (useRegion) => {
|
|
52
52
|
const { S3 } = require('@aws-sdk/client-s3')
|
|
53
|
-
const key = useRegion ? '_s3ClientRegional' : '_s3Client'
|
|
53
|
+
const key = '_s3Client'// useRegion ? '_s3ClientRegional' : '_s3Client'
|
|
54
54
|
return this[key] || (this[key] = new S3({
|
|
55
|
-
region: useRegion ? this.awsRegion : undefined,
|
|
55
|
+
// ...(region: useRegion ? this.awsRegion : undefined,
|
|
56
|
+
region: this.awsRegion, // if region is missing it throws an error, but only in production...
|
|
56
57
|
credentials: {
|
|
57
58
|
accessKeyId: this.awsAccessKeyId,
|
|
58
59
|
secretAccessKey: this.awsSecretAccessKey,
|
package/test/blacklisting.js
CHANGED
|
@@ -218,18 +218,26 @@ test('find project basic', async () => {
|
|
|
218
218
|
}})
|
|
219
219
|
|
|
220
220
|
// pass: test inclusion of projections via native mongodb project option
|
|
221
|
-
let
|
|
221
|
+
let find11 = await user.findOne({
|
|
222
222
|
query: user1._id,
|
|
223
|
-
project:
|
|
223
|
+
project: {'animal.name': 1, 'animals.name': 1},
|
|
224
224
|
})
|
|
225
|
-
|
|
225
|
+
const find11Expected = {
|
|
226
226
|
_id: user1._id,
|
|
227
227
|
animal: { name: 'max' },
|
|
228
228
|
animals: [
|
|
229
229
|
{ name: 'ponyo' },
|
|
230
230
|
{ name: 'freddy' },
|
|
231
231
|
],
|
|
232
|
+
}
|
|
233
|
+
expect(find11).toEqual(find11Expected)
|
|
234
|
+
|
|
235
|
+
// pass: test inclusion of projections via native mongodb project option
|
|
236
|
+
let find1 = await user.findOne({
|
|
237
|
+
query: user1._id,
|
|
238
|
+
project: ['animal.name', 'animals.name'],
|
|
232
239
|
})
|
|
240
|
+
expect(find1).toEqual(find11Expected)
|
|
233
241
|
|
|
234
242
|
// pass: test exclusion of projections via native mongodb project option
|
|
235
243
|
let find2 = await user.findOne({
|
package/test/populate.js
CHANGED
|
@@ -83,14 +83,18 @@ test('model populate', async () => {
|
|
|
83
83
|
})
|
|
84
84
|
|
|
85
85
|
test('model populate array', async () => {
|
|
86
|
-
let bird = db.model('
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
86
|
+
let bird = db.model('bird_pop', {
|
|
87
|
+
fields: {
|
|
88
|
+
name: { type: 'string' },
|
|
89
|
+
},
|
|
90
|
+
})
|
|
91
|
+
let user = db.model('user_pop', {
|
|
92
|
+
fields: {
|
|
93
|
+
birds: [{ model: 'bird_pop' }],
|
|
94
|
+
animal: { birds: [{ model: 'bird_pop' }] },
|
|
95
|
+
animals: [{ bird: { model: 'bird_pop' }, num: { type: 'number' } }],
|
|
96
|
+
},
|
|
97
|
+
})
|
|
94
98
|
let bird1 = await bird.insert({ data: { name: 'ponyo' }})
|
|
95
99
|
let bird2 = await bird.insert({ data: { name: 'jack' }})
|
|
96
100
|
let bird3 = await bird.insert({ data: { name: 'sophie' }})
|