feat(release): 1.0.0
This commit is contained in:
168
src/Storage/Operators/Update/Utils.ts
Normal file
168
src/Storage/Operators/Update/Utils.ts
Normal file
@@ -0,0 +1,168 @@
|
||||
import * as dot from "dot-prop";
|
||||
import { deepEqual } from "fast-equals";
|
||||
import { Query } from "mingo";
|
||||
|
||||
import { clone } from "../../../Clone.js";
|
||||
import { Document, Filter, WithId } from "../../../Types.js";
|
||||
|
||||
type UpdateValue = (data: any, key: string, target: string) => any;
|
||||
|
||||
export function setPositionalData<TSchema extends Document = Document>(
|
||||
document: WithId<TSchema>,
|
||||
criteria: Filter<WithId<TSchema>>,
|
||||
key: string,
|
||||
update: {
|
||||
object: UpdateValue;
|
||||
value: UpdateValue;
|
||||
}
|
||||
): boolean {
|
||||
const { filter, path, target } = getPositionalFilter(criteria, key);
|
||||
|
||||
const values = getPropertyValues(document, path);
|
||||
const items =
|
||||
typeof filter === "object"
|
||||
? getPositionalUpdateQuery(clone(values), key, filter, target, update.object)
|
||||
: getPositionalUpdate(clone(values), key, filter, target, update.value);
|
||||
|
||||
dot.setProperty(document, path, items);
|
||||
|
||||
return deepEqual(values, items) === false;
|
||||
}
|
||||
|
||||
function getPropertyValues(document: Document, path: string): string[] {
|
||||
const values = dot.getProperty(document, path);
|
||||
if (values === undefined) {
|
||||
throw new Error("Values is undefined");
|
||||
}
|
||||
if (Array.isArray(values) === false) {
|
||||
throw new Error("Values is not an array");
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
export function getPositionalUpdate(
|
||||
items: any[],
|
||||
key: string,
|
||||
filter: string,
|
||||
target: string,
|
||||
updateValue: UpdateValue
|
||||
): any[] {
|
||||
let index = 0;
|
||||
for (const item of items) {
|
||||
if (item === filter) {
|
||||
items[index] = updateValue(items[index], key, target);
|
||||
break;
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
export function getPositionalUpdateQuery(
|
||||
items: any[],
|
||||
key: string,
|
||||
filter: Filter<any>,
|
||||
target: string,
|
||||
updateValue: UpdateValue
|
||||
): any[] {
|
||||
let index = 0;
|
||||
for (const item of items) {
|
||||
if (new Query(filter).test(item) === true) {
|
||||
if (target === "") {
|
||||
items[index] = updateValue(items[index], key, target);
|
||||
} else {
|
||||
dot.setProperty(item, target, updateValue(items[index], key, target));
|
||||
}
|
||||
break;
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
export function getPositionalFilter(criteria: Filter<any>, key: string): PositionalFilter {
|
||||
const [leftPath, rightPath] = key.split("$");
|
||||
|
||||
const lKey = trimSeparators(leftPath);
|
||||
const rKey = trimSeparators(rightPath);
|
||||
|
||||
for (const key in criteria) {
|
||||
const result = getPositionalCriteriaFilter(key, lKey, rKey, criteria);
|
||||
if (result !== undefined) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
filter: criteria[lKey],
|
||||
path: lKey,
|
||||
target: rKey
|
||||
};
|
||||
}
|
||||
|
||||
function getPositionalCriteriaFilter(
|
||||
key: string,
|
||||
lKey: string,
|
||||
rKey: string,
|
||||
criteria: Filter<any>
|
||||
): PositionalFilter | undefined {
|
||||
if (key.includes(lKey) === true) {
|
||||
const isObject = typeof criteria[key] === "object";
|
||||
if (key.includes(".") === true || isObject === true) {
|
||||
return {
|
||||
filter:
|
||||
trimSeparators(key.replace(lKey, "")) === ""
|
||||
? (criteria[key] as any).$elemMatch !== undefined
|
||||
? (criteria[key] as any).$elemMatch
|
||||
: criteria[key]
|
||||
: {
|
||||
[trimSeparators(key.replace(lKey, ""))]: criteria[key]
|
||||
},
|
||||
path: lKey,
|
||||
target: rKey
|
||||
};
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function trimSeparators(value: string): string {
|
||||
return value.replace(/^\.+|\.+$/gm, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* A position filter is used to find documents to update in an array of values.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```ts
|
||||
* const document = {
|
||||
* grades: [
|
||||
* { grade: 80, mean: 75, std: 8 },
|
||||
* { grade: 85, mean: 90, std: 5 },
|
||||
* { grade: 85, mean: 85, std: 8 }
|
||||
* ]
|
||||
* }
|
||||
*
|
||||
* updateOne({ "grades.grade": 85 }, { $set: { "grades.$.std": 6 } } })
|
||||
* ```
|
||||
*
|
||||
* In the above example the filter would be `{ grade: 85 }` which is used to find
|
||||
* objects to update in an array of values.
|
||||
*/
|
||||
type PositionalFilter = {
|
||||
/**
|
||||
* The filter to use to find the values to update in an array.
|
||||
*/
|
||||
filter: any;
|
||||
|
||||
/**
|
||||
* The path to the array of values of the parent document. Eg. `grades`.
|
||||
*/
|
||||
path: string;
|
||||
|
||||
/**
|
||||
* The path to the key to update in the array of values. Eg. `std`.
|
||||
*/
|
||||
target: string;
|
||||
};
|
||||
Reference in New Issue
Block a user