@primate/mongodb 0.5.0 → 0.7.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/lib/private/ColumnTypes.d.ts +5 -5
- package/lib/private/MongoDB.d.ts +17 -13
- package/lib/private/MongoDB.js +187 -103
- package/lib/private/typemap.js +17 -1
- package/package.json +13 -13
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { TypedArray } from "@rcompat/type";
|
|
2
|
-
import type { Binary, Decimal128,
|
|
3
|
-
type Param = bigint | Binary | boolean | Date | Decimal128 | number |
|
|
1
|
+
import type { JSONValue, TypedArray } from "@rcompat/type";
|
|
2
|
+
import type { Binary, Decimal128, UUID } from "mongodb";
|
|
3
|
+
type Param = bigint | Binary | boolean | Date | Decimal128 | number | string | TypedArray | JSONValue;
|
|
4
4
|
type Validate<T extends {
|
|
5
5
|
[K in keyof T]: Param;
|
|
6
6
|
}> = T;
|
|
@@ -12,10 +12,10 @@ type ColumnTypes = Validate<{
|
|
|
12
12
|
DOUBLE: number;
|
|
13
13
|
INT: number;
|
|
14
14
|
LONG: bigint;
|
|
15
|
-
PRIMARY: ObjectId;
|
|
16
15
|
STRING: string;
|
|
17
16
|
TIME: string;
|
|
18
|
-
UUID:
|
|
17
|
+
UUID: UUID;
|
|
18
|
+
JSON: JSONValue;
|
|
19
19
|
}>;
|
|
20
20
|
export type { ColumnTypes as default };
|
|
21
21
|
//# sourceMappingURL=ColumnTypes.d.ts.map
|
package/lib/private/MongoDB.d.ts
CHANGED
|
@@ -1,22 +1,26 @@
|
|
|
1
|
-
import type { As, DataDict, DB, Sort, With } from "@primate/core/db";
|
|
1
|
+
import type { As, DataDict, DB, Schema, Sort, With } from "@primate/core/db";
|
|
2
2
|
import type { Dict } from "@rcompat/type";
|
|
3
|
-
import
|
|
4
|
-
declare const schema: import("pema").ObjectType<{
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
import { MongoClient } from "mongodb";
|
|
4
|
+
declare const schema: import("pema").ObjectType<import("pema").NormalizeSchemaObject<{
|
|
5
|
+
readonly host: import("pema").DefaultType<import("pema").StringType, "localhost">;
|
|
6
|
+
readonly port: import("pema").DefaultType<import("pema").UintType<"u32">, 27017>;
|
|
7
|
+
readonly database: import("pema").StringType;
|
|
8
|
+
readonly username: import("pema").OptionalType<import("pema").StringType>;
|
|
9
|
+
readonly password: import("pema").OptionalType<import("pema").StringType>;
|
|
10
|
+
}>, {
|
|
11
|
+
host: string;
|
|
12
|
+
port: number;
|
|
13
|
+
database: string;
|
|
14
|
+
username: string | undefined;
|
|
15
|
+
password: string | undefined;
|
|
10
16
|
}>;
|
|
11
|
-
export default class MongoDB implements DB {
|
|
17
|
+
export default class MongoDB implements DB<MongoClient> {
|
|
12
18
|
#private;
|
|
13
19
|
static config: typeof schema.input;
|
|
14
20
|
constructor(config?: typeof schema.input);
|
|
21
|
+
get client(): MongoClient;
|
|
15
22
|
close(): Promise<void>;
|
|
16
|
-
get schema():
|
|
17
|
-
create: (_as: As, _store: StoreSchema) => Promise<void>;
|
|
18
|
-
delete: (name: string) => Promise<void>;
|
|
19
|
-
};
|
|
23
|
+
get schema(): Schema;
|
|
20
24
|
create<O extends Dict>(as: As, record: Dict): Promise<O>;
|
|
21
25
|
read(as: As, args: {
|
|
22
26
|
count: true;
|
package/lib/private/MongoDB.js
CHANGED
|
@@ -1,20 +1,17 @@
|
|
|
1
1
|
import typemap from "#typemap";
|
|
2
|
-
import
|
|
3
|
-
import E from "@primate/core/db/
|
|
2
|
+
import base from "@primate/core/db";
|
|
3
|
+
import E from "@primate/core/db/errors";
|
|
4
4
|
import assert from "@rcompat/assert";
|
|
5
5
|
import is from "@rcompat/is";
|
|
6
|
-
import { MongoClient
|
|
6
|
+
import { MongoClient } from "mongodb";
|
|
7
7
|
import p from "pema";
|
|
8
8
|
const schema = p({
|
|
9
|
-
database: p.string,
|
|
10
9
|
host: p.string.default("localhost"),
|
|
11
|
-
password: p.string.optional(),
|
|
12
10
|
port: p.uint.port().default(27017),
|
|
11
|
+
database: p.string,
|
|
13
12
|
username: p.string.optional(),
|
|
13
|
+
password: p.string.optional(),
|
|
14
14
|
});
|
|
15
|
-
function is_object_id(x) {
|
|
16
|
-
return x instanceof ObjectId;
|
|
17
|
-
}
|
|
18
15
|
function get_limit(limit) {
|
|
19
16
|
return limit ?? 0; // 0 = no limit
|
|
20
17
|
}
|
|
@@ -52,6 +49,73 @@ async function bind_value(key, value) {
|
|
|
52
49
|
function unbind_value(key, value) {
|
|
53
50
|
return typemap[key].unbind(value);
|
|
54
51
|
}
|
|
52
|
+
function to_mongo_pk(field, pk) {
|
|
53
|
+
return field === pk ? "_id" : field;
|
|
54
|
+
}
|
|
55
|
+
async function bind(as, object) {
|
|
56
|
+
const pk = as.pk;
|
|
57
|
+
const out = {};
|
|
58
|
+
for (const [field, value] of Object.entries(object)) {
|
|
59
|
+
const mongo_field = to_mongo_pk(field, pk);
|
|
60
|
+
const datatype = as.types[field];
|
|
61
|
+
if (value === null) {
|
|
62
|
+
out[mongo_field] = null;
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (is.dict(value) && datatype !== "json") {
|
|
66
|
+
const ops = Object.entries(value);
|
|
67
|
+
if (ops.length === 0)
|
|
68
|
+
throw E.operator_empty(field);
|
|
69
|
+
for (const [op, op_value] of ops) {
|
|
70
|
+
const existing = (out[mongo_field] ?? {});
|
|
71
|
+
let next;
|
|
72
|
+
switch (op) {
|
|
73
|
+
case "$like":
|
|
74
|
+
next = { $regex: like_to_regex(String(op_value)) };
|
|
75
|
+
break;
|
|
76
|
+
case "$ilike":
|
|
77
|
+
next = { $regex: like_to_regex(String(op_value)), $options: "i" };
|
|
78
|
+
break;
|
|
79
|
+
case "$ne":
|
|
80
|
+
case "$gt":
|
|
81
|
+
case "$gte":
|
|
82
|
+
case "$lt":
|
|
83
|
+
case "$lte":
|
|
84
|
+
next = { [op]: await bind_value(datatype, op_value) };
|
|
85
|
+
break;
|
|
86
|
+
case "$after":
|
|
87
|
+
next = { $gt: await bind_value(datatype, op_value) };
|
|
88
|
+
break;
|
|
89
|
+
case "$before":
|
|
90
|
+
next = { $lt: await bind_value(datatype, op_value) };
|
|
91
|
+
break;
|
|
92
|
+
case "$in": {
|
|
93
|
+
const array = op_value;
|
|
94
|
+
const bound = await Promise.all(array.map(v => bind_value(datatype, v)));
|
|
95
|
+
next = { $in: bound };
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
default:
|
|
99
|
+
throw E.operator_unknown(field, op);
|
|
100
|
+
}
|
|
101
|
+
out[mongo_field] = { ...existing, ...next };
|
|
102
|
+
}
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (field === pk) {
|
|
106
|
+
const type = as.types[pk];
|
|
107
|
+
if (base.is_uuid_type(type)) {
|
|
108
|
+
out._id = await bind_value(type, value);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
out._id = value;
|
|
112
|
+
}
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
out[mongo_field] = await bind_value(datatype, value);
|
|
116
|
+
}
|
|
117
|
+
return out;
|
|
118
|
+
}
|
|
55
119
|
function like_to_regex(pattern) {
|
|
56
120
|
return "^" + pattern
|
|
57
121
|
.replace(/\\%/g, "<<PERCENT>>")
|
|
@@ -62,15 +126,37 @@ function like_to_regex(pattern) {
|
|
|
62
126
|
.replace(/<<PERCENT>>/g, "%")
|
|
63
127
|
.replace(/<<UNDERSCORE>>/g, "_") + "$";
|
|
64
128
|
}
|
|
129
|
+
function columns_to_types(row) {
|
|
130
|
+
switch (row.type) {
|
|
131
|
+
case "string": return ["string", "url", "time", "i128", "u128"];
|
|
132
|
+
case "int": return ["i8", "u8", "i16", "u16", "i32", "u32"];
|
|
133
|
+
case "long": return ["i64"];
|
|
134
|
+
case "double": return ["f32", "f64"];
|
|
135
|
+
case "bool": return ["boolean"];
|
|
136
|
+
case "date": return ["datetime"];
|
|
137
|
+
case "binData": return row.name === "_id" ? base.UUID_TYPES : ["blob"];
|
|
138
|
+
case "decimal": return ["u64"];
|
|
139
|
+
case "objectId": return ["string"];
|
|
140
|
+
case "object": return ["json"];
|
|
141
|
+
case "array": return ["json"];
|
|
142
|
+
default: return [];
|
|
143
|
+
}
|
|
144
|
+
}
|
|
65
145
|
export default class MongoDB {
|
|
66
146
|
static config;
|
|
67
147
|
#factory;
|
|
68
148
|
#database;
|
|
69
149
|
#client;
|
|
70
150
|
constructor(config) {
|
|
71
|
-
const { host, port, database } = schema.parse(config);
|
|
72
|
-
const
|
|
73
|
-
|
|
151
|
+
const { host, port, database, username, password, } = schema.parse(config);
|
|
152
|
+
const uri = new URL(`mongodb://${host}:${port}/${database}`);
|
|
153
|
+
uri.searchParams.set("directConnection", "true");
|
|
154
|
+
if (username !== undefined) {
|
|
155
|
+
uri.username = username;
|
|
156
|
+
uri.password = password ?? "";
|
|
157
|
+
uri.searchParams.set("authSource", database);
|
|
158
|
+
}
|
|
159
|
+
const client = new MongoClient(uri.toString());
|
|
74
160
|
this.#database = database;
|
|
75
161
|
this.#factory = async () => {
|
|
76
162
|
await client.connect();
|
|
@@ -81,17 +167,69 @@ export default class MongoDB {
|
|
|
81
167
|
this.#client ??= await this.#factory();
|
|
82
168
|
return this.#client.db(this.#database).collection(name);
|
|
83
169
|
}
|
|
170
|
+
get client() {
|
|
171
|
+
return this.#client;
|
|
172
|
+
}
|
|
84
173
|
async close() {
|
|
85
|
-
|
|
174
|
+
const client = this.#client;
|
|
175
|
+
this.#client = undefined;
|
|
176
|
+
await client?.close();
|
|
86
177
|
}
|
|
87
178
|
get schema() {
|
|
88
179
|
return {
|
|
89
|
-
create: async (
|
|
90
|
-
|
|
180
|
+
create: async (collection) => {
|
|
181
|
+
const client = this.#client ??= await this.#factory();
|
|
182
|
+
const db = client.db(this.#database);
|
|
183
|
+
// ignore if already exists
|
|
184
|
+
await db.createCollection(collection).catch(() => { });
|
|
185
|
+
},
|
|
186
|
+
delete: async (collection) => {
|
|
187
|
+
const client = this.#client ??= await this.#factory();
|
|
188
|
+
const db = client.db(this.#database);
|
|
189
|
+
// ignore if doesn't exist
|
|
190
|
+
await db.dropCollection(collection).catch(() => { });
|
|
91
191
|
},
|
|
92
|
-
|
|
192
|
+
introspect: async (name, pk) => {
|
|
193
|
+
const client = (this.#client ??= await this.#factory());
|
|
194
|
+
const db = client.db(this.#database);
|
|
195
|
+
const collections = await db.listCollections({ name }).toArray();
|
|
196
|
+
if (collections.length === 0)
|
|
197
|
+
return null;
|
|
93
198
|
const collection = await this.#collection(name);
|
|
94
|
-
await collection.
|
|
199
|
+
const rows = await collection.aggregate([
|
|
200
|
+
{ $limit: 1 },
|
|
201
|
+
{ $project: { fields: { $objectToArray: "$$ROOT" } } },
|
|
202
|
+
{ $unwind: "$fields" },
|
|
203
|
+
{ $project: { _id: 0, name: "$fields.k", type: { $type: "$fields.v" } } },
|
|
204
|
+
]).toArray();
|
|
205
|
+
if (rows.length === 0)
|
|
206
|
+
return {};
|
|
207
|
+
const result = {};
|
|
208
|
+
for (const row of rows) {
|
|
209
|
+
if (pk !== undefined && row.name === "_id") {
|
|
210
|
+
if (pk !== null)
|
|
211
|
+
result[pk] = columns_to_types(row);
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
result[row.name] = columns_to_types(row);
|
|
215
|
+
}
|
|
216
|
+
return result;
|
|
217
|
+
},
|
|
218
|
+
alter: async (name, diff) => {
|
|
219
|
+
const client = this.#client ??= await this.#factory();
|
|
220
|
+
const db = client.db(this.#database);
|
|
221
|
+
const collections = await db.listCollections({ name }).toArray();
|
|
222
|
+
if (collections.length === 0)
|
|
223
|
+
throw E.table_not_found(name);
|
|
224
|
+
const collection = await this.#collection(name);
|
|
225
|
+
if (diff.rename.length > 0) {
|
|
226
|
+
const $rename = Object.fromEntries(diff.rename);
|
|
227
|
+
await collection.updateMany({}, { $rename });
|
|
228
|
+
}
|
|
229
|
+
if (diff.drop.length > 0) {
|
|
230
|
+
const $unset = Object.fromEntries(diff.drop.map(f => [f, ""]));
|
|
231
|
+
await collection.updateMany({}, { $unset });
|
|
232
|
+
}
|
|
95
233
|
},
|
|
96
234
|
};
|
|
97
235
|
}
|
|
@@ -101,64 +239,6 @@ export default class MongoDB {
|
|
|
101
239
|
#from_mongo_pk(field, pk) {
|
|
102
240
|
return field === "_id" && pk !== null ? pk : field;
|
|
103
241
|
}
|
|
104
|
-
async #bind(as, object) {
|
|
105
|
-
const pk = as.pk;
|
|
106
|
-
const out = {};
|
|
107
|
-
for (const [field, value] of Object.entries(object)) {
|
|
108
|
-
const mongo_field = this.#to_mongo_pk(field, pk);
|
|
109
|
-
const datatype = as.types[field];
|
|
110
|
-
if (value === null) {
|
|
111
|
-
out[mongo_field] = null;
|
|
112
|
-
continue;
|
|
113
|
-
}
|
|
114
|
-
if (is.dict(value)) {
|
|
115
|
-
const ops = Object.entries(value);
|
|
116
|
-
if (ops.length === 0)
|
|
117
|
-
throw E.operator_empty(field);
|
|
118
|
-
for (const [op, op_value] of ops) {
|
|
119
|
-
const existing = (out[mongo_field] ?? {});
|
|
120
|
-
let next;
|
|
121
|
-
switch (op) {
|
|
122
|
-
case "$like":
|
|
123
|
-
next = { $regex: like_to_regex(String(op_value)) };
|
|
124
|
-
break;
|
|
125
|
-
case "$ilike":
|
|
126
|
-
next = { $regex: like_to_regex(String(op_value)), $options: "i" };
|
|
127
|
-
break;
|
|
128
|
-
case "$ne":
|
|
129
|
-
case "$gt":
|
|
130
|
-
case "$gte":
|
|
131
|
-
case "$lt":
|
|
132
|
-
case "$lte":
|
|
133
|
-
next = { [op]: await bind_value(datatype, op_value) };
|
|
134
|
-
break;
|
|
135
|
-
case "$after":
|
|
136
|
-
next = { $gt: await bind_value(datatype, op_value) };
|
|
137
|
-
break;
|
|
138
|
-
case "$before":
|
|
139
|
-
next = { $lt: await bind_value(datatype, op_value) };
|
|
140
|
-
break;
|
|
141
|
-
default:
|
|
142
|
-
throw E.operator_unknown(field, op);
|
|
143
|
-
}
|
|
144
|
-
out[mongo_field] = { ...existing, ...next };
|
|
145
|
-
}
|
|
146
|
-
continue;
|
|
147
|
-
}
|
|
148
|
-
if (field === pk) {
|
|
149
|
-
const type = as.types[pk];
|
|
150
|
-
if (type === "string" && ObjectId.isValid(value)) {
|
|
151
|
-
out._id = new ObjectId(value);
|
|
152
|
-
}
|
|
153
|
-
else {
|
|
154
|
-
out._id = value;
|
|
155
|
-
}
|
|
156
|
-
continue;
|
|
157
|
-
}
|
|
158
|
-
out[mongo_field] = await bind_value(datatype, value);
|
|
159
|
-
}
|
|
160
|
-
return out;
|
|
161
|
-
}
|
|
162
242
|
#unbind(as, doc) {
|
|
163
243
|
const pk = as.pk;
|
|
164
244
|
const out = {};
|
|
@@ -169,8 +249,10 @@ export default class MongoDB {
|
|
|
169
249
|
continue;
|
|
170
250
|
}
|
|
171
251
|
if (field === "_id") {
|
|
172
|
-
|
|
173
|
-
out[user_field] =
|
|
252
|
+
const type = as.types[user_field];
|
|
253
|
+
out[user_field] = base.is_uuid_type(type)
|
|
254
|
+
? unbind_value(type, value)
|
|
255
|
+
: value;
|
|
174
256
|
continue;
|
|
175
257
|
}
|
|
176
258
|
out[user_field] = unbind_value(datatype, value);
|
|
@@ -181,13 +263,13 @@ export default class MongoDB {
|
|
|
181
263
|
const pk = as.pk;
|
|
182
264
|
const type = as.types[pk];
|
|
183
265
|
const collection = await this.#collection(as.table);
|
|
184
|
-
if (type
|
|
185
|
-
return
|
|
266
|
+
if (base.is_uuid_type(type))
|
|
267
|
+
return base.generate_uuid(type);
|
|
186
268
|
// for numeric types, find max and increment
|
|
187
269
|
const pipeline = [{ $group: { _id: null, max: { $max: "$_id" } } }];
|
|
188
270
|
const results = await collection.aggregate(pipeline).toArray();
|
|
189
271
|
const max = results.length === 0 ? 0 : results[0].max ?? 0;
|
|
190
|
-
if (
|
|
272
|
+
if (base.BIGINT_STRING_TYPES.includes(type))
|
|
191
273
|
return BigInt(max) + 1n;
|
|
192
274
|
return Number(max) + 1;
|
|
193
275
|
}
|
|
@@ -201,15 +283,17 @@ export default class MongoDB {
|
|
|
201
283
|
const type = as.types[pk];
|
|
202
284
|
if (pk in record) {
|
|
203
285
|
pk_value = record[pk];
|
|
204
|
-
doc._id =
|
|
205
|
-
?
|
|
286
|
+
doc._id = base.is_uuid_type(type)
|
|
287
|
+
? await bind_value(type, pk_value)
|
|
206
288
|
: pk_value;
|
|
207
289
|
}
|
|
208
290
|
else if (as.generate_pk !== false) {
|
|
209
291
|
const generated = await this.#generate_pk(as);
|
|
210
|
-
doc._id = generated;
|
|
211
292
|
// convert to user-facing value
|
|
212
|
-
pk_value =
|
|
293
|
+
pk_value = generated;
|
|
294
|
+
doc._id = base.is_uuid_type(type)
|
|
295
|
+
? await bind_value(type, generated)
|
|
296
|
+
: generated;
|
|
213
297
|
}
|
|
214
298
|
else {
|
|
215
299
|
throw E.pk_required(pk);
|
|
@@ -232,37 +316,37 @@ export default class MongoDB {
|
|
|
232
316
|
if (args.count === true)
|
|
233
317
|
return this.#count(as, args.where);
|
|
234
318
|
// relations always use phased approach
|
|
235
|
-
if (
|
|
319
|
+
if (base.withed(args))
|
|
236
320
|
return this.#read_phased(as, args);
|
|
237
321
|
return this.#read(as, args);
|
|
238
322
|
}
|
|
239
323
|
async #count(as, where) {
|
|
240
|
-
const filter = await
|
|
324
|
+
const filter = await bind(as, where);
|
|
241
325
|
const collection = await this.#collection(as.table);
|
|
242
326
|
const count = await collection.countDocuments(filter);
|
|
243
327
|
return count;
|
|
244
328
|
}
|
|
245
329
|
async #read(as, args) {
|
|
246
|
-
const filter = await
|
|
330
|
+
const filter = await bind(as, args.where);
|
|
247
331
|
const collection = await this.#collection(as.table);
|
|
248
332
|
const projection = get_projection(as.pk, args.fields);
|
|
249
333
|
const sort = get_sort(args.sort);
|
|
250
334
|
const limit = get_limit(args.limit);
|
|
251
335
|
const options = { useBigInt64: true };
|
|
252
|
-
if (projection)
|
|
336
|
+
if (is.defined(projection))
|
|
253
337
|
options.projection = projection;
|
|
254
|
-
if (sort)
|
|
338
|
+
if (is.defined(sort))
|
|
255
339
|
options.sort = sort;
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
.
|
|
259
|
-
|
|
340
|
+
const cursor = collection.find(filter, options).limit(limit);
|
|
341
|
+
if (is.defined(args.offset))
|
|
342
|
+
cursor.skip(args.offset);
|
|
343
|
+
const docs = await cursor.toArray();
|
|
260
344
|
return docs.map(doc => this.#unbind(as, doc));
|
|
261
345
|
}
|
|
262
346
|
async #read_phased(as, args) {
|
|
263
|
-
const fields =
|
|
347
|
+
const fields = base.expand(as, args.fields, args.with);
|
|
264
348
|
const rows = await this.#read(as, { ...args, fields });
|
|
265
|
-
const out = rows.map(row =>
|
|
349
|
+
const out = rows.map(row => base.project(row, args.fields));
|
|
266
350
|
for (const [name, relation] of Object.entries(args.with)) {
|
|
267
351
|
await this.#attach_relation(as, { rows, out, name, relation });
|
|
268
352
|
}
|
|
@@ -298,22 +382,22 @@ export default class MongoDB {
|
|
|
298
382
|
}
|
|
299
383
|
const rows = grouped.get(join_value) ?? [];
|
|
300
384
|
args.out[i][args.name] = is_many
|
|
301
|
-
? rows.map(r =>
|
|
302
|
-
: rows[0] ?
|
|
385
|
+
? rows.map(r => base.project(r, relation.fields))
|
|
386
|
+
: rows[0] ? base.project(rows[0], relation.fields) : null;
|
|
303
387
|
}
|
|
304
388
|
}
|
|
305
389
|
async #load_related(args) {
|
|
306
390
|
// build filter with $in for join values
|
|
307
|
-
const filter = await
|
|
391
|
+
const filter = await bind(args.as, args.where);
|
|
308
392
|
const by_field = this.#to_mongo_pk(args.by, args.as.pk);
|
|
309
393
|
// convert join values to ObjectId if needed
|
|
310
|
-
const
|
|
311
|
-
const in_values =
|
|
312
|
-
? args.join_values.map(v =>
|
|
394
|
+
const fk_type = args.as.types[args.by];
|
|
395
|
+
const in_values = base.is_uuid_type(fk_type)
|
|
396
|
+
? await Promise.all(args.join_values.map(v => bind_value(fk_type, v)))
|
|
313
397
|
: args.join_values;
|
|
314
398
|
filter[by_field] = { $in: in_values };
|
|
315
399
|
const collection = await this.#collection(args.as.table);
|
|
316
|
-
const fields_with_by =
|
|
400
|
+
const fields_with_by = base.fields(args.fields, args.by);
|
|
317
401
|
const projection = get_projection(args.as.pk, fields_with_by);
|
|
318
402
|
const sort = get_sort(args.sort);
|
|
319
403
|
const options = { useBigInt64: true };
|
|
@@ -344,7 +428,7 @@ export default class MongoDB {
|
|
|
344
428
|
async update(as, args) {
|
|
345
429
|
assert.nonempty(args.set);
|
|
346
430
|
assert.dict(args.where);
|
|
347
|
-
const filter = await
|
|
431
|
+
const filter = await bind(as, args.where);
|
|
348
432
|
const collection = await this.#collection(as.table);
|
|
349
433
|
const $set = {};
|
|
350
434
|
const $unset = {};
|
|
@@ -367,7 +451,7 @@ export default class MongoDB {
|
|
|
367
451
|
}
|
|
368
452
|
async delete(as, args) {
|
|
369
453
|
assert.nonempty(args.where);
|
|
370
|
-
const filter = await
|
|
454
|
+
const filter = await bind(as, args.where);
|
|
371
455
|
const collection = await this.#collection(as.table);
|
|
372
456
|
const result = await collection.deleteMany(filter);
|
|
373
457
|
return result.deletedCount;
|
package/lib/private/typemap.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Binary, Decimal128 } from "mongodb";
|
|
1
|
+
import { Binary, Decimal128, UUID } from "mongodb";
|
|
2
2
|
function identity(column) {
|
|
3
3
|
return {
|
|
4
4
|
bind: value => value,
|
|
@@ -45,6 +45,7 @@ const typemap = {
|
|
|
45
45
|
i32: identity("INT"),
|
|
46
46
|
i64: identity("LONG"),
|
|
47
47
|
i8: identity("INT"),
|
|
48
|
+
json: identity("JSON"),
|
|
48
49
|
string: identity("STRING"),
|
|
49
50
|
time: identity("TIME"),
|
|
50
51
|
u128: {
|
|
@@ -69,6 +70,21 @@ const typemap = {
|
|
|
69
70
|
return new URL(value);
|
|
70
71
|
},
|
|
71
72
|
},
|
|
73
|
+
uuid: {
|
|
74
|
+
bind: (value) => new UUID(value),
|
|
75
|
+
column: "UUID",
|
|
76
|
+
unbind: (value) => value.toString(),
|
|
77
|
+
},
|
|
78
|
+
uuid_v4: {
|
|
79
|
+
bind: (value) => new UUID(value),
|
|
80
|
+
column: "UUID",
|
|
81
|
+
unbind: (value) => value.toString(),
|
|
82
|
+
},
|
|
83
|
+
uuid_v7: {
|
|
84
|
+
bind: (value) => new UUID(value),
|
|
85
|
+
column: "UUID",
|
|
86
|
+
unbind: (value) => value.toString(),
|
|
87
|
+
},
|
|
72
88
|
};
|
|
73
89
|
export default typemap;
|
|
74
90
|
//# sourceMappingURL=typemap.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@primate/mongodb",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "MongoDB databases for Primate",
|
|
5
5
|
"homepage": "https://primate.run/docs/database/mongodb",
|
|
6
6
|
"bugs": "https://github.com/primate-run/primate/issues",
|
|
@@ -17,28 +17,28 @@
|
|
|
17
17
|
"!/**/*.spec.*"
|
|
18
18
|
],
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@rcompat/assert": "^0.
|
|
21
|
-
"@rcompat/dict": "^0.
|
|
22
|
-
"@rcompat/is": "^0.
|
|
23
|
-
"mongodb": "^
|
|
24
|
-
"@primate/core": "^0.
|
|
25
|
-
"pema": "0.
|
|
20
|
+
"@rcompat/assert": "^0.13.0",
|
|
21
|
+
"@rcompat/dict": "^0.10.0",
|
|
22
|
+
"@rcompat/is": "^0.11.0",
|
|
23
|
+
"mongodb": "^7.2.0",
|
|
24
|
+
"@primate/core": "^0.7.0",
|
|
25
|
+
"pema": "0.7.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@rcompat/type": "^0.
|
|
28
|
+
"@rcompat/type": "^0.17.0"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
|
-
"primate": "^0.
|
|
31
|
+
"primate": "^0.38.0"
|
|
32
32
|
},
|
|
33
33
|
"imports": {
|
|
34
34
|
"#*": {
|
|
35
|
-
"
|
|
35
|
+
"@primate/source": "./src/private/*.ts",
|
|
36
36
|
"default": "./lib/private/*.js"
|
|
37
37
|
}
|
|
38
38
|
},
|
|
39
39
|
"exports": {
|
|
40
40
|
".": {
|
|
41
|
-
"
|
|
41
|
+
"@primate/source": "./src/public/index.ts",
|
|
42
42
|
"default": "./lib/public/index.js"
|
|
43
43
|
}
|
|
44
44
|
},
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"build": "npm run clean && tsc",
|
|
47
47
|
"clean": "rm -rf ./lib",
|
|
48
48
|
"lint": "eslint .",
|
|
49
|
-
"test": "npm run
|
|
50
|
-
"
|
|
49
|
+
"test": "npm run db && npx proby",
|
|
50
|
+
"db": "scripts/db.sh"
|
|
51
51
|
}
|
|
52
52
|
}
|