Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat RxCollection support bulkRemove #2845

Merged
merged 1 commit into from
Feb 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs-src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@
* [Functions](./rx-collection.md#functions)
* [$](./rx-collection.md#observe-)
* [insert()](./rx-collection.md#insert)
* [bulkInsert()](./rx-collection.md#bulkinsert)
* [bulkRemove()](./rx-collection.md#bulkremove)
* [newDocument()](./rx-collection.md#newdocument)
* [upsert()](./rx-collection.md#upsert)
* [atomicUpsert()](./rx-collection.md#atomicupsert)
Expand Down
16 changes: 16 additions & 0 deletions docs-src/rx-collection.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,22 @@ const result = await myCollection.bulkInsert([{

NOTICE: `bulkInsert` will not fail on update conflicts and you cannot expect that on failure the other documents are not inserted.

### bulkRemove()

When you want to remove many documents at once, use bulk remove. Returns an object with a `success`- and `error`-array.

```js
const result = await myCollection.bulkRemove([
'primary1',
'primary2'
]);

// > {
// success: [RxDocument, RxDocument],
// error: []
// }
```

### newDocument()
Sometimes it can be helpful to spawn and use documents before saving them into the database.
This is useful especially when you want to use the ORM methods or prefill values from form data.
Expand Down
78 changes: 76 additions & 2 deletions src/rx-collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ import {
createInsertEvent,
RxChangeEventInsert,
RxChangeEventUpdate,
RxChangeEventDelete
RxChangeEventDelete,
createDeleteEvent
} from './rx-change-event';
import {
newRxError,
Expand Down Expand Up @@ -84,7 +85,8 @@ import type {
RxDumpCollectionAny,
MangoQuery,
MangoQueryNoLimit,
RxCacheReplacementPolicy
RxCacheReplacementPolicy,
WithPouchMeta
} from './types';
import type {
RxGraphQLReplicationState
Expand Down Expand Up @@ -465,6 +467,78 @@ export class RxCollectionBase<
});
}

async bulkRemove(
ids: string[]
): Promise<{
success: RxDocument<RxDocumentType, OrmMethods>[],
error: any[]
}> {
const rxDocumentMap = await this.findByIds(ids);
const docsData: WithPouchMeta<RxDocumentType>[] = [];
const docsMap: Map<string, WithPouchMeta<RxDocumentType>> = new Map();
Array.from(rxDocumentMap.values()).forEach(rxDocument => {
const data = rxDocument.toJSON(true);
docsData.push(data);
docsMap.set(rxDocument.primary, data);
});

await Promise.all(
docsData.map(doc => {
const primary = (doc as any)[this.schema.primaryPath];
return this._runHooks('pre', 'remove', doc, rxDocumentMap.get(primary));
})
);

docsData.forEach(doc => doc._deleted = true);

const removeDocs = docsData.map(doc => this._handleToPouch(doc));

let startTime: number;

const results = await this.database.lockedRun(async () => {
startTime = now();
const bulkResults = await this.pouch.bulkDocs(removeDocs);
return bulkResults;
});

const endTime = now();

const okResults = results.filter(r => r.ok);

await Promise.all(
okResults.map(r => {
return this._runHooks(
'post',
'remove',
docsMap.get(r.id),
rxDocumentMap.get(r.id)
);
})
);

okResults.forEach(r => {
const rxDocument = rxDocumentMap.get(r.id) as RxDocument<RxDocumentType, OrmMethods>;
const emitEvent = createDeleteEvent(
this as any,
docsMap.get(r.id) as any,
rxDocument._data,
startTime,
endTime,
rxDocument as any,
);
this.$emit(emitEvent);
});

const rxDocuments: any[] = okResults.map(r => {
return rxDocumentMap.get(r.id);
});

return {
success: rxDocuments,
error: okResults.filter(r => !r.ok)
};
}

/**
* same as insert but overwrites existing document with same primary
*/
Expand Down
33 changes: 33 additions & 0 deletions test/unit/hooks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,23 @@ config.parallel('hooks.test.js', () => {
assert.strictEqual(doc2.get('passportId'), human.passportId);
c.database.destroy();
});
it('should call pre remove hook before bulkRemove', async () => {
const c = await humansCollection.create(5);
const docList = await c.find().exec();
const primaryList = docList.map(doc => doc.primary);

let count = 0;
c.preRemove((data, instance) => {
assert.ok(isRxDocument(instance));
assert.ok(data.age);
count++;
}, true);

await c.bulkRemove(primaryList);
assert.strictEqual(count, 5);

c.database.destroy();
});
});
describe('negative', () => { });
});
Expand Down Expand Up @@ -416,6 +433,22 @@ config.parallel('hooks.test.js', () => {
assert.ok(hasRun);
c.database.destroy();
});
it('should call post remove hook after bulkRemove', async () => {
const c = await humansCollection.create(5);
const docList = await c.find().exec();
const primaryList = docList.map(doc => doc.primary);

let count = 0;
c.postRemove((data, instance) => {
assert.ok(isRxDocument(instance));
assert.ok(data.age);
count++;
}, true);
await c.bulkRemove(primaryList);
assert.strictEqual(count, 5);

c.database.destroy();
});
});
describe('negative', () => { });
});
Expand Down
31 changes: 30 additions & 1 deletion test/unit/reactive-collection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import AsyncTestUtil from 'async-test-util';
import {
first
} from 'rxjs/operators';
import { RxChangeEvent } from '../../src/rx-change-event';
import { RxChangeEvent, RxChangeEventDelete } from '../../src/rx-change-event';

config.parallel('reactive-collection.test.js', () => {
describe('.insert()', () => {
Expand Down Expand Up @@ -97,6 +97,35 @@ config.parallel('reactive-collection.test.js', () => {
});
});
});
describe('.bulkRemove()', () => {
describe('positive', () => {
it('should fire on bulk remove', async () => {
const c = await humansCollection.create(10);

const emittedCollection: RxChangeEventDelete[] = [];
const colSub = c.remove$.subscribe((ce) => {
emittedCollection.push(ce);
});

const docList = await c.find().exec();
const primaryList = docList.map(doc => doc.primary);

await c.bulkRemove(primaryList);

const changeEvent = emittedCollection[0];

assert.strictEqual(changeEvent.operation, 'DELETE');
assert.strictEqual(changeEvent.collectionName, 'human');
assert.strictEqual(changeEvent.documentId, docList[0].primary);
assert.ok(isRxDocument(changeEvent.rxDocument));
assert.ok(changeEvent.documentData);
assert.ok(changeEvent.previousData);

colSub.unsubscribe();
c.database.destroy();
});
});
});
describe('.remove()', () => {
describe('positive', () => {
it('should fire on remove', async () => {
Expand Down
19 changes: 19 additions & 0 deletions test/unit/rx-collection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,25 @@ config.parallel('rx-collection.test.js', () => {

});
});
describe('.bulkRemove()', () => {
describe('positive', () => {
it('should remove some humans', async () => {
const c = await humansCollection.create(10);
const docList = await c.find().exec();

assert.strictEqual(docList.length, 10);

const primaryList = docList.map(doc => doc.primary);
const ret = await c.bulkRemove(primaryList);
assert.strictEqual(ret.success.length, 10);

const finalList = await c.find().exec();
assert.strictEqual(finalList.length, 0);

c.database.destroy();
});
});
});
describe('.find()', () => {
describe('find all', () => {
describe('positive', () => {
Expand Down