assai 1.1.1 → 2.1.0

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.
Files changed (38) hide show
  1. package/README.md +23 -22
  2. package/dist/src/factories/create_mongo_collection.d.mts +60 -18
  3. package/dist/src/types.d.ts +1 -1
  4. package/dist/src/usecases/mongo/operation/aggregate.d.mts +16 -0
  5. package/dist/src/usecases/mongo/operation/bulk_write.d.mts +15 -0
  6. package/dist/src/usecases/mongo/operation/find.d.mts +3 -1
  7. package/dist/src/usecases/mongo/operation/find_one.d.mts +4 -1
  8. package/dist/src/usecases/mongo/operation/find_one_and_delete.d.mts +16 -0
  9. package/dist/src/usecases/mongo/operation/find_one_and_replace.d.mts +18 -0
  10. package/dist/src/usecases/mongo/operation/find_one_and_update.d.mts +18 -0
  11. package/dist/src/usecases/mongo/operation/index.d.mts +5 -0
  12. package/dist/src/usecases/mongo/operation/insert_many.d.mts +3 -1
  13. package/dist/src/usecases/mongo/operation/insert_one.d.mts +3 -1
  14. package/dist/src/usecases/mongo/operation/update_many.d.mts +3 -1
  15. package/dist/src/usecases/mongo/operation/update_one.d.mts +4 -1
  16. package/dist/src/usecases/mongo/transformers/id/rename_find_options.d.mts +3 -4
  17. package/dist/src/usecases/mongo/transformers/input_transformer.d.mts +10 -0
  18. package/dist/src/usecases/mongo/transformers/output_transformer.d.mts +10 -0
  19. package/dist/src/usecases/mongo/transformers/timestamps.d.mts +6 -0
  20. package/jsconfig.json +16 -19
  21. package/package.json +5 -4
  22. package/src/factories/create_mongo_collection.mjs +75 -68
  23. package/src/types.ts +1 -15
  24. package/src/usecases/mongo/operation/aggregate.mjs +26 -0
  25. package/src/usecases/mongo/operation/bulk_write.mjs +54 -0
  26. package/src/usecases/mongo/operation/find.mjs +7 -6
  27. package/src/usecases/mongo/operation/find_one.mjs +9 -2
  28. package/src/usecases/mongo/operation/find_one_and_delete.mjs +23 -0
  29. package/src/usecases/mongo/operation/find_one_and_replace.mjs +24 -0
  30. package/src/usecases/mongo/operation/find_one_and_update.mjs +40 -0
  31. package/src/usecases/mongo/operation/index.mjs +5 -0
  32. package/src/usecases/mongo/operation/insert_many.mjs +7 -4
  33. package/src/usecases/mongo/operation/insert_one.mjs +10 -4
  34. package/src/usecases/mongo/operation/update_many.mjs +22 -1
  35. package/src/usecases/mongo/operation/update_one.mjs +23 -1
  36. package/src/usecases/mongo/transformers/input_transformer.mjs +33 -0
  37. package/src/usecases/mongo/transformers/output_transformer.mjs +19 -0
  38. package/src/usecases/mongo/transformers/timestamps.mjs +28 -0
@@ -1,8 +1,9 @@
1
1
  import { Collection } from 'mongodb'
2
2
  import {
3
+ aggregate, bulkWrite,
3
4
  count, deleteMany, deleteOne,
4
5
  deleteOneOrThrow,
5
- find, findOne,
6
+ find, findOne, findOneAndDelete, findOneAndReplace, findOneAndUpdate,
6
7
  getClient,
7
8
  insertMany, insertOne, updateMany, updateOne
8
9
  } from '../usecases/mongo/index.mjs'
@@ -13,8 +14,7 @@ import {
13
14
  * This method will read the string DATABASE_URL to create a connection. If you have it in another
14
15
  * location, you will need to pass it at `connectionString` property inside the options parameter.
15
16
  *
16
- * The connection is cached by default. Use `collectionGetter` and `cachedCollectionGetter` to
17
- * customize this behavior.
17
+ * The connection is cached by default. Use `collectionGetter` to customize this behavior.
18
18
  * @template {import('../types.js').MongoDocument} T
19
19
  * @param {string} name
20
20
  * @param {IcreateCollectionOptions<T>} [options]
@@ -23,12 +23,12 @@ export function createMongoCollection(name, options = {}) {
23
23
 
24
24
  /** @type {Collection<T> | null} */
25
25
  let _collection = null
26
- const { validator } = options
26
+ const collectionOptions = options
27
27
 
28
28
  async function getCollection() {
29
29
  const {
30
30
  connectionString, dbName, options: createOptions,
31
- cachableCollectionGetter, collectionGetter,
31
+ collectionGetter,
32
32
  } = options
33
33
 
34
34
  // Connection getter from options
@@ -37,12 +37,6 @@ export function createMongoCollection(name, options = {}) {
37
37
  // Checking cache
38
38
  if (_collection) return _collection
39
39
 
40
- // Cache function from options
41
- if (cachableCollectionGetter != null) {
42
- _collection = await cachableCollectionGetter()
43
- return _collection
44
- }
45
-
46
40
  // Default connection getter
47
41
  const client = await getClient({ connectionString })
48
42
  let db = client.db(dbName, createOptions)
@@ -51,31 +45,6 @@ export function createMongoCollection(name, options = {}) {
51
45
  return _collection
52
46
  }
53
47
 
54
- /**
55
- *
56
- * @param {import('../types.js').Optional<T, 'id'>} doc
57
- */
58
- function validate(doc) {
59
- if (validator == null) {
60
- return
61
- }
62
- if (typeof validator == 'function') {
63
- return validator(doc)
64
- } else {
65
- for (const [field, fieldValidator] of Object.entries(validator)) {
66
- if (!fieldValidator) {
67
- continue
68
- }
69
- const value = fieldValidator(doc[field])
70
- if (value) {
71
- // @ts-ignore
72
- doc[field] = value
73
- }
74
- }
75
- return doc
76
- }
77
- }
78
-
79
48
  return {
80
49
  /**
81
50
  * Returns the native driver.
@@ -91,7 +60,9 @@ export function createMongoCollection(name, options = {}) {
91
60
  * @param {import('../types.js').FindOptions<T, K>} options
92
61
  * @returns {Promise<T[]>}
93
62
  */
94
- find: async (query, options = {}) => await find({ query, options, getCollection }),
63
+ find: async (query, options = {}) => {
64
+ return await find({ query, options, getCollection, collectionOptions })
65
+ },
95
66
  /**
96
67
  * @template {import('../types.js').Projection<T> | undefined} K
97
68
  * @param {import('mongodb').Filter<T>} query
@@ -99,20 +70,17 @@ export function createMongoCollection(name, options = {}) {
99
70
  * @returns {Promise<T | null>}
100
71
  */
101
72
  findOne: async (query, options = {}) => {
102
- return await findOne({ query, options, getCollection })
73
+ return await findOne({ query, options, collectionOptions, getCollection })
103
74
  },
104
75
  /**
105
76
  * @param {import('../types.js').Optional<T, 'id'>} doc
106
77
  * @returns {Promise<T>}
107
78
  */
108
79
  insertOne: async (doc) => {
109
- const result = validate(doc)
110
- if (result) {
111
- doc = result
112
- }
113
80
  return await insertOne({
114
81
  doc,
115
82
  getCollection,
83
+ collectionOptions,
116
84
  })
117
85
  },
118
86
  /**
@@ -120,7 +88,7 @@ export function createMongoCollection(name, options = {}) {
120
88
  * @param {import('../types.js').Optional<T, 'id'>[]} docs
121
89
  * @returns {Promise<T[]>}
122
90
  */
123
- insertMany: async (docs) => await insertMany({ docs, getCollection }),
91
+ insertMany: async (docs) => await insertMany({ docs, collectionOptions, getCollection }),
124
92
  /**
125
93
  * @param {import('mongodb').Filter<T>} query
126
94
  * @returns {Promise<boolean>}
@@ -144,7 +112,7 @@ export function createMongoCollection(name, options = {}) {
144
112
  * @param {import('mongodb').UpdateOptions} [options]
145
113
  */
146
114
  updateOne: async (query, update, options) => await updateOne({
147
- query, update, options, getCollection,
115
+ query, update, options, collectionOptions, getCollection,
148
116
  }),
149
117
  /**
150
118
  * @param {import('mongodb').Filter<T>} query
@@ -152,8 +120,56 @@ export function createMongoCollection(name, options = {}) {
152
120
  * @param {import('mongodb').UpdateOptions} [options]
153
121
  */
154
122
  updateMany: async (query, update, options) => await updateMany({
155
- query, update, options, getCollection,
156
- })
123
+ query, update, options, collectionOptions, getCollection,
124
+ }),
125
+ /**
126
+ * @param {import('mongodb').Document[]} pipeline
127
+ * @param {import('mongodb').AggregateOptions} [options]
128
+ * @returns {Promise<T[]>}
129
+ */
130
+ aggregate: async (pipeline, options) => await aggregate({
131
+ pipeline, options, getCollection, collectionOptions,
132
+ }),
133
+ /**
134
+ * @param {import('mongodb').AnyBulkWriteOperation<T>[]} operations
135
+ * @param {import('mongodb').BulkWriteOptions} [options]
136
+ */
137
+ bulkWrite: async (operations, options) => await bulkWrite({
138
+ operations, options, getCollection, collectionOptions,
139
+ }),
140
+ /**
141
+ * Atomically finds a document, applies the update, and returns the document.
142
+ * By default returns the document as it was **before** the update.
143
+ * Pass `{ returnDocument: 'after' }` in options to get the updated version.
144
+ * @param {import('mongodb').Filter<T>} query
145
+ * @param {import('mongodb').UpdateFilter<T>} update
146
+ * @param {import('mongodb').FindOneAndUpdateOptions} [options]
147
+ * @returns {Promise<T | null>}
148
+ */
149
+ findOneAndUpdate: async (query, update, options) => await findOneAndUpdate({
150
+ query, update, options, getCollection, collectionOptions,
151
+ }),
152
+ /**
153
+ * Atomically finds a document, deletes it, and returns the deleted document.
154
+ * @param {import('mongodb').Filter<T>} query
155
+ * @param {import('mongodb').FindOneAndDeleteOptions} [options]
156
+ * @returns {Promise<T | null>}
157
+ */
158
+ findOneAndDelete: async (query, options) => await findOneAndDelete({
159
+ query, options, getCollection, collectionOptions,
160
+ }),
161
+ /**
162
+ * Atomically finds a document, replaces it with `replacement`, and returns a document.
163
+ * By default returns the document as it was **before** the replacement.
164
+ * Pass `{ returnDocument: 'after' }` in options to get the new version.
165
+ * @param {import('mongodb').Filter<T>} query
166
+ * @param {import('mongodb').WithoutId<T>} replacement
167
+ * @param {import('mongodb').FindOneAndReplaceOptions} [options]
168
+ * @returns {Promise<T | null>}
169
+ */
170
+ findOneAndReplace: async (query, replacement, options) => await findOneAndReplace({
171
+ query, replacement, options, getCollection, collectionOptions,
172
+ }),
157
173
  }
158
174
  }
159
175
 
@@ -178,14 +194,6 @@ export function createMongoCollection(name, options = {}) {
178
194
  * collectionGetter: async () => await getMyCollection()
179
195
  * ```
180
196
  *
181
- * @property {IcollectionGetter<T>} [options.cachableCollectionGetter]
182
- * Same as `collectionGetter`. But the object is cached.
183
- *
184
- * Example:
185
- * ```js
186
- * cachableCollectionGetter: async () => await getMyCollection()
187
- * ```
188
- *
189
197
  * @property {string} [options.connectionString]
190
198
  * A custom connection string. If none is given, `process.env.DATABASE_URL` will be used.
191
199
  *
@@ -193,23 +201,22 @@ export function createMongoCollection(name, options = {}) {
193
201
  *
194
202
  * @property {import('mongodb').DbOptions} [options] The options used when initializing the client.
195
203
  *
196
- * @property {IfieldValidator<T> | IgenericValidator<T>} [validator]
204
+ * @property {ItimestampConfiguration} [timestamps] Configured how the package manages 'createdAt'
205
+ * and 'updatedAt'.
197
206
  */
198
207
 
199
208
  /**
200
- * @template T
201
- * @typedef {Partial<Record<keyof T, ((prop: any) => any)>>} IfieldValidator
202
- */
203
-
204
- /**
205
- * @template T
206
- * @callback IgenericValidator
207
- * The validator function used in insert operations.
208
- * This function should throw an exception if the validation fails.
209
- * @param {*} doc
210
- * @returns {T | null} The validator can return the desired document. Or null.
211
- *
212
- * If null is returned, the input parameter will be used normaly in inserts.
209
+ * @typedef {object} ItimestampConfiguration
210
+ * @property {'fromId' | 'generate' | 'none'} createdAt
211
+ * Changes how the 'createdAt' field is managed:
212
+ * - 'fromId': the field is generated when you use `find` or `findOne` and is calculated using _id.
213
+ * But it does not exist in the database.
214
+ * - 'generate': the field is generated when you use `insertOne` or `insertMany`. The value exist in
215
+ * the database.
216
+ * - 'none': no value is generated at any time.
217
+ * @property {'generate' | 'none'} updatedAt
218
+ * - 'generate': the value is generated when you use `insertOne` or `insertMany` and is updated on
219
+ * `updateOne` and `updateMany`.
213
220
  */
214
221
 
215
222
  /**
package/src/types.ts CHANGED
@@ -27,20 +27,6 @@ type GivenProjectionReturnType<T extends MongoDocument, P extends NonNullProject
27
27
  export type ProjectionReturnType<T extends MongoDocument, P extends Projection<T>> =
28
28
  P extends NonNullProjection<T> ? GivenProjectionReturnType<T, P> : T
29
29
 
30
- // export type ProjectionReturnType<T extends MongoDocument, P extends Projection<T>> =
31
- // P extends Partial<Record<keyof T, 0>>
32
- // ? {
33
- // [K in keyof T as P[K] extends 0 ? never : K]: T[K]
34
- // }
35
- // : (P extends Partial<Record<keyof T, 0 | 1>> ? {
36
- // [K in keyof T as P[K] extends 1 ? K : (
37
- // K extends 'id' ? (
38
- // P[K] extends 0 ? never : K
39
- // ) :
40
- // never
41
- // )]: T[K]
42
- // } : T)
43
-
44
- export type FindOptions<T extends MongoDocument, K extends Projection<T>> = Omit<FO<T>, 'projection'> & {
30
+ export type FindOptions<T extends MongoDocument, K extends Projection<T>> = Omit<FO, 'projection'> & {
45
31
  projection?: K
46
32
  }
@@ -0,0 +1,26 @@
1
+ import { Collection } from 'mongodb'
2
+ import { renameToMongoId, stringsIntoId } from '../transformers/index.mjs'
3
+ import { outputTransformer } from '../transformers/output_transformer.mjs'
4
+
5
+ /**
6
+ * @template {import('../../../types.js').MongoDocument} T
7
+ * @param {object} parameter
8
+ * @param {() => Promise<Collection<T>>} parameter.getCollection
9
+ * @param {import('mongodb').Document[]} parameter.pipeline
10
+ * @param {import('mongodb').AggregateOptions} [parameter.options]
11
+ * @param {import('../../../factories/create_mongo_collection.mjs').IcreateCollectionOptions<T>} [parameter.collectionOptions]
12
+ * @returns {Promise<T[]>}
13
+ */
14
+ export async function aggregate({ getCollection, pipeline, options, collectionOptions }) {
15
+ for (const stage of pipeline) {
16
+ if (stage.$match != null) {
17
+ stage.$match = renameToMongoId(stage.$match)
18
+ }
19
+ }
20
+ pipeline = stringsIntoId(pipeline)
21
+
22
+ const col = await getCollection()
23
+ const docs = await col.aggregate(pipeline, options).toArray()
24
+ // @ts-ignore
25
+ return docs.map((doc) => outputTransformer({ document: doc, collectionOptions }))
26
+ }
@@ -0,0 +1,54 @@
1
+ import { Collection } from 'mongodb'
2
+ import { renameToMongoId, stringsIntoId } from '../transformers/index.mjs'
3
+ import { inputTransformer } from '../transformers/input_transformer.mjs'
4
+
5
+ /**
6
+ * @template {import('../../../types.js').MongoDocument} T
7
+ * @param {object} parameter
8
+ * @param {() => Promise<Collection<T>>} parameter.getCollection
9
+ * @param {import('mongodb').AnyBulkWriteOperation<T>[]} parameter.operations
10
+ * @param {import('mongodb').BulkWriteOptions} [parameter.options]
11
+ * @param {import('../../../factories/create_mongo_collection.mjs').IcreateCollectionOptions<T>} [parameter.collectionOptions]
12
+ */
13
+ export async function bulkWrite({ getCollection, operations, options, collectionOptions }) {
14
+ for (const op of operations) {
15
+ /** @type {any} */
16
+ const o = op
17
+ if (o.insertOne != null) {
18
+ o.insertOne.document = inputTransformer({
19
+ document: o.insertOne.document,
20
+ collectionOptions,
21
+ })
22
+ } else if (o.updateOne != null) {
23
+ o.updateOne.filter = renameToMongoId(o.updateOne.filter)
24
+ stringsIntoId(o.updateOne.filter)
25
+ stringsIntoId(o.updateOne.update)
26
+ } else if (o.updateMany != null) {
27
+ o.updateMany.filter = renameToMongoId(o.updateMany.filter)
28
+ stringsIntoId(o.updateMany.filter)
29
+ stringsIntoId(o.updateMany.update)
30
+ } else if (o.deleteOne != null) {
31
+ o.deleteOne.filter = renameToMongoId(o.deleteOne.filter)
32
+ stringsIntoId(o.deleteOne.filter)
33
+ } else if (o.deleteMany != null) {
34
+ o.deleteMany.filter = renameToMongoId(o.deleteMany.filter)
35
+ stringsIntoId(o.deleteMany.filter)
36
+ } else if (o.replaceOne != null) {
37
+ o.replaceOne.filter = renameToMongoId(o.replaceOne.filter)
38
+ stringsIntoId(o.replaceOne.filter)
39
+ stringsIntoId(o.replaceOne.replacement)
40
+ }
41
+ }
42
+
43
+ const col = await getCollection()
44
+ const result = await col.bulkWrite(operations, options)
45
+
46
+ if (result.insertedIds) {
47
+ for (const key of Object.keys(result.insertedIds)) {
48
+ // @ts-ignore
49
+ result.insertedIds[key] = result.insertedIds[key].toHexString()
50
+ }
51
+ }
52
+
53
+ return result
54
+ }
@@ -1,11 +1,10 @@
1
1
  import { Collection } from 'mongodb'
2
2
  import {
3
- idsIntoString,
4
3
  renameFindOptions,
5
- renameToDevId,
6
4
  renameToMongoId,
7
5
  stringsIntoId
8
6
  } from '../transformers/index.mjs'
7
+ import { outputTransformer } from '../transformers/output_transformer.mjs'
9
8
 
10
9
  /**
11
10
  * @template {import('../../../types.js').MongoDocument} T
@@ -14,9 +13,10 @@ import {
14
13
  * @param {() => Promise<Collection<T>>} parameter.getCollection
15
14
  * @param {import('mongodb').Filter<T>} parameter.query
16
15
  * @param {import('../../../types.js').FindOptions<T, K>} [parameter.options]
16
+ * @param {import('../../../factories/create_mongo_collection.mjs').IcreateCollectionOptions<T>} [parameter.collectionOptions]
17
17
  * @returns {Promise<T[]>}
18
18
  */
19
- export async function find({ getCollection, query, options }) {
19
+ export async function find({ getCollection, query, options, collectionOptions }) {
20
20
  query = renameToMongoId(query)
21
21
  options = renameFindOptions(options)
22
22
 
@@ -26,9 +26,10 @@ export async function find({ getCollection, query, options }) {
26
26
 
27
27
  const docs = await col.find(query, options).toArray()
28
28
  const fixedDocs = docs.map((doc) => {
29
- idsIntoString(doc)
30
- const transformedDoc = renameToDevId(doc)
31
- return transformedDoc
29
+ return outputTransformer({
30
+ document: doc,
31
+ collectionOptions,
32
+ })
32
33
  })
33
34
  return fixedDocs
34
35
  }
@@ -1,4 +1,5 @@
1
1
  import { Collection } from 'mongodb'
2
+ import { outputTransformer } from '../transformers/output_transformer.mjs'
2
3
  import { find } from './find.mjs'
3
4
 
4
5
  /**
@@ -8,8 +9,10 @@ import { find } from './find.mjs'
8
9
  * @param {import('mongodb').Filter<T>} param.query
9
10
  * @param {import('../../../types.js').FindOptions<T, K>} [param.options]
10
11
  * @param {() => Promise<Collection<T>>} param.getCollection
12
+ * @param {import('../../../factories/create_mongo_collection.mjs').IcreateCollectionOptions<T>} param.collectionOptions
13
+ * @returns {Promise<T | null>}
11
14
  */
12
- export async function findOne({ query, options, getCollection }) {
15
+ export async function findOne({ query, options, collectionOptions, getCollection }) {
13
16
  const docs = await find({
14
17
  query,
15
18
  options: {
@@ -22,5 +25,9 @@ export async function findOne({ query, options, getCollection }) {
22
25
  if (doc == null) {
23
26
  return null
24
27
  }
25
- return doc
28
+
29
+ return outputTransformer({
30
+ document: doc,
31
+ collectionOptions,
32
+ })
26
33
  }
@@ -0,0 +1,23 @@
1
+ import { Collection } from 'mongodb'
2
+ import { renameToMongoId, stringsIntoId } from '../transformers/index.mjs'
3
+ import { outputTransformer } from '../transformers/output_transformer.mjs'
4
+
5
+ /**
6
+ * @template {import('../../../types.js').MongoDocument} T
7
+ * @param {object} param
8
+ * @param {import('mongodb').Filter<T>} param.query
9
+ * @param {import('mongodb').FindOneAndDeleteOptions} [param.options]
10
+ * @param {() => Promise<Collection<T>>} param.getCollection
11
+ * @param {import('../../../factories/create_mongo_collection.mjs').IcreateCollectionOptions<T>} [param.collectionOptions]
12
+ * @returns {Promise<T | null>}
13
+ */
14
+ export async function findOneAndDelete({ query, options, collectionOptions, getCollection }) {
15
+ query = renameToMongoId(query)
16
+ stringsIntoId(query)
17
+ const col = await getCollection()
18
+
19
+ const doc = await col.findOneAndDelete(query, options ?? {})
20
+ if (doc == null) return null
21
+
22
+ return outputTransformer({ document: doc, collectionOptions })
23
+ }
@@ -0,0 +1,24 @@
1
+ import { Collection } from 'mongodb'
2
+ import { renameToMongoId, stringsIntoId } from '../transformers/index.mjs'
3
+ import { outputTransformer } from '../transformers/output_transformer.mjs'
4
+
5
+ /**
6
+ * @template {import('../../../types.js').MongoDocument} T
7
+ * @param {object} param
8
+ * @param {import('mongodb').Filter<T>} param.query
9
+ * @param {import('mongodb').WithoutId<T>} param.replacement
10
+ * @param {import('mongodb').FindOneAndReplaceOptions} [param.options]
11
+ * @param {() => Promise<Collection<T>>} param.getCollection
12
+ * @param {import('../../../factories/create_mongo_collection.mjs').IcreateCollectionOptions<T>} [param.collectionOptions]
13
+ * @returns {Promise<T | null>}
14
+ */
15
+ export async function findOneAndReplace({ query, replacement, options, collectionOptions, getCollection }) {
16
+ query = renameToMongoId(query)
17
+ stringsIntoId(query)
18
+ const col = await getCollection()
19
+
20
+ const doc = await col.findOneAndReplace(query, replacement, options ?? {})
21
+ if (doc == null) return null
22
+
23
+ return outputTransformer({ document: doc, collectionOptions })
24
+ }
@@ -0,0 +1,40 @@
1
+ import { Collection } from 'mongodb'
2
+ import { renameToMongoId, stringsIntoId } from '../transformers/index.mjs'
3
+ import { outputTransformer } from '../transformers/output_transformer.mjs'
4
+
5
+ /**
6
+ * @template {import('../../../types.js').MongoDocument} T
7
+ * @param {object} param
8
+ * @param {import('mongodb').Filter<T>} param.query
9
+ * @param {import('mongodb').UpdateFilter<T>} param.update
10
+ * @param {import('mongodb').FindOneAndUpdateOptions} [param.options]
11
+ * @param {() => Promise<Collection<T>>} param.getCollection
12
+ * @param {import('../../../factories/create_mongo_collection.mjs').IcreateCollectionOptions<T>} [param.collectionOptions]
13
+ * @returns {Promise<T | null>}
14
+ */
15
+ export async function findOneAndUpdate({ query, update, options, collectionOptions, getCollection }) {
16
+ query = renameToMongoId(query)
17
+ stringsIntoId(query)
18
+ stringsIntoId(update)
19
+ const col = await getCollection()
20
+
21
+ const { timestamps } = collectionOptions ?? {}
22
+ const { updatedAt } = timestamps ?? { updatedAt: 'generate' }
23
+
24
+ const { $set } = update
25
+ const hasUpdatedAt = $set != null && $set.updatedAt != null
26
+ if (!hasUpdatedAt && updatedAt === 'generate') {
27
+ if ($set != null) {
28
+ // @ts-ignore
29
+ $set.updatedAt = new Date()
30
+ } else {
31
+ // @ts-ignore
32
+ update.$set = { updatedAt: new Date() }
33
+ }
34
+ }
35
+
36
+ const doc = await col.findOneAndUpdate(query, update, options ?? {})
37
+ if (doc == null) return null
38
+
39
+ return outputTransformer({ document: doc, collectionOptions })
40
+ }
@@ -1,9 +1,14 @@
1
1
  export * from './additional/index.mjs'
2
+ export * from './aggregate.mjs'
3
+ export * from './bulk_write.mjs'
2
4
  export * from './count.mjs'
3
5
  export * from './delete_many.mjs'
4
6
  export * from './delete_one.mjs'
5
7
  export * from './find.mjs'
6
8
  export * from './find_one.mjs'
9
+ export * from './find_one_and_delete.mjs'
10
+ export * from './find_one_and_replace.mjs'
11
+ export * from './find_one_and_update.mjs'
7
12
  export * from './insert_many.mjs'
8
13
  export * from './insert_one.mjs'
9
14
  export * from './update_many.mjs'
@@ -1,14 +1,15 @@
1
1
  import { Collection, ObjectId } from 'mongodb'
2
- import { renameToMongoId, stringsIntoId } from '../transformers/index.mjs'
2
+ import { inputTransformer } from '../transformers/input_transformer.mjs'
3
3
 
4
4
  /**
5
5
  * @template {import('../../../types.js').MongoDocument} T
6
6
  * @param {object} param
7
7
  * @param {import('../../../types.js').Optional<T, 'id'>[]} param.docs
8
8
  * @param {() => Promise<Collection<T>>} param.getCollection
9
+ * @param {import('../../../factories/create_mongo_collection.mjs').IcreateCollectionOptions<T>} param.collectionOptions
9
10
  * @returns {Promise<T[]>}
10
11
  */
11
- export async function insertMany({ docs, getCollection }) {
12
+ export async function insertMany({ docs, collectionOptions, getCollection }) {
12
13
  if (docs.length == 0) return []
13
14
 
14
15
  // We need to clone the array to prevent the function from changing the original input object.
@@ -16,8 +17,10 @@ export async function insertMany({ docs, getCollection }) {
16
17
  docs = [...docs]
17
18
 
18
19
  for (let i = 0; i < docs.length; i++) {
19
- docs[i] = renameToMongoId(docs[i])
20
- stringsIntoId(docs[i])
20
+ docs[i] = inputTransformer({
21
+ document: docs[i],
22
+ collectionOptions: collectionOptions
23
+ })
21
24
  }
22
25
  const col = await getCollection()
23
26
  const result = await col.insertMany(
@@ -1,16 +1,20 @@
1
1
  import { Collection, ObjectId } from 'mongodb'
2
- import { renameToMongoId, stringsIntoId } from '../transformers/index.mjs'
2
+ import { inputTransformer } from '../transformers/input_transformer.mjs'
3
3
 
4
4
  /**
5
5
  * @template {import('../../../types.js').MongoDocument} T
6
6
  * @param {object} param
7
7
  * @param {import('../../../types.js').Optional<T, 'id'>} param.doc
8
8
  * @param {() => Promise<Collection<T>>} param.getCollection
9
+ * @param {import('../../../factories/create_mongo_collection.mjs').IcreateCollectionOptions<T>} [param.collectionOptions]
9
10
  * @returns {Promise<T>}
10
11
  */
11
- export async function insertOne({ doc, getCollection }) {
12
- doc = renameToMongoId(doc)
13
- stringsIntoId(doc)
12
+ export async function insertOne({ doc, collectionOptions, getCollection }) {
13
+ doc = inputTransformer({
14
+ document: doc,
15
+ collectionOptions
16
+ })
17
+
14
18
  const col = await getCollection()
15
19
  const result = await col.insertOne(
16
20
  // @ts-ignore
@@ -22,6 +26,8 @@ export async function insertOne({ doc, getCollection }) {
22
26
  id = id.toHexString()
23
27
  }
24
28
 
29
+ delete doc._id
30
+
25
31
  // @ts-ignore
26
32
  return { id, ...doc }
27
33
  }
@@ -8,11 +8,32 @@ import { renameToMongoId, stringsIntoId } from '../transformers/index.mjs'
8
8
  * @param {import('mongodb').UpdateFilter<T>} param.update
9
9
  * @param {import('mongodb').UpdateOptions} [param.options]
10
10
  * @param {() => Promise<Collection<T>>} param.getCollection
11
+ * @param {import('../../../factories/create_mongo_collection.mjs').IcreateCollectionOptions<T>} [param.collectionOptions]
11
12
  */
12
- export async function updateMany({ query, update, options, getCollection }) {
13
+ export async function updateMany({ query, update, options, collectionOptions, getCollection }) {
13
14
  query = renameToMongoId(query)
14
15
  stringsIntoId(query)
15
16
  stringsIntoId(update)
17
+
18
+ const { timestamps } = collectionOptions ?? {}
19
+ const { updatedAt } = timestamps ?? {
20
+ updatedAt: 'generate'
21
+ }
22
+
23
+ const { $set } = update
24
+ const hasUpdatedAt = $set != null && $set.updatedAt != null
25
+ if (!hasUpdatedAt && updatedAt === 'generate') {
26
+ if ($set != null) {
27
+ // @ts-ignore
28
+ $set.updatedAt = new Date()
29
+ } else {
30
+ // @ts-ignore
31
+ update.$set = {
32
+ updatedAt: new Date()
33
+ }
34
+ }
35
+ }
36
+
16
37
  const col = await getCollection()
17
38
  const r = await col.updateMany(query, update, options)
18
39
  return r
@@ -8,12 +8,34 @@ import { renameToMongoId, stringsIntoId } from '../transformers/index.mjs'
8
8
  * @param {import('mongodb').UpdateFilter<T>} param.update
9
9
  * @param {import('mongodb').UpdateOptions} [param.options]
10
10
  * @param {() => Promise<Collection<T>>} param.getCollection
11
+ * @param {import('../../../factories/create_mongo_collection.mjs').IcreateCollectionOptions<T>} [param.collectionOptions]
12
+ * @returns {Promise<boolean>}
11
13
  */
12
- export async function updateOne({ query, update, options, getCollection }) {
14
+ export async function updateOne({ query, update, options, collectionOptions, getCollection }) {
13
15
  query = renameToMongoId(query)
14
16
  stringsIntoId(query)
15
17
  stringsIntoId(update)
16
18
  const col = await getCollection()
19
+
20
+ const { timestamps } = collectionOptions ?? {}
21
+ const { updatedAt } = timestamps ?? {
22
+ updatedAt: 'generate'
23
+ }
24
+
25
+ const { $set } = update
26
+ const hasUpdatedAt = $set != null && $set.updatedAt != null
27
+ if (!hasUpdatedAt && updatedAt === 'generate') {
28
+ if ($set != null) {
29
+ // @ts-ignore
30
+ $set.updatedAt = new Date()
31
+ } else {
32
+ // @ts-ignore
33
+ update.$set = {
34
+ updatedAt: new Date()
35
+ }
36
+ }
37
+ }
38
+
17
39
  const r = await col.updateOne(query, update, options)
18
40
  return r.matchedCount > 0
19
41
  }