@powersync/service-module-mongodb-storage 0.0.0-dev-20250828134335 → 0.0.0-dev-20250901073220
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 +22 -5
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/migrations/db/migrations/1752661449910-connection-reporting.d.ts +3 -0
- package/dist/migrations/db/migrations/1752661449910-connection-reporting.js +36 -0
- package/dist/migrations/db/migrations/1752661449910-connection-reporting.js.map +1 -0
- package/dist/storage/MongoBucketStorage.js +1 -1
- package/dist/storage/MongoBucketStorage.js.map +1 -1
- package/dist/storage/MongoReportStorage.d.ts +18 -0
- package/dist/storage/MongoReportStorage.js +154 -0
- package/dist/storage/MongoReportStorage.js.map +1 -0
- package/dist/storage/implementation/MongoBucketBatch.js +1 -1
- package/dist/storage/implementation/MongoBucketBatch.js.map +1 -1
- package/dist/storage/implementation/MongoChecksums.d.ts +34 -0
- package/dist/storage/implementation/MongoChecksums.js +274 -0
- package/dist/storage/implementation/MongoChecksums.js.map +1 -0
- package/dist/storage/implementation/MongoCompactor.js +26 -29
- package/dist/storage/implementation/MongoCompactor.js.map +1 -1
- package/dist/storage/implementation/MongoStorageProvider.d.ts +1 -1
- package/dist/storage/implementation/MongoStorageProvider.js +7 -3
- package/dist/storage/implementation/MongoStorageProvider.js.map +1 -1
- package/dist/storage/implementation/MongoSyncBucketStorage.d.ts +2 -11
- package/dist/storage/implementation/MongoSyncBucketStorage.js +7 -207
- package/dist/storage/implementation/MongoSyncBucketStorage.js.map +1 -1
- package/dist/storage/implementation/PersistedBatch.js +1 -1
- package/dist/storage/implementation/PersistedBatch.js.map +1 -1
- package/dist/storage/implementation/db.d.ts +6 -1
- package/dist/storage/implementation/db.js +16 -0
- package/dist/storage/implementation/db.js.map +1 -1
- package/dist/storage/implementation/models.d.ts +4 -1
- package/dist/storage/storage-index.d.ts +3 -2
- package/dist/storage/storage-index.js +3 -2
- package/dist/storage/storage-index.js.map +1 -1
- package/dist/utils/test-utils.d.ts +11 -0
- package/dist/utils/test-utils.js +40 -0
- package/dist/utils/test-utils.js.map +1 -0
- package/dist/{storage/implementation → utils}/util.d.ts +2 -34
- package/dist/{storage/implementation → utils}/util.js +0 -54
- package/dist/utils/util.js.map +1 -0
- package/dist/utils/utils-index.d.ts +2 -0
- package/dist/utils/utils-index.js +3 -0
- package/dist/utils/utils-index.js.map +1 -0
- package/package.json +7 -7
- package/src/index.ts +1 -0
- package/src/migrations/db/migrations/1752661449910-connection-reporting.ts +58 -0
- package/src/storage/MongoBucketStorage.ts +1 -1
- package/src/storage/MongoReportStorage.ts +177 -0
- package/src/storage/implementation/MongoBucketBatch.ts +1 -1
- package/src/storage/implementation/MongoChecksums.ts +320 -0
- package/src/storage/implementation/MongoCompactor.ts +56 -56
- package/src/storage/implementation/MongoStorageProvider.ts +9 -4
- package/src/storage/implementation/MongoSyncBucketStorage.ts +7 -255
- package/src/storage/implementation/PersistedBatch.ts +1 -1
- package/src/storage/implementation/db.ts +18 -0
- package/src/storage/implementation/models.ts +4 -1
- package/src/storage/storage-index.ts +3 -2
- package/src/utils/test-utils.ts +55 -0
- package/src/{storage/implementation → utils}/util.ts +2 -59
- package/src/utils/utils-index.ts +2 -0
- package/test/src/__snapshots__/connection-report-storage.test.ts.snap +215 -0
- package/test/src/connection-report-storage.test.ts +133 -0
- package/test/src/util.ts +6 -2
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/storage/implementation/MongoTestStorageFactoryGenerator.d.ts +0 -7
- package/dist/storage/implementation/MongoTestStorageFactoryGenerator.js +0 -18
- package/dist/storage/implementation/MongoTestStorageFactoryGenerator.js.map +0 -1
- package/dist/storage/implementation/util.js.map +0 -1
- package/src/storage/implementation/MongoTestStorageFactoryGenerator.ts +0 -28
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import * as bson from 'bson';
|
|
2
2
|
import { mongo } from '@powersync/lib-service-mongodb';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { BucketDataDocument } from './models.js';
|
|
3
|
+
import { storage, utils } from '@powersync/service-core';
|
|
4
|
+
import { BucketDataDocument } from '../storage/implementation/models.js';
|
|
6
5
|
export declare function idPrefixFilter<T>(prefix: Partial<T>, rest: (keyof T)[]): mongo.Condition<T>;
|
|
7
6
|
export declare function generateSlotName(prefix: string, sync_rules_id: number): string;
|
|
8
7
|
/**
|
|
@@ -22,35 +21,4 @@ export declare function readSingleBatch<T>(cursor: mongo.AbstractCursor<T>): Pro
|
|
|
22
21
|
}>;
|
|
23
22
|
export declare function mapOpEntry(row: BucketDataDocument): utils.OplogEntry;
|
|
24
23
|
export declare function replicaIdToSubkey(table: bson.ObjectId, id: storage.ReplicaId): string;
|
|
25
|
-
/**
|
|
26
|
-
* Helper for unit tests
|
|
27
|
-
*/
|
|
28
|
-
export declare const connectMongoForTests: (url: string, isCI: boolean) => PowerSyncMongo;
|
|
29
24
|
export declare function setSessionSnapshotTime(session: mongo.ClientSession, time: bson.Timestamp): void;
|
|
30
|
-
export declare const CHECKSUM_QUERY_GROUP_STAGE: {
|
|
31
|
-
$group: {
|
|
32
|
-
_id: string;
|
|
33
|
-
checksum_total: {
|
|
34
|
-
$sum: {
|
|
35
|
-
$toLong: string;
|
|
36
|
-
};
|
|
37
|
-
};
|
|
38
|
-
count: {
|
|
39
|
-
$sum: number;
|
|
40
|
-
};
|
|
41
|
-
has_clear_op: {
|
|
42
|
-
$max: {
|
|
43
|
-
$cond: (number | {
|
|
44
|
-
$eq: string[];
|
|
45
|
-
})[];
|
|
46
|
-
};
|
|
47
|
-
};
|
|
48
|
-
last_op: {
|
|
49
|
-
$max: string;
|
|
50
|
-
};
|
|
51
|
-
};
|
|
52
|
-
};
|
|
53
|
-
/**
|
|
54
|
-
* Convert output of CHECKSUM_QUERY_GROUP_STAGE into a checksum.
|
|
55
|
-
*/
|
|
56
|
-
export declare function checksumFromAggregate(doc: bson.Document): PartialOrFullChecksum;
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import * as bson from 'bson';
|
|
2
2
|
import * as crypto from 'crypto';
|
|
3
3
|
import * as uuid from 'uuid';
|
|
4
|
-
import { mongo } from '@powersync/lib-service-mongodb';
|
|
5
4
|
import { storage, utils } from '@powersync/service-core';
|
|
6
|
-
import { PowerSyncMongo } from './db.js';
|
|
7
5
|
import { ServiceAssertionError } from '@powersync/lib-services-framework';
|
|
8
6
|
export function idPrefixFilter(prefix, rest) {
|
|
9
7
|
let filter = {
|
|
@@ -96,19 +94,6 @@ export function replicaIdToSubkey(table, id) {
|
|
|
96
94
|
return uuid.v5(repr, utils.ID_NAMESPACE);
|
|
97
95
|
}
|
|
98
96
|
}
|
|
99
|
-
/**
|
|
100
|
-
* Helper for unit tests
|
|
101
|
-
*/
|
|
102
|
-
export const connectMongoForTests = (url, isCI) => {
|
|
103
|
-
// Short timeout for tests, to fail fast when the server is not available.
|
|
104
|
-
// Slightly longer timeouts for CI, to avoid arbitrary test failures
|
|
105
|
-
const client = new mongo.MongoClient(url, {
|
|
106
|
-
connectTimeoutMS: isCI ? 15_000 : 5_000,
|
|
107
|
-
socketTimeoutMS: isCI ? 15_000 : 5_000,
|
|
108
|
-
serverSelectionTimeoutMS: isCI ? 15_000 : 2_500
|
|
109
|
-
});
|
|
110
|
-
return new PowerSyncMongo(client);
|
|
111
|
-
};
|
|
112
97
|
export function setSessionSnapshotTime(session, time) {
|
|
113
98
|
// This is a workaround for the lack of direct support for snapshot reads in the MongoDB driver.
|
|
114
99
|
if (!session.snapshotEnabled) {
|
|
@@ -121,43 +106,4 @@ export function setSessionSnapshotTime(session, time) {
|
|
|
121
106
|
throw new ServiceAssertionError(`Session snapshotTime is already set`);
|
|
122
107
|
}
|
|
123
108
|
}
|
|
124
|
-
export const CHECKSUM_QUERY_GROUP_STAGE = {
|
|
125
|
-
$group: {
|
|
126
|
-
_id: '$_id.b',
|
|
127
|
-
// Historically, checksum may be stored as 'int' or 'double'.
|
|
128
|
-
// More recently, this should be a 'long'.
|
|
129
|
-
// $toLong ensures that we always sum it as a long, avoiding inaccuracies in the calculations.
|
|
130
|
-
checksum_total: { $sum: { $toLong: '$checksum' } },
|
|
131
|
-
count: { $sum: 1 },
|
|
132
|
-
has_clear_op: {
|
|
133
|
-
$max: {
|
|
134
|
-
$cond: [{ $eq: ['$op', 'CLEAR'] }, 1, 0]
|
|
135
|
-
}
|
|
136
|
-
},
|
|
137
|
-
last_op: { $max: '$_id.o' }
|
|
138
|
-
}
|
|
139
|
-
};
|
|
140
|
-
/**
|
|
141
|
-
* Convert output of CHECKSUM_QUERY_GROUP_STAGE into a checksum.
|
|
142
|
-
*/
|
|
143
|
-
export function checksumFromAggregate(doc) {
|
|
144
|
-
const partialChecksum = Number(BigInt(doc.checksum_total) & 0xffffffffn) & 0xffffffff;
|
|
145
|
-
const bucket = doc._id;
|
|
146
|
-
if (doc.has_clear_op == 1) {
|
|
147
|
-
return {
|
|
148
|
-
// full checksum - replaces any previous one
|
|
149
|
-
bucket,
|
|
150
|
-
checksum: partialChecksum,
|
|
151
|
-
count: doc.count
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
else {
|
|
155
|
-
return {
|
|
156
|
-
// partial checksum - is added to a previous one
|
|
157
|
-
bucket,
|
|
158
|
-
partialCount: doc.count,
|
|
159
|
-
partialChecksum
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
109
|
//# sourceMappingURL=util.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../../src/utils/util.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils-index.js","sourceRoot":"","sources":["../../src/utils/utils-index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAC;AAC9C,cAAc,WAAW,CAAC"}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@powersync/service-module-mongodb-storage",
|
|
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-20250901073220",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"license": "FSL-1.1-ALv2",
|
|
8
8
|
"type": "module",
|
|
@@ -27,15 +27,15 @@
|
|
|
27
27
|
"lru-cache": "^10.2.2",
|
|
28
28
|
"ts-codec": "^1.3.0",
|
|
29
29
|
"uuid": "^11.1.0",
|
|
30
|
-
"@powersync/lib-service-mongodb": "0.0.0-dev-
|
|
31
|
-
"@powersync/lib-services-framework": "0.
|
|
32
|
-
"@powersync/service-core": "0.0.0-dev-
|
|
30
|
+
"@powersync/lib-service-mongodb": "0.0.0-dev-20250901073220",
|
|
31
|
+
"@powersync/lib-services-framework": "0.0.0-dev-20250901073220",
|
|
32
|
+
"@powersync/service-core": "0.0.0-dev-20250901073220",
|
|
33
|
+
"@powersync/service-types": "0.0.0-dev-20250901073220",
|
|
33
34
|
"@powersync/service-jsonbig": "0.17.11",
|
|
34
|
-
"@powersync/service-sync-rules": "0.29.0"
|
|
35
|
-
"@powersync/service-types": "0.13.0"
|
|
35
|
+
"@powersync/service-sync-rules": "0.29.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@powersync/service-core-tests": "0.0.0-dev-
|
|
38
|
+
"@powersync/service-core-tests": "0.0.0-dev-20250901073220"
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "tsc -b",
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { migrations } from '@powersync/service-core';
|
|
2
|
+
import * as storage from '../../../storage/storage-index.js';
|
|
3
|
+
import { MongoStorageConfig } from '../../../types/types.js';
|
|
4
|
+
|
|
5
|
+
export const up: migrations.PowerSyncMigrationFunction = async (context) => {
|
|
6
|
+
const {
|
|
7
|
+
service_context: { configuration }
|
|
8
|
+
} = context;
|
|
9
|
+
const db = storage.createPowerSyncMongo(configuration.storage as MongoStorageConfig);
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
await db.createConnectionReportingCollection();
|
|
13
|
+
|
|
14
|
+
await db.connection_report_events.createIndex(
|
|
15
|
+
{
|
|
16
|
+
connected_at: 1,
|
|
17
|
+
jwt_exp: 1,
|
|
18
|
+
disconnected_at: 1
|
|
19
|
+
},
|
|
20
|
+
{ name: 'connection_list_index' }
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
await db.connection_report_events.createIndex(
|
|
24
|
+
{
|
|
25
|
+
user_id: 1
|
|
26
|
+
},
|
|
27
|
+
{ name: 'connection_user_id_index' }
|
|
28
|
+
);
|
|
29
|
+
await db.connection_report_events.createIndex(
|
|
30
|
+
{
|
|
31
|
+
client_id: 1
|
|
32
|
+
},
|
|
33
|
+
{ name: 'connection_client_id_index' }
|
|
34
|
+
);
|
|
35
|
+
await db.connection_report_events.createIndex(
|
|
36
|
+
{
|
|
37
|
+
sdk: 1
|
|
38
|
+
},
|
|
39
|
+
{ name: 'connection_index' }
|
|
40
|
+
);
|
|
41
|
+
} finally {
|
|
42
|
+
await db.client.close();
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const down: migrations.PowerSyncMigrationFunction = async (context) => {
|
|
47
|
+
const {
|
|
48
|
+
service_context: { configuration }
|
|
49
|
+
} = context;
|
|
50
|
+
|
|
51
|
+
const db = storage.createPowerSyncMongo(configuration.storage as MongoStorageConfig);
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
await db.db.dropCollection('connection_report_events');
|
|
55
|
+
} finally {
|
|
56
|
+
await db.client.close();
|
|
57
|
+
}
|
|
58
|
+
};
|
|
@@ -12,7 +12,7 @@ import { PowerSyncMongo } from './implementation/db.js';
|
|
|
12
12
|
import { SyncRuleDocument } from './implementation/models.js';
|
|
13
13
|
import { MongoPersistedSyncRulesContent } from './implementation/MongoPersistedSyncRulesContent.js';
|
|
14
14
|
import { MongoSyncBucketStorage } from './implementation/MongoSyncBucketStorage.js';
|
|
15
|
-
import { generateSlotName } from '
|
|
15
|
+
import { generateSlotName } from '../utils/util.js';
|
|
16
16
|
|
|
17
17
|
export class MongoBucketStorage
|
|
18
18
|
extends BaseObserver<storage.BucketStorageFactoryListener>
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { mongo } from '@powersync/lib-service-mongodb';
|
|
2
|
+
import { storage } from '@powersync/service-core';
|
|
3
|
+
import { event_types } from '@powersync/service-types';
|
|
4
|
+
import { PowerSyncMongo } from './implementation/db.js';
|
|
5
|
+
import { logger } from '@powersync/lib-services-framework';
|
|
6
|
+
|
|
7
|
+
export class MongoReportStorage implements storage.ReportStorage {
|
|
8
|
+
private readonly client: mongo.MongoClient;
|
|
9
|
+
public readonly db: PowerSyncMongo;
|
|
10
|
+
|
|
11
|
+
constructor(db: PowerSyncMongo) {
|
|
12
|
+
this.client = db.client;
|
|
13
|
+
this.db = db;
|
|
14
|
+
}
|
|
15
|
+
async deleteOldConnectionData(data: event_types.DeleteOldConnectionData): Promise<void> {
|
|
16
|
+
const { date } = data;
|
|
17
|
+
const result = await this.db.connection_report_events.deleteMany({
|
|
18
|
+
connected_at: { $lt: date },
|
|
19
|
+
$or: [
|
|
20
|
+
{ disconnected_at: { $exists: true } },
|
|
21
|
+
{ jwt_exp: { $lt: new Date() }, disconnected_at: { $exists: false } }
|
|
22
|
+
]
|
|
23
|
+
});
|
|
24
|
+
if (result.deletedCount > 0) {
|
|
25
|
+
logger.info(
|
|
26
|
+
`TTL from ${date.toISOString()}: ${result.deletedCount} MongoDB documents have been removed from connection_report_events.`
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async getClientConnectionReports(
|
|
32
|
+
data: event_types.ClientConnectionReportRequest
|
|
33
|
+
): Promise<event_types.ClientConnectionReportResponse> {
|
|
34
|
+
const { start, end } = data;
|
|
35
|
+
const result = await this.db.connection_report_events
|
|
36
|
+
.aggregate<event_types.ClientConnectionReportResponse>([
|
|
37
|
+
{
|
|
38
|
+
$match: {
|
|
39
|
+
connected_at: { $lte: end, $gte: start }
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
this.connectionsFacetPipeline(),
|
|
43
|
+
this.connectionsProjectPipeline()
|
|
44
|
+
])
|
|
45
|
+
.toArray();
|
|
46
|
+
return result[0];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async reportClientConnection(data: event_types.ClientConnectionBucketData): Promise<void> {
|
|
50
|
+
const updateFilter = this.updateDocFilter(data.user_id, data.client_id!);
|
|
51
|
+
await this.db.connection_report_events.findOneAndUpdate(
|
|
52
|
+
updateFilter,
|
|
53
|
+
{
|
|
54
|
+
$set: data,
|
|
55
|
+
$unset: {
|
|
56
|
+
disconnected_at: ''
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
upsert: true
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
async reportClientDisconnection(data: event_types.ClientDisconnectionEventData): Promise<void> {
|
|
65
|
+
const { connected_at, user_id, client_id } = data;
|
|
66
|
+
await this.db.connection_report_events.findOneAndUpdate(
|
|
67
|
+
{
|
|
68
|
+
client_id,
|
|
69
|
+
user_id,
|
|
70
|
+
connected_at
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
$set: {
|
|
74
|
+
disconnected_at: data.disconnected_at
|
|
75
|
+
},
|
|
76
|
+
$unset: {
|
|
77
|
+
jwt_exp: ''
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
async getConnectedClients(): Promise<event_types.ClientConnectionReportResponse> {
|
|
83
|
+
const result = await this.db.connection_report_events
|
|
84
|
+
.aggregate<event_types.ClientConnectionReportResponse>([
|
|
85
|
+
{
|
|
86
|
+
$match: {
|
|
87
|
+
disconnected_at: { $exists: false },
|
|
88
|
+
jwt_exp: { $gt: new Date() }
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
this.connectionsFacetPipeline(),
|
|
92
|
+
this.connectionsProjectPipeline()
|
|
93
|
+
])
|
|
94
|
+
.toArray();
|
|
95
|
+
return result[0];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async [Symbol.asyncDispose]() {
|
|
99
|
+
// No-op
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private parseJsDate(date: Date) {
|
|
103
|
+
const year = date.getUTCFullYear();
|
|
104
|
+
const month = date.getUTCMonth();
|
|
105
|
+
const today = date.getUTCDate();
|
|
106
|
+
const day = date.getUTCDay();
|
|
107
|
+
return {
|
|
108
|
+
year,
|
|
109
|
+
month,
|
|
110
|
+
today,
|
|
111
|
+
day,
|
|
112
|
+
parsedDate: date
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private connectionsFacetPipeline() {
|
|
117
|
+
return {
|
|
118
|
+
$facet: {
|
|
119
|
+
unique_users: [
|
|
120
|
+
{
|
|
121
|
+
$group: {
|
|
122
|
+
_id: '$user_id'
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
$count: 'count'
|
|
127
|
+
}
|
|
128
|
+
],
|
|
129
|
+
sdk_versions_array: [
|
|
130
|
+
{
|
|
131
|
+
$group: {
|
|
132
|
+
_id: '$sdk',
|
|
133
|
+
total: { $sum: 1 },
|
|
134
|
+
client_ids: { $addToSet: '$client_id' },
|
|
135
|
+
user_ids: { $addToSet: '$user_id' }
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
$project: {
|
|
140
|
+
_id: 0,
|
|
141
|
+
sdk: '$_id',
|
|
142
|
+
users: { $size: '$user_ids' },
|
|
143
|
+
clients: { $size: '$client_ids' }
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
$sort: {
|
|
148
|
+
sdk: 1
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private connectionsProjectPipeline() {
|
|
157
|
+
return {
|
|
158
|
+
$project: {
|
|
159
|
+
users: { $ifNull: [{ $arrayElemAt: ['$unique_users.count', 0] }, 0] },
|
|
160
|
+
sdks: '$sdk_versions_array'
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
private updateDocFilter(userId: string, clientId: string) {
|
|
166
|
+
const { year, month, today } = this.parseJsDate(new Date());
|
|
167
|
+
const nextDay = today + 1;
|
|
168
|
+
return {
|
|
169
|
+
user_id: userId,
|
|
170
|
+
client_id: clientId,
|
|
171
|
+
connected_at: {
|
|
172
|
+
$gte: new Date(Date.UTC(year, month, today)),
|
|
173
|
+
$lt: new Date(Date.UTC(year, month, nextDay))
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -28,7 +28,7 @@ import { MongoIdSequence } from './MongoIdSequence.js';
|
|
|
28
28
|
import { batchCreateCustomWriteCheckpoints } from './MongoWriteCheckpointAPI.js';
|
|
29
29
|
import { cacheKey, OperationBatch, RecordOperation } from './OperationBatch.js';
|
|
30
30
|
import { PersistedBatch } from './PersistedBatch.js';
|
|
31
|
-
import { idPrefixFilter } from '
|
|
31
|
+
import { idPrefixFilter } from '../../utils/util.js';
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
34
|
* 15MB
|