rxdb
Version:
A local-first realtime NoSQL Database for JavaScript applications - https://rxdb.info/
220 lines (216 loc) • 8.62 kB
JavaScript
import { Subject } from 'rxjs';
import { now, ensureNotFalsy, hasPremiumFlag } from "../utils/index.js";
import { attachmentObjectId, closeDexieDb, fromStorageToDexie, getDexieDbWithTables, getDocsInDb, RX_STORAGE_NAME_DEXIE } from "./dexie-helper.js";
import { dexieCount, dexieQuery } from "./dexie-query.js";
import { getPrimaryFieldOfPrimaryKey } from "../../rx-schema-helper.js";
import { categorizeBulkWriteRows, flatCloneDocWithMeta } from "../../rx-storage-helper.js";
import { addRxStorageMultiInstanceSupport } from "../../rx-storage-multiinstance.js";
import { newRxError } from "../../rx-error.js";
var instanceId = now();
var shownNonPremiumLog = false;
export class RxStorageInstanceDexie {
changes$ = new Subject();
instanceId = instanceId++;
constructor(storage, databaseName, collectionName, schema, internals, options, settings, devMode) {
this.storage = storage;
this.databaseName = databaseName;
this.collectionName = collectionName;
this.schema = schema;
this.internals = internals;
this.options = options;
this.settings = settings;
this.devMode = devMode;
this.primaryPath = getPrimaryFieldOfPrimaryKey(this.schema.primaryKey);
}
async bulkWrite(documentWrites, context) {
ensureNotClosed(this);
if (!shownNonPremiumLog && !(await hasPremiumFlag())) {
console.warn(['-------------- RxDB Open Core RxStorage -------------------------------', 'You are using the free Dexie.js based RxStorage implementation from RxDB https://rxdb.info/rx-storage-dexie.html?console=dexie ', 'While this is a great option, we want to let you know that there are faster storage solutions available in our premium plugins.', 'For professional users and production environments, we highly recommend considering these premium options to enhance performance and reliability.', ' https://rxdb.info/premium/?console=dexie ', 'If you already purchased premium access you can disable this log by calling the setPremiumFlag() function from rxdb-premium/plugins/shared.', '---------------------------------------------------------------------'].join('\n'));
shownNonPremiumLog = true;
} else {
shownNonPremiumLog = true;
}
/**
* Check some assumptions to ensure RxDB
* does not call the storage with an invalid write.
*/
documentWrites.forEach(row => {
// ensure revision is set
if (!row.document._rev || row.previous && !row.previous._rev) {
throw newRxError('SNH', {
args: {
row
}
});
}
});
var state = await this.internals;
var ret = {
error: []
};
/**
* Some storages might add any _meta fields
* internally. To ensure RxDB can work with that in the
* test suite, we add a random field here.
*/
if (this.devMode) {
documentWrites = documentWrites.map(row => {
var doc = flatCloneDocWithMeta(row.document);
return {
previous: row.previous,
document: doc
};
});
}
var documentKeys = documentWrites.map(writeRow => writeRow.document[this.primaryPath]);
var categorized;
await state.dexieDb.transaction('rw', state.dexieTable, state.dexieAttachmentsTable, async () => {
var docsInDbMap = new Map();
var docsInDbWithInternals = await getDocsInDb(this.internals, documentKeys);
docsInDbWithInternals.forEach(docWithDexieInternals => {
var doc = docWithDexieInternals;
if (doc) {
docsInDbMap.set(doc[this.primaryPath], doc);
}
return doc;
});
categorized = categorizeBulkWriteRows(this, this.primaryPath, docsInDbMap, documentWrites, context);
ret.error = categorized.errors;
/**
* Batch up the database operations
* so we can later run them in bulk.
*/
var bulkPutDocs = [];
categorized.bulkInsertDocs.forEach(row => {
bulkPutDocs.push(row.document);
});
categorized.bulkUpdateDocs.forEach(row => {
bulkPutDocs.push(row.document);
});
bulkPutDocs = bulkPutDocs.map(d => fromStorageToDexie(state.booleanIndexes, d));
if (bulkPutDocs.length > 0) {
await state.dexieTable.bulkPut(bulkPutDocs);
}
// handle attachments
var putAttachments = [];
categorized.attachmentsAdd.forEach(attachment => {
putAttachments.push({
id: attachmentObjectId(attachment.documentId, attachment.attachmentId),
data: attachment.attachmentData.data
});
});
categorized.attachmentsUpdate.forEach(attachment => {
putAttachments.push({
id: attachmentObjectId(attachment.documentId, attachment.attachmentId),
data: attachment.attachmentData.data
});
});
await state.dexieAttachmentsTable.bulkPut(putAttachments);
await state.dexieAttachmentsTable.bulkDelete(categorized.attachmentsRemove.map(attachment => attachmentObjectId(attachment.documentId, attachment.attachmentId)));
});
categorized = ensureNotFalsy(categorized);
if (categorized.eventBulk.events.length > 0) {
var lastState = ensureNotFalsy(categorized.newestRow).document;
categorized.eventBulk.checkpoint = {
id: lastState[this.primaryPath],
lwt: lastState._meta.lwt
};
this.changes$.next(categorized.eventBulk);
}
return ret;
}
async findDocumentsById(ids, deleted) {
ensureNotClosed(this);
var state = await this.internals;
var ret = [];
await state.dexieDb.transaction('r', state.dexieTable, async () => {
var docsInDb = await getDocsInDb(this.internals, ids);
docsInDb.forEach(documentInDb => {
if (documentInDb && (!documentInDb._deleted || deleted)) {
ret.push(documentInDb);
}
});
});
return ret;
}
query(preparedQuery) {
ensureNotClosed(this);
return dexieQuery(this, preparedQuery);
}
async count(preparedQuery) {
if (preparedQuery.queryPlan.selectorSatisfiedByIndex) {
var result = await dexieCount(this, preparedQuery);
return {
count: result,
mode: 'fast'
};
} else {
var _result = await dexieQuery(this, preparedQuery);
return {
count: _result.documents.length,
mode: 'slow'
};
}
}
changeStream() {
ensureNotClosed(this);
return this.changes$.asObservable();
}
async cleanup(minimumDeletedTime) {
ensureNotClosed(this);
var state = await this.internals;
await state.dexieDb.transaction('rw', state.dexieTable, async () => {
var maxDeletionTime = now() - minimumDeletedTime;
var toRemove = await state.dexieTable.where('_meta.lwt').below(maxDeletionTime).toArray();
var removeIds = [];
toRemove.forEach(doc => {
if (doc._deleted === '1') {
removeIds.push(doc[this.primaryPath]);
}
});
await state.dexieTable.bulkDelete(removeIds);
});
return true;
}
async getAttachmentData(documentId, attachmentId, _digest) {
ensureNotClosed(this);
var state = await this.internals;
var id = attachmentObjectId(documentId, attachmentId);
return await state.dexieDb.transaction('r', state.dexieAttachmentsTable, async () => {
var attachment = await state.dexieAttachmentsTable.get(id);
if (attachment) {
return attachment.data;
} else {
throw new Error('attachment missing documentId: ' + documentId + ' attachmentId: ' + attachmentId);
}
});
}
async remove() {
ensureNotClosed(this);
var state = await this.internals;
await state.dexieTable.clear();
return this.close();
}
close() {
if (this.closed) {
return this.closed;
}
this.closed = (async () => {
this.changes$.complete();
await closeDexieDb(this.internals);
})();
return this.closed;
}
}
export async function createDexieStorageInstance(storage, params, settings) {
var internals = getDexieDbWithTables(params.databaseName, params.collectionName, settings, params.schema);
var instance = new RxStorageInstanceDexie(storage, params.databaseName, params.collectionName, params.schema, internals, params.options, settings, params.devMode);
await addRxStorageMultiInstanceSupport(RX_STORAGE_NAME_DEXIE, params, instance);
return Promise.resolve(instance);
}
function ensureNotClosed(instance) {
if (instance.closed) {
throw new Error('RxStorageInstanceDexie is closed ' + instance.databaseName + '-' + instance.collectionName);
}
}
//# sourceMappingURL=rx-storage-instance-dexie.js.map