package org.apache.lucene.search.spans; /** * Copyright 2004 The Apache Software Foundation * * Licensed 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. * * Acknowledgements: * * A significant amount of new and/or modified code in this module * was made possible by a grant from the Andrew W. Mellon Foundation, * as part of the Melvyl Recommender Project. */ import java.io.IOException; import java.util.Collection; import java.util.List; import java.util.ArrayList; import org.apache.lucene.index.IndexReader; import org.apache.lucene.search.Query; import org.apache.lucene.search.Searcher; /** Matches spans which are near one another. Unlike a standard 'near' query, * all of the sub-queries need not be present. One can specify <i>slop</i>, the * maximum edit distance for those that are present in a given document. * If enabled, in-order matches are scored higher than out-of-order. */ public class SpanOrNearQuery extends SpanQuery { private SpanQuery[] clauses; private int slop; private boolean penalizeOutOfOrder; private String field; /** Construct a SpanOrNearQuery. Matches spans matching a span from each * clause if present, with up to <code>slop</code> total edit distance * between them. * * When <code>penalizeOutOfOrder</code> is true, out of order clauses are * scored lower than in-order clauses. */ public SpanOrNearQuery(SpanQuery[] clauses, int slop, boolean penalizeOutOfOrder) { // Verify that all clauses are for the same field. this.clauses = clauses; for (int i = 0; i < clauses.length; i++) { SpanQuery clause = clauses[i]; if (i == 0) field = clause.getField(); else if (!clause.getField().equals(field)) throw new IllegalArgumentException("Clauses must have same field."); } this.slop = slop; this.penalizeOutOfOrder = penalizeOutOfOrder; } /** Return the clauses whose spans are matched. */ public SpanQuery[] getClauses() { return clauses; } /** Return the maximum number of intervening unmatched positions permitted.*/ public int getSlop() { return slop; } /** Return true if matches are penalized for being out of order. */ public boolean penalizeOutOfOrder() { return penalizeOutOfOrder; } /** Set the maximum edit distance permitted.*/ public void setSlop(int slop) { this.slop = slop; } public String getField() { return field; } public Collection getTerms() { Collection terms = new ArrayList(); for (int i = 0; i < clauses.length; i++) terms.addAll(clauses[i].getTerms()); return terms; } public Query[] getSubQueries() { return clauses; } public Query rewrite(IndexReader reader) throws IOException { List newClauses = new ArrayList(clauses.length); boolean anyChanged = false; for (int i = 0; i < clauses.length; i++) { SpanQuery rewrittenClause = (SpanQuery)clauses[i].rewrite(reader); newClauses.add(rewrittenClause); if (clauses[i] != rewrittenClause) anyChanged = true; } if (!anyChanged) return this; SpanOrNearQuery clone = (SpanOrNearQuery)this.clone(); clone.clauses = (SpanQuery[])newClauses.toArray( new SpanQuery[newClauses.size()]); return clone; } public String toString(String field) { StringBuffer buffer = new StringBuffer(); buffer.append("spanOrNear(["); for (int i = 0; i < clauses.length; i++) { buffer.append(clauses[i].toString(field)); if (i < clauses.length - 1) buffer.append(", "); } buffer.append("], "); buffer.append(slop); buffer.append(", "); buffer.append(penalizeOutOfOrder); buffer.append(")"); if (getBoost() != 1.0f) buffer.append("^" + getBoost()); return buffer.toString(); } public Spans getSpans(final IndexReader reader, final Searcher searcher) throws IOException { if (clauses.length == 1) // optimize 1-clause case return clauses[0].getSpans(reader, searcher); return new OrNearSpans(this, reader, searcher); } }