package org.apache.lucene.queryparser.spans;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.util.ArrayList;
import java.util.List;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.spans.SpanBoostQuery;
import org.apache.lucene.search.spans.SpanMultiTermQueryWrapper;
import org.apache.lucene.search.spans.SpanQuery;
import org.apache.lucene.search.spans.SpanTermQuery;
abstract class AbstractSpanQueryParser extends SpanQueryParserBase {
private final String defaultField;
/**
*
* @param field default field
* @param analyzer full term analyzer
* @param multiTermAnalyzer multiterm analyzer
*/
AbstractSpanQueryParser(String field, Analyzer analyzer, Analyzer multiTermAnalyzer) {
super(analyzer, multiTermAnalyzer);
this.defaultField = field;
}
/**
*
* @param s string to parse
* @return query
* @throws ParseException if an exception was found during parsing
*/
abstract public Query parse(String s) throws ParseException;
/**
* Recursively called to parse a span query
* <p>
* This assumes that there are no FIELD tokens, no BOOLEAN operators,
* no MatchAllDocsQueries and that {@link #getAnalyzer(String)}
* will return a non-null value.
* @param tokens list of tokens
* @param field default field
* @param parentClause the parent clause
* @return a SpanQuery
* @throws ParseException if a problem was found during parsing
*/
SpanQuery _parsePureSpanClause(final List<SQPToken> tokens,
String field, SQPClause parentClause)
throws ParseException {
int start = parentClause.getTokenOffsetStart();
int end = parentClause.getTokenOffsetEnd();
//test if special handling needed for spannear with one component?
if (end-start == 1) {
if (parentClause instanceof SQPNearClause) {
SQPNearClause nc = (SQPNearClause)parentClause;
SQPToken t = tokens.get(start);
if (t instanceof SQPTerm) {
SpanQuery ret = trySpecialHandlingForSpanNearWithOneComponent(field, (SQPTerm)t, nc);
if (ret != null) {
ret = addBoostOrPositionRangeIfExists(ret, parentClause);
return ret;
}
}
}
}
List<SpanQuery> queries = new ArrayList<>();
int i = start;
while (i < end) {
SQPToken t = tokens.get(i);
SpanQuery q;
if (t instanceof SQPClause) {
SQPClause c = (SQPClause)t;
q = _parsePureSpanClause(tokens, field, c);
i = c.getTokenOffsetEnd();
} else if (t instanceof SQPTerminal) {
q = buildSpanTerminal(field, (SQPTerminal)t);
i++;
} else {
throw new ParseException("Can't process field, boolean operators or a match all docs query in a pure span.");
}
queries.add(q);
}
SpanQuery ret = buildSpanQueryClause(queries, parentClause);
ret = addBoostOrPositionRangeIfExists(ret, parentClause);
return ret;
}
private SpanQuery trySpecialHandlingForSpanNearWithOneComponent(String field,
SQPTerm token, SQPNearClause clause)
throws ParseException {
int slop = (clause.getSlop() == null) ? getPhraseSlop() : clause.getSlop();
boolean order = true;
if (clause.getInOrder() != null) {
order = clause.getInOrder();
}
return (SpanQuery)specialHandlingForSpanNearWithOneComponent(field,
token.getString(), slop, order);
}
private SpanQuery buildSpanQueryClause(List<SpanQuery> queries, SQPClause clause)
throws ParseException {
//queries can be null
//queries can contain null elements
if (queries == null) {
return getEmptySpanQuery();
}
SpanQuery q;
if (clause instanceof SQPOrClause) {
q = buildSpanOrQuery(queries);
} else if (clause instanceof SQPNearClause) {
int slop = ((SQPNearClause)clause).getSlop() == null ? getPhraseSlop() :
((SQPNearClause)clause).getSlop();
Boolean inOrder = ((SQPNearClause)clause).getInOrder();
boolean order;
if (inOrder == null) {
order = slop <= 0;
} else {
order = inOrder.booleanValue();
}
q = buildSpanNearQuery(queries,
slop, order);
} else if (clause instanceof SQPNotNearClause) {
q = buildSpanNotNearQuery(queries,
((SQPNotNearClause)clause).getNotPre(),
((SQPNotNearClause)clause).getNotPost());
} else {
//throw early and loudly. This should never happen.
throw new IllegalArgumentException("clause not recognized: "+clause.getClass());
}
if (clause.getBoost() != null) {
q = new SpanBoostQuery(q, clause.getBoost());
}
//now update boost if clause only had one child
if (clause.getBoost() != null && (
q instanceof SpanTermQuery ||
q instanceof SpanMultiTermQueryWrapper)) {
q = new SpanBoostQuery(q, clause.getBoost());
}
return q;
}
/**
*
* @return default field
*/
public String getField() {
return defaultField;
}
}