@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
JavaScript
/**
* 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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