diff --git a/.changeset/pretty-hotels-wonder.md b/.changeset/pretty-hotels-wonder.md new file mode 100644 index 000000000..e00f15a82 --- /dev/null +++ b/.changeset/pretty-hotels-wonder.md @@ -0,0 +1,5 @@ +--- +'@powersync/service-core': patch +--- + +Fix bucket parameters grouping. diff --git a/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts b/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts index 77cf9c100..0e8f92f89 100644 --- a/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts +++ b/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts @@ -265,7 +265,7 @@ export class MongoSyncBucketStorage }, { $group: { - _id: '$key', + _id: { key: '$key', lookup: '$lookup' }, bucket_parameters: { $first: '$bucket_parameters' } diff --git a/packages/service-core/test/src/data_storage.test.ts b/packages/service-core/test/src/data_storage.test.ts index df8249b6a..35b3f62ce 100644 --- a/packages/service-core/test/src/data_storage.test.ts +++ b/packages/service-core/test/src/data_storage.test.ts @@ -119,6 +119,76 @@ bucket_definitions: ]); }); + test('it should use the latest version after updates', async () => { + const sync_rules = testRules( + ` +bucket_definitions: + mybucket: + parameters: + - SELECT id AS todo_id + FROM todos + WHERE list_id IN token_parameters.list_id + data: [] + ` + ); + + const storage = (await factory()).getInstance(sync_rules); + + const table = makeTestTable('todos', ['id', 'list_id']); + + await storage.startBatch(BATCH_OPTIONS, async (batch) => { + // Create two todos which initially belong to different lists + await batch.save({ + sourceTable: table, + tag: SaveOperationTag.INSERT, + after: { + id: 'todo1', + list_id: 'list1' + }, + afterReplicaId: rid('todo1') + }); + await batch.save({ + sourceTable: table, + tag: SaveOperationTag.INSERT, + after: { + id: 'todo2', + list_id: 'list2' + }, + afterReplicaId: rid('todo2') + }); + }); + + const result2 = await storage.startBatch(BATCH_OPTIONS, async (batch) => { + // Update the second todo item to now belong to list 1 + await batch.save({ + sourceTable: table, + tag: SaveOperationTag.UPDATE, + after: { + id: 'todo2', + list_id: 'list1' + }, + afterReplicaId: rid('todo2') + }); + }); + + // We specifically request the todo_ids for both lists. + // There removal operation for the association of `list2`::`todo2` should not interfere with the new + // association of `list1`::`todo2` + const parameters = await storage.getParameterSets(BigInt(result2!.flushed_op).toString(), [ + ['mybucket', '1', 'list1'], + ['mybucket', '1', 'list2'] + ]); + + expect(parameters.sort((a, b) => (a.todo_id as string).localeCompare(b.todo_id as string))).toEqual([ + { + todo_id: 'todo1' + }, + { + todo_id: 'todo2' + } + ]); + }); + test('save and load parameters with different number types', async () => { const sync_rules = testRules( `