diff --git a/CHANGELOG.md b/CHANGELOG.md index a4de882d692..3404fab4679 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ Bugfixes: - LokiJS: Do not call `saveDatabase()` when no persistence adapter is given. - + - Query returns outdated result in second subscription [#3498](https://github.com/pubkey/rxdb/issues/3498) Thanks [@swnf](https://github.com/swnf) ### 10.5.3 (19 November 2021) Bugfixes: diff --git a/src/rx-query.ts b/src/rx-query.ts index 4ced7673243..9c79836c5a0 100644 --- a/src/rx-query.ts +++ b/src/rx-query.ts @@ -8,7 +8,8 @@ import { mergeMap, filter, map, - tap + tap, + shareReplay } from 'rxjs/operators'; import { sortObject, @@ -46,7 +47,7 @@ import { } from './rx-document-prototype-merge'; import { calculateNewResults } from './event-reduce'; import { triggerCacheReplacement } from './query-cache'; -import { getStateSet, QueryMatcher } from 'event-reduce-js'; +import type { QueryMatcher } from 'event-reduce-js'; import { _handleToStorageInstance } from './rx-collection-helper'; let _queryCount = 0; @@ -105,7 +106,10 @@ export class RxQueryBase< } }); }), - filter((docs: any[]) => !!docs), // not if previous returned false + // not if previous returned false + filter((docs: any[]) => !!docs), + // copy the array so it wont matter if the user modifies it + map((docs: any[]) => docs.slice(0)), map((docs: any[]) => { if (this.op === 'findOne') { // findOne()-queries emit document or null @@ -116,10 +120,9 @@ export class RxQueryBase< return docs; } }), - map(docs => { - // copy the array so it wont matter if the user modifies it - const ret = Array.isArray(docs) ? docs.slice() : docs; - return ret; + shareReplay({ + bufferSize: 1, + refCount: true }) ).asObservable(); diff --git a/test/unit.test.ts b/test/unit.test.ts index 55e1ba93778..c9b9470ed92 100644 --- a/test/unit.test.ts +++ b/test/unit.test.ts @@ -10,7 +10,6 @@ import './unit/rx-schema.test.js'; import './unit/key-compression.test.js'; import './unit/bug-report.test.js'; import './unit/rx-database.test.js'; -import './unit/event-reduce.test.js'; // TODO move down under idle queue import './unit/rx-collection.test.js'; import './unit/rx-document.test.js'; import './unit/primary.test.js'; @@ -20,6 +19,7 @@ import './unit/cache-replacement-policy.test'; import './unit/rx-query.test.js'; import './unit/query-builder.test.js'; import './unit/idle-queue.test.js'; +import './unit/event-reduce.test.js'; import './unit/reactive-collection.test.js'; import './unit/reactive-query.test.js'; import './unit/reactive-document.test.js'; diff --git a/test/unit/rx-query.test.ts b/test/unit/rx-query.test.ts index 8012b950542..0145d202ceb 100644 --- a/test/unit/rx-query.test.ts +++ b/test/unit/rx-query.test.ts @@ -1456,6 +1456,60 @@ config.parallel('rx-query.test.js', () => { assert.ok(res2); assert.strictEqual(resOne2.age, 42); + db.destroy(); + }); + it('#3498 RxQuery returns outdated result in second subscription', async () => { + const schema = { + version: 0, + primaryKey: 'id', + type: 'object', + properties: { + id: { + type: 'string' + }, + field: { + type: 'boolean' + } + } + }; + const db = await createRxDatabase({ + name: randomCouchString(10), + storage: getRxStoragePouch('memory'), + eventReduce: true, + ignoreDuplicate: true + }); + const collection = (await db.addCollections({ + collection: { + schema + } + })).collection; + + const doc = await collection.insert({ id: 'testid', field: false }); + + // Bug only happens the second time the query is used + const result1 = await collection.find({ selector: { field: false } }).exec(); + assert.strictEqual(result1.length, 1); + + await doc.update({ + $set: { + field: true + } + }); + + const obs = collection.find({ selector: { field: false } }).$; + const result2a: any[][] = []; + const result2b: any[][] = []; + const sub2 = obs.subscribe((d) => result2b.push(d)); + const sub1 = obs.subscribe((d) => result2a.push(d)); + + await promiseWait(5); + + sub1.unsubscribe(); + sub2.unsubscribe(); + + assert.strictEqual(Math.max(...result2a.map(r => r.length)), 0); + assert.strictEqual(Math.max(...result2b.map(r => r.length)), 0); + db.destroy(); }); });