@irrelon/forerunnerdb-core
Version:
ForerunnerDB core utilities for operating on JSON data.
97 lines (82 loc) • 3.42 kB
JavaScript
import {matchPipeline} from "./match";
import {queryToPipeline} from "./build";
import {
testFlight,
EnumTestFlightResult
} from "./testFlight";
/**
* @typedef {Object} RemoveOptions
* @property {Boolean} [$one=false] When true, will only update the first
* document that matched the query. The return array will only contain
* one element at most.
* @property {Boolean} [$ordered=false] When true, will stop processing on
* the first update failure but leave previous updates intact.
* @property {Boolean} [$atomic=false] When true, will cancel all updates on
* the first update failure, reverting any previous updates.
* @property {Boolean} [$skipAssignment=false] When true, the passed `data`
* will not be updated at all but the returned array will still contain the
* updated documents.
* @property {Function} [$preFlight] If passed, will be called with the document
* to update and if the preFlight function returns true, we will continue but if
* it returns false we will cancel the update.
* @property {Function} [$postFlight] If passed, will be called with the document
* being updated and the result of the update and if the $postFlight function
* returns true, we will continue but if it returns false we will cancel the
* update.
*/
/**
* Remove does a search for matching records in an array of data based on
* the passed query and then removes the matching records from the array.
* @param {Array<Object>} dataArr The array of data to query.
* @param {Object} query A query object.
* @param {RemoveOptions} [options] An options object.
* @returns {Promise<Array<Object>>} The new array of documents with the
* matching documents removed.
*/
export const remove = async (dataArr, query = {}, options = {}) => {
// Break query into operations
const pipeline = queryToPipeline(query);
const removed = [];
const removeMap = [];
const preFlightArr = [];
const postFlightArr = [];
if (options.$preFlight) {
preFlightArr.push(options.$preFlight);
}
if (options.$postFlight) {
postFlightArr.push(options.$postFlight);
}
const executeFlight = (originalDoc) => {
return {...originalDoc}
};
// Loop through each item of data and check if it matches the query
for (let currentIndex = 0; currentIndex < dataArr.length; currentIndex++) {
const originalDoc = dataArr[currentIndex];
const matchResult = matchPipeline(pipeline, originalDoc, {"originalQuery": query});
// If the document did not match our query, start on the next one
if (!matchResult) continue;
const removedDoc = await testFlight([originalDoc], preFlightArr, executeFlight, postFlightArr, {
"$atomic": options.$atomic,
"$ordered": options.$ordered
});
// If we failed testFlight, skip to the next record
if (removedDoc === EnumTestFlightResult.CANCEL) continue;
if (removedDoc === EnumTestFlightResult.CANCEL_ORDERED) break;
if (removedDoc === EnumTestFlightResult.CANCEL_ATOMIC) return [];
removeMap.push(currentIndex);
removed.push(removedDoc);
if (options.$one === true) {
// Quit since we only wanted to update the first matching record ($one)
break;
}
}
if (!options.$skipAssignment) {
// Update the document array by removing the matching records
removeMap.reverse().forEach((documentIndex) => {
dataArr.splice(documentIndex, 1);
});
}
// TODO support $immutable and return a whole new dataArr
return removed;
};
export default remove;