UNPKG

@push.rocks/smartdata

Version:

An advanced library for NoSQL data organization and manipulation using TypeScript with support for MongoDB, data validation, collections, and custom data types.

623 lines 47.2 kB
/** * Lucene to MongoDB query adapter for SmartData */ import * as plugins from './plugins.js'; import { logger } from './logging.js'; /** * Lucene query parser */ export class LuceneParser { pos = 0; input = ''; tokens = []; constructor() { } /** * Parse a Lucene query string into an AST */ parse(query) { this.input = query.trim(); this.pos = 0; this.tokens = this.tokenize(this.input); return this.parseQuery(); } /** * Tokenize the input string into tokens */ tokenize(input) { const specialChars = /[()\[\]{}"~^:]/; const operators = /AND|OR|NOT|TO/; let tokens = []; let current = ''; let inQuote = false; for (let i = 0; i < input.length; i++) { const char = input[i]; // Handle quoted strings if (char === '"') { if (inQuote) { tokens.push(current + char); current = ''; inQuote = false; } else { if (current) tokens.push(current); current = char; inQuote = true; } continue; } if (inQuote) { current += char; continue; } // Handle whitespace if (char === ' ' || char === '\t' || char === '\n') { if (current) { tokens.push(current); current = ''; } continue; } // Handle special characters if (specialChars.test(char)) { if (current) { tokens.push(current); current = ''; } tokens.push(char); continue; } current += char; // Check if current is an operator if (operators.test(current) && (i + 1 === input.length || /\s/.test(input[i + 1]))) { tokens.push(current); current = ''; } } if (current) tokens.push(current); return tokens; } /** * Parse the main query expression */ parseQuery() { const left = this.parseBooleanOperand(); if (this.pos < this.tokens.length) { const token = this.tokens[this.pos]; if (token === 'AND' || token === 'OR') { this.pos++; const right = this.parseQuery(); return { type: token, left, right, }; } else if (token === 'NOT' || token === '-') { this.pos++; const right = this.parseQuery(); return { type: 'NOT', left, right, }; } } return left; } /** * Parse boolean operands (terms, phrases, fields, groups) */ parseBooleanOperand() { if (this.pos >= this.tokens.length) { throw new Error('Unexpected end of input'); } const token = this.tokens[this.pos]; // Handle grouping with parentheses if (token === '(') { this.pos++; const group = this.parseQuery(); if (this.pos < this.tokens.length && this.tokens[this.pos] === ')') { this.pos++; return { type: 'GROUP', value: group }; } else { throw new Error('Unclosed group'); } } // Handle fields (field:value) if (this.pos + 1 < this.tokens.length && this.tokens[this.pos + 1] === ':') { const field = token; this.pos += 2; // Skip field and colon if (this.pos < this.tokens.length) { const value = this.parseBooleanOperand(); return { type: 'FIELD', field, value }; } else { throw new Error('Expected value after field'); } } // Handle range queries if (token === '[' || token === '{') { return this.parseRange(); } // Handle phrases ("term term") if (token.startsWith('"') && token.endsWith('"')) { const phrase = token.slice(1, -1); this.pos++; // Check for proximity operator let proximity; if (this.pos < this.tokens.length && this.tokens[this.pos] === '~') { this.pos++; if (this.pos < this.tokens.length && /^\d+$/.test(this.tokens[this.pos])) { proximity = parseInt(this.tokens[this.pos], 10); this.pos++; } else { throw new Error('Expected number after proximity operator'); } } return { type: 'PHRASE', value: phrase, proximity }; } // Handle wildcards if (token.includes('*') || token.includes('?')) { this.pos++; return { type: 'WILDCARD', value: token }; } // Handle fuzzy searches if (this.pos + 1 < this.tokens.length && this.tokens[this.pos + 1] === '~') { const term = token; this.pos += 2; // Skip term and tilde let maxEdits = 2; // Default if (this.pos < this.tokens.length && /^\d+$/.test(this.tokens[this.pos])) { maxEdits = parseInt(this.tokens[this.pos], 10); this.pos++; } return { type: 'FUZZY', value: term, maxEdits }; } // Simple term this.pos++; return { type: 'TERM', value: token }; } /** * Parse range queries */ parseRange() { const includeLower = this.tokens[this.pos] === '['; const includeUpper = this.tokens[this.pos + 4] === ']'; // Ensure tokens for lower, TO, upper, and closing bracket exist if (this.pos + 4 >= this.tokens.length) { throw new Error('Invalid range query syntax'); } this.pos++; // Skip open bracket const lower = this.tokens[this.pos]; this.pos++; if (this.tokens[this.pos] !== 'TO') { throw new Error('Expected TO in range query'); } this.pos++; const upper = this.tokens[this.pos]; this.pos++; if (this.tokens[this.pos] !== (includeLower ? ']' : '}')) { throw new Error('Invalid range query closing bracket'); } this.pos++; // For simplicity, assuming the field is handled separately return { type: 'RANGE', field: '', // This will be filled by the field node lower, upper, includeLower, includeUpper, }; } } /** * Transformer for Lucene AST to MongoDB query * FIXED VERSION - proper MongoDB query structure */ export class LuceneToMongoTransformer { defaultFields; constructor(defaultFields = []) { this.defaultFields = defaultFields; } /** * Escape special characters for use in RegExp patterns */ escapeRegex(input) { return input.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } /** * Transform a Lucene AST node to a MongoDB query */ transform(node, searchFields) { switch (node.type) { case 'TERM': return this.transformTerm(node, searchFields); case 'PHRASE': return this.transformPhrase(node, searchFields); case 'FIELD': return this.transformField(node); case 'AND': return this.transformAnd(node); case 'OR': return this.transformOr(node); case 'NOT': return this.transformNot(node); case 'RANGE': return this.transformRange(node); case 'WILDCARD': return this.transformWildcard(node, searchFields); case 'FUZZY': return this.transformFuzzy(node, searchFields); case 'GROUP': return this.transform(node.value, searchFields); default: throw new Error(`Unsupported node type: ${node.type}`); } } /** * Transform a term to MongoDB query * FIXED: properly structured $or query for multiple fields */ transformTerm(node, searchFields) { // Build regex pattern, support wildcard (*) and fuzzy (?) if present const term = node.value; // Determine regex pattern: wildcard conversion or exact escape let pattern; if (term.includes('*') || term.includes('?')) { pattern = this.luceneWildcardToRegex(term); } else { pattern = this.escapeRegex(term); } // Search across provided fields or default fields const fields = searchFields && searchFields.length > 0 ? searchFields : this.defaultFields; const orConditions = fields.map((field) => ({ [field]: { $regex: pattern, $options: 'i' }, })); return { $or: orConditions }; } /** * Transform a phrase to MongoDB query * FIXED: properly structured $or query for multiple fields */ transformPhrase(node, searchFields) { // Use regex across provided fields or default fields, respecting word boundaries const parts = node.value.split(/\s+/).map((t) => this.escapeRegex(t)); const pattern = parts.join('\\s+'); const fields = searchFields && searchFields.length > 0 ? searchFields : this.defaultFields; const orConditions = fields.map((field) => ({ [field]: { $regex: pattern, $options: 'i' }, })); return { $or: orConditions }; } /** * Transform a field query to MongoDB query */ transformField(node) { // Handle special case for range queries on fields if (node.value.type === 'RANGE') { const rangeNode = node.value; rangeNode.field = node.field; return this.transformRange(rangeNode); } // Handle special case for wildcards on fields if (node.value.type === 'WILDCARD') { return { [node.field]: { $regex: this.luceneWildcardToRegex(node.value.value), $options: 'i', }, }; } // Handle special case for fuzzy searches on fields if (node.value.type === 'FUZZY') { return { [node.field]: { $regex: this.createFuzzyRegex(node.value.value), $options: 'i', }, }; } // Special case for exact term matches on fields (supporting wildcard characters) if (node.value.type === 'TERM') { const val = node.value.value; if (val.includes('*') || val.includes('?')) { const regex = this.luceneWildcardToRegex(val); return { [node.field]: { $regex: regex, $options: 'i' } }; } return { [node.field]: { $regex: val, $options: 'i' } }; } // Special case for phrase matches on fields if (node.value.type === 'PHRASE') { return { [node.field]: { $regex: `${node.value.value.replace(/\s+/g, '\\s+')}`, $options: 'i', }, }; } // For other cases, we'll transform the value and apply it to the field const transformedValue = this.transform(node.value); // If the transformed value uses $text, we need to adapt it for the field if (transformedValue.$text) { return { [node.field]: { $regex: transformedValue.$text.$search, $options: 'i' } }; } // Handle $or and $and cases if (transformedValue.$or || transformedValue.$and) { // This is a bit complex - we need to restructure the query to apply the field // For now, simplify by just using a regex on the field const term = this.extractTermFromBooleanQuery(transformedValue); if (term) { return { [node.field]: { $regex: term, $options: 'i' } }; } } return { [node.field]: transformedValue }; } /** * Extract a term from a boolean query (simplification) */ extractTermFromBooleanQuery(query) { if (query.$or && Array.isArray(query.$or) && query.$or.length > 0) { const firstClause = query.$or[0]; for (const field in firstClause) { if (firstClause[field].$regex) { return firstClause[field].$regex; } } } if (query.$and && Array.isArray(query.$and) && query.$and.length > 0) { const firstClause = query.$and[0]; for (const field in firstClause) { if (firstClause[field].$regex) { return firstClause[field].$regex; } } } return null; } /** * Transform AND operator to MongoDB query * FIXED: $and must be an array */ transformAnd(node) { return { $and: [this.transform(node.left), this.transform(node.right)] }; } /** * Transform OR operator to MongoDB query * FIXED: $or must be an array */ transformOr(node) { return { $or: [this.transform(node.left), this.transform(node.right)] }; } /** * Transform NOT operator to MongoDB query * FIXED: $and must be an array and $not usage */ transformNot(node) { const leftQuery = this.transform(node.left); const rightQuery = this.transform(node.right); // Create a query that includes left but excludes right if (rightQuery.$text) { // For text searches, we need a different approach // We'll use a negated regex instead const searchTerm = rightQuery.$text.$search.replace(/"/g, ''); // Determine the fields to apply the negation to const notConditions = []; for (const field in leftQuery) { if (field !== '$or' && field !== '$and') { notConditions.push({ [field]: { $not: { $regex: searchTerm, $options: 'i' } }, }); } } // If left query has $or or $and, we need to handle it differently if (leftQuery.$or) { return { $and: [leftQuery, { $nor: [{ $or: notConditions }] }], }; } else { // Simple case - just add $not to each field return { $and: [leftQuery, { $and: notConditions }], }; } } else { // For other queries, we can use $not directly // We need to handle different structures based on the rightQuery let notQuery = {}; if (rightQuery.$or) { notQuery = { $nor: rightQuery.$or }; } else if (rightQuery.$and) { // Convert $and to $nor notQuery = { $nor: rightQuery.$and }; } else { // Simple field condition for (const field in rightQuery) { notQuery[field] = { $not: rightQuery[field] }; } } return { $and: [leftQuery, notQuery] }; } } /** * Transform range query to MongoDB query */ transformRange(node) { const range = {}; if (node.lower !== '*') { range[node.includeLower ? '$gte' : '$gt'] = this.parseValue(node.lower); } if (node.upper !== '*') { range[node.includeUpper ? '$lte' : '$lt'] = this.parseValue(node.upper); } return { [node.field]: range }; } /** * Transform wildcard query to MongoDB query * FIXED: properly structured for multiple fields */ transformWildcard(node, searchFields) { // Convert Lucene wildcards to MongoDB regex const regex = this.luceneWildcardToRegex(node.value); // If specific fields are provided, search wildcard across those fields if (searchFields && searchFields.length > 0) { const orConditions = searchFields.map((field) => ({ [field]: { $regex: regex, $options: 'i' }, })); return { $or: orConditions }; } // By default, apply to the default field return { $regex: regex, $options: 'i' }; } /** * Transform fuzzy query to MongoDB query * FIXED: properly structured for multiple fields */ transformFuzzy(node, searchFields) { // MongoDB doesn't have built-in fuzzy search // This is a very basic approach using regex const regex = this.createFuzzyRegex(node.value); // If specific fields are provided, search fuzzy term across those fields if (searchFields && searchFields.length > 0) { const orConditions = searchFields.map((field) => ({ [field]: { $regex: regex, $options: 'i' }, })); return { $or: orConditions }; } // By default, apply to the default field return { $regex: regex, $options: 'i' }; } /** * Convert Lucene wildcards to MongoDB regex patterns */ luceneWildcardToRegex(wildcardPattern) { // Replace Lucene wildcards with regex equivalents // * => .* // ? => . // Also escape regex special chars return wildcardPattern .replace(/([.+^${}()|\\])/g, '\\$1') // Escape regex special chars .replace(/\*/g, '.*') .replace(/\?/g, '.'); } /** * Create a simplified fuzzy search regex */ createFuzzyRegex(term) { // For a very simple approach, we allow some characters to be optional let regex = ''; for (let i = 0; i < term.length; i++) { // Make every other character optional (simplified fuzzy) if (i % 2 === 1) { regex += term[i] + '?'; } else { regex += term[i]; } } return regex; } /** * Parse string values to appropriate types (numbers, dates, etc.) */ parseValue(value) { // Try to parse as number if (/^-?\d+$/.test(value)) { return parseInt(value, 10); } if (/^-?\d+\.\d+$/.test(value)) { return parseFloat(value); } // Try to parse as date (simplified) const date = new Date(value); if (!isNaN(date.getTime())) { return date; } // Default to string return value; } } /** * Main adapter class */ export class SmartdataLuceneAdapter { parser; transformer; defaultSearchFields = []; /** * @param defaultSearchFields - Optional array of field names to search across when no field is specified */ constructor(defaultSearchFields) { this.parser = new LuceneParser(); // Pass default searchable fields into transformer this.transformer = new LuceneToMongoTransformer(defaultSearchFields || []); if (defaultSearchFields) { this.defaultSearchFields = defaultSearchFields; } } /** * Convert a Lucene query string to a MongoDB query object * @param luceneQuery - The Lucene query string to convert * @param searchFields - Optional array of field names to search across (overrides defaultSearchFields) */ convert(luceneQuery, searchFields) { try { // For simple single-term queries (no field:, boolean, grouping), use simpler regex if (!luceneQuery.includes(':') && !luceneQuery.includes(' AND ') && !luceneQuery.includes(' OR ') && !luceneQuery.includes(' NOT ') && !luceneQuery.includes('(') && !luceneQuery.includes('[')) { const fieldsToSearch = searchFields || this.defaultSearchFields; if (fieldsToSearch && fieldsToSearch.length > 0) { // Handle wildcard characters in query let pattern = luceneQuery; if (luceneQuery.includes('*') || luceneQuery.includes('?')) { // Use transformer to convert wildcard pattern pattern = this.transformer.luceneWildcardToRegex(luceneQuery); } return { $or: fieldsToSearch.map((field) => ({ [field]: { $regex: pattern, $options: 'i' }, })), }; } } // For more complex queries, use the full parser // Parse the Lucene query into an AST const ast = this.parser.parse(luceneQuery); // Use provided searchFields, fall back to defaultSearchFields const fieldsToSearch = searchFields || this.defaultSearchFields; // Transform the AST to a MongoDB query return this.transformWithFields(ast, fieldsToSearch); } catch (error) { logger.log('error', `Failed to convert Lucene query "${luceneQuery}":`, error); throw new Error(`Failed to convert Lucene query: ${error}`); } } /** * Helper method to transform the AST with field information */ transformWithFields(node, searchFields) { // Special case for term nodes without a specific field if (node.type === 'TERM' || node.type === 'PHRASE' || node.type === 'WILDCARD' || node.type === 'FUZZY') { return this.transformer.transform(node, searchFields); } // For other node types, use the standard transformation return this.transformer.transform(node); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5sdWNlbmUuYWRhcHRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2NsYXNzZXMubHVjZW5lLmFkYXB0ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFDSCxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUN4QyxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sY0FBYyxDQUFDO0FBOEV0Qzs7R0FFRztBQUNILE1BQU0sT0FBTyxZQUFZO0lBQ2YsR0FBRyxHQUFXLENBQUMsQ0FBQztJQUNoQixLQUFLLEdBQVcsRUFBRSxDQUFDO0lBQ25CLE1BQU0sR0FBYSxFQUFFLENBQUM7SUFFOUIsZ0JBQWUsQ0FBQztJQUVoQjs7T0FFRztJQUNILEtBQUssQ0FBQyxLQUFhO1FBQ2pCLElBQUksQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDLElBQUksRUFBRSxDQUFDO1FBQzFCLElBQUksQ0FBQyxHQUFHLEdBQUcsQ0FBQyxDQUFDO1FBQ2IsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUV4QyxPQUFPLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztJQUMzQixDQUFDO0lBRUQ7O09BRUc7SUFDSyxRQUFRLENBQUMsS0FBYTtRQUM1QixNQUFNLFlBQVksR0FBRyxnQkFBZ0IsQ0FBQztRQUN0QyxNQUFNLFNBQVMsR0FBRyxlQUFlLENBQUM7UUFFbEMsSUFBSSxNQUFNLEdBQWEsRUFBRSxDQUFDO1FBQzFCLElBQUksT0FBTyxHQUFHLEVBQUUsQ0FBQztRQUNqQixJQUFJLE9BQU8sR0FBRyxLQUFLLENBQUM7UUFFcEIsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztZQUN0QyxNQUFNLElBQUksR0FBRyxLQUFLLENBQUMsQ0FBQyxDQUFDLENB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