0
votes

I am using Lucene.Net 3.0.3 and need to search in a specific field (ignoring any fields specified in the query). I am given a parsed Lucene Query so I am unable to generate a parsed query with my required field.

Is there any functionality in Lucene that allows me to loop through the terms of the query and change the field ? The given query would be a complex query where there would be spans, clauses etc.

Or perhaps there is some way of forcing Lucene to ignore the fields given in the parsed query and used a specified field only.

2
You could, conceivably, iterate through the generated queries and construct a new query from the terms obtained, but there would be a certain level of complexity to it. Sounds like this would be a lot easier to implement before the query had been passed into the parser, simply stripping off field specifications from the query string itself.femtoRgon

2 Answers

0
votes

If I understand you correctly, you want a query like

+body:hello +(+body:test -header:xy)

interpreted like

+body:hello +(body:test -body:xy)

.

For this, you can write a replacer method which traverses through the parsed tree and replaces the fields when reaching the objects containing the field name (e.g. Term, PhraseQuery, SpanNearQuery or whatever you have).

In Java you can change the field name of objects from those classes only via reflection. The other option would be to clone the parsed tree with other field names.

You can write a "replacer method" which takes a query object and handles it depending on the query type (i.e. for boolean query, phrase query, term query, ...).

  • In case of a boolean query you can iterate trough the clauses and delegate the sub-queries recursively to the replacer method.
  • In case of a term query you can replace the field in the term.
  • In case of a phrase query you can replace the field in the query.
  • and so on...

If you would choose the cloning way your methods would return a new object instead.

Edit:

Here is an example code for Java with 4 query types (boosts not considered):

public static Query forceField(Query q, String field) {
    if(q instanceof BooleanQuery) {
        BooleanQuery newQ = new BooleanQuery();
        for (BooleanClause clause : (BooleanQuery)q) {
            newQ.add(forceField(clause.getQuery(), field), clause.getOccur());
        }
        return newQ;
    }else if(q instanceof TermQuery) {
        return new TermQuery(new Term(field, ((TermQuery)q).getTerm().text()));
    }else if(q instanceof PhraseQuery) {
        PhraseQuery phraseQuery = new PhraseQuery();
        Term[] terms = ((PhraseQuery)q).getTerms();
        for (int i = 0; i < terms.length; i++) {
            phraseQuery.add(new Term(field, terms[i].text()), ((PhraseQuery)q).getPositions()[i]);
        }
        return phraseQuery;
    }else if(q instanceof WildcardQuery) {
        return new WildcardQuery(new Term(field, ((WildcardQuery)q).getTerm().text()));
    } else {
        throw new UnsupportedOperationException("Query type not known: " + q.getClass());
    }
}

Another option which is not so clean would be to use the toString of the whole query and replace all field in it and parse it again.

0
votes

In case anyone come across this in the future a cleaner solution will be to create a new query parser and re parsed the query with the field

public class FieldInjectQueryParser extends QueryParser {
    private final String field;

    public FieldInjectQueryParser(String field, Analyzer analyzer) {
        super(field, analyzer);
        this.field = field;
    }

    @Override
    protected Query newTermQuery(Term term) {
        return super.newTermQuery(createInjectTerm(term));
    }

    @Override
    protected Query newPrefixQuery(Term prefix) {
        return super.newPrefixQuery(createInjectTerm(prefix));
    }

    @Override
    protected Query newRegexpQuery(Term regexp) {
        return super.newRegexpQuery(createInjectTerm(regexp));
    }

    @Override
    protected Query newFuzzyQuery(Term term, float minimumSimilarity, int prefixLength) {
        return super.newFuzzyQuery(createInjectTerm(term), minimumSimilarity, prefixLength);
    }

    @Override
    protected Query newWildcardQuery(Term t) {
        return super.newWildcardQuery(createInjectTerm(t));
    }

    private Term createInjectTerm(Term term) {
        return new Term(this.field, term.text());
    }
}