/*
 * 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.
 */
package org.apache.lucene.queryparser.surround.query;
/*
SpanNearClauseFactory:

Operations:

- create for a field name and an indexreader.

- add a weighted Term
  this should add a corresponding SpanTermQuery, or
  increase the weight of an existing one.
  
- add a weighted subquery SpanNearQuery 

- create a clause for SpanNearQuery from the things added above.
  For this, create an array of SpanQuery's from the added ones.
  The clause normally is a SpanOrQuery over the added subquery SpanNearQuery
  the SpanTermQuery's for the added Term's
*/

/* When  it is necessary to suppress double subqueries as much as possible:
   hashCode() and equals() on unweighted SpanQuery are needed (possibly via getTerms(),
   the terms are individually hashable).
   Idem SpanNearQuery: hash on the subqueries and the slop.
   Evt. merge SpanNearQuery's by adding the weights of the corresponding subqueries.
 */
 
/* To be determined:
   Are SpanQuery weights handled correctly during search by Lucene?
   Should the resulting SpanOrQuery be sorted?
   Could other SpanQueries be added for use in this factory:
   - SpanOrQuery: in principle yes, but it only has access to its terms
                  via getTerms(); are the corresponding weights available?
   - SpanFirstQuery: treat similar to subquery SpanNearQuery. (ok?)
   - SpanNotQuery: treat similar to subquery SpanNearQuery. (ok?)
 */

import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;

import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.spans.SpanBoostQuery;
import org.apache.lucene.search.spans.SpanOrQuery;
import org.apache.lucene.search.spans.SpanQuery;
import org.apache.lucene.search.spans.SpanTermQuery;


Factory for SpanOrQuery
/** * Factory for {@link SpanOrQuery} */
public class SpanNearClauseFactory { // FIXME: rename to SpanClauseFactory public SpanNearClauseFactory(IndexReader reader, String fieldName, BasicQueryFactory qf) { this.reader = reader; this.fieldName = fieldName; this.weightBySpanQuery = new HashMap<>(); this.qf = qf; } private IndexReader reader; private String fieldName; private HashMap<SpanQuery, Float> weightBySpanQuery; private BasicQueryFactory qf; public IndexReader getIndexReader() {return reader;} public String getFieldName() {return fieldName;} public BasicQueryFactory getBasicQueryFactory() {return qf;} public int size() {return weightBySpanQuery.size();} public void clear() {weightBySpanQuery.clear();} protected void addSpanQueryWeighted(SpanQuery sq, float weight) { Float w = weightBySpanQuery.get(sq); if (w != null) w = Float.valueOf(w.floatValue() + weight); else w = Float.valueOf(weight); weightBySpanQuery.put(sq, w); } public void addTermWeighted(Term t, float weight) throws IOException { SpanTermQuery stq = qf.newSpanTermQuery(t); /* CHECKME: wrap in Hashable...? */ addSpanQueryWeighted(stq, weight); } public void addSpanQuery(Query q) { if (q.getClass() == MatchNoDocsQuery.class) return; if (! (q instanceof SpanQuery)) throw new AssertionError("Expected SpanQuery: " + q.toString(getFieldName())); float boost = 1f; if (q instanceof SpanBoostQuery) { SpanBoostQuery bq = (SpanBoostQuery) q; boost = bq.getBoost(); q = bq.getQuery(); } addSpanQueryWeighted((SpanQuery)q, boost); } public SpanQuery makeSpanClause() { SpanQuery [] spanQueries = new SpanQuery[size()]; Iterator<SpanQuery> sqi = weightBySpanQuery.keySet().iterator(); int i = 0; while (sqi.hasNext()) { SpanQuery sq = sqi.next(); float boost = weightBySpanQuery.get(sq); if (boost != 1f) { sq = new SpanBoostQuery(sq, boost); } spanQueries[i++] = sq; } if (spanQueries.length == 1) return spanQueries[0]; else return new SpanOrQuery(spanQueries); } }