monastery 3.0.15 → 3.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/changelog.md CHANGED
@@ -2,6 +2,8 @@
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.16](https://github.com/boycce/monastery/compare/3.0.15...3.0.16) (2024-05-02)
6
+
5
7
  ### [3.0.15](https://github.com/boycce/monastery/compare/3.0.14...3.0.15) (2024-05-01)
6
8
 
7
9
  ### [3.0.14](https://github.com/boycce/monastery/compare/3.0.13...3.0.14) (2024-05-01)
@@ -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
- model: 'book'
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 via a find operation.
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. Although at this time arrays are not supported,
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`](./...rawMethods), or simply pass a MongoDB lookup object to populate. When passing a lookup object, the
83
- populated field still needs to be defined in `definition.fields` if you want to call any related hooks,
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
- myBooks: [{
111
- model: 'book',
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: { id: '$_id' },
124
+ let: { userId: '$_id' },
123
125
  pipeline: [{
124
126
  $match: {
125
127
  $expr: {
126
- $eq: ['$bookOwnerId', '$$id']
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
- // Convert array into a document for non-array targets
145
- if (!arrayTarget) {
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
- ...(util.isDefined(opts.sort) ? [{ $sort: opts.sort }] : []),
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,
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.15",
5
+ "version": "3.0.16",
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": "^16.5.4",
40
- "mongodb": "^5.9.2",
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
  },
@@ -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/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('bird', { fields: {
87
- name: { type: 'string' },
88
- }})
89
- let user = db.model('user', { fields: {
90
- birds: [{ model: 'bird' }],
91
- animal: { birds: [{ model: 'bird' }] },
92
- animals: [{ bird: { model: 'bird' }, num: { type: 'number' } }],
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' }})