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.

620 lines 47 kB
/** * Lucene to MongoDB query adapter for SmartData */ import * as plugins from './plugins.js'; /** * Lucene query parser */ export class LuceneParser { constructor() { this.pos = 0; this.input = ''; this.tokens = []; } /** * 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 { 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 { /** * @param defaultSearchFields - Optional array of field names to search across when no field is specified */ constructor(defaultSearchFields) { this.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) { console.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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5sdWNlbmUuYWRhcHRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2NsYXNzZXMubHVjZW5lLmFkYXB0ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFDSCxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQThFeEM7O0dBRUc7QUFDSCxNQUFNLE9BQU8sWUFBWTtJQUt2QjtRQUpRLFFBQUcsR0FBVyxDQUFDLENBQUM7UUFDaEIsVUFBSyxHQUFXLEVBQUUsQ0FBQztRQUNuQixXQUFNLEdBQWEsRUFBRSxDQUFDO0lBRWYsQ0FBQztJQUVoQjs7T0FFRztJQUNILEtBQUssQ0FBQyxLQUFhO1FBQ2pCLElBQUksQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDLElBQUksRUFBRSxDQUFDO1FBQzFCLElBQUksQ0FBQyxHQUFHLEdBQUcsQ0FBQyxDQUFDO1FBQ2IsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUV4QyxPQUFPLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztJQUMzQixDQUFDO0lBRUQ7O09BRUc7SUFDSyxRQUFRLENBQUMsS0FBYTtRQUM1QixNQUFNLFlBQVksR0FBRyxnQkFBZ0IsQ0FBQztRQUN0QyxNQUFNLFNBQVMsR0FBRyxlQUFlLENBQUM7UUFFbEMsSUFBSSxNQUFNLEdBQWEsRUFBRSxDQUFDO1FBQzFCLElBQUksT0FBTyxHQUFHLEVBQUUsQ0FBQztRQUNqQixJQUFJLE9BQU8sR0FBRyxLQUFLLENBQUM7UUFFcEIsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztZQUN0QyxNQUFNLElBQUksR0FBRyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFFdEIsd0JBQXdCO1lBQ3hCLElBQUksSUFBSSxLQUFLLEdBQUcs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