@powersync/service-module-mongodb 0.0.0-dev-20250825132649 → 0.0.0-dev-20250827072023
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/CHANGELOG.md +9 -7
- package/dist/replication/ChangeStream.d.ts +2 -2
- package/dist/replication/MongoRelation.d.ts +3 -3
- package/dist/replication/MongoRelation.js +14 -13
- package/dist/replication/MongoRelation.js.map +1 -1
- package/package.json +10 -10
- package/src/replication/ChangeStream.ts +2 -2
- package/src/replication/MongoRelation.ts +26 -16
- package/test/src/mongo_test.test.ts +58 -16
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @powersync/service-module-mongodb
|
|
2
2
|
|
|
3
|
-
## 0.0.0-dev-
|
|
3
|
+
## 0.0.0-dev-20250827072023
|
|
4
4
|
|
|
5
5
|
### Patch Changes
|
|
6
6
|
|
|
@@ -11,15 +11,17 @@
|
|
|
11
11
|
- Updated dependencies [29a368e]
|
|
12
12
|
- Updated dependencies [f1d187b]
|
|
13
13
|
- Updated dependencies [0fad466]
|
|
14
|
+
- Updated dependencies [5284fb5]
|
|
15
|
+
- Updated dependencies [5284fb5]
|
|
14
16
|
- Updated dependencies [a700ec9]
|
|
15
17
|
- Updated dependencies [060b829]
|
|
16
18
|
- Updated dependencies [d49bebe]
|
|
17
|
-
- @powersync/service-sync-rules@0.0.0-dev-
|
|
18
|
-
- @powersync/service-core@0.0.0-dev-
|
|
19
|
-
- @powersync/service-types@0.0.0-dev-
|
|
20
|
-
- @powersync/lib-services-framework@0.0.0-dev-
|
|
21
|
-
- @powersync/lib-service-mongodb@0.0.0-dev-
|
|
22
|
-
- @powersync/service-jsonbig@0.0.0-dev-
|
|
19
|
+
- @powersync/service-sync-rules@0.0.0-dev-20250827072023
|
|
20
|
+
- @powersync/service-core@0.0.0-dev-20250827072023
|
|
21
|
+
- @powersync/service-types@0.0.0-dev-20250827072023
|
|
22
|
+
- @powersync/lib-services-framework@0.0.0-dev-20250827072023
|
|
23
|
+
- @powersync/lib-service-mongodb@0.0.0-dev-20250827072023
|
|
24
|
+
- @powersync/service-jsonbig@0.0.0-dev-20250827072023
|
|
23
25
|
|
|
24
26
|
## 0.11.0
|
|
25
27
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { mongo } from '@powersync/lib-service-mongodb';
|
|
2
2
|
import { DatabaseConnectionError, Logger } from '@powersync/lib-services-framework';
|
|
3
3
|
import { MetricsEngine, SourceEntityDescriptor, SourceTable, storage } from '@powersync/service-core';
|
|
4
|
-
import { DatabaseInputRow,
|
|
4
|
+
import { DatabaseInputRow, SqliteInputRow, SqlSyncRules, TablePattern } from '@powersync/service-sync-rules';
|
|
5
5
|
import { MongoManager } from './MongoManager.js';
|
|
6
6
|
export interface ChangeStreamOptions {
|
|
7
7
|
connections: MongoManager;
|
|
@@ -88,7 +88,7 @@ export declare class ChangeStream {
|
|
|
88
88
|
initialReplication(snapshotLsn: string | null): Promise<void>;
|
|
89
89
|
private setupCheckpointsCollection;
|
|
90
90
|
private getSourceNamespaceFilters;
|
|
91
|
-
static getQueryData(results: Iterable<DatabaseInputRow>): Generator<
|
|
91
|
+
static getQueryData(results: Iterable<DatabaseInputRow>): Generator<SqliteInputRow>;
|
|
92
92
|
private snapshotTable;
|
|
93
93
|
private getRelation;
|
|
94
94
|
private getCollectionInfo;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { mongo } from '@powersync/lib-service-mongodb';
|
|
2
2
|
import { storage } from '@powersync/service-core';
|
|
3
|
-
import {
|
|
3
|
+
import { SqliteInputRow, SqliteInputValue } from '@powersync/service-sync-rules';
|
|
4
4
|
export declare function getMongoRelation(source: mongo.ChangeStreamNameSpace): storage.SourceEntityDescriptor;
|
|
5
5
|
/**
|
|
6
6
|
* For in-memory cache only.
|
|
7
7
|
*/
|
|
8
8
|
export declare function getCacheIdentifier(source: storage.SourceEntityDescriptor | storage.SourceTable): string;
|
|
9
|
-
export declare function constructAfterRecord(document: mongo.Document):
|
|
10
|
-
export declare function toMongoSyncRulesValue(data: any):
|
|
9
|
+
export declare function constructAfterRecord(document: mongo.Document): SqliteInputRow;
|
|
10
|
+
export declare function toMongoSyncRulesValue(data: any): SqliteInputValue;
|
|
11
11
|
/**
|
|
12
12
|
* Id for checkpoints not associated with any specific replication stream.
|
|
13
13
|
*
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { mongo } from '@powersync/lib-service-mongodb';
|
|
2
2
|
import { storage } from '@powersync/service-core';
|
|
3
|
-
import {
|
|
3
|
+
import { JsonContainer } from '@powersync/service-jsonbig';
|
|
4
|
+
import { CustomArray, CustomObject, CustomSqliteValue, DateTimeValue } from '@powersync/service-sync-rules';
|
|
4
5
|
import { ErrorCode, ServiceError } from '@powersync/lib-services-framework';
|
|
5
6
|
import { MongoLSN } from '../common/MongoLSN.js';
|
|
6
7
|
import { CHECKPOINTS_COLLECTION } from './replication-utils.js';
|
|
@@ -63,7 +64,8 @@ export function toMongoSyncRulesValue(data) {
|
|
|
63
64
|
return data.toHexString();
|
|
64
65
|
}
|
|
65
66
|
else if (data instanceof Date) {
|
|
66
|
-
|
|
67
|
+
const isoString = data.toISOString();
|
|
68
|
+
return new DateTimeValue(isoString);
|
|
67
69
|
}
|
|
68
70
|
else if (data instanceof mongo.Binary) {
|
|
69
71
|
return new Uint8Array(data.buffer);
|
|
@@ -81,8 +83,7 @@ export function toMongoSyncRulesValue(data) {
|
|
|
81
83
|
return JSON.stringify({ pattern: data.source, options: data.flags });
|
|
82
84
|
}
|
|
83
85
|
else if (Array.isArray(data)) {
|
|
84
|
-
|
|
85
|
-
return JSONBig.stringify(data.map((element) => filterJsonData(element)));
|
|
86
|
+
return new CustomArray(data, filterJsonData);
|
|
86
87
|
}
|
|
87
88
|
else if (data instanceof Uint8Array) {
|
|
88
89
|
return data;
|
|
@@ -91,18 +92,14 @@ export function toMongoSyncRulesValue(data) {
|
|
|
91
92
|
return data.toString();
|
|
92
93
|
}
|
|
93
94
|
else if (typeof data == 'object') {
|
|
94
|
-
|
|
95
|
-
for (let key of Object.keys(data)) {
|
|
96
|
-
record[key] = filterJsonData(data[key]);
|
|
97
|
-
}
|
|
98
|
-
return JSONBig.stringify(record);
|
|
95
|
+
return new CustomObject(data, filterJsonData);
|
|
99
96
|
}
|
|
100
97
|
else {
|
|
101
98
|
return null;
|
|
102
99
|
}
|
|
103
100
|
}
|
|
104
101
|
const DEPTH_LIMIT = 20;
|
|
105
|
-
function filterJsonData(data, depth = 0) {
|
|
102
|
+
function filterJsonData(data, context, depth = 0) {
|
|
106
103
|
const autoBigNum = true;
|
|
107
104
|
if (depth > DEPTH_LIMIT) {
|
|
108
105
|
// This is primarily to prevent infinite recursion
|
|
@@ -135,7 +132,8 @@ function filterJsonData(data, depth = 0) {
|
|
|
135
132
|
return data;
|
|
136
133
|
}
|
|
137
134
|
else if (data instanceof Date) {
|
|
138
|
-
|
|
135
|
+
const isoString = data.toISOString();
|
|
136
|
+
return new DateTimeValue(isoString).toSqliteValue(context);
|
|
139
137
|
}
|
|
140
138
|
else if (data instanceof mongo.ObjectId) {
|
|
141
139
|
return data.toHexString();
|
|
@@ -159,11 +157,14 @@ function filterJsonData(data, depth = 0) {
|
|
|
159
157
|
return { pattern: data.source, options: data.flags };
|
|
160
158
|
}
|
|
161
159
|
else if (Array.isArray(data)) {
|
|
162
|
-
return data.map((element) => filterJsonData(element, depth + 1));
|
|
160
|
+
return data.map((element) => filterJsonData(element, context, depth + 1));
|
|
163
161
|
}
|
|
164
162
|
else if (ArrayBuffer.isView(data)) {
|
|
165
163
|
return undefined;
|
|
166
164
|
}
|
|
165
|
+
else if (data instanceof CustomSqliteValue) {
|
|
166
|
+
return data.toSqliteValue(context);
|
|
167
|
+
}
|
|
167
168
|
else if (data instanceof JsonContainer) {
|
|
168
169
|
// Can be stringified directly when using our JSONBig implementation
|
|
169
170
|
return data;
|
|
@@ -171,7 +172,7 @@ function filterJsonData(data, depth = 0) {
|
|
|
171
172
|
else if (typeof data == 'object') {
|
|
172
173
|
let record = {};
|
|
173
174
|
for (let key of Object.keys(data)) {
|
|
174
|
-
record[key] = filterJsonData(data[key], depth + 1);
|
|
175
|
+
record[key] = filterJsonData(data[key], context, depth + 1);
|
|
175
176
|
}
|
|
176
177
|
return record;
|
|
177
178
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MongoRelation.js","sourceRoot":"","sources":["../../src/replication/MongoRelation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"MongoRelation.js","sourceRoot":"","sources":["../../src/replication/MongoRelation.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@powersync/service-module-mongodb",
|
|
3
3
|
"repository": "https://github.com/powersync-ja/powersync-service",
|
|
4
4
|
"types": "dist/index.d.ts",
|
|
5
|
-
"version": "0.0.0-dev-
|
|
5
|
+
"version": "0.0.0-dev-20250827072023",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"license": "FSL-1.1-ALv2",
|
|
8
8
|
"type": "module",
|
|
@@ -25,17 +25,17 @@
|
|
|
25
25
|
"bson": "^6.10.3",
|
|
26
26
|
"ts-codec": "^1.3.0",
|
|
27
27
|
"uuid": "^11.1.0",
|
|
28
|
-
"@powersync/lib-
|
|
29
|
-
"@powersync/lib-
|
|
30
|
-
"@powersync/service-
|
|
31
|
-
"@powersync/service-
|
|
32
|
-
"@powersync/service-
|
|
33
|
-
"@powersync/service-types": "0.0.0-dev-
|
|
28
|
+
"@powersync/lib-service-mongodb": "0.0.0-dev-20250827072023",
|
|
29
|
+
"@powersync/lib-services-framework": "0.0.0-dev-20250827072023",
|
|
30
|
+
"@powersync/service-core": "0.0.0-dev-20250827072023",
|
|
31
|
+
"@powersync/service-jsonbig": "0.0.0-dev-20250827072023",
|
|
32
|
+
"@powersync/service-sync-rules": "0.0.0-dev-20250827072023",
|
|
33
|
+
"@powersync/service-types": "0.0.0-dev-20250827072023"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@powersync/service-core-tests": "0.0.0-dev-
|
|
37
|
-
"@powersync/service-module-mongodb-storage": "0.0.0-dev-
|
|
38
|
-
"@powersync/service-module-postgres-storage": "0.0.0-dev-
|
|
36
|
+
"@powersync/service-core-tests": "0.0.0-dev-20250827072023",
|
|
37
|
+
"@powersync/service-module-mongodb-storage": "0.0.0-dev-20250827072023",
|
|
38
|
+
"@powersync/service-module-postgres-storage": "0.0.0-dev-20250827072023"
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "tsc -b",
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
SourceTable,
|
|
18
18
|
storage
|
|
19
19
|
} from '@powersync/service-core';
|
|
20
|
-
import { DatabaseInputRow, SqliteRow, SqlSyncRules, TablePattern } from '@powersync/service-sync-rules';
|
|
20
|
+
import { DatabaseInputRow, SqliteInputRow, SqliteRow, SqlSyncRules, TablePattern } from '@powersync/service-sync-rules';
|
|
21
21
|
import { ReplicationMetric } from '@powersync/service-types';
|
|
22
22
|
import { MongoLSN } from '../common/MongoLSN.js';
|
|
23
23
|
import { PostImagesOption } from '../types/types.js';
|
|
@@ -439,7 +439,7 @@ export class ChangeStream {
|
|
|
439
439
|
return { $match: { ns: { $in: $inFilters } }, multipleDatabases };
|
|
440
440
|
}
|
|
441
441
|
|
|
442
|
-
static *getQueryData(results: Iterable<DatabaseInputRow>): Generator<
|
|
442
|
+
static *getQueryData(results: Iterable<DatabaseInputRow>): Generator<SqliteInputRow> {
|
|
443
443
|
for (let row of results) {
|
|
444
444
|
yield constructAfterRecord(row);
|
|
445
445
|
}
|
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
import { mongo } from '@powersync/lib-service-mongodb';
|
|
2
2
|
import { storage } from '@powersync/service-core';
|
|
3
3
|
import { JSONBig, JsonContainer } from '@powersync/service-jsonbig';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
CompatibilityContext,
|
|
6
|
+
CustomArray,
|
|
7
|
+
CustomObject,
|
|
8
|
+
CustomSqliteValue,
|
|
9
|
+
DatabaseInputValue,
|
|
10
|
+
SqliteInputRow,
|
|
11
|
+
SqliteInputValue,
|
|
12
|
+
SqliteRow,
|
|
13
|
+
SqliteValue,
|
|
14
|
+
DateTimeValue
|
|
15
|
+
} from '@powersync/service-sync-rules';
|
|
5
16
|
|
|
6
17
|
import { ErrorCode, ServiceError } from '@powersync/lib-services-framework';
|
|
7
18
|
import { MongoLSN } from '../common/MongoLSN.js';
|
|
@@ -27,15 +38,15 @@ export function getCacheIdentifier(source: storage.SourceEntityDescriptor | stor
|
|
|
27
38
|
return `${source.schema}.${source.name}`;
|
|
28
39
|
}
|
|
29
40
|
|
|
30
|
-
export function constructAfterRecord(document: mongo.Document):
|
|
31
|
-
let record:
|
|
41
|
+
export function constructAfterRecord(document: mongo.Document): SqliteInputRow {
|
|
42
|
+
let record: SqliteInputRow = {};
|
|
32
43
|
for (let key of Object.keys(document)) {
|
|
33
44
|
record[key] = toMongoSyncRulesValue(document[key]);
|
|
34
45
|
}
|
|
35
46
|
return record;
|
|
36
47
|
}
|
|
37
48
|
|
|
38
|
-
export function toMongoSyncRulesValue(data: any):
|
|
49
|
+
export function toMongoSyncRulesValue(data: any): SqliteInputValue {
|
|
39
50
|
const autoBigNum = true;
|
|
40
51
|
if (data === null) {
|
|
41
52
|
return null;
|
|
@@ -60,7 +71,8 @@ export function toMongoSyncRulesValue(data: any): SqliteValue {
|
|
|
60
71
|
} else if (data instanceof mongo.UUID) {
|
|
61
72
|
return data.toHexString();
|
|
62
73
|
} else if (data instanceof Date) {
|
|
63
|
-
|
|
74
|
+
const isoString = data.toISOString();
|
|
75
|
+
return new DateTimeValue(isoString);
|
|
64
76
|
} else if (data instanceof mongo.Binary) {
|
|
65
77
|
return new Uint8Array(data.buffer);
|
|
66
78
|
} else if (data instanceof mongo.Long) {
|
|
@@ -72,18 +84,13 @@ export function toMongoSyncRulesValue(data: any): SqliteValue {
|
|
|
72
84
|
} else if (data instanceof RegExp) {
|
|
73
85
|
return JSON.stringify({ pattern: data.source, options: data.flags });
|
|
74
86
|
} else if (Array.isArray(data)) {
|
|
75
|
-
|
|
76
|
-
return JSONBig.stringify(data.map((element) => filterJsonData(element)));
|
|
87
|
+
return new CustomArray(data, filterJsonData);
|
|
77
88
|
} else if (data instanceof Uint8Array) {
|
|
78
89
|
return data;
|
|
79
90
|
} else if (data instanceof JsonContainer) {
|
|
80
91
|
return data.toString();
|
|
81
92
|
} else if (typeof data == 'object') {
|
|
82
|
-
|
|
83
|
-
for (let key of Object.keys(data)) {
|
|
84
|
-
record[key] = filterJsonData(data[key]);
|
|
85
|
-
}
|
|
86
|
-
return JSONBig.stringify(record);
|
|
93
|
+
return new CustomObject(data, filterJsonData);
|
|
87
94
|
} else {
|
|
88
95
|
return null;
|
|
89
96
|
}
|
|
@@ -91,7 +98,7 @@ export function toMongoSyncRulesValue(data: any): SqliteValue {
|
|
|
91
98
|
|
|
92
99
|
const DEPTH_LIMIT = 20;
|
|
93
100
|
|
|
94
|
-
function filterJsonData(data: any, depth = 0): any {
|
|
101
|
+
function filterJsonData(data: any, context: CompatibilityContext, depth = 0): any {
|
|
95
102
|
const autoBigNum = true;
|
|
96
103
|
if (depth > DEPTH_LIMIT) {
|
|
97
104
|
// This is primarily to prevent infinite recursion
|
|
@@ -117,7 +124,8 @@ function filterJsonData(data: any, depth = 0): any {
|
|
|
117
124
|
} else if (typeof data == 'bigint') {
|
|
118
125
|
return data;
|
|
119
126
|
} else if (data instanceof Date) {
|
|
120
|
-
|
|
127
|
+
const isoString = data.toISOString();
|
|
128
|
+
return new DateTimeValue(isoString).toSqliteValue(context);
|
|
121
129
|
} else if (data instanceof mongo.ObjectId) {
|
|
122
130
|
return data.toHexString();
|
|
123
131
|
} else if (data instanceof mongo.UUID) {
|
|
@@ -133,16 +141,18 @@ function filterJsonData(data: any, depth = 0): any {
|
|
|
133
141
|
} else if (data instanceof RegExp) {
|
|
134
142
|
return { pattern: data.source, options: data.flags };
|
|
135
143
|
} else if (Array.isArray(data)) {
|
|
136
|
-
return data.map((element) => filterJsonData(element, depth + 1));
|
|
144
|
+
return data.map((element) => filterJsonData(element, context, depth + 1));
|
|
137
145
|
} else if (ArrayBuffer.isView(data)) {
|
|
138
146
|
return undefined;
|
|
147
|
+
} else if (data instanceof CustomSqliteValue) {
|
|
148
|
+
return data.toSqliteValue(context);
|
|
139
149
|
} else if (data instanceof JsonContainer) {
|
|
140
150
|
// Can be stringified directly when using our JSONBig implementation
|
|
141
151
|
return data;
|
|
142
152
|
} else if (typeof data == 'object') {
|
|
143
153
|
let record: Record<string, any> = {};
|
|
144
154
|
for (let key of Object.keys(data)) {
|
|
145
|
-
record[key] = filterJsonData(data[key], depth + 1);
|
|
155
|
+
record[key] = filterJsonData(data[key], context, depth + 1);
|
|
146
156
|
}
|
|
147
157
|
return record;
|
|
148
158
|
} else {
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { mongo } from '@powersync/lib-service-mongodb';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
applyRowContext,
|
|
4
|
+
CompatibilityContext,
|
|
5
|
+
CompatibilityEdition,
|
|
6
|
+
SqliteInputRow,
|
|
7
|
+
SqlSyncRules
|
|
8
|
+
} from '@powersync/service-sync-rules';
|
|
3
9
|
import { describe, expect, test } from 'vitest';
|
|
4
10
|
|
|
5
11
|
import { MongoRouteAPIAdapter } from '@module/api/MongoRouteAPIAdapter.js';
|
|
@@ -138,8 +144,10 @@ describe('mongo data types', () => {
|
|
|
138
144
|
]);
|
|
139
145
|
}
|
|
140
146
|
|
|
141
|
-
function checkResults(transformed:
|
|
142
|
-
|
|
147
|
+
function checkResults(transformed: SqliteInputRow[]) {
|
|
148
|
+
const sqliteValue = transformed.map((e) => applyRowContext(e, CompatibilityContext.FULL_BACKWARDS_COMPATIBILITY));
|
|
149
|
+
|
|
150
|
+
expect(sqliteValue[0]).toMatchObject({
|
|
143
151
|
_id: 1n,
|
|
144
152
|
text: 'text',
|
|
145
153
|
uuid: 'baeb2514-4c57-436d-b3cc-c1256211656d',
|
|
@@ -152,17 +160,17 @@ describe('mongo data types', () => {
|
|
|
152
160
|
null: null,
|
|
153
161
|
decimal: '3.14'
|
|
154
162
|
});
|
|
155
|
-
expect(
|
|
163
|
+
expect(sqliteValue[1]).toMatchObject({
|
|
156
164
|
_id: 2n,
|
|
157
165
|
nested: '{"test":"thing"}'
|
|
158
166
|
});
|
|
159
167
|
|
|
160
|
-
expect(
|
|
168
|
+
expect(sqliteValue[2]).toMatchObject({
|
|
161
169
|
_id: 3n,
|
|
162
170
|
date: '2023-03-06 13:47:00.000Z'
|
|
163
171
|
});
|
|
164
172
|
|
|
165
|
-
expect(
|
|
173
|
+
expect(sqliteValue[3]).toMatchObject({
|
|
166
174
|
_id: 4n,
|
|
167
175
|
objectId: '66e834cc91d805df11fa0ecb',
|
|
168
176
|
timestamp: 1958505087099n,
|
|
@@ -177,9 +185,9 @@ describe('mongo data types', () => {
|
|
|
177
185
|
});
|
|
178
186
|
|
|
179
187
|
// This must specifically be null, and not undefined.
|
|
180
|
-
expect(
|
|
188
|
+
expect(sqliteValue[4].undefined).toBeNull();
|
|
181
189
|
|
|
182
|
-
expect(
|
|
190
|
+
expect(sqliteValue[5]).toMatchObject({
|
|
183
191
|
_id: 6n,
|
|
184
192
|
int4: -1n,
|
|
185
193
|
int8: -9007199254740993n,
|
|
@@ -188,8 +196,10 @@ describe('mongo data types', () => {
|
|
|
188
196
|
});
|
|
189
197
|
}
|
|
190
198
|
|
|
191
|
-
function checkResultsNested(transformed:
|
|
192
|
-
|
|
199
|
+
function checkResultsNested(transformed: SqliteInputRow[]) {
|
|
200
|
+
const sqliteValue = transformed.map((e) => applyRowContext(e, CompatibilityContext.FULL_BACKWARDS_COMPATIBILITY));
|
|
201
|
+
|
|
202
|
+
expect(sqliteValue[0]).toMatchObject({
|
|
193
203
|
_id: 1n,
|
|
194
204
|
text: `["text"]`,
|
|
195
205
|
uuid: '["baeb2514-4c57-436d-b3cc-c1256211656d"]',
|
|
@@ -204,22 +214,22 @@ describe('mongo data types', () => {
|
|
|
204
214
|
|
|
205
215
|
// Note: Depending on to what extent we use the original postgres value, the whitespace may change, and order may change.
|
|
206
216
|
// We do expect that decimals and big numbers are preserved.
|
|
207
|
-
expect(
|
|
217
|
+
expect(sqliteValue[1]).toMatchObject({
|
|
208
218
|
_id: 2n,
|
|
209
219
|
nested: '[{"test":"thing"}]'
|
|
210
220
|
});
|
|
211
221
|
|
|
212
|
-
expect(
|
|
222
|
+
expect(sqliteValue[2]).toMatchObject({
|
|
213
223
|
_id: 3n,
|
|
214
224
|
date: '["2023-03-06 13:47:00.000Z"]'
|
|
215
225
|
});
|
|
216
226
|
|
|
217
|
-
expect(
|
|
227
|
+
expect(sqliteValue[3]).toMatchObject({
|
|
218
228
|
_id: 5n,
|
|
219
229
|
undefined: '[null]'
|
|
220
230
|
});
|
|
221
231
|
|
|
222
|
-
expect(
|
|
232
|
+
expect(sqliteValue[4]).toMatchObject({
|
|
223
233
|
_id: 6n,
|
|
224
234
|
int4: '[-1]',
|
|
225
235
|
int8: '[-9007199254740993]',
|
|
@@ -227,7 +237,7 @@ describe('mongo data types', () => {
|
|
|
227
237
|
decimal: '["-3.14"]'
|
|
228
238
|
});
|
|
229
239
|
|
|
230
|
-
expect(
|
|
240
|
+
expect(sqliteValue[5]).toMatchObject({
|
|
231
241
|
_id: 10n,
|
|
232
242
|
objectId: '["66e834cc91d805df11fa0ecb"]',
|
|
233
243
|
timestamp: '[1958505087099]',
|
|
@@ -522,13 +532,45 @@ bucket_definitions:
|
|
|
522
532
|
errors: []
|
|
523
533
|
});
|
|
524
534
|
});
|
|
535
|
+
|
|
536
|
+
test('date format', async () => {
|
|
537
|
+
const { db, client } = await connectMongoData();
|
|
538
|
+
const collection = db.collection('test_data');
|
|
539
|
+
try {
|
|
540
|
+
await setupTable(db);
|
|
541
|
+
await collection.insertOne({
|
|
542
|
+
fraction: new Date('2023-03-06 15:47:01.123+02'),
|
|
543
|
+
noFraction: new Date('2023-03-06 15:47:01+02')
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
const rawResults = await db
|
|
547
|
+
.collection('test_data')
|
|
548
|
+
.find({}, { sort: { _id: 1 } })
|
|
549
|
+
.toArray();
|
|
550
|
+
const [row] = [...ChangeStream.getQueryData(rawResults)];
|
|
551
|
+
|
|
552
|
+
const oldFormat = applyRowContext(row, CompatibilityContext.FULL_BACKWARDS_COMPATIBILITY);
|
|
553
|
+
expect(oldFormat).toMatchObject({
|
|
554
|
+
fraction: '2023-03-06 13:47:01.123Z',
|
|
555
|
+
noFraction: '2023-03-06 13:47:01.000Z'
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
const newFormat = applyRowContext(row, new CompatibilityContext(CompatibilityEdition.SYNC_STREAMS));
|
|
559
|
+
expect(newFormat).toMatchObject({
|
|
560
|
+
fraction: '2023-03-06T13:47:01.123Z',
|
|
561
|
+
noFraction: '2023-03-06T13:47:01.000Z'
|
|
562
|
+
});
|
|
563
|
+
} finally {
|
|
564
|
+
await client.close();
|
|
565
|
+
}
|
|
566
|
+
});
|
|
525
567
|
});
|
|
526
568
|
|
|
527
569
|
/**
|
|
528
570
|
* Return all the inserts from the first transaction in the replication stream.
|
|
529
571
|
*/
|
|
530
572
|
async function getReplicationTx(replicationStream: mongo.ChangeStream, count: number) {
|
|
531
|
-
let transformed:
|
|
573
|
+
let transformed: SqliteInputRow[] = [];
|
|
532
574
|
for await (const doc of replicationStream) {
|
|
533
575
|
// Specifically filter out map_input / map_output collections
|
|
534
576
|
if (!(doc as any)?.ns?.coll?.startsWith('test_data')) {
|