@abejarano/ts-mongodb-criteria 1.9.0 → 1.11.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.
package/README.md CHANGED
@@ -182,8 +182,9 @@ Use the `collection` argument inside `ensureIndexes` to avoid recursion.
182
182
  MongoRepository provides ready-to-use public methods for repositories that extend it:
183
183
 
184
184
  - `list(criteria, fieldsToExclude?)` for paginated queries
185
- - `one(filter)` to fetch a single entity
186
- - `upsert(entity)` to persist an aggregate
185
+ - `many(filter, transaction?)` to fetch multiple entities matching a filter
186
+ - `one(filter, transaction?)` to fetch a single entity
187
+ - `upsert(entity, transaction?)` to persist an aggregate
187
188
  Internal helpers are private, so repositories should call these public methods
188
189
  directly.
189
190
 
@@ -214,6 +215,10 @@ await MongoTransaction.run(async (tx) => {
214
215
  await userRepository.upsert(user, tx)
215
216
  await auditRepository.upsert(auditEntry, tx)
216
217
  await sessionRepository.delete({ userId: user.getId() }, undefined, tx)
218
+
219
+ // Reads can observe writes made earlier in this transaction.
220
+ const persistedUser = await userRepository.one({ id: user.getId() }, tx)
221
+ const activeUsers = await userRepository.many({ status: "active" }, tx)
217
222
  })
218
223
  ```
219
224
 
@@ -232,8 +237,8 @@ async deactivateUser(id: string, tx: MongoTransaction): Promise<void> {
232
237
 
233
238
  MongoDB transactions require a replica set or a sharded cluster; standalone
234
239
  MongoDB instances do not support them. This initial API applies the transaction
235
- context only to `upsert`, `delete`, and protected `updateOne`; `one` and `list`
236
- remain regular reads.
240
+ context to `upsert`, `delete`, protected `updateOne`, `one`, and `list`.
241
+ Pass `tx` as the third argument to `list` after `fieldsToExclude`, and as the second argument to `many`.
237
242
 
238
243
  **Your First Query in 30 Seconds:**
239
244
 
@@ -11,9 +11,12 @@ class MongoRepository {
11
11
  this.criteriaConverter = new MongoCriteriaConverter_1.MongoCriteriaConverter();
12
12
  }
13
13
  /** Finds a single entity and hydrates it via the aggregate's fromPrimitives. */
14
- async one(filter) {
14
+ async one(filter, transaction) {
15
15
  const collection = await this.collection();
16
- const result = await collection.findOne(filter);
16
+ const session = MongoTransaction_1.MongoTransaction.sessionFor(transaction);
17
+ const result = session
18
+ ? await collection.findOne(filter, { session })
19
+ : await collection.findOne(filter);
17
20
  if (!result) {
18
21
  return null;
19
22
  }
@@ -22,14 +25,25 @@ class MongoRepository {
22
25
  id: result._id.toString(),
23
26
  });
24
27
  }
28
+ async many(filter, transaction) {
29
+ const collection = await this.collection();
30
+ const session = MongoTransaction_1.MongoTransaction.sessionFor(transaction);
31
+ const documents = await collection
32
+ .find(filter, session === undefined ? undefined : { session })
33
+ .toArray();
34
+ return documents.map((document) => this.aggregateRootClass.fromPrimitives({
35
+ ...document,
36
+ id: document._id.toString(),
37
+ }));
38
+ }
25
39
  /** Upserts an aggregate by delegating to persist with its id. */
26
40
  async upsert(entity, transaction) {
27
41
  await this.persist(entity.getId(), entity, transaction);
28
42
  }
29
43
  /** Lists entities by criteria and returns a paginated response. */
30
- async list(criteria, fieldsToExclude = []) {
31
- const documents = await this.searchByCriteria(criteria, fieldsToExclude);
32
- return this.paginate(documents);
44
+ async list(criteria, fieldsToExclude = [], transaction) {
45
+ const documents = await this.searchByCriteria(criteria, fieldsToExclude, transaction);
46
+ return this.paginate(documents, transaction);
33
47
  }
34
48
  /**
35
49
  * Deletes documents from the database based on a filter and optional options.
@@ -80,13 +94,14 @@ class MongoRepository {
80
94
  },
81
95
  }, transaction);
82
96
  }
83
- async searchByCriteria(criteria, fieldsToExclude = []) {
97
+ async searchByCriteria(criteria, fieldsToExclude = [], transaction) {
84
98
  this.criteria = criteria;
85
99
  this.query = this.criteriaConverter.convert(criteria);
86
100
  const collection = await this.collection();
101
+ const session = MongoTransaction_1.MongoTransaction.sessionFor(transaction);
87
102
  if (fieldsToExclude.length === 0) {
88
103
  const results = await collection
89
- .find(this.query.filter, {})
104
+ .find(this.query.filter, session ? { session } : {})
90
105
  .sort(this.query.sort)
91
106
  .skip(this.query.skip)
92
107
  .limit(this.query.limit)
@@ -98,16 +113,19 @@ class MongoRepository {
98
113
  projection[field] = 0;
99
114
  });
100
115
  const results = await collection
101
- .find(this.query.filter, { projection })
116
+ .find(this.query.filter, session ? { projection, session } : { projection })
102
117
  .sort(this.query.sort)
103
118
  .skip(this.query.skip)
104
119
  .limit(this.query.limit)
105
120
  .toArray();
106
121
  return results.map(({ _id, ...rest }) => rest);
107
122
  }
108
- async paginate(documents) {
123
+ async paginate(documents, transaction) {
109
124
  const collection = await this.collection();
110
- const count = await collection.countDocuments(this.query.filter);
125
+ const session = MongoTransaction_1.MongoTransaction.sessionFor(transaction);
126
+ const count = session
127
+ ? await collection.countDocuments(this.query.filter, { session })
128
+ : await collection.countDocuments(this.query.filter);
111
129
  const limit = this.criteria?.limit || 10;
112
130
  const currentPage = this.criteria?.currentPage || 1;
113
131
  const hasNextPage = currentPage * limit < count;
@@ -8,9 +8,12 @@ export class MongoRepository {
8
8
  this.criteriaConverter = new MongoCriteriaConverter();
9
9
  }
10
10
  /** Finds a single entity and hydrates it via the aggregate's fromPrimitives. */
11
- async one(filter) {
11
+ async one(filter, transaction) {
12
12
  const collection = await this.collection();
13
- const result = await collection.findOne(filter);
13
+ const session = MongoTransaction.sessionFor(transaction);
14
+ const result = session
15
+ ? await collection.findOne(filter, { session })
16
+ : await collection.findOne(filter);
14
17
  if (!result) {
15
18
  return null;
16
19
  }
@@ -19,14 +22,25 @@ export class MongoRepository {
19
22
  id: result._id.toString(),
20
23
  });
21
24
  }
25
+ async many(filter, transaction) {
26
+ const collection = await this.collection();
27
+ const session = MongoTransaction.sessionFor(transaction);
28
+ const documents = await collection
29
+ .find(filter, session === undefined ? undefined : { session })
30
+ .toArray();
31
+ return documents.map((document) => this.aggregateRootClass.fromPrimitives({
32
+ ...document,
33
+ id: document._id.toString(),
34
+ }));
35
+ }
22
36
  /** Upserts an aggregate by delegating to persist with its id. */
23
37
  async upsert(entity, transaction) {
24
38
  await this.persist(entity.getId(), entity, transaction);
25
39
  }
26
40
  /** Lists entities by criteria and returns a paginated response. */
27
- async list(criteria, fieldsToExclude = []) {
28
- const documents = await this.searchByCriteria(criteria, fieldsToExclude);
29
- return this.paginate(documents);
41
+ async list(criteria, fieldsToExclude = [], transaction) {
42
+ const documents = await this.searchByCriteria(criteria, fieldsToExclude, transaction);
43
+ return this.paginate(documents, transaction);
30
44
  }
31
45
  /**
32
46
  * Deletes documents from the database based on a filter and optional options.
@@ -77,13 +91,14 @@ export class MongoRepository {
77
91
  },
78
92
  }, transaction);
79
93
  }
80
- async searchByCriteria(criteria, fieldsToExclude = []) {
94
+ async searchByCriteria(criteria, fieldsToExclude = [], transaction) {
81
95
  this.criteria = criteria;
82
96
  this.query = this.criteriaConverter.convert(criteria);
83
97
  const collection = await this.collection();
98
+ const session = MongoTransaction.sessionFor(transaction);
84
99
  if (fieldsToExclude.length === 0) {
85
100
  const results = await collection
86
- .find(this.query.filter, {})
101
+ .find(this.query.filter, session ? { session } : {})
87
102
  .sort(this.query.sort)
88
103
  .skip(this.query.skip)
89
104
  .limit(this.query.limit)
@@ -95,16 +110,19 @@ export class MongoRepository {
95
110
  projection[field] = 0;
96
111
  });
97
112
  const results = await collection
98
- .find(this.query.filter, { projection })
113
+ .find(this.query.filter, session ? { projection, session } : { projection })
99
114
  .sort(this.query.sort)
100
115
  .skip(this.query.skip)
101
116
  .limit(this.query.limit)
102
117
  .toArray();
103
118
  return results.map(({ _id, ...rest }) => rest);
104
119
  }
105
- async paginate(documents) {
120
+ async paginate(documents, transaction) {
106
121
  const collection = await this.collection();
107
- const count = await collection.countDocuments(this.query.filter);
122
+ const session = MongoTransaction.sessionFor(transaction);
123
+ const count = session
124
+ ? await collection.countDocuments(this.query.filter, { session })
125
+ : await collection.countDocuments(this.query.filter);
108
126
  const limit = this.criteria?.limit || 10;
109
127
  const currentPage = this.criteria?.currentPage || 1;
110
128
  const hasNextPage = currentPage * limit < count;
@@ -3,8 +3,9 @@ import { AggregateRoot } from "../AggregateRoot";
3
3
  import { DeleteOptions } from "mongodb";
4
4
  import { MongoTransaction } from "./MongoTransaction";
5
5
  export interface IRepository<T extends AggregateRoot> {
6
- one(filter: object): Promise<T | null>;
7
- list<D>(criteria: Criteria, fieldsToExclude?: string[]): Promise<Paginate<D>>;
6
+ many(filter: object, transaction?: MongoTransaction): Promise<T[]>;
7
+ one(filter: object, transaction?: MongoTransaction): Promise<T | null>;
8
+ list<D>(criteria: Criteria, fieldsToExclude?: string[], transaction?: MongoTransaction): Promise<Paginate<D>>;
8
9
  upsert(entity: T, transaction?: MongoTransaction): Promise<void>;
9
10
  delete(filter: object, options?: DeleteOptions, transaction?: MongoTransaction): Promise<void>;
10
11
  }
@@ -11,11 +11,12 @@ export declare abstract class MongoRepository<T extends AggregateRoot> {
11
11
  protected constructor(aggregateRootClass: AggregateRootClass<T>);
12
12
  abstract collectionName(): string;
13
13
  /** Finds a single entity and hydrates it via the aggregate's fromPrimitives. */
14
- one(filter: object): Promise<T | null>;
14
+ one(filter: object, transaction?: MongoTransaction): Promise<T | null>;
15
+ many(filter: object, transaction?: MongoTransaction): Promise<T[]>;
15
16
  /** Upserts an aggregate by delegating to persist with its id. */
16
17
  upsert(entity: T, transaction?: MongoTransaction): Promise<void>;
17
18
  /** Lists entities by criteria and returns a paginated response. */
18
- list<D>(criteria: Criteria, fieldsToExclude?: string[]): Promise<Paginate<D>>;
19
+ list<D>(criteria: Criteria, fieldsToExclude?: string[], transaction?: MongoTransaction): Promise<Paginate<D>>;
19
20
  /**
20
21
  * Deletes documents from the database based on a filter and optional options.
21
22
  *
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@abejarano/ts-mongodb-criteria",
3
3
  "author": "angel bejarano / angel.bejarano@jaspesoft.com",
4
- "version": "1.9.0",
4
+ "version": "1.11.0",
5
5
  "description": "Patrón Criteria para consultas MongoDB en TypeScript",
6
6
  "main": "dist/cjs/index.js",
7
7
  "module": "dist/esm/index.js",