mongoose-killer 0.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.
- package/LICENSE +21 -0
- package/README.md +198 -0
- package/dist/cjs/CastIdsConflictError.d.ts +11 -0
- package/dist/cjs/CastIdsConflictError.d.ts.map +1 -0
- package/dist/cjs/CastIdsConflictError.js +27 -0
- package/dist/cjs/Model.d.ts +51 -0
- package/dist/cjs/Model.d.ts.map +1 -0
- package/dist/cjs/Model.js +205 -0
- package/dist/cjs/Query.d.ts +85 -0
- package/dist/cjs/Query.d.ts.map +1 -0
- package/dist/cjs/Query.js +439 -0
- package/dist/cjs/castFilter.d.ts +25 -0
- package/dist/cjs/castFilter.d.ts.map +1 -0
- package/dist/cjs/castFilter.js +100 -0
- package/dist/cjs/createGetModel.d.ts +3 -0
- package/dist/cjs/createGetModel.d.ts.map +1 -0
- package/dist/cjs/createGetModel.js +47 -0
- package/dist/cjs/index.d.ts +4 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +12 -0
- package/dist/cjs/populate.d.ts +23 -0
- package/dist/cjs/populate.d.ts.map +1 -0
- package/dist/cjs/populate.js +185 -0
- package/dist/cjs/types.d.ts +14 -0
- package/dist/cjs/types.d.ts.map +1 -0
- package/dist/cjs/types.js +2 -0
- package/dist/esm/CastIdsConflictError.js +23 -0
- package/dist/esm/Model.js +201 -0
- package/dist/esm/Query.js +434 -0
- package/dist/esm/castFilter.js +96 -0
- package/dist/esm/createGetModel.js +43 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/populate.js +180 -0
- package/dist/esm/types.js +1 -0
- package/package.json +74 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ben Thayer
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# Viper
|
|
2
|
+
|
|
3
|
+
In a fight of Mongoose vs. Viper, only 1 in 10 Vipers come out on top. This is the 10th Viper.
|
|
4
|
+
|
|
5
|
+
Viper is a drop-in Mongoose replacement built for speed.
|
|
6
|
+
|
|
7
|
+
Under the hood, it uses the native MongoDB driver without any fluff for optimal performance while maintaining the same convenient API as Mongoose.
|
|
8
|
+
|
|
9
|
+
## Quickstart
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install mongoose-killer mongodb
|
|
13
|
+
```
|
|
14
|
+
```ts
|
|
15
|
+
import { createGetModel } from "mongoose-killer";
|
|
16
|
+
import { MongoClient } from "mongodb";
|
|
17
|
+
|
|
18
|
+
const client = new MongoClient(process.env.MONGO_URI!);
|
|
19
|
+
await client.connect();
|
|
20
|
+
const db = client.db("app");
|
|
21
|
+
|
|
22
|
+
const getModel = createGetModel({
|
|
23
|
+
db,
|
|
24
|
+
models: {
|
|
25
|
+
User: "users",
|
|
26
|
+
Post: "posts",
|
|
27
|
+
},
|
|
28
|
+
populates: {
|
|
29
|
+
Post: {
|
|
30
|
+
authorID: { collection: "users" },
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const User = getModel("User");
|
|
36
|
+
const Post = getModel("Post");
|
|
37
|
+
|
|
38
|
+
await User.create({ name: "Alice" });
|
|
39
|
+
|
|
40
|
+
const posts = await Post.find({ published: true })
|
|
41
|
+
.populate("authorID")
|
|
42
|
+
.sort({ createdAt: -1 })
|
|
43
|
+
.limit(10)
|
|
44
|
+
.lean();
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## What it is
|
|
48
|
+
|
|
49
|
+
- **A `Model` API, not an ODM.** No schemas, no validation, no hooks,
|
|
50
|
+
no virtuals. The native MongoDB driver does the work; Viper just
|
|
51
|
+
exposes it through the chainable, thenable, `.populate()`-aware
|
|
52
|
+
interface mongoose users already think in.
|
|
53
|
+
- **Drop-in equivalence is enforced.** The test suite runs every test
|
|
54
|
+
against real Mongoose *and* Viper, asserting identical results. If a
|
|
55
|
+
call shape isn't covered by `tests/`, assume it isn't supported.
|
|
56
|
+
- **TypeScript-first.** Source is TS, types ship with the package, ESM
|
|
57
|
+
and CJS both work.
|
|
58
|
+
|
|
59
|
+
## What it isn't
|
|
60
|
+
|
|
61
|
+
- A general-purpose ODM. The supported surface is whatever the test
|
|
62
|
+
suite covers — see "What's supported" below.
|
|
63
|
+
- A schema layer. Documents are plain objects in and out. If you want
|
|
64
|
+
validation, do it before you call Viper.
|
|
65
|
+
|
|
66
|
+
## Installing
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npm install mongoose-killer mongodb
|
|
70
|
+
# or
|
|
71
|
+
yarn add mongoose-killer mongodb
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`mongodb` is a peer dependency — Viper doesn't bundle a driver, so the
|
|
75
|
+
client and Viper agree on driver version.
|
|
76
|
+
|
|
77
|
+
## What's supported
|
|
78
|
+
|
|
79
|
+
- **Standard calls**: `find`, `findOne`, `findById`, `findOneAndUpdate`,
|
|
80
|
+
`findByIdAndUpdate`, `findOneAndDelete`, `findByIdAndDelete`,
|
|
81
|
+
`updateOne`, `updateMany`, `deleteOne`, `deleteMany`, `create`,
|
|
82
|
+
`insertMany`, `bulkWrite`, `aggregate`, `countDocuments`, `distinct`,
|
|
83
|
+
`exists`.
|
|
84
|
+
- **Populate**: string path, object spec with `select`/`match`, nested,
|
|
85
|
+
array-of-refs (`tags._id`), array of specs, static
|
|
86
|
+
`Model.populate(docs, spec)`. Always `$in`-batched, never N+1.
|
|
87
|
+
- **Method chaining**: `lean`, `sort`, `limit`, `skip`, `select`, `hint`,
|
|
88
|
+
`comment`, `session`, `read`, `populate`, `where`, `or`, `and`,
|
|
89
|
+
`nor`, `option`.
|
|
90
|
+
- **Sessions & Transactions**: `{ session }` threads through every op for transactions.
|
|
91
|
+
- **Thenable API**: `await`, `.then`, `.catch`, `.finally`, `.exec()`.
|
|
92
|
+
|
|
93
|
+
## What's *not* supported
|
|
94
|
+
|
|
95
|
+
- Schemas, virtuals, hooks (`pre`/`post`), getters, setters, casting,
|
|
96
|
+
validation, defaults.
|
|
97
|
+
- `new Model(doc)` / `doc.save()`. Use `Model.create(doc)`.
|
|
98
|
+
- Mongoose-specific connection/event API. You own the `MongoClient`
|
|
99
|
+
and pass us a `Db`.
|
|
100
|
+
- Mongoose's `Types` namespace — import `ObjectId` / `Decimal128`
|
|
101
|
+
straight from `bson`.
|
|
102
|
+
|
|
103
|
+
## Compatibility Features
|
|
104
|
+
|
|
105
|
+
### ObjectId Auto-casting
|
|
106
|
+
|
|
107
|
+
Like Mongoose, Viper automatically casts 24-char hex strings to
|
|
108
|
+
`ObjectId` in filter positions. **On by default** for drop-in parity.
|
|
109
|
+
|
|
110
|
+
Since Viper doesn't know your schemas, this cast is applied to *every*
|
|
111
|
+
24-hex string in a filter, not just the ones that should be IDs. If
|
|
112
|
+
you have a field that stores 24-character hex strings that aren't IDs,
|
|
113
|
+
this behavior will be incorrect for your use case.
|
|
114
|
+
|
|
115
|
+
You can easily opt out of this behavior by changing the global default
|
|
116
|
+
or overriding per query.
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
// Default — on
|
|
121
|
+
const getModel = createGetModel({ db, models });
|
|
122
|
+
const getModel = createGetModel({ db, models, autoCastIds: true }); // Same but explicit
|
|
123
|
+
|
|
124
|
+
// Opt out globally
|
|
125
|
+
const getModel = createGetModel({ db, models, autoCastIds: false });
|
|
126
|
+
|
|
127
|
+
// Override per query
|
|
128
|
+
await User.findOne({ _id: hexId }).castIds(); // force on
|
|
129
|
+
await User.findOne({ slug: hexLookingSlug }).skipCastIds(); // force off
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
If both `.castIds()` and `.skipCastIds()` get called on the same query
|
|
133
|
+
(probably a bug), Viper throws by default. You can change that via
|
|
134
|
+
`castIdsConflictPolicy: 'firstWins' | 'lastWins' | 'defaultWins'`.
|
|
135
|
+
|
|
136
|
+
### `.id` field
|
|
137
|
+
|
|
138
|
+
Like Mongoose, Viper exposes `.id` on non-lean documents as the
|
|
139
|
+
stringified `_id`.
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
const post = await Post.findOne({ slug: "hello-world" });
|
|
143
|
+
// post._id is an ObjectId, post.id is its string form
|
|
144
|
+
|
|
145
|
+
console.log(post._id); // ObjectId("507f1f77bcf86cd799439011")
|
|
146
|
+
console.log(post.id); // "507f1f77bcf86cd799439011"
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
If a document has no `_id`, `.id` returns `null` (matching Mongoose).
|
|
150
|
+
The `.id` field will not be detected by `JSON.stringify`, `Object.keys`
|
|
151
|
+
and other similar functions, so you won't end up with an id field when
|
|
152
|
+
storing in Redis, for example.
|
|
153
|
+
|
|
154
|
+
`.lean()` returns plain driver objects with no `.id` virtual, also
|
|
155
|
+
matching Mongoose.
|
|
156
|
+
|
|
157
|
+
## TypeScript and ESM
|
|
158
|
+
|
|
159
|
+
Both TypeScript and ESM import syntax are supported by default.
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
## Running the tests
|
|
163
|
+
|
|
164
|
+
Requires a local MongoDB at `mongodb://localhost:27017`
|
|
165
|
+
|
|
166
|
+
With docker-compose like this:
|
|
167
|
+
```docker-compose
|
|
168
|
+
services:
|
|
169
|
+
mongo:
|
|
170
|
+
image: mongo:7
|
|
171
|
+
ports:
|
|
172
|
+
- "27017:27017"
|
|
173
|
+
ulimits:
|
|
174
|
+
nofile:
|
|
175
|
+
soft: 64000
|
|
176
|
+
hard: 64000
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
With docker like this:
|
|
180
|
+
```bash
|
|
181
|
+
docker run --ulimit nofile=64000:64000 -p 27017:27017 mongo:7
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
If using replica set mode, you will need to include directConnection=true
|
|
185
|
+
|
|
186
|
+
Setting ulimits is required because the internal socket limit default is low and running the tests create a lot of sockets.
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
yarn test # default — runs against Viper
|
|
191
|
+
yarn test:real # against real mongoose
|
|
192
|
+
yarn test:both # back-to-back; the drop-in proof
|
|
193
|
+
yarn typecheck # tsc --noEmit
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## License
|
|
197
|
+
|
|
198
|
+
MIT.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare class CastIdsConflictError extends Error {
|
|
2
|
+
readonly name = "CastIdsConflictError";
|
|
3
|
+
readonly castIdsCallCount: number;
|
|
4
|
+
readonly skipCastIdsCallCount: number;
|
|
5
|
+
constructor(opts: {
|
|
6
|
+
castIdsCallCount: number;
|
|
7
|
+
skipCastIdsCallCount: number;
|
|
8
|
+
});
|
|
9
|
+
static is(err: unknown): err is CastIdsConflictError;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=CastIdsConflictError.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CastIdsConflictError.d.ts","sourceRoot":"","sources":["../../src/CastIdsConflictError.ts"],"names":[],"mappings":"AASA,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,IAAI,0BAA0B;IACvC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;gBAE1B,IAAI,EAAE;QAAE,gBAAgB,EAAE,MAAM,CAAC;QAAC,oBAAoB,EAAE,MAAM,CAAA;KAAE;IAS5E,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,oBAAoB;CAMrD"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Thrown at query exec time when a single Query has had both
|
|
3
|
+
// `.castIds()` and `.skipCastIds()` called on it AND the configured
|
|
4
|
+
// conflict policy is "throw".
|
|
5
|
+
//
|
|
6
|
+
// Exported as a named class so callers can do `instanceof` in error
|
|
7
|
+
// handlers. The static `is(err)` helper is here for callers that
|
|
8
|
+
// receive errors across module-boundaries (different bundles can
|
|
9
|
+
// produce instanceof-incompatible classes with the same name).
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.CastIdsConflictError = void 0;
|
|
12
|
+
class CastIdsConflictError extends Error {
|
|
13
|
+
name = "CastIdsConflictError";
|
|
14
|
+
castIdsCallCount;
|
|
15
|
+
skipCastIdsCallCount;
|
|
16
|
+
constructor(opts) {
|
|
17
|
+
super(`Conflicting calls on the same Query: ${opts.castIdsCallCount}× .castIds() and ${opts.skipCastIdsCallCount}× .skipCastIds(). ` +
|
|
18
|
+
`The configured castIdsConflictPolicy is "throw" — pass createGetModel({ castIdsConflictPolicy: "lastWins" | "firstWins" | "defaultWins" }) to resolve conflicts automatically, or remove one of the calls.`);
|
|
19
|
+
this.castIdsCallCount = opts.castIdsCallCount;
|
|
20
|
+
this.skipCastIdsCallCount = opts.skipCastIdsCallCount;
|
|
21
|
+
}
|
|
22
|
+
static is(err) {
|
|
23
|
+
return (err instanceof Error &&
|
|
24
|
+
err.name === "CastIdsConflictError");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.CastIdsConflictError = CastIdsConflictError;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { Collection, Db } from "mongodb";
|
|
2
|
+
import { Query } from "./Query.js";
|
|
3
|
+
import type { CastIdsConflictPolicy } from "./types.js";
|
|
4
|
+
type ModelInit = {
|
|
5
|
+
name: string;
|
|
6
|
+
collection: Collection;
|
|
7
|
+
db: Db;
|
|
8
|
+
populates: Record<string, {
|
|
9
|
+
collection: string;
|
|
10
|
+
}>;
|
|
11
|
+
collectionToModelName: Map<string, string>;
|
|
12
|
+
getModelByName: (name: string) => any;
|
|
13
|
+
autoCastIds: boolean;
|
|
14
|
+
castIdsConflictPolicy: CastIdsConflictPolicy;
|
|
15
|
+
};
|
|
16
|
+
export declare class Model {
|
|
17
|
+
name: string;
|
|
18
|
+
collection: Collection;
|
|
19
|
+
populates: Record<string, {
|
|
20
|
+
collection: string;
|
|
21
|
+
}>;
|
|
22
|
+
collectionToModelName: Map<string, string>;
|
|
23
|
+
getModelByName: (name: string) => any;
|
|
24
|
+
autoCastIds: boolean;
|
|
25
|
+
castIdsConflictPolicy: CastIdsConflictPolicy;
|
|
26
|
+
db: {
|
|
27
|
+
db: Db;
|
|
28
|
+
};
|
|
29
|
+
constructor(init: ModelInit);
|
|
30
|
+
find(filter?: any, projection?: any, options?: any): Query;
|
|
31
|
+
findOne(filter?: any, projection?: any, options?: any): Query;
|
|
32
|
+
findById(id: any, projection?: any, options?: any): Query;
|
|
33
|
+
findOneAndUpdate(filter: any, update: any, options?: any): Query;
|
|
34
|
+
findByIdAndUpdate(id: any, update: any, options?: any): Query;
|
|
35
|
+
findOneAndDelete(filter: any, options?: any): Query;
|
|
36
|
+
findByIdAndDelete(id: any, options?: any): Query;
|
|
37
|
+
updateOne(filter: any, update: any, options?: any): Query;
|
|
38
|
+
updateMany(filter: any, update: any, options?: any): Query;
|
|
39
|
+
deleteOne(filter: any, options?: any): Query;
|
|
40
|
+
deleteMany(filter: any, options?: any): Query;
|
|
41
|
+
countDocuments(filter?: any, options?: any): Query;
|
|
42
|
+
distinct(field: string, filter?: any, options?: any): Query;
|
|
43
|
+
exists(filter: any, options?: any): Query;
|
|
44
|
+
aggregate(pipeline?: any[], options?: any): Query;
|
|
45
|
+
create(input: any, options?: any): Promise<any>;
|
|
46
|
+
insertMany(docs: any[], options?: any): Promise<any[]>;
|
|
47
|
+
bulkWrite(ops: any[], options?: any): Promise<any>;
|
|
48
|
+
populate(docs: any, arg: any): Promise<any>;
|
|
49
|
+
}
|
|
50
|
+
export {};
|
|
51
|
+
//# sourceMappingURL=Model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Model.d.ts","sourceRoot":"","sources":["../../src/Model.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Model = void 0;
|
|
4
|
+
const bson_1 = require("bson");
|
|
5
|
+
const Query_js_1 = require("./Query.js");
|
|
6
|
+
const populate_js_1 = require("./populate.js");
|
|
7
|
+
// Mongoose `Model` is half class, half magic. We mimic the surface the
|
|
8
|
+
// codebase actually uses (per inventory): the static entry verbs.
|
|
9
|
+
// Instances are not a thing — call sites never `new Model()`.
|
|
10
|
+
class Model {
|
|
11
|
+
name;
|
|
12
|
+
collection;
|
|
13
|
+
populates;
|
|
14
|
+
collectionToModelName;
|
|
15
|
+
getModelByName;
|
|
16
|
+
autoCastIds;
|
|
17
|
+
castIdsConflictPolicy;
|
|
18
|
+
// Mongoose exposes Model.db as a Connection-like object, and
|
|
19
|
+
// Connection.db is the native Db handle. Some libraries reach
|
|
20
|
+
// through `Model.db.db` to grab the native Db and issue a raw
|
|
21
|
+
// `db.collection(name).findOne({})` — bypassing the ODM (often
|
|
22
|
+
// to avoid lossy type handling like mongoose's historical
|
|
23
|
+
// Decimal128 quirks).
|
|
24
|
+
//
|
|
25
|
+
// We mimic that two-hop shape: `model.db` is a wrapper object whose
|
|
26
|
+
// `.db` is the native Db. Anything else hanging off Connection is
|
|
27
|
+
// not currently used by any call site, so we leave it unset.
|
|
28
|
+
db;
|
|
29
|
+
constructor(init) {
|
|
30
|
+
this.name = init.name;
|
|
31
|
+
this.collection = init.collection;
|
|
32
|
+
this.populates = init.populates;
|
|
33
|
+
this.collectionToModelName = init.collectionToModelName;
|
|
34
|
+
this.getModelByName = init.getModelByName;
|
|
35
|
+
this.autoCastIds = init.autoCastIds;
|
|
36
|
+
this.castIdsConflictPolicy = init.castIdsConflictPolicy;
|
|
37
|
+
this.db = { db: init.db };
|
|
38
|
+
}
|
|
39
|
+
// ---- query entry verbs --------------------------------------------
|
|
40
|
+
find(filter = {}, projection, options) {
|
|
41
|
+
const q = new Query_js_1.Query({ model: this, op: "find", filter, options });
|
|
42
|
+
if (projection)
|
|
43
|
+
q.select(projection);
|
|
44
|
+
if (options?.sort)
|
|
45
|
+
q.sort(options.sort);
|
|
46
|
+
if (options?.limit !== undefined)
|
|
47
|
+
q.limit(options.limit);
|
|
48
|
+
if (options?.skip !== undefined)
|
|
49
|
+
q.skip(options.skip);
|
|
50
|
+
return q;
|
|
51
|
+
}
|
|
52
|
+
findOne(filter = {}, projection, options) {
|
|
53
|
+
const q = new Query_js_1.Query({ model: this, op: "findOne", filter, options });
|
|
54
|
+
if (projection)
|
|
55
|
+
q.select(projection);
|
|
56
|
+
return q;
|
|
57
|
+
}
|
|
58
|
+
findById(id, projection, options) {
|
|
59
|
+
return this.findOne({ _id: coerceId(id) }, projection, options);
|
|
60
|
+
}
|
|
61
|
+
findOneAndUpdate(filter, update, options) {
|
|
62
|
+
return new Query_js_1.Query({
|
|
63
|
+
model: this,
|
|
64
|
+
op: "findOneAndUpdate",
|
|
65
|
+
filter,
|
|
66
|
+
update,
|
|
67
|
+
options,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
findByIdAndUpdate(id, update, options) {
|
|
71
|
+
return this.findOneAndUpdate({ _id: coerceId(id) }, update, options);
|
|
72
|
+
}
|
|
73
|
+
findOneAndDelete(filter, options) {
|
|
74
|
+
return new Query_js_1.Query({
|
|
75
|
+
model: this,
|
|
76
|
+
op: "findOneAndDelete",
|
|
77
|
+
filter,
|
|
78
|
+
options,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
findByIdAndDelete(id, options) {
|
|
82
|
+
return this.findOneAndDelete({ _id: coerceId(id) }, options);
|
|
83
|
+
}
|
|
84
|
+
updateOne(filter, update, options) {
|
|
85
|
+
return new Query_js_1.Query({
|
|
86
|
+
model: this,
|
|
87
|
+
op: "updateOne",
|
|
88
|
+
filter,
|
|
89
|
+
update,
|
|
90
|
+
options,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
updateMany(filter, update, options) {
|
|
94
|
+
return new Query_js_1.Query({
|
|
95
|
+
model: this,
|
|
96
|
+
op: "updateMany",
|
|
97
|
+
filter,
|
|
98
|
+
update,
|
|
99
|
+
options,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
deleteOne(filter, options) {
|
|
103
|
+
return new Query_js_1.Query({
|
|
104
|
+
model: this,
|
|
105
|
+
op: "deleteOne",
|
|
106
|
+
filter,
|
|
107
|
+
options,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
deleteMany(filter, options) {
|
|
111
|
+
return new Query_js_1.Query({
|
|
112
|
+
model: this,
|
|
113
|
+
op: "deleteMany",
|
|
114
|
+
filter,
|
|
115
|
+
options,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
countDocuments(filter = {}, options) {
|
|
119
|
+
return new Query_js_1.Query({
|
|
120
|
+
model: this,
|
|
121
|
+
op: "countDocuments",
|
|
122
|
+
filter,
|
|
123
|
+
options,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
distinct(field, filter = {}, options) {
|
|
127
|
+
return new Query_js_1.Query({
|
|
128
|
+
model: this,
|
|
129
|
+
op: "distinct",
|
|
130
|
+
filter,
|
|
131
|
+
distinctField: field,
|
|
132
|
+
options,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
exists(filter, options) {
|
|
136
|
+
return new Query_js_1.Query({
|
|
137
|
+
model: this,
|
|
138
|
+
op: "exists",
|
|
139
|
+
filter,
|
|
140
|
+
options,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
aggregate(pipeline = [], options) {
|
|
144
|
+
return new Query_js_1.Query({
|
|
145
|
+
model: this,
|
|
146
|
+
op: "aggregate",
|
|
147
|
+
pipeline,
|
|
148
|
+
options,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
// ---- writes that aren't chains ------------------------------------
|
|
152
|
+
// Mongoose's create returns the inserted doc(s) with _id set.
|
|
153
|
+
// Inventory uses both forms: create(doc) and create([docs], options).
|
|
154
|
+
async create(input, options) {
|
|
155
|
+
if (Array.isArray(input)) {
|
|
156
|
+
const docs = input.map(prepInsert);
|
|
157
|
+
if (docs.length === 0)
|
|
158
|
+
return [];
|
|
159
|
+
await this.collection.insertMany(docs, options ?? {});
|
|
160
|
+
return docs;
|
|
161
|
+
}
|
|
162
|
+
const doc = prepInsert(input);
|
|
163
|
+
await this.collection.insertOne(doc, options ?? {});
|
|
164
|
+
return doc;
|
|
165
|
+
}
|
|
166
|
+
async insertMany(docs, options) {
|
|
167
|
+
const prepared = docs.map(prepInsert);
|
|
168
|
+
if (prepared.length === 0)
|
|
169
|
+
return [];
|
|
170
|
+
await this.collection.insertMany(prepared, options ?? {});
|
|
171
|
+
return prepared;
|
|
172
|
+
}
|
|
173
|
+
async bulkWrite(ops, options) {
|
|
174
|
+
return this.collection.bulkWrite(ops, options ?? {});
|
|
175
|
+
}
|
|
176
|
+
// Static populate: takes pre-fetched doc(s) and decorates them.
|
|
177
|
+
// Used in a handful of inventory sites for ad-hoc populate after
|
|
178
|
+
// a custom aggregation.
|
|
179
|
+
async populate(docs, arg) {
|
|
180
|
+
const isArr = Array.isArray(docs);
|
|
181
|
+
const arr = isArr ? docs : [docs];
|
|
182
|
+
if (arr.length === 0)
|
|
183
|
+
return docs;
|
|
184
|
+
const specs = (0, populate_js_1.normalizePopulateArg)(arg);
|
|
185
|
+
const populated = await (0, populate_js_1.runPopulate)(arr, specs, {
|
|
186
|
+
ownerModel: this,
|
|
187
|
+
session: undefined,
|
|
188
|
+
});
|
|
189
|
+
return isArr ? populated : populated[0];
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
exports.Model = Model;
|
|
193
|
+
// Mongoose auto-assigns _id on insert; the driver does the same, but
|
|
194
|
+
// only if _id is missing. We let the driver handle it — no work here.
|
|
195
|
+
// Kept as a hook in case we ever need to deep-clone or strip Mongoose-
|
|
196
|
+
// only fields.
|
|
197
|
+
const prepInsert = (doc) => doc;
|
|
198
|
+
const coerceId = (id) => {
|
|
199
|
+
if (id instanceof bson_1.ObjectId)
|
|
200
|
+
return id;
|
|
201
|
+
if (typeof id === "string" && /^[0-9a-fA-F]{24}$/.test(id)) {
|
|
202
|
+
return new bson_1.ObjectId(id);
|
|
203
|
+
}
|
|
204
|
+
return id;
|
|
205
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { Collection, ClientSession } from "mongodb";
|
|
2
|
+
import type { CastIdsConflictPolicy } from "./types.js";
|
|
3
|
+
type Op = "find" | "findOne" | "findOneAndUpdate" | "findOneAndDelete" | "updateOne" | "updateMany" | "deleteOne" | "deleteMany" | "countDocuments" | "distinct" | "exists" | "aggregate";
|
|
4
|
+
type ModelLike = {
|
|
5
|
+
name: string;
|
|
6
|
+
collection: Collection;
|
|
7
|
+
populates: Record<string, {
|
|
8
|
+
collection: string;
|
|
9
|
+
}>;
|
|
10
|
+
collectionToModelName: Map<string, string>;
|
|
11
|
+
getModelByName: (name: string) => any;
|
|
12
|
+
autoCastIds: boolean;
|
|
13
|
+
castIdsConflictPolicy: CastIdsConflictPolicy;
|
|
14
|
+
};
|
|
15
|
+
type QueryInit = {
|
|
16
|
+
model: ModelLike;
|
|
17
|
+
op: Op;
|
|
18
|
+
filter?: any;
|
|
19
|
+
update?: any;
|
|
20
|
+
pipeline?: any[];
|
|
21
|
+
distinctField?: string;
|
|
22
|
+
options?: any;
|
|
23
|
+
};
|
|
24
|
+
export declare class Query implements PromiseLike<any> {
|
|
25
|
+
private model;
|
|
26
|
+
private op;
|
|
27
|
+
private filter;
|
|
28
|
+
private update;
|
|
29
|
+
private pipeline;
|
|
30
|
+
private distinctField;
|
|
31
|
+
private _sort;
|
|
32
|
+
private _limit;
|
|
33
|
+
private _skip;
|
|
34
|
+
private _select;
|
|
35
|
+
private _hint;
|
|
36
|
+
private _comment;
|
|
37
|
+
private _session;
|
|
38
|
+
private _readPref;
|
|
39
|
+
private _populates;
|
|
40
|
+
private _lean;
|
|
41
|
+
private _castIdsOps;
|
|
42
|
+
private opOptions;
|
|
43
|
+
private _executed;
|
|
44
|
+
private _execPromise;
|
|
45
|
+
constructor(init: QueryInit);
|
|
46
|
+
lean(on?: boolean): this;
|
|
47
|
+
sort(spec: any): this;
|
|
48
|
+
limit(n: number): this;
|
|
49
|
+
skip(n: number): this;
|
|
50
|
+
select(spec: any): this;
|
|
51
|
+
hint(h: any): this;
|
|
52
|
+
comment(c: string): this;
|
|
53
|
+
session(s: ClientSession | null | undefined): this;
|
|
54
|
+
read(pref: any): this;
|
|
55
|
+
castIds(): this;
|
|
56
|
+
skipCastIds(): this;
|
|
57
|
+
populate(arg?: any): this;
|
|
58
|
+
where(field: string, value?: any): this;
|
|
59
|
+
or(conditions: any[]): this;
|
|
60
|
+
and(conditions: any[]): this;
|
|
61
|
+
nor(conditions: any[]): this;
|
|
62
|
+
option(o: any): this;
|
|
63
|
+
exec(): Promise<any>;
|
|
64
|
+
then<TResolve = any, TReject = never>(onResolve?: ((v: any) => TResolve | PromiseLike<TResolve>) | null, onReject?: ((e: any) => TReject | PromiseLike<TReject>) | null): Promise<TResolve | TReject>;
|
|
65
|
+
catch<T = never>(onReject?: ((e: any) => T | PromiseLike<T>) | null): Promise<any | T>;
|
|
66
|
+
finally(onFinally?: (() => void) | null): Promise<any>;
|
|
67
|
+
private run;
|
|
68
|
+
private castedFilter;
|
|
69
|
+
private resolveCastIds;
|
|
70
|
+
private findOptions;
|
|
71
|
+
private writeOptions;
|
|
72
|
+
private runFind;
|
|
73
|
+
private runFindOne;
|
|
74
|
+
private runFindOneAndUpdate;
|
|
75
|
+
private runFindOneAndDelete;
|
|
76
|
+
private runUpdate;
|
|
77
|
+
private runDelete;
|
|
78
|
+
private runCount;
|
|
79
|
+
private runDistinct;
|
|
80
|
+
private runExists;
|
|
81
|
+
private runAggregate;
|
|
82
|
+
}
|
|
83
|
+
export declare const addIdVirtual: (doc: any) => void;
|
|
84
|
+
export {};
|
|
85
|
+
//# sourceMappingURL=Query.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Query.d.ts","sourceRoot":"","sources":["../../src/Query.ts"],"names":[],"mappings":"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"}
|