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 +62 -0
- package/dist/plugin/methods.js +3 -2
- package/dist/plugin/pipeline.js +17 -12
- package/package.json +1 -1
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.
|
package/dist/plugin/methods.js
CHANGED
|
@@ -70,8 +70,9 @@ function createSmartQuery(options) {
|
|
|
70
70
|
useMongoDirectly = true;
|
|
71
71
|
typesenseIds = [];
|
|
72
72
|
typesenseTotal = 0;
|
|
73
|
-
if (!(typesense_1.globalTypesenseClient &&
|
|
74
|
-
|
|
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]);
|
package/dist/plugin/pipeline.js
CHANGED
|
@@ -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
|
-
|
|
96
|
-
|
|
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
|
|
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
|
|
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
|
|
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(),
|
|
152
|
-
$filtroActual[key] = (
|
|
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,
|
|
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] = (
|
|
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,
|
|
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
|
-
|
|
213
|
-
|
|
217
|
+
_b.sent();
|
|
218
|
+
_b.label = 2;
|
|
214
219
|
case 2: return [2 /*return*/, $localMatch];
|
|
215
220
|
}
|
|
216
221
|
});
|