UNPKG

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
/** * 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; } }