@mikro-orm/core 7.1.10-dev.8 → 7.1.10
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/drivers/DatabaseDriver.d.ts +7 -0
- package/drivers/DatabaseDriver.js +72 -9
- package/package.json +1 -1
- package/platforms/Platform.d.ts +2 -0
- package/platforms/Platform.js +4 -0
- package/utils/Cursor.js +1 -6
- package/utils/Utils.js +1 -1
|
@@ -64,6 +64,13 @@ export declare abstract class DatabaseDriver<C extends Connection> implements ID
|
|
|
64
64
|
orderBy: OrderDefinition<T>[];
|
|
65
65
|
where: FilterQuery<T>;
|
|
66
66
|
};
|
|
67
|
+
/**
|
|
68
|
+
* Restores the JS value of a single cursor offset: ISO strings become `Date` instances based on the
|
|
69
|
+
* property type (never based on the string shape alone), and custom types are restored via
|
|
70
|
+
* `convertToJSValue`. Values compared against a JSON document keep their serialized form instead,
|
|
71
|
+
* unless the platform preserves native date types inside JSON documents (mongo).
|
|
72
|
+
*/
|
|
73
|
+
private mapCursorOffset;
|
|
67
74
|
protected createCursorCondition<T extends object>(definition: (readonly [keyof T & string, QueryOrder])[], offsets: Dictionary[], inverse: boolean, meta: EntityMetadata<T>): FilterQuery<T>;
|
|
68
75
|
/** @internal */
|
|
69
76
|
mapDataToFieldNames(data: Dictionary, stringifyJsonArrays: boolean, properties?: Record<string, EntityProperty>, convertCustomTypes?: boolean, object?: boolean): Dictionary;
|
|
@@ -7,8 +7,11 @@ import { EntityManager } from '../EntityManager.js';
|
|
|
7
7
|
import { CursorError, ValidationError } from '../errors.js';
|
|
8
8
|
import { DriverException } from '../exceptions.js';
|
|
9
9
|
import { helper } from '../entity/wrap.js';
|
|
10
|
+
import { Reference } from '../entity/Reference.js';
|
|
10
11
|
import { PolymorphicRef } from '../entity/PolymorphicRef.js';
|
|
11
12
|
import { JsonType } from '../types/JsonType.js';
|
|
13
|
+
import { DateTimeType } from '../types/DateTimeType.js';
|
|
14
|
+
import { QueryHelper } from '../utils/QueryHelper.js';
|
|
12
15
|
import { MikroORM } from '../MikroORM.js';
|
|
13
16
|
/** Abstract base class for all database drivers, implementing common driver logic. */
|
|
14
17
|
export class DatabaseDriver {
|
|
@@ -141,13 +144,24 @@ export class DatabaseDriver {
|
|
|
141
144
|
return !!val && typeof val === 'object' && key in val;
|
|
142
145
|
};
|
|
143
146
|
const createCursor = (val, key, inverse = false) => {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
+
const def = Reference.unwrapReference((isCursor(val, key) ? val[key] : val));
|
|
148
|
+
let offsets;
|
|
149
|
+
// entity (and reference) instances are supported as cursors too, their properties are read the same way
|
|
150
|
+
if (Utils.isPlainObject(def) || Utils.isEntity(def)) {
|
|
151
|
+
// POJO values are already JS values, extract them ordered per the definition,
|
|
152
|
+
// without the JSON round trip `Cursor.for` + `Cursor.decode` would impose
|
|
153
|
+
offsets = definition.map(([key]) => {
|
|
154
|
+
if (def[key] === undefined) {
|
|
155
|
+
throw CursorError.missingValue(meta.className, key);
|
|
156
|
+
}
|
|
157
|
+
return def[key];
|
|
158
|
+
});
|
|
147
159
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
160
|
+
else {
|
|
161
|
+
/* v8 ignore next */
|
|
162
|
+
offsets = def ? Cursor.decode(def) : [];
|
|
163
|
+
}
|
|
164
|
+
if (definition.length > 0 && definition.length === offsets.length) {
|
|
151
165
|
return this.createCursorCondition(definition, offsets, inverse, meta);
|
|
152
166
|
}
|
|
153
167
|
/* v8 ignore next */
|
|
@@ -174,19 +188,67 @@ export class DatabaseDriver {
|
|
|
174
188
|
const dir = Utils.xor(desc, isLast) ? 'desc' : 'asc';
|
|
175
189
|
return { [prop]: dir };
|
|
176
190
|
};
|
|
191
|
+
// the cursor condition is created at the driver level, after the EM already converted custom types
|
|
192
|
+
// in the user `where`, so we need to run the same conversion over it explicitly
|
|
193
|
+
const where = QueryHelper.processWhere({
|
|
194
|
+
where: ($and.length > 1 ? { $and } : { ...$and[0] }),
|
|
195
|
+
entityName: meta.class,
|
|
196
|
+
metadata: this.metadata,
|
|
197
|
+
platform: this.platform,
|
|
198
|
+
convertCustomTypes: options.convertCustomTypes,
|
|
199
|
+
});
|
|
177
200
|
return {
|
|
178
201
|
orderBy: definition.map(([prop, direction]) => createOrderBy(prop, direction)),
|
|
179
|
-
where
|
|
202
|
+
where,
|
|
180
203
|
};
|
|
181
204
|
}
|
|
205
|
+
/**
|
|
206
|
+
* Restores the JS value of a single cursor offset: ISO strings become `Date` instances based on the
|
|
207
|
+
* property type (never based on the string shape alone), and custom types are restored via
|
|
208
|
+
* `convertToJSValue`. Values compared against a JSON document keep their serialized form instead,
|
|
209
|
+
* unless the platform preserves native date types inside JSON documents (mongo).
|
|
210
|
+
*/
|
|
211
|
+
mapCursorOffset(prop, value, insideJson) {
|
|
212
|
+
if (Utils.isScalarReference(value)) {
|
|
213
|
+
value = value.unwrap();
|
|
214
|
+
}
|
|
215
|
+
// scalar direction on a relation orders by its primary key
|
|
216
|
+
if (Utils.isEntity(value, true)) {
|
|
217
|
+
value = helper(value).getPrimaryKey();
|
|
218
|
+
}
|
|
219
|
+
if (value == null) {
|
|
220
|
+
return value;
|
|
221
|
+
}
|
|
222
|
+
if (insideJson && !this.platform.preservesDatesInsideJson()) {
|
|
223
|
+
// compared against the JSON document, which holds the serialized form
|
|
224
|
+
if (value instanceof Date) {
|
|
225
|
+
return value.toISOString();
|
|
226
|
+
}
|
|
227
|
+
// restore the JS value from the serialized form, `processWhere` then converts it to
|
|
228
|
+
// the database form, which is what the JSON document holds for custom typed props
|
|
229
|
+
return prop?.customType ? prop.customType.convertToJSValue(value, this.platform) : value;
|
|
230
|
+
}
|
|
231
|
+
if (typeof value === 'string' &&
|
|
232
|
+
(prop?.runtimeType === 'Date' ||
|
|
233
|
+
(prop?.customType && this.platform.getMappedType(prop.columnTypes?.[0] ?? '') instanceof DateTimeType))) {
|
|
234
|
+
value = new Date(value);
|
|
235
|
+
}
|
|
236
|
+
return prop?.customType ? prop.customType.convertToJSValue(value, this.platform) : value;
|
|
237
|
+
}
|
|
182
238
|
createCursorCondition(definition, offsets, inverse, meta) {
|
|
183
|
-
const createCondition = (prop, direction, offset, eq = false, path = prop) => {
|
|
239
|
+
const createCondition = (prop, direction, offset, eq = false, path = prop, properties = meta.properties, insideJson = false) => {
|
|
240
|
+
const propMeta = properties[prop];
|
|
184
241
|
if (Utils.isPlainObject(direction)) {
|
|
185
242
|
if (offset === undefined) {
|
|
186
243
|
throw CursorError.missingValue(meta.className, path);
|
|
187
244
|
}
|
|
245
|
+
// POJO cursors can carry entity, reference or embeddable class instances, read their properties directly
|
|
246
|
+
offset = Reference.unwrapReference(offset);
|
|
247
|
+
const childProps = propMeta?.kind === ReferenceKind.EMBEDDED ? propMeta.embeddedProps : propMeta?.targetMeta?.properties;
|
|
248
|
+
insideJson ||=
|
|
249
|
+
(propMeta?.kind === ReferenceKind.EMBEDDED && !!propMeta.object) || propMeta?.customType instanceof JsonType;
|
|
188
250
|
const value = Utils.keys(direction).reduce((o, key) => {
|
|
189
|
-
Object.assign(o, createCondition(key, direction[key], offset?.[key], eq, `${path}.${key}
|
|
251
|
+
Object.assign(o, createCondition(key, direction[key], offset?.[key], eq, `${path}.${key}`, childProps ?? {}, insideJson));
|
|
190
252
|
return o;
|
|
191
253
|
}, {});
|
|
192
254
|
return { [prop]: value };
|
|
@@ -209,6 +271,7 @@ export class DatabaseDriver {
|
|
|
209
271
|
if (offset === undefined) {
|
|
210
272
|
throw CursorError.missingValue(meta.className, path);
|
|
211
273
|
}
|
|
274
|
+
offset = this.mapCursorOffset(propMeta, offset, insideJson);
|
|
212
275
|
// Handle null offset (intentional null cursor value)
|
|
213
276
|
if (offset === null) {
|
|
214
277
|
if (eq) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/core",
|
|
3
|
-
"version": "7.1.10
|
|
3
|
+
"version": "7.1.10",
|
|
4
4
|
"description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"data-mapper",
|
package/platforms/Platform.d.ts
CHANGED
|
@@ -227,6 +227,8 @@ export declare abstract class Platform {
|
|
|
227
227
|
formatIndexHint(indexNames: string[]): string | undefined;
|
|
228
228
|
/** Whether the driver automatically parses JSON columns into JS objects. */
|
|
229
229
|
convertsJsonAutomatically(): boolean;
|
|
230
|
+
/** Whether date values inside JSON documents keep their native type (e.g. BSON dates), instead of being serialized to ISO strings. */
|
|
231
|
+
preservesDatesInsideJson(): boolean;
|
|
230
232
|
/** Converts a JS value to its JSON database representation (typically JSON.stringify). */
|
|
231
233
|
convertJsonToDatabaseValue(value: unknown, context?: TransformContext): unknown;
|
|
232
234
|
/** Converts a database JSON value to its JS representation. */
|
package/platforms/Platform.js
CHANGED
|
@@ -465,6 +465,10 @@ export class Platform {
|
|
|
465
465
|
convertsJsonAutomatically() {
|
|
466
466
|
return true;
|
|
467
467
|
}
|
|
468
|
+
/** Whether date values inside JSON documents keep their native type (e.g. BSON dates), instead of being serialized to ISO strings. */
|
|
469
|
+
preservesDatesInsideJson() {
|
|
470
|
+
return false;
|
|
471
|
+
}
|
|
468
472
|
/** Converts a JS value to its JSON database representation (typically JSON.stringify). */
|
|
469
473
|
convertJsonToDatabaseValue(value, context) {
|
|
470
474
|
return JSON.stringify(value);
|
package/utils/Cursor.js
CHANGED
|
@@ -150,12 +150,7 @@ export class Cursor {
|
|
|
150
150
|
return Buffer.from(JSON.stringify(value)).toString('base64url');
|
|
151
151
|
}
|
|
152
152
|
static decode(value) {
|
|
153
|
-
return JSON.parse(Buffer.from(value, 'base64url').toString('utf8'))
|
|
154
|
-
if (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}/.exec(value)) {
|
|
155
|
-
return new Date(value);
|
|
156
|
-
}
|
|
157
|
-
return value;
|
|
158
|
-
});
|
|
153
|
+
return JSON.parse(Buffer.from(value, 'base64url').toString('utf8'));
|
|
159
154
|
}
|
|
160
155
|
static getDefinition(meta, orderBy) {
|
|
161
156
|
return Utils.asArray(orderBy).flatMap(order => {
|
package/utils/Utils.js
CHANGED
|
@@ -153,7 +153,7 @@ export function parseJsonSafe(value) {
|
|
|
153
153
|
/** Collection of general-purpose utility methods used throughout the ORM. */
|
|
154
154
|
export class Utils {
|
|
155
155
|
static PK_SEPARATOR = '~~~';
|
|
156
|
-
static #ORM_VERSION = '7.1.10
|
|
156
|
+
static #ORM_VERSION = '7.1.10';
|
|
157
157
|
/**
|
|
158
158
|
* Checks if the argument is instance of `Object`. Returns false for arrays.
|
|
159
159
|
*/
|