@rebasepro/server-mongo 0.13.0 → 0.13.1-canary.g3660bd5
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/dist/db/MongoConditionBuilder.d.ts +25 -1
- package/dist/db/MongoConditionBuilder.d.ts.map +1 -1
- package/dist/db/MongoDataService.d.ts +8 -1
- package/dist/db/MongoDataService.d.ts.map +1 -1
- package/dist/index.es.js +68 -30
- package/dist/index.es.js.map +1 -1
- package/dist/services/MongoDriver.d.ts +2 -2
- package/dist/services/MongoDriver.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/db/MongoConditionBuilder.ts +78 -35
- package/src/db/MongoDataService.ts +24 -5
- package/src/services/MongoDriver.ts +21 -18
|
@@ -49,7 +49,7 @@ export declare class MongoDriver implements DataDriver {
|
|
|
49
49
|
/**
|
|
50
50
|
* Fetch a collection of rows
|
|
51
51
|
*/
|
|
52
|
-
fetchCollection<M extends Record<string, any>>(
|
|
52
|
+
fetchCollection<M extends Record<string, any>>(props: FetchCollectionProps<M>): Promise<Record<string, unknown>[]>;
|
|
53
53
|
/**
|
|
54
54
|
* Listen to collection changes
|
|
55
55
|
*/
|
|
@@ -81,7 +81,7 @@ export declare class MongoDriver implements DataDriver {
|
|
|
81
81
|
/**
|
|
82
82
|
* Count rows in a collection
|
|
83
83
|
*/
|
|
84
|
-
count<M extends Record<string, any>>({ path, collection, filter }: FetchCollectionProps<M>): Promise<number>;
|
|
84
|
+
count<M extends Record<string, any>>({ path, collection, filter, logical, searchString }: FetchCollectionProps<M>): Promise<number>;
|
|
85
85
|
/**
|
|
86
86
|
* Generate a unique subscription ID
|
|
87
87
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MongoDriver.d.ts","sourceRoot":"","sources":["../../src/services/MongoDriver.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAC7B,OAAO,EACH,UAAU,EACV,WAAW,EAEX,gBAAgB,EAChB,oBAAoB,EACpB,aAAa,EACb,qBAAqB,EACrB,cAAc,EACd,SAAS,EAET,2BAA2B,EAC3B,IAAI,EACJ,YAAY,EAEZ,aAAa,EAEhB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAQ5D;;;;;GAKG;AACH,qBAAa,WAAY,YAAW,UAAU;IAYtC,OAAO,CAAC,EAAE;aAGM,QAAQ,CAAC,EAAE,2BAA2B;IAd1D,GAAG,SAAa;IAChB,WAAW,UAAQ;IAEnB,OAAO,CAAC,WAAW,CAAmB;IACtC,OAAO,CAAC,eAAe,CAAuB;IACvC,cAAc,EAAE,mBAAmB,CAAC;IACpC,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,IAAI,EAAE,aAAa,CAAC;IACpB,MAAM,CAAC,EAAE,YAAY,CAAC;gBAGjB,EAAE,EAAE,EAAE,EACd,eAAe,CAAC,EAAE,oBAAoB,EACtC,cAAc,CAAC,EAAE,mBAAmB,EACpB,QAAQ,CAAC,EAAE,2BAA2B,YAAA,EACtD,IAAI,CAAC,EAAE,IAAI;IAUf;;OAEG;IACH,WAAW,IAAI,IAAI;IAInB;;;OAGG;IACH,0BAA0B,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxD,UAAU,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,SAAS,EAC3C,IAAI,EAAE,MAAM;;;;;;;;;;;IA2BhB;;OAEG;IACG,eAAe,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"MongoDriver.d.ts","sourceRoot":"","sources":["../../src/services/MongoDriver.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebasepro/server-mongo",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.13.
|
|
4
|
+
"version": "0.13.1-canary.g3660bd5",
|
|
5
5
|
"description": "MongoDB backend for Rebase",
|
|
6
6
|
"funding": {
|
|
7
7
|
"url": "https://github.com/sponsors/rebaseco"
|
|
@@ -40,9 +40,9 @@
|
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"mongodb": "^7.5.0",
|
|
42
42
|
"ws": "^8.21.1",
|
|
43
|
-
"@rebasepro/
|
|
44
|
-
"@rebasepro/
|
|
45
|
-
"@rebasepro/
|
|
43
|
+
"@rebasepro/types": "0.13.1-canary.g3660bd5",
|
|
44
|
+
"@rebasepro/utils": "0.13.1-canary.g3660bd5",
|
|
45
|
+
"@rebasepro/common": "0.13.1-canary.g3660bd5"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/jest": "^30.0.0",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"vite": "^8.1.5"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
58
|
-
"@rebasepro/server": "0.13.
|
|
58
|
+
"@rebasepro/server": "0.13.1-canary.g3660bd5"
|
|
59
59
|
},
|
|
60
60
|
"peerDependenciesMeta": {
|
|
61
61
|
"@rebasepro/server": {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Translates Rebase filter conditions to MongoDB query operators.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { CollectionConfig, FilterValues, WhereFilterOp } from "@rebasepro/types";
|
|
7
|
+
import { CollectionConfig, FilterCondition, FilterValues, LogicalCondition, WhereFilterOp } from "@rebasepro/types";
|
|
8
8
|
import { Filter, Document } from "mongodb";
|
|
9
9
|
import { logger } from "@rebasepro/server";
|
|
10
10
|
|
|
@@ -67,48 +67,81 @@ export class MongoConditionBuilder {
|
|
|
67
67
|
if (!filterParam) continue;
|
|
68
68
|
|
|
69
69
|
const [op, value] = filterParam as [WhereFilterOp, any];
|
|
70
|
+
const condition = this.buildCondition(field, op, value);
|
|
71
|
+
if (condition) conditions.push(condition);
|
|
72
|
+
}
|
|
70
73
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
conditions.push({ [field]: { $eq: null } });
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
if (op === "is-not-null") {
|
|
77
|
-
conditions.push({ [field]: { $ne: null } });
|
|
78
|
-
continue;
|
|
79
|
-
}
|
|
74
|
+
return conditions;
|
|
75
|
+
}
|
|
80
76
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
77
|
+
/**
|
|
78
|
+
* One field, one operator, one value.
|
|
79
|
+
*
|
|
80
|
+
* Extracted so that `logical` groups translate through exactly this code.
|
|
81
|
+
* A group written as a second dialect is a group where `array-contains` or
|
|
82
|
+
* `ilike` quietly means something else than it does in `filter`, which is
|
|
83
|
+
* the kind of difference nobody finds until a query returns the wrong rows.
|
|
84
|
+
*/
|
|
85
|
+
private static buildCondition(
|
|
86
|
+
field: string,
|
|
87
|
+
op: WhereFilterOp,
|
|
88
|
+
value: any
|
|
89
|
+
): Filter<Document> | undefined {
|
|
90
|
+
// Null-testing operators ignore their value.
|
|
91
|
+
if (op === "is-null") return { [field]: { $eq: null } };
|
|
92
|
+
if (op === "is-not-null") return { [field]: { $ne: null } };
|
|
91
93
|
|
|
92
|
-
|
|
94
|
+
// Pattern matching → regular expressions.
|
|
95
|
+
if (op === "like" || op === "ilike" || op === "not-like" || op === "not-ilike") {
|
|
96
|
+
const caseInsensitive = op === "ilike" || op === "not-ilike";
|
|
97
|
+
const regex = likePatternToRegExp(value, caseInsensitive);
|
|
98
|
+
const negated = op === "not-like" || op === "not-ilike";
|
|
99
|
+
return { [field]: negated ? { $not: regex } : { $regex: regex } };
|
|
100
|
+
}
|
|
93
101
|
|
|
94
|
-
|
|
95
|
-
logger.warn(`Unsupported filter operator: ${op}`);
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
102
|
+
const mongoOp = REBASE_TO_MONGO_OP[op];
|
|
98
103
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
104
|
+
if (!mongoOp) {
|
|
105
|
+
logger.warn(`Unsupported filter operator: ${op}`);
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Handle array-contains specially
|
|
110
|
+
if (op === "array-contains") {
|
|
111
|
+
return { [field]: { $elemMatch: { $eq: value } } };
|
|
112
|
+
}
|
|
113
|
+
return { [field]: { [mongoOp]: value } };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Translate an `or(...)` / `and(...)` group, nesting included.
|
|
118
|
+
*
|
|
119
|
+
* Returns `undefined` for a group with nothing in it. `$or: []` is an error
|
|
120
|
+
* in Mongo and `$and: []` matches every document, so neither is a
|
|
121
|
+
* defensible reading of "no conditions".
|
|
122
|
+
*/
|
|
123
|
+
static buildLogicalConditions(logical: LogicalCondition | undefined): Filter<Document> | undefined {
|
|
124
|
+
if (!logical || !Array.isArray(logical.conditions)) return undefined;
|
|
125
|
+
|
|
126
|
+
const parts: Filter<Document>[] = [];
|
|
127
|
+
for (const entry of logical.conditions) {
|
|
128
|
+
if (!entry) continue;
|
|
129
|
+
if ("type" in entry && "conditions" in entry) {
|
|
130
|
+
const nested = this.buildLogicalConditions(entry as LogicalCondition);
|
|
131
|
+
if (nested) parts.push(nested);
|
|
132
|
+
continue;
|
|
108
133
|
}
|
|
134
|
+
const { column, operator, value } = entry as FilterCondition;
|
|
135
|
+
const condition = this.buildCondition(column, operator, value);
|
|
136
|
+
if (condition) parts.push(condition);
|
|
109
137
|
}
|
|
110
138
|
|
|
111
|
-
|
|
139
|
+
// Through the same combiners the rest of this class uses, so a
|
|
140
|
+
// one-condition group reads as the bare condition — identical to how
|
|
141
|
+
// `filter` would have expressed it — rather than as `{ $and: [x] }`.
|
|
142
|
+
return logical.type === "or"
|
|
143
|
+
? this.combineConditionsWithOr(parts)
|
|
144
|
+
: this.combineConditionsWithAnd(parts);
|
|
112
145
|
}
|
|
113
146
|
|
|
114
147
|
/**
|
|
@@ -191,6 +224,13 @@ export class MongoConditionBuilder {
|
|
|
191
224
|
*/
|
|
192
225
|
static buildQuery<M extends Record<string, any>>(options: {
|
|
193
226
|
filter?: FilterValues<Extract<keyof M, string>>;
|
|
227
|
+
/**
|
|
228
|
+
* An `or(...)`/`and(...)` group, AND-ed with `filter` and
|
|
229
|
+
* `searchString` — the three are independent, as `FindParams`
|
|
230
|
+
* documents. Absent here until now, so a group that reached this
|
|
231
|
+
* driver was dropped and the read ran unfiltered.
|
|
232
|
+
*/
|
|
233
|
+
logical?: LogicalCondition;
|
|
194
234
|
searchString?: string;
|
|
195
235
|
properties?: CollectionConfig["properties"];
|
|
196
236
|
}): Filter<Document> {
|
|
@@ -202,6 +242,9 @@ export class MongoConditionBuilder {
|
|
|
202
242
|
conditions.push(...filterConditions);
|
|
203
243
|
}
|
|
204
244
|
|
|
245
|
+
const logicalCondition = this.buildLogicalConditions(options.logical);
|
|
246
|
+
if (logicalCondition) conditions.push(logicalCondition);
|
|
247
|
+
|
|
205
248
|
// Add search conditions
|
|
206
249
|
if (options.searchString && options.properties) {
|
|
207
250
|
const searchConditions = this.buildSearchConditions(
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { Db, ObjectId, Collection, Document, FindOptions, Filter } from "mongodb";
|
|
9
|
-
import { FilterValues, DataRepository, CollectionConfig, EntityReference } from "@rebasepro/types";
|
|
9
|
+
import { FilterValues, DataRepository, CollectionConfig, EntityReference, LogicalCondition } from "@rebasepro/types";
|
|
10
10
|
import { MongoConditionBuilder } from "./MongoConditionBuilder";
|
|
11
11
|
import { logger } from "@rebasepro/server";
|
|
12
12
|
|
|
@@ -193,9 +193,12 @@ export class MongoDataService implements DataRepository {
|
|
|
193
193
|
collectionPath: string,
|
|
194
194
|
options: {
|
|
195
195
|
filter?: FilterValues<Extract<keyof M, string>>;
|
|
196
|
+
/** An `or(...)`/`and(...)` group, AND-ed with `filter`. */
|
|
197
|
+
logical?: LogicalCondition;
|
|
196
198
|
orderBy?: string;
|
|
197
199
|
order?: "desc" | "asc";
|
|
198
200
|
limit?: number;
|
|
201
|
+
offset?: number;
|
|
199
202
|
startAfter?: any;
|
|
200
203
|
searchString?: string;
|
|
201
204
|
databaseId?: string;
|
|
@@ -208,6 +211,7 @@ export class MongoDataService implements DataRepository {
|
|
|
208
211
|
// Build query
|
|
209
212
|
const query = options.rawQuery ?? MongoConditionBuilder.buildQuery<M>({
|
|
210
213
|
filter: options.filter,
|
|
214
|
+
logical: options.logical,
|
|
211
215
|
searchString: options.searchString,
|
|
212
216
|
properties: options.collection?.properties ?? {}
|
|
213
217
|
});
|
|
@@ -226,9 +230,14 @@ export class MongoDataService implements DataRepository {
|
|
|
226
230
|
findOptions.limit = options.limit;
|
|
227
231
|
}
|
|
228
232
|
|
|
229
|
-
// Apply pagination
|
|
233
|
+
// Apply pagination. `offset` is the parameter the REST layer and the
|
|
234
|
+
// SDK both speak; it was absent here, so every `?offset=` reaching this
|
|
235
|
+
// driver was discarded and page three served page one. `startAfter`
|
|
236
|
+
// stays as the older spelling and wins when both are given.
|
|
230
237
|
if (options.startAfter !== undefined) {
|
|
231
238
|
findOptions.skip = Number(options.startAfter);
|
|
239
|
+
} else if (options.offset) {
|
|
240
|
+
findOptions.skip = options.offset;
|
|
232
241
|
}
|
|
233
242
|
|
|
234
243
|
const docs = await collection.find(query, findOptions).toArray();
|
|
@@ -265,15 +274,25 @@ export class MongoDataService implements DataRepository {
|
|
|
265
274
|
collectionPath: string,
|
|
266
275
|
options: {
|
|
267
276
|
filter?: FilterValues<Extract<keyof M, string>>;
|
|
277
|
+
/** An `or(...)`/`and(...)` group, AND-ed with `filter`. */
|
|
278
|
+
logical?: LogicalCondition;
|
|
279
|
+
searchString?: string;
|
|
280
|
+
collection?: CollectionConfig;
|
|
268
281
|
databaseId?: string;
|
|
269
282
|
rawQuery?: Filter<Document>;
|
|
270
283
|
} = {}
|
|
271
284
|
): Promise<number> {
|
|
272
285
|
const collection = this.getCollection(collectionPath);
|
|
273
286
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
287
|
+
// Every narrowing the listing applies has to apply here too, or the
|
|
288
|
+
// count describes a different query than the one it is reported
|
|
289
|
+
// against. `logical` and `searchString` were both missing.
|
|
290
|
+
const query = options.rawQuery ?? MongoConditionBuilder.buildQuery<M>({
|
|
291
|
+
filter: options.filter,
|
|
292
|
+
logical: options.logical,
|
|
293
|
+
searchString: options.searchString,
|
|
294
|
+
properties: options.collection?.properties ?? {}
|
|
295
|
+
});
|
|
277
296
|
|
|
278
297
|
return collection.countDocuments(query);
|
|
279
298
|
}
|
|
@@ -109,23 +109,17 @@ propertyCallbacks: undefined };
|
|
|
109
109
|
/**
|
|
110
110
|
* Fetch a collection of rows
|
|
111
111
|
*/
|
|
112
|
-
async fetchCollection<M extends Record<string, any>>(
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}: FetchCollectionProps<M>): Promise<Record<string, unknown>[]> {
|
|
112
|
+
async fetchCollection<M extends Record<string, any>>(
|
|
113
|
+
props: FetchCollectionProps<M>
|
|
114
|
+
): Promise<Record<string, unknown>[]> {
|
|
115
|
+
// Forwarded whole rather than re-listed. The hand-written list here
|
|
116
|
+
// named eight of the eleven fields `FetchCollectionProps` declares, so
|
|
117
|
+
// `logical` and `offset` were accepted by every type-checked boundary
|
|
118
|
+
// above and then dropped — an `or(...)` query ran unfiltered and
|
|
119
|
+
// `?offset=` served page one.
|
|
120
|
+
const { path, collection, ...query } = props;
|
|
122
121
|
const rows = await this.dataService.fetchCollection<M>(path, {
|
|
123
|
-
|
|
124
|
-
limit,
|
|
125
|
-
startAfter,
|
|
126
|
-
orderBy,
|
|
127
|
-
order,
|
|
128
|
-
searchString,
|
|
122
|
+
...query,
|
|
129
123
|
collection: collection as CollectionConfig
|
|
130
124
|
});
|
|
131
125
|
|
|
@@ -656,9 +650,18 @@ propertyCallbacks: undefined };
|
|
|
656
650
|
async count<M extends Record<string, any>>({
|
|
657
651
|
path,
|
|
658
652
|
collection,
|
|
659
|
-
filter
|
|
653
|
+
filter,
|
|
654
|
+
logical,
|
|
655
|
+
searchString
|
|
660
656
|
}: FetchCollectionProps<M>): Promise<number> {
|
|
661
|
-
|
|
657
|
+
// The same narrowing the listing gets, or the total describes a
|
|
658
|
+
// different query than the rows it is reported beside.
|
|
659
|
+
return this.dataService.count<M>(path, {
|
|
660
|
+
filter,
|
|
661
|
+
logical,
|
|
662
|
+
searchString,
|
|
663
|
+
collection: collection as CollectionConfig
|
|
664
|
+
});
|
|
662
665
|
}
|
|
663
666
|
|
|
664
667
|
/**
|