@equantic/linq
Version:
eQuantic Linq for JavaScript
145 lines (144 loc) • 6.5 kB
JavaScript
import { Filtering } from './Filtering.js';
import { parseExpression, parseComposite } from './base.js';
/**
* Represents a composite filtering operation that combines multiple filtering conditions using logical operators.
* Supports And/Or operations for regular conditions and Any/All operations for collection-based filtering.
*
* @template TData - The type of data being filtered
* @template TColumn - The type of column/field path being filtered
*/
export class CompositeFiltering extends Filtering {
/** Array of filtering conditions to be combined using the composite operator */
values;
/** The composite operator used to combine the filtering conditions */
compositeOperator;
constructor(...args) {
// Handle different constructor overloads for the super class
let superColumn;
if (args.length === 3 && Array.isArray(args[2])) {
// Constructor(operator, column, values) - pass column to super
superColumn = args[1];
}
super(superColumn, null, 'eq'); // Super constructor needs column
this.compositeOperator = args.length > 0 ? args[0] : 'and';
this.values = [];
// Handle different constructor overloads
let valuesArray = [];
if (args.length === 2 && Array.isArray(args[1])) {
// Constructor(operator, values)
valuesArray = args[1];
}
else if (args.length === 3 && Array.isArray(args[2])) {
// Constructor(operator, column, values)
this.column = args[1];
valuesArray = args[2];
}
valuesArray.forEach((value) => {
if (typeof value === 'string') {
const composite = parseComposite(value, this.values);
if (!composite) {
const parsed = parseExpression(value);
const f = new Filtering(parsed.column, parsed.value, parsed.operator);
this.values.push(f);
}
}
else
this.values.push(value);
});
}
/**
* Adds a filtering condition to the composite filter.
* @param filtering - The filtering condition to add
*/
addValue(filtering) {
this.values.push(filtering);
}
/**
* Converts the composite filtering instance to its string representation.
* For collection operations (Any/All), formats as 'collection:operator(args)'.
* For regular operations (And/Or), formats as 'operator(args)'.
* @returns A string representation of the composite filtering operation
*/
toString() {
if (this.values.length == 0)
return super.toString();
const args = this.values.map((v) => v.toString());
// Handle collection operations (any/all) with explicit column
if (this.column && (this.compositeOperator === 'any' || this.compositeOperator === 'all')) {
return `${this.column}:${this.compositeOperator}(${args.join(',')})`;
}
// Handle collection operations (any/all) by extracting from args
if (this.compositeOperator === 'any' || this.compositeOperator === 'all') {
// For collection operations, we need to extract the collection property from the first value
if (args.length > 0 && args[0].includes('.')) {
const firstArg = args[0];
const dotIndex = firstArg.indexOf('.');
if (dotIndex > 0) {
const collectionProperty = firstArg.substring(0, dotIndex);
// Remove collection property prefix from all args
const innerArgs = args.map(arg => {
if (arg.startsWith(`${collectionProperty}.`)) {
return arg.substring(collectionProperty.length + 1);
}
return arg;
});
return `${collectionProperty}:${this.compositeOperator}(${innerArgs.join(',')})`;
}
}
}
// Handle regular composite operations (and/or)
return `${this.compositeOperator}(${args.join(',')})`;
}
/**
* Creates a new CompositeFiltering instance using the AND operator.
* All specified conditions must be true for the filter to match.
* @template TData - The type of data being filtered
* @param values - Array of string filter expressions or filtering instances to combine with AND
* @returns A new CompositeFiltering instance with AND operation
*/
static and(...values) {
return new CompositeFiltering('and', values);
}
/**
* Creates a new CompositeFiltering instance using the OR operator.
* At least one of the specified conditions must be true for the filter to match.
* @template TData - The type of data being filtered
* @param values - Array of string filter expressions or filtering instances to combine with OR
* @returns A new CompositeFiltering instance with OR operation
*/
static or(...values) {
return new CompositeFiltering('or', values);
}
/**
* Creates a new CompositeFiltering instance using the ANY operator for collections.
* Returns true if any item in the collection matches the specified conditions.
* @template TData - The type of data being filtered
* @param values - Array of string filter expressions or filtering instances for collection items
* @returns A new CompositeFiltering instance with ANY operation
*/
static any(...values) {
return new CompositeFiltering('any', values);
}
/**
* Creates a new CompositeFiltering instance using the ALL operator for collections.
* Returns true if all items in the collection match the specified conditions.
* @template TData - The type of data being filtered
* @param values - Array of string filter expressions or filtering instances for collection items
* @returns A new CompositeFiltering instance with ALL operation
*/
static all(...values) {
return new CompositeFiltering('all', values);
}
}
/**
* Alias for CompositeFiltering class for more concise usage.
* @example
* ```typescript
* // Using full class name
* const filter1 = CompositeFiltering.and('name:eq(John)', 'age:gt(25)');
*
* // Using alias
* const filter2 = CF.and('name:eq(John)', 'age:gt(25)');
* ```
*/
export const CF = CompositeFiltering;