fix(ci & formatting): Minor fixes: update CI workflow image and npmci package references, adjust package.json and readme URLs, and apply consistent code formatting.
This commit is contained in:
@@ -4,7 +4,17 @@
|
||||
import * as plugins from './plugins.js';
|
||||
|
||||
// Types
|
||||
type NodeType = 'TERM' | 'PHRASE' | 'FIELD' | 'AND' | 'OR' | 'NOT' | 'RANGE' | 'WILDCARD' | 'FUZZY' | 'GROUP';
|
||||
type NodeType =
|
||||
| 'TERM'
|
||||
| 'PHRASE'
|
||||
| 'FIELD'
|
||||
| 'AND'
|
||||
| 'OR'
|
||||
| 'NOT'
|
||||
| 'RANGE'
|
||||
| 'WILDCARD'
|
||||
| 'FUZZY'
|
||||
| 'GROUP';
|
||||
|
||||
interface QueryNode {
|
||||
type: NodeType;
|
||||
@@ -59,7 +69,15 @@ interface GroupNode extends QueryNode {
|
||||
value: AnyQueryNode;
|
||||
}
|
||||
|
||||
type AnyQueryNode = TermNode | PhraseNode | FieldNode | BooleanNode | RangeNode | WildcardNode | FuzzyNode | GroupNode;
|
||||
type AnyQueryNode =
|
||||
| TermNode
|
||||
| PhraseNode
|
||||
| FieldNode
|
||||
| BooleanNode
|
||||
| RangeNode
|
||||
| WildcardNode
|
||||
| FuzzyNode
|
||||
| GroupNode;
|
||||
|
||||
/**
|
||||
* Lucene query parser
|
||||
@@ -68,9 +86,9 @@ export class LuceneParser {
|
||||
private pos: number = 0;
|
||||
private input: string = '';
|
||||
private tokens: string[] = [];
|
||||
|
||||
|
||||
constructor() {}
|
||||
|
||||
|
||||
/**
|
||||
* Parse a Lucene query string into an AST
|
||||
*/
|
||||
@@ -78,24 +96,24 @@ export class LuceneParser {
|
||||
this.input = query.trim();
|
||||
this.pos = 0;
|
||||
this.tokens = this.tokenize(this.input);
|
||||
|
||||
|
||||
return this.parseQuery();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tokenize the input string into tokens
|
||||
*/
|
||||
private tokenize(input: string): string[] {
|
||||
const specialChars = /[()\[\]{}"~^:]/;
|
||||
const operators = /AND|OR|NOT|TO/;
|
||||
|
||||
|
||||
let tokens: string[] = [];
|
||||
let current = '';
|
||||
let inQuote = false;
|
||||
|
||||
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
const char = input[i];
|
||||
|
||||
|
||||
// Handle quoted strings
|
||||
if (char === '"') {
|
||||
if (inQuote) {
|
||||
@@ -109,12 +127,12 @@ export class LuceneParser {
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (inQuote) {
|
||||
current += char;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// Handle whitespace
|
||||
if (char === ' ' || char === '\t' || char === '\n') {
|
||||
if (current) {
|
||||
@@ -123,7 +141,7 @@ export class LuceneParser {
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// Handle special characters
|
||||
if (specialChars.test(char)) {
|
||||
if (current) {
|
||||
@@ -133,38 +151,37 @@ export class LuceneParser {
|
||||
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]))) {
|
||||
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
|
||||
*/
|
||||
private parseQuery(): AnyQueryNode {
|
||||
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 as 'AND' | 'OR',
|
||||
left,
|
||||
right
|
||||
right,
|
||||
};
|
||||
} else if (token === 'NOT' || token === '-') {
|
||||
this.pos++;
|
||||
@@ -172,14 +189,14 @@ export class LuceneParser {
|
||||
return {
|
||||
type: 'NOT',
|
||||
left,
|
||||
right
|
||||
right,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return left;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse boolean operands (terms, phrases, fields, groups)
|
||||
*/
|
||||
@@ -187,14 +204,14 @@ export class LuceneParser {
|
||||
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 } as GroupNode;
|
||||
@@ -202,12 +219,12 @@ export class LuceneParser {
|
||||
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 } as FieldNode;
|
||||
@@ -215,17 +232,17 @@ export class LuceneParser {
|
||||
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: number | undefined;
|
||||
if (this.pos < this.tokens.length && this.tokens[this.pos] === '~') {
|
||||
@@ -237,64 +254,64 @@ export class LuceneParser {
|
||||
throw new Error('Expected number after proximity operator');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return { type: 'PHRASE', value: phrase, proximity } as PhraseNode;
|
||||
}
|
||||
|
||||
|
||||
// Handle wildcards
|
||||
if (token.includes('*') || token.includes('?')) {
|
||||
this.pos++;
|
||||
return { type: 'WILDCARD', value: token } as WildcardNode;
|
||||
}
|
||||
|
||||
|
||||
// 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 } as FuzzyNode;
|
||||
}
|
||||
|
||||
|
||||
// Simple term
|
||||
this.pos++;
|
||||
return { type: 'TERM', value: token } as TermNode;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse range queries
|
||||
*/
|
||||
private parseRange(): RangeNode {
|
||||
const includeLower = this.tokens[this.pos] === '[';
|
||||
const includeUpper = this.tokens[this.pos + 4] === ']';
|
||||
|
||||
|
||||
this.pos++; // Skip open bracket
|
||||
|
||||
|
||||
if (this.pos + 4 >= this.tokens.length) {
|
||||
throw new Error('Invalid range query syntax');
|
||||
}
|
||||
|
||||
|
||||
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',
|
||||
@@ -302,7 +319,7 @@ export class LuceneParser {
|
||||
lower,
|
||||
upper,
|
||||
includeLower,
|
||||
includeUpper
|
||||
includeUpper,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -313,7 +330,7 @@ export class LuceneParser {
|
||||
*/
|
||||
export class LuceneToMongoTransformer {
|
||||
constructor() {}
|
||||
|
||||
|
||||
/**
|
||||
* Transform a Lucene AST node to a MongoDB query
|
||||
*/
|
||||
@@ -343,7 +360,7 @@ export class LuceneToMongoTransformer {
|
||||
throw new Error(`Unsupported node type: ${(node as any).type}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Transform a term to MongoDB query
|
||||
* FIXED: properly structured $or query for multiple fields
|
||||
@@ -352,17 +369,17 @@ export class LuceneToMongoTransformer {
|
||||
// If specific fields are provided, search across those fields
|
||||
if (searchFields && searchFields.length > 0) {
|
||||
// Create an $or query to search across multiple fields
|
||||
const orConditions = searchFields.map(field => ({
|
||||
[field]: { $regex: node.value, $options: 'i' }
|
||||
const orConditions = searchFields.map((field) => ({
|
||||
[field]: { $regex: node.value, $options: 'i' },
|
||||
}));
|
||||
|
||||
|
||||
return { $or: orConditions };
|
||||
}
|
||||
|
||||
|
||||
// Otherwise, use text search (requires a text index on desired fields)
|
||||
return { $text: { $search: node.value } };
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Transform a phrase to MongoDB query
|
||||
* FIXED: properly structured $or query for multiple fields
|
||||
@@ -370,17 +387,17 @@ export class LuceneToMongoTransformer {
|
||||
private transformPhrase(node: PhraseNode, searchFields?: string[]): any {
|
||||
// If specific fields are provided, search phrase across those fields
|
||||
if (searchFields && searchFields.length > 0) {
|
||||
const orConditions = searchFields.map(field => ({
|
||||
[field]: { $regex: `${node.value.replace(/\s+/g, '\\s+')}`, $options: 'i' }
|
||||
const orConditions = searchFields.map((field) => ({
|
||||
[field]: { $regex: `${node.value.replace(/\s+/g, '\\s+')}`, $options: 'i' },
|
||||
}));
|
||||
|
||||
|
||||
return { $or: orConditions };
|
||||
}
|
||||
|
||||
|
||||
// For phrases, we use a regex to ensure exact matches
|
||||
return { $text: { $search: `"${node.value}"` } };
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Transform a field query to MongoDB query
|
||||
*/
|
||||
@@ -391,50 +408,50 @@ export class LuceneToMongoTransformer {
|
||||
rangeNode.field = node.field;
|
||||
return this.transformRange(rangeNode);
|
||||
}
|
||||
|
||||
|
||||
// Handle special case for wildcards on fields
|
||||
if (node.value.type === 'WILDCARD') {
|
||||
return {
|
||||
[node.field]: {
|
||||
return {
|
||||
[node.field]: {
|
||||
$regex: this.luceneWildcardToRegex((node.value as WildcardNode).value),
|
||||
$options: 'i'
|
||||
}
|
||||
$options: 'i',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// Handle special case for fuzzy searches on fields
|
||||
if (node.value.type === 'FUZZY') {
|
||||
return {
|
||||
[node.field]: {
|
||||
return {
|
||||
[node.field]: {
|
||||
$regex: this.createFuzzyRegex((node.value as FuzzyNode).value),
|
||||
$options: 'i'
|
||||
}
|
||||
$options: 'i',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// Special case for exact term matches on fields
|
||||
if (node.value.type === 'TERM') {
|
||||
return { [node.field]: { $regex: (node.value as TermNode).value, $options: 'i' } };
|
||||
}
|
||||
|
||||
|
||||
// Special case for phrase matches on fields
|
||||
if (node.value.type === 'PHRASE') {
|
||||
return {
|
||||
[node.field]: {
|
||||
return {
|
||||
[node.field]: {
|
||||
$regex: `${(node.value as PhraseNode).value.replace(/\s+/g, '\\s+')}`,
|
||||
$options: 'i'
|
||||
}
|
||||
$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
|
||||
@@ -444,10 +461,10 @@ export class LuceneToMongoTransformer {
|
||||
return { [node.field]: { $regex: term, $options: 'i' } };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return { [node.field]: transformedValue };
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extract a term from a boolean query (simplification)
|
||||
*/
|
||||
@@ -460,7 +477,7 @@ export class LuceneToMongoTransformer {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (query.$and && Array.isArray(query.$and) && query.$and.length > 0) {
|
||||
const firstClause = query.$and[0];
|
||||
for (const field in firstClause) {
|
||||
@@ -469,10 +486,10 @@ export class LuceneToMongoTransformer {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Transform AND operator to MongoDB query
|
||||
* FIXED: $and must be an array
|
||||
@@ -480,7 +497,7 @@ export class LuceneToMongoTransformer {
|
||||
private transformAnd(node: BooleanNode): any {
|
||||
return { $and: [this.transform(node.left), this.transform(node.right)] };
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Transform OR operator to MongoDB query
|
||||
* FIXED: $or must be an array
|
||||
@@ -488,7 +505,7 @@ export class LuceneToMongoTransformer {
|
||||
private transformOr(node: BooleanNode): any {
|
||||
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
|
||||
@@ -496,43 +513,40 @@ export class LuceneToMongoTransformer {
|
||||
private transformNot(node: BooleanNode): any {
|
||||
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' } }
|
||||
[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 }] }
|
||||
]
|
||||
$and: [leftQuery, { $nor: [{ $or: notConditions }] }],
|
||||
};
|
||||
} else {
|
||||
// Simple case - just add $not to each field
|
||||
return {
|
||||
$and: [leftQuery, { $and: notConditions }]
|
||||
$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) {
|
||||
@@ -544,28 +558,28 @@ export class LuceneToMongoTransformer {
|
||||
notQuery[field] = { $not: rightQuery[field] };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return { $and: [leftQuery, notQuery] };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Transform range query to MongoDB query
|
||||
*/
|
||||
private transformRange(node: RangeNode): any {
|
||||
const range: any = {};
|
||||
|
||||
|
||||
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
|
||||
@@ -573,20 +587,20 @@ export class LuceneToMongoTransformer {
|
||||
private transformWildcard(node: WildcardNode, searchFields?: string[]): any {
|
||||
// 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' }
|
||||
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
|
||||
@@ -595,20 +609,20 @@ export class LuceneToMongoTransformer {
|
||||
// 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' }
|
||||
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
|
||||
*/
|
||||
@@ -622,7 +636,7 @@ export class LuceneToMongoTransformer {
|
||||
.replace(/\*/g, '.*')
|
||||
.replace(/\?/g, '.');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a simplified fuzzy search regex
|
||||
*/
|
||||
@@ -639,7 +653,7 @@ export class LuceneToMongoTransformer {
|
||||
}
|
||||
return regex;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse string values to appropriate types (numbers, dates, etc.)
|
||||
*/
|
||||
@@ -648,17 +662,17 @@ export class LuceneToMongoTransformer {
|
||||
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;
|
||||
}
|
||||
@@ -671,7 +685,7 @@ export class SmartdataLuceneAdapter {
|
||||
private parser: LuceneParser;
|
||||
private transformer: LuceneToMongoTransformer;
|
||||
private defaultSearchFields: string[] = [];
|
||||
|
||||
|
||||
/**
|
||||
* @param defaultSearchFields - Optional array of field names to search across when no field is specified
|
||||
*/
|
||||
@@ -682,7 +696,7 @@ export class SmartdataLuceneAdapter {
|
||||
this.defaultSearchFields = defaultSearchFields;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a Lucene query string to a MongoDB query object
|
||||
* @param luceneQuery - The Lucene query string to convert
|
||||
@@ -691,32 +705,33 @@ export class SmartdataLuceneAdapter {
|
||||
convert(luceneQuery: string, searchFields?: string[]): any {
|
||||
try {
|
||||
// For simple single term queries, create a simpler query structure
|
||||
if (!luceneQuery.includes(':') &&
|
||||
!luceneQuery.includes(' AND ') &&
|
||||
!luceneQuery.includes(' OR ') &&
|
||||
!luceneQuery.includes(' NOT ') &&
|
||||
!luceneQuery.includes('(') &&
|
||||
!luceneQuery.includes('[')) {
|
||||
|
||||
if (
|
||||
!luceneQuery.includes(':') &&
|
||||
!luceneQuery.includes(' AND ') &&
|
||||
!luceneQuery.includes(' OR ') &&
|
||||
!luceneQuery.includes(' NOT ') &&
|
||||
!luceneQuery.includes('(') &&
|
||||
!luceneQuery.includes('[')
|
||||
) {
|
||||
// This is a simple term, use a more direct approach
|
||||
const fieldsToSearch = searchFields || this.defaultSearchFields;
|
||||
|
||||
|
||||
if (fieldsToSearch && fieldsToSearch.length > 0) {
|
||||
return {
|
||||
$or: fieldsToSearch.map(field => ({
|
||||
[field]: { $regex: luceneQuery, $options: 'i' }
|
||||
}))
|
||||
$or: fieldsToSearch.map((field) => ({
|
||||
[field]: { $regex: luceneQuery, $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) {
|
||||
@@ -724,18 +739,22 @@ export class SmartdataLuceneAdapter {
|
||||
throw new Error(`Failed to convert Lucene query: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper method to transform the AST with field information
|
||||
*/
|
||||
private transformWithFields(node: AnyQueryNode, searchFields: string[]): any {
|
||||
// Special case for term nodes without a specific field
|
||||
if (node.type === 'TERM' || node.type === 'PHRASE' ||
|
||||
node.type === 'WILDCARD' || node.type === 'FUZZY') {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user