mongoose-smart-query 1.4.1 → 1.4.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/AGENTS.md ADDED
@@ -0,0 +1,62 @@
1
+ # AGENTS.md
2
+
3
+ Mongoose plugin published as `mongoose-smart-query`. Built with `tsc` (ES5, CJS output), tested with `jest` (ts-jest), linted with `eslint` (flat config v10). Package manager is **pnpm** (`packageManager: pnpm@10.29.3`).
4
+
5
+ ## Commands
6
+
7
+ All scripts in `package.json` assume `pnpm`:
8
+
9
+ - `pnpm install` — install deps
10
+ - `pnpm run build` — `tsc` to `dist/`. The `tsconfig.json` uses `"files": ["src/index.ts"]` so **only the entry and its transitive imports** are type-checked. Other files may have type errors but won't block the build.
11
+ - `pnpm test` — `jest` (picks up `*.spec.ts` under `test/`)
12
+ - `pnpm run test:coverage` — `jest --coverage`
13
+ - `pnpm run lint` — `eslint --ext .ts .`
14
+
15
+ Run a single test: `pnpm test -- test/typesense.spec.ts` or `pnpm test -- -t "word by word search"`.
16
+
17
+ **No husky / lint-staged** — the `precommit` / `prepush` scripts in `package.json` are regular scripts, not auto-triggered hooks.
18
+
19
+ ## CI vs local
20
+
21
+ CI (`.github/workflows/testing.yml`): Node 14.x, **npm** (not pnpm), MongoDB 4.2 via `supercharge/mongodb-github-action@1.3.0`. This is much older than the `mongoose ^8` / `typesense ^3.0.6` peerDeps.
22
+
23
+ ## Test & MongoDB prerequisites
24
+
25
+ | Spec file | Mongo connection | Requirements |
26
+ |---|---|---|
27
+ | `test/typesense.spec.ts` | **None** — mocks `typesense` via `jest.mock('typesense', ...)` | Fastest feedback when changing Typesense code |
28
+ | `test/mongoose-smart-query.spec.ts` (via `load-database.ts:8`) | `mongodb://localhost/<db>?replicaSet=rs0` | Needs a **replica set named `rs0`**. Plain `mongod` fails. Setup: `mongod --replSet rs0 --port 27017` then `mongosh` → `rs.initiate({_id:"rs0", members:[{_id:0, host:"localhost:27017"}]})` |
29
+ | `test/pagination.spec.ts:8`, `test/hybrid_search.spec.ts:8`, `test/array-casting.spec.ts:8` | `mongodb://127.0.0.1:27017/<dbname>` | Plain `mongod` enough |
30
+
31
+ ## Architecture
32
+
33
+ - **`src/index.ts`** — entry. Default-exports the plugin; re-exports `types/`, `utils/`, `typesense/`.
34
+ - **`src/plugin/index.ts`** — `mongooseSmartQuery(schema, options)`. Attaches three statics:
35
+ - `__smartQueryGetPipeline(query, forCount?, prePipeline?, typesenseIds?)` → `{ pipeline, lookupsConfirmados }`
36
+ - `smartQuery(query, options?)` — public API. Supports `{ autoPaginate: true }` → `{ data, pagination }` shape
37
+ - `smartCount(query)`
38
+ - **Pipeline assembly**: `src/plugin/pipeline.ts` (Mongo aggregation). Methods: `src/plugin/methods.ts` (Mongo + optional Typesense pre-filter + post-agg `$lookup` hydration via `connection.collection`).
39
+ - **`src/types/`** — `PluginOptions`, `SmartQueryStatics`, `SmartQueryResult`, `LookupConfirmado`, etc.
40
+ - **`src/utils/`** — `stringToQuery` (parses `name friends { name }` into projection), `normalizeSearchText`, `parseValue`, `removeKeys`, `getListOfPossibleLookups`, `asignarLookups`, `getCampo`, `reemplazarSubdoc`.
41
+ - **`src/typesense/`** — `config.ts` (singleton `globalTypesenseClient` + `setTypesenseConfig()`), `builder.ts` (`buildTypesenseSearchParameters`, `hasUnindexedFields`).
42
+
43
+ ### Default values in code vs docs
44
+
45
+ - `defaultSort` defaults to `'-_id'` in code (`src/plugin/index.ts:13`, `pipeline.ts:27`), but the README table says `'-id'`. Docs are wrong; always apply `'-_id'`.
46
+ - `allFieldsQueryName` (`'$getAllFields'` in code) — undocumented `true`/`false` query key that skips projection and returns every field except `protectedFields`.
47
+ - `collation` option — accepted in `PluginOptions` but undocumented in README. Passed to `aggregate()` calls.
48
+
49
+ ## Typesense gotchas
50
+
51
+ - `setTypesenseConfig()` writes a **module-level singleton** (`globalTypesenseClient`). One call at startup affects every model with a `typesense` option.
52
+ - Typesense routing triggers **only if every non-special key** in the query maps to a Typesense field (`hasUnindexedFields` in `src/typesense/builder.ts:177`). Otherwise falls back to MongoDB, logging `"Error fetching from Typesense, falling back to MongoDB:"` (or `"...count..."` equivalent).
53
+ - `mongoField` mapping (`{ name: 'issueDate', mongoField: 'fecha_emision' }`) — the builder resolves field lookups via `f.mongoField || f.name` throughout.
54
+ - Date-like values on `int`/`float` Typesense fields are auto-converted to Unix-ms timestamps (`src/typesense/builder.ts:87-110`).
55
+
56
+ ## Style & formatting
57
+
58
+ Prettier (`.prettierrc.json`): **no semicolons**, single quotes, 2-space indent, `trailingComma: "all"`, `printWidth: 80`. ESLint flat config (`eslint.config.mjs`) uses `@eslint/js` + `typescript-eslint` recommended, only overriding `@typescript-eslint/no-explicit-any` → `warn`.
59
+
60
+ ## Build artifacts
61
+
62
+ `dist/` is published (`main: ./dist/index.js`, `types: ./dist/index.d.ts`). `.npmignore` excludes `src/`, `test/`, config files, `*.tgz`, and `.github/`. Local `mongoose-smart-query-*.tgz` files in root are gitignored.
@@ -70,8 +70,9 @@ function createSmartQuery(options) {
70
70
  useMongoDirectly = true;
71
71
  typesenseIds = [];
72
72
  typesenseTotal = 0;
73
- if (!(typesense_1.globalTypesenseClient && typesense)) return [3 /*break*/, 4];
74
- if (!!(0, typesense_1.hasUnindexedFields)(query, typesense.schema, options)) return [3 /*break*/, 4];
73
+ if (!(typesense_1.globalTypesenseClient &&
74
+ typesense &&
75
+ !(0, typesense_1.hasUnindexedFields)(query, typesense.schema, options))) return [3 /*break*/, 4];
75
76
  _g.label = 1;
76
77
  case 1:
77
78
  _g.trys.push([1, 3, , 4]);
@@ -92,22 +92,27 @@ function createSmartQueryGetPipeline(schema, options) {
92
92
  getDefault = function () { return __awaiter(_this, void 0, void 0, function () {
93
93
  var $localMatch, lookupFinalMatch, $or, _loop_1, keyInicial;
94
94
  var _this = this;
95
- return __generator(this, function (_a) {
96
- switch (_a.label) {
95
+ var _a;
96
+ return __generator(this, function (_b) {
97
+ switch (_b.label) {
97
98
  case 0:
98
99
  $localMatch = {};
99
100
  lookupFinalMatch = {};
100
101
  $or = [];
101
102
  _loop_1 = function (keyInicial) {
102
- var _b, _c;
103
+ var _c, _d;
103
104
  var path = schema.path(keyInicial);
104
105
  if (!path && !keyInicial.includes('.'))
105
106
  return "continue";
107
+ var arrayPath = path;
108
+ var instance = (path === null || path === void 0 ? void 0 : path.instance) === 'Array'
109
+ ? (_a = arrayPath === null || arrayPath === void 0 ? void 0 : arrayPath.caster) === null || _a === void 0 ? void 0 : _a.instance
110
+ : path === null || path === void 0 ? void 0 : path.instance;
106
111
  var key = keyInicial;
107
112
  var lookupKey = void 0;
108
113
  var valorQuery = query[key];
109
114
  if (keyInicial.includes('.')) {
110
- var _d = keyInicial.split('.'), keyForanea = _d[0], subKey = _d[1];
115
+ var _e = keyInicial.split('.'), keyForanea = _e[0], subKey = _e[1];
111
116
  var foraneaKey = schema.path(keyForanea);
112
117
  if (foraneaKey && foraneaKey.options.ref) {
113
118
  key = subKey;
@@ -134,7 +139,7 @@ function createSmartQueryGetPipeline(schema, options) {
134
139
  values.push([match[0], match[1], match[2]]);
135
140
  }
136
141
  for (var _i = 0, values_1 = values; _i < values_1.length; _i++) {
137
- var _e = values_1[_i], operator = _e[1], value = _e[2];
142
+ var _f = values_1[_i], operator = _f[1], value = _f[2];
138
143
  switch (operator) {
139
144
  case '$exists':
140
145
  $filtroActual[key] = { $exists: value !== 'false' };
@@ -148,18 +153,18 @@ function createSmartQueryGetPipeline(schema, options) {
148
153
  case '$nin': {
149
154
  var findin = value
150
155
  .split(',')
151
- .map(function (item) { return (0, utils_1.parseValue)(item.trim(), path === null || path === void 0 ? void 0 : path.instance); });
152
- $filtroActual[key] = (_b = {}, _b[operator] = findin, _b);
156
+ .map(function (item) { return (0, utils_1.parseValue)(item.trim(), instance); });
157
+ $filtroActual[key] = (_c = {}, _c[operator] = findin, _c);
153
158
  break;
154
159
  }
155
160
  default: {
156
- var parsedValue = (0, utils_1.parseValue)(value, path === null || path === void 0 ? void 0 : path.instance);
161
+ var parsedValue = (0, utils_1.parseValue)(value, instance);
157
162
  if (operator) {
158
163
  if (typeof $filtroActual[key] === 'object') {
159
164
  $filtroActual[key][operator] = parsedValue;
160
165
  }
161
166
  else {
162
- $filtroActual[key] = (_c = {}, _c[operator] = parsedValue, _c);
167
+ $filtroActual[key] = (_d = {}, _d[operator] = parsedValue, _d);
163
168
  }
164
169
  }
165
170
  else {
@@ -182,7 +187,7 @@ function createSmartQueryGetPipeline(schema, options) {
182
187
  }
183
188
  }
184
189
  else {
185
- $toAdd[key] = (0, utils_1.parseValue)(valorQuery, path === null || path === void 0 ? void 0 : path.instance);
190
+ $toAdd[key] = (0, utils_1.parseValue)(valorQuery, instance);
186
191
  }
187
192
  };
188
193
  for (keyInicial in query) {
@@ -209,8 +214,8 @@ function createSmartQueryGetPipeline(schema, options) {
209
214
  });
210
215
  }); }))];
211
216
  case 1:
212
- _a.sent();
213
- _a.label = 2;
217
+ _b.sent();
218
+ _b.label = 2;
214
219
  case 2: return [2 /*return*/, $localMatch];
215
220
  }
216
221
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mongoose-smart-query",
3
- "version": "1.4.1",
3
+ "version": "1.4.2",
4
4
  "description": "Optimize rest requests",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",