adonis-odm
Version:
A comprehensive MongoDB ODM for AdonisJS with Lucid-style API, type-safe relationships, embedded documents, and transaction support
251 lines (250 loc) • 7.32 kB
JavaScript
/**
* Where Conditions Builder
*
* Handles all where condition logic for the ModelQueryBuilder
*/
export class WhereConditionsBuilder {
filters = {};
orConditions = [];
/**
* Get the current filters
*/
getFilters() {
return this.filters;
}
/**
* Get the current OR conditions
*/
getOrConditions() {
return this.orConditions;
}
/**
* Set filters (used for cloning)
*/
setFilters(filters) {
this.filters = filters;
}
/**
* Set OR conditions (used for cloning)
*/
setOrConditions(orConditions) {
this.orConditions = orConditions;
}
/**
* Get the final MongoDB filter object
*/
getFinalFilters() {
if (this.orConditions.length === 0) {
return this.filters;
}
const filterEntries = Object.entries(this.filters);
if (filterEntries.length === 0) {
return { $or: this.orConditions };
}
const baseFilters = {};
let lastAndField = null;
let lastAndCondition = null;
for (let i = 0; i < filterEntries.length; i++) {
const [field, condition] = filterEntries[i];
if (i === filterEntries.length - 1) {
lastAndField = field;
lastAndCondition = condition;
}
else {
baseFilters[field] = condition;
}
}
const orArray = [];
if (lastAndField && lastAndCondition) {
orArray.push({ [lastAndField]: lastAndCondition });
}
orArray.push(...this.orConditions);
if (Object.keys(baseFilters).length > 0) {
return {
$and: [baseFilters, { $or: orArray }],
};
}
else {
return { $or: orArray };
}
}
/**
* Map query operator to MongoDB operator
*/
mapOperatorToMongo(operator) {
const operatorMap = {
'eq': '$eq',
'ne': '$ne',
'gt': '$gt',
'gte': '$gte',
'lt': '$lt',
'lte': '$lte',
'in': '$in',
'nin': '$nin',
'exists': '$exists',
'regex': '$regex',
'like': '$regex',
'=': '$eq',
'!=': '$ne',
'>': '$gt',
'>=': '$gte',
'<': '$lt',
'<=': '$lte',
};
return operatorMap[operator] || '$eq';
}
/**
* Serialize value for MongoDB
*/
serializeValue(value, _field) {
if (value === null || value === undefined) {
return value;
}
if (value instanceof Date) {
return value;
}
if (typeof value === 'string' && value.match(/^[0-9a-fA-F]{24}$/)) {
return value;
}
return value;
}
where(field, operatorOrValue, value) {
if (value === undefined) {
this.filters = {
...this.filters,
[field]: this.serializeValue(operatorOrValue, field),
};
}
else {
const operator = operatorOrValue;
const mongoOperator = this.mapOperatorToMongo(operator);
let serializedValue = this.serializeValue(value, field);
if (operator === 'like') {
const pattern = String(value).replace(/%/g, '.*');
serializedValue = new RegExp(pattern, 'i');
}
const existingFilter = this.filters[field];
if (existingFilter && typeof existingFilter === 'object' && existingFilter !== null) {
this.filters = {
...this.filters,
[field]: { ...existingFilter, [mongoOperator]: serializedValue },
};
}
else {
this.filters = {
...this.filters,
[field]: { [mongoOperator]: serializedValue },
};
}
}
return this;
}
whereNot(field, operatorOrValue, value) {
if (value === undefined) {
this.filters = {
...this.filters,
[field]: { $ne: this.serializeValue(operatorOrValue, field) },
};
}
else {
const operator = operatorOrValue;
const mongoOperator = this.mapOperatorToMongo(operator);
let serializedValue = this.serializeValue(value, field);
if (operator === 'like') {
const pattern = String(value).replace(/%/g, '.*');
serializedValue = { $not: new RegExp(pattern, 'i') };
}
else {
serializedValue = { $not: { [mongoOperator]: serializedValue } };
}
this.filters = {
...this.filters,
[field]: serializedValue,
};
}
return this;
}
orWhere(field, operatorOrValue, value) {
const condition = {};
if (value === undefined) {
condition[field] = this.serializeValue(operatorOrValue, field);
}
else {
const operator = operatorOrValue;
const mongoOperator = this.mapOperatorToMongo(operator);
let serializedValue = this.serializeValue(value, field);
if (operator === 'like') {
const pattern = String(value).replace(/%/g, '.*');
serializedValue = new RegExp(pattern, 'i');
}
condition[field] = { [mongoOperator]: serializedValue };
}
this.orConditions.push(condition);
return this;
}
/**
* Where in condition
*/
whereIn(field, values) {
const serializedValues = values.map((value) => this.serializeValue(value, field));
this.filters = {
...this.filters,
[field]: { $in: serializedValues },
};
return this;
}
/**
* Where not in condition
*/
whereNotIn(field, values) {
const serializedValues = values.map((value) => this.serializeValue(value, field));
this.filters = {
...this.filters,
[field]: { $nin: serializedValues },
};
return this;
}
/**
* Where between condition
*/
whereBetween(field, range) {
const [min, max] = range;
this.filters = {
...this.filters,
[field]: {
$gte: this.serializeValue(min, field),
$lte: this.serializeValue(max, field),
},
};
return this;
}
/**
* Where null condition
*/
whereNull(field) {
this.filters = {
...this.filters,
[field]: null,
};
return this;
}
/**
* Where not null condition
*/
whereNotNull(field) {
this.filters = {
...this.filters,
[field]: { $ne: null },
};
return this;
}
/**
* Clone the where conditions builder
*/
clone() {
const cloned = new WhereConditionsBuilder();
cloned.setFilters({ ...this.filters });
cloned.setOrConditions([...this.orConditions]);
return cloned;
}
}