@bedrockio/model 0.11.0 → 0.11.2

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
@@ -1,3 +1,13 @@
1
+ ## 0.11.2
2
+
3
+ - Fixed issue with non-async hooks hanging.
4
+ - Further verson bumps.
5
+
6
+ ## 0.11.1
7
+
8
+ - Changed default sort field in search to `_id`.
9
+ - Some verson bumps.
10
+
1
11
  ## 0.11.0
2
12
 
3
13
  - Added clone module.
package/README.md CHANGED
@@ -755,6 +755,32 @@ The [validation](#validation) generated for search using `getSearchValidation`
755
755
  is inherently looser and allows more fields to be passed to allow complex
756
756
  searches compatible with the above.
757
757
 
758
+ #### Default Sort Order
759
+
760
+ When using `search` the default sort order is:
761
+
762
+ ```json
763
+ {
764
+ "field": "_id",
765
+ "order": "asc"
766
+ }
767
+ ```
768
+
769
+ The justification for this as the default is:
770
+
771
+ - Ascending sort order matches the behavior of `find` methods.
772
+ - For the majority of cases `_id` behaves identically to `createdAt` with one
773
+ major difference: for very large collections queries can become signifcantly
774
+ slow without an index on the `createdAt` field. Using `_id` prefers the
775
+ default (and heavily optimized) `_id` index instead. For cases where
776
+ `createdAt` is semantically distinct, simply pass it as the sort field
777
+ instead.
778
+ - Mongo collections use `$natural` sort by default which follows disk order. For
779
+ small collections this is **generally** the same as insert order, however
780
+ documents may appear out of order for a number of different reasons which is
781
+ not acceptable for search operations. When maximum efficiency is needed (logs,
782
+ event streams, etc), `$natural` can still be passed.
783
+
758
784
  ### Cache
759
785
 
760
786
  The cache module allows a simple way to cache foreign fields on a document and
package/dist/cjs/const.js CHANGED
@@ -7,8 +7,8 @@ exports.SEARCH_DEFAULTS = exports.POPULATE_MAX_DEPTH = void 0;
7
7
  const SEARCH_DEFAULTS = exports.SEARCH_DEFAULTS = {
8
8
  limit: 50,
9
9
  sort: {
10
- field: 'createdAt',
11
- order: 'desc'
10
+ field: '_id',
11
+ order: 'asc'
12
12
  }
13
13
  };
14
14
  const POPULATE_MAX_DEPTH = exports.POPULATE_MAX_DEPTH = 5;
@@ -26,11 +26,7 @@ function applySearch(schema, definition) {
26
26
  search: config = {}
27
27
  } = definition;
28
28
  schema.static('search', function search(body = {}) {
29
- const options = {
30
- ..._const.SEARCH_DEFAULTS,
31
- ...config.query,
32
- ...body
33
- };
29
+ const options = mergeOptions(_const.SEARCH_DEFAULTS, config.query, body);
34
30
  const {
35
31
  ids,
36
32
  keyword,
@@ -113,9 +109,13 @@ function resolveSort(sort, schema) {
113
109
  sort = [sort];
114
110
  }
115
111
  for (let {
112
+ name,
116
113
  field
117
114
  } of sort) {
118
- if (!field || !schema.path(field)) {
115
+ if (name) {
116
+ throw new Error('Sort property "name" is not allowed. Use "field" instead.');
117
+ }
118
+ if (!field.startsWith('$') && !schema.path(field)) {
119
119
  throw new Error(`Unknown sort field "${field}".`);
120
120
  }
121
121
  }
@@ -371,4 +371,27 @@ function isForeignField(schema, path) {
371
371
  return false;
372
372
  }
373
373
  return !!(0, _utils.resolveRefPath)(schema, path);
374
+ }
375
+
376
+ // Merge options together. Do not perform deep merge
377
+ // however the sort options do need to also be merged.
378
+ function mergeOptions(...sources) {
379
+ let result = {};
380
+ for (let source of sources) {
381
+ result = {
382
+ ...result,
383
+ ...source,
384
+ sort: mergeSort(result.sort, source?.sort)
385
+ };
386
+ }
387
+ return result;
388
+ }
389
+ function mergeSort(sort1, sort2) {
390
+ if (Array.isArray(sort2)) {
391
+ return sort2;
392
+ }
393
+ return {
394
+ ...sort1,
395
+ ...sort2
396
+ };
374
397
  }
@@ -485,6 +485,8 @@ function runHook(query, fn, check, next, args) {
485
485
  const ret = fn.apply(query, args);
486
486
  if (ret instanceof Promise) {
487
487
  ret.finally(next);
488
+ } else {
489
+ next();
488
490
  }
489
491
  } else {
490
492
  next();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bedrockio/model",
3
- "version": "0.11.0",
3
+ "version": "0.11.2",
4
4
  "description": "Bedrock utilities for model creation.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -30,8 +30,8 @@
30
30
  "lodash": "^4.17.21"
31
31
  },
32
32
  "peerDependencies": {
33
- "@bedrockio/yada": "^1.3.0",
34
- "mongoose": "^8.6.2"
33
+ "@bedrockio/yada": "^1.4.1",
34
+ "mongoose": "^8.13.1"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@babel/cli": "^7.26.4",
@@ -40,18 +40,18 @@
40
40
  "@bedrockio/eslint-plugin": "^1.1.7",
41
41
  "@bedrockio/prettier-config": "^1.0.2",
42
42
  "@bedrockio/yada": "^1.4.1",
43
- "@shelf/jest-mongodb": "^4.3.2",
43
+ "@shelf/jest-mongodb": "^5.1.0",
44
44
  "eslint": "^9.19.0",
45
45
  "jest": "^29.7.0",
46
46
  "jest-environment-node": "^29.7.0",
47
- "mongodb": "^6.12.0",
48
- "mongoose": "^8.8.4",
47
+ "mongodb": "^6.15.0",
48
+ "mongoose": "^8.13.1",
49
49
  "prettier": "^3.4.2",
50
50
  "typescript": "^5.7.2"
51
51
  },
52
52
  "prettier": "@bedrockio/prettier-config",
53
53
  "volta": {
54
- "node": "23.7.0",
54
+ "node": "22.14.0",
55
55
  "yarn": "1.22.22"
56
56
  }
57
57
  }
package/src/const.js CHANGED
@@ -1,8 +1,8 @@
1
1
  export const SEARCH_DEFAULTS = {
2
2
  limit: 50,
3
3
  sort: {
4
- field: 'createdAt',
5
- order: 'desc',
4
+ field: '_id',
5
+ order: 'asc',
6
6
  },
7
7
  };
8
8
 
package/src/search.js CHANGED
@@ -27,11 +27,7 @@ export function applySearch(schema, definition) {
27
27
  const { search: config = {} } = definition;
28
28
 
29
29
  schema.static('search', function search(body = {}) {
30
- const options = {
31
- ...SEARCH_DEFAULTS,
32
- ...config.query,
33
- ...body,
34
- };
30
+ const options = mergeOptions(SEARCH_DEFAULTS, config.query, body);
35
31
 
36
32
  const { ids, keyword, skip = 0, limit, sort, ...rest } = options;
37
33
 
@@ -133,8 +129,13 @@ function resolveSort(sort, schema) {
133
129
  } else if (!Array.isArray(sort)) {
134
130
  sort = [sort];
135
131
  }
136
- for (let { field } of sort) {
137
- if (!field || !schema.path(field)) {
132
+ for (let { name, field } of sort) {
133
+ if (name) {
134
+ throw new Error(
135
+ 'Sort property "name" is not allowed. Use "field" instead.',
136
+ );
137
+ }
138
+ if (!field.startsWith('$') && !schema.path(field)) {
138
139
  throw new Error(`Unknown sort field "${field}".`);
139
140
  }
140
141
  }
@@ -411,3 +412,25 @@ function isForeignField(schema, path) {
411
412
  }
412
413
  return !!resolveRefPath(schema, path);
413
414
  }
415
+
416
+ // Merge options together. Do not perform deep merge
417
+ // however the sort options do need to also be merged.
418
+ function mergeOptions(...sources) {
419
+ let result = {};
420
+ for (let source of sources) {
421
+ result = {
422
+ ...result,
423
+ ...source,
424
+ sort: mergeSort(result.sort, source?.sort),
425
+ };
426
+ }
427
+
428
+ return result;
429
+ }
430
+
431
+ function mergeSort(sort1, sort2) {
432
+ if (Array.isArray(sort2)) {
433
+ return sort2;
434
+ }
435
+ return { ...sort1, ...sort2 };
436
+ }
@@ -538,6 +538,8 @@ function runHook(query, fn, check, next, args) {
538
538
  const ret = fn.apply(query, args);
539
539
  if (ret instanceof Promise) {
540
540
  ret.finally(next);
541
+ } else {
542
+ next();
541
543
  }
542
544
  } else {
543
545
  next();
@@ -1 +1 @@
1
- {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../src/search.js"],"names":[],"mappings":"AAsBA,gEAwDC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eAWY,CAAC;;;;;;;;;;;;;;;;;EAcZ"}
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../src/search.js"],"names":[],"mappings":"AAsBA,gEAoDC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eAYa,CAAC;;;;;;;;;;;;;;;;;EAab"}