MongoDB query filtering in JavaScript
—
Advanced functionality for custom operations, fine-grained control, and extending Sift's capabilities.
Create query testers with custom operation sets, allowing for fine-grained control over which operations are available.
Creates a query tester without built-in operations, enabling custom operation sets.
/**
* Creates a query tester without built-in operations for custom operation sets
* @param query - Query object using available operations
* @param options - Configuration including custom operations and comparison function
* @returns Filter function that can be used with Array.filter() or for testing values
*/
function createQueryTester<TItem, TSchema = TItem>(
query: Query<TSchema>,
options?: Partial<Options>
): (item: TItem) => boolean;Usage Examples:
import { createQueryTester, $eq, $gt, $in } from "sift";
// Create tester with only specific operations
const customFilter = createQueryTester(
{ age: { $gt: 18 }, status: { $in: ["active", "pending"] } },
{
operations: { $eq, $gt, $in },
compare: (a, b) => a === b // Custom comparison
}
);
const users = [
{ age: 25, status: "active" },
{ age: 16, status: "pending" },
{ age: 30, status: "inactive" }
];
const filtered = users.filter(customFilter);
// [{ age: 25, status: "active" }]Creates a query operation with all default MongoDB operations.
/**
* Creates a query operation with all default MongoDB operations
* @param query - Query object
* @param ownerQuery - Parent query object
* @param options - Partial options (operations will be merged with defaults)
* @returns QueryOperation instance
*/
function createDefaultQueryOperation<TItem, TSchema extends TItem = TItem>(
query: Query<TSchema>,
ownerQuery: any,
options?: Partial<Options>
): QueryOperation<TItem>;Creates a query operation from a query object with custom options.
/**
* Creates a query operation from a query object
* @param query - Query object
* @param ownerQuery - Parent query object (optional)
* @param options - Partial options including operations and comparison function
* @returns QueryOperation instance
*/
function createQueryOperation<TItem, TSchema = TItem>(
query: Query<TSchema>,
ownerQuery?: any,
options?: Partial<Options>
): QueryOperation<TItem>;Low-level operation creation and testing functionality for building custom operations.
Creates a tester function from an operation instance.
/**
* Creates a tester function from an operation instance
* @param operation - Operation instance to create tester for
* @returns Function that tests items against the operation
*/
function createOperationTester<TItem>(
operation: Operation<TItem>
): (item: TItem, key?: Key, owner?: any) => boolean;Creates an equals operation for custom operation development.
/**
* Creates an equals operation for custom operation development
* @param params - Parameters for the operation (value to match or test function)
* @param ownerQuery - Parent query object
* @param options - Operation options including comparison function
* @returns EqualsOperation instance
*/
function createEqualsOperation(
params: any,
ownerQuery: any,
options: Options
): EqualsOperation;Usage Examples:
import {
createQueryOperation,
createOperationTester,
createEqualsOperation,
$eq
} from "sift";
// Create custom operation
const customEquals = createEqualsOperation(
(value) => value > 10,
null,
{
operations: { $eq },
compare: (a, b) => a === b
}
);
// Test the operation
const tester = createOperationTester(customEquals);
console.log(tester(15)); // true
console.log(tester(5)); // false
// Build complex query operation
const queryOp = createQueryOperation(
{ age: { $eq: 25 } },
null,
{
operations: { $eq },
compare: (a, b) => a === b
}
);
const queryTester = createOperationTester(queryOp);
console.log(queryTester({ age: 25 })); // trueHelper functions for comparison, type checking, and operation creation.
Creates a tester function from a value, function, or regular expression.
/**
* Creates a tester function from a value, function, or regular expression
* @param a - Value, function, or RegExp to create tester from
* @param compare - Comparison function for value testing
* @returns Tester function
*/
function createTester(a: any, compare: Comparator): Tester;
type Tester = (
item: any,
key?: Key,
owner?: any,
root?: boolean,
leaf?: boolean
) => boolean;Checks if a query object contains operation keys.
/**
* Checks if a query object contains operation keys
* @param query - Query object to inspect
* @param options - Options containing available operations
* @returns True if query contains operations, false otherwise
*/
function containsOperation(query: any, options: Options): boolean;Higher-order function for creating numerical operations with type coercion.
/**
* Creates numerical operations with automatic type coercion
* @param createTester - Function that creates the test logic
* @returns Operation creator function
*/
function numericalOperation(
createTester: (value: any) => Tester
): OperationCreator<any>;
/**
* Higher-order function for numerical operation creators
* @param createNumericalOperation - Operation creator for numerical operations
* @returns Wrapped operation creator
*/
function numericalOperationCreator(
createNumericalOperation: OperationCreator<any>
): OperationCreator<any>;Usage Examples:
import {
createTester,
containsOperation,
numericalOperation,
$eq
} from "sift";
// Create custom tester from function
const customTester = createTester(
(value) => typeof value === "string" && value.length > 5,
(a, b) => a === b
);
console.log(customTester("hello world")); // true
console.log(customTester("hi")); // false
// Check if query contains operations
const hasOps = containsOperation(
{ age: { $eq: 25 } },
{ operations: { $eq }, compare: (a, b) => a === b }
); // true
const noOps = containsOperation(
{ name: "Alice" },
{ operations: { $eq }, compare: (a, b) => a === b }
); // false
// Create custom numerical operation
const $between = numericalOperation((range) => (value) => {
const [min, max] = range;
return value >= min && value <= max;
});
// Use custom operation
const customQuery = createQueryTester(
{ score: [70, 90] }, // between 70 and 90
{ operations: { $between } }
);Utility functions for type detection and value comparison.
Creates type checking functions for runtime type validation.
/**
* Creates type checking functions for runtime validation
* @param type - Type name to check for
* @returns Type guard function
*/
function typeChecker<TType>(type: string): (value: any) => value is TType;/**
* Converts values to comparable form (handles Dates, arrays, toJSON)
* @param value - Value to make comparable
* @returns Comparable representation of value
*/
function comparable(value: any): any;
/**
* Coerces potentially null values to null
* @param value - Value to coerce
* @returns Coerced value
*/
function coercePotentiallyNull(value: any): any;
/**
* Deep equality comparison function
* @param a - First value
* @param b - Second value
* @returns True if values are deeply equal
*/
function equals(a: any, b: any): boolean;
/**
* Type guard for arrays
* @param value - Value to check
* @returns True if value is an array
*/
function isArray(value: any): value is Array<any>;
/**
* Type guard for objects
* @param value - Value to check
* @returns True if value is an object
*/
function isObject(value: any): value is Object;
/**
* Type guard for functions
* @param value - Value to check
* @returns True if value is a function
*/
function isFunction(value: any): value is Function;
/**
* Checks if key is a property (not a function) of an object
* @param item - Object to check
* @param key - Key to test
* @returns True if key is a non-function property
*/
function isProperty(item: any, key: any): boolean;
/**
* Checks if value is a plain object (not instance of custom class)
* @param value - Value to check
* @returns True if value is a vanilla object
*/
function isVanillaObject(value: any): boolean;Usage Examples:
import {
typeChecker,
comparable,
equals,
isArray,
isVanillaObject
} from "sift";
// Create type checkers
const isString = typeChecker<string>("String");
const isNumber = typeChecker<number>("Number");
console.log(isString("hello")); // true
console.log(isNumber(42)); // true
console.log(isString(42)); // false
// Value comparison
const date1 = new Date("2023-01-01");
const date2 = new Date("2023-01-01");
console.log(comparable(date1)); // timestamp number
console.log(equals(date1, date2)); // true (deep equality)
// Type checking
console.log(isArray([1, 2, 3])); // true
console.log(isVanillaObject({ a: 1 })); // true
console.log(isVanillaObject(new Date())); // false
// Custom comparison in operations
const customSift = createQueryTester(
{ created: new Date("2023-01-01") },
{
operations: { $eq },
compare: (a, b) => {
// Custom date comparison ignoring time
if (a instanceof Date && b instanceof Date) {
return a.toDateString() === b.toDateString();
}
return equals(a, b);
}
}
);Framework for developing custom query operations.
/**
* Base abstract class for all operations
*/
abstract class BaseOperation<TParams, TItem = any> implements Operation<TItem> {
readonly keep: boolean;
readonly done: boolean;
abstract readonly propop: boolean;
constructor(
readonly params: TParams,
readonly ownerQuery: any,
readonly options: Options,
readonly name?: string
);
protected init(): void;
reset(): void;
abstract next(
item: any,
key: Key,
parent: any,
root: boolean,
leaf?: boolean
): void;
}
/**
* Equals operation class for value matching
*/
class EqualsOperation<TParam> extends BaseOperation<TParam> {
readonly propop = true;
constructor(params: TParam, ownerQuery: any, options: Options);
next(item: any, key: Key, parent: any): void;
}
/**
* Query operation class for complex queries
*/
class QueryOperation<TItem> implements Operation<TItem> {
readonly propop = true;
readonly keep: boolean;
readonly done: boolean;
next(item: TItem, key: Key, parent: any, root: boolean): void;
reset(): void;
}Usage Examples:
import { BaseOperation, createQueryTester, Options, Query } from "sift";
// Custom operation example
class $startsWith extends BaseOperation<string> {
readonly propop = true;
private testValue: string;
init() {
this.testValue = this.params.toLowerCase();
}
next(item: any) {
if (typeof item === "string") {
if (item.toLowerCase().startsWith(this.testValue)) {
this.done = true;
this.keep = true;
}
}
}
}
// Operation creator function
const createStartsWithOp = (
params: string,
ownerQuery: Query<any>,
options: Options,
name: string
) => new $startsWith(params, ownerQuery, options, name);
// Use custom operation
const customFilter = createQueryTester(
{ name: { $startsWith: "Al" } },
{ operations: { $startsWith: createStartsWithOp } }
);
const users = [
{ name: "Alice" },
{ name: "Bob" },
{ name: "Alexander" }
];
const filtered = users.filter(customFilter);
// [{ name: "Alice" }, { name: "Alexander" }]Sift operations can throw errors in specific circumstances:
Error if params is not an objectError if params is an empty arrayError if nested operations are used in $in/$nin valuesError if string type alias doesn't existError in CSP mode when using string expressionsError for unknown $ operatorsError Handling Examples:
import sift from "sift";
try {
// This will throw - empty array not allowed
const filter = sift({ $and: [] });
} catch (error) {
console.log(error.message); // "$and/$or/$nor must be a nonempty array"
}
try {
// This will throw - malformed $elemMatch
const filter = sift({ items: { $elemMatch: "invalid" } });
} catch (error) {
console.log(error.message); // "Malformed query. $elemMatch must by an object."
}
try {
// This will throw - unknown type alias
const filter = sift({ field: { $type: "invalidtype" } });
} catch (error) {
console.log(error.message); // "Type alias does not exist"
}Install with Tessl CLI
npx tessl i tessl/npm-sift