/* * 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. */ using System; using System.Linq; using Lucene.Net.Index; using Lucene.Net.Support; using IndexReader = Lucene.Net.Index.IndexReader; using ToStringUtils = Lucene.Net.Util.ToStringUtils; using Query = Lucene.Net.Search.Query; namespace Lucene.Net.Search.Spans { /// Matches spans which are near one another. One can specify slop, the /// maximum number of intervening unmatched positions, as well as whether /// matches are required to be in-order. /// [Serializable] public class SpanNearQuery : SpanQuery, System.ICloneable { protected internal System.Collections.Generic.IList clauses; protected internal int internalSlop; protected internal bool inOrder; protected internal System.String internalField; private readonly bool collectPayloads; /// Construct a SpanNearQuery. Matches spans matching a span from each /// clause, with up to slop total unmatched positions between /// them. * When inOrder is true, the spans from each clause /// must be * ordered as in clauses. /// public SpanNearQuery(SpanQuery[] clauses, int slop, bool inOrder):this(clauses, slop, inOrder, true) { } public SpanNearQuery(SpanQuery[] clauses, int slop, bool inOrder, bool collectPayloads) { // copy clauses array into an ArrayList this.clauses = new System.Collections.Generic.List(clauses.Length); for (int i = 0; i < clauses.Length; i++) { SpanQuery clause = clauses[i]; if (i == 0) { // check field internalField = clause.Field; } else if (!clause.Field.Equals(internalField)) { throw new System.ArgumentException("Clauses must have same field."); } this.clauses.Add(clause); } this.collectPayloads = collectPayloads; this.internalSlop = slop; this.inOrder = inOrder; } /// Return the clauses whose spans are matched. public virtual SpanQuery[] GetClauses() { // Return a copy return clauses.ToArray(); } /// Return the maximum number of intervening unmatched positions permitted. public virtual int Slop { get { return internalSlop; } } /// Return true if matches are required to be in-order. public virtual bool IsInOrder { get { return inOrder; } } public override string Field { get { return internalField; } } public override void ExtractTerms(System.Collections.Generic.ISet terms) { foreach (SpanQuery clause in clauses) { clause.ExtractTerms(terms); } } public override System.String ToString(System.String field) { System.Text.StringBuilder buffer = new System.Text.StringBuilder(); buffer.Append("spanNear(["); System.Collections.Generic.IEnumerator i = clauses.GetEnumerator(); while (i.MoveNext()) { SpanQuery clause = i.Current; buffer.Append(clause.ToString(field)); buffer.Append(", "); } if (clauses.Count > 0) buffer.Length -= 2; buffer.Append("], "); buffer.Append(internalSlop); buffer.Append(", "); buffer.Append(inOrder); buffer.Append(")"); buffer.Append(ToStringUtils.Boost(Boost)); return buffer.ToString(); } public override Spans GetSpans(IndexReader reader) { if (clauses.Count == 0) // optimize 0-clause case return new SpanOrQuery(GetClauses()).GetSpans(reader); if (clauses.Count == 1) // optimize 1-clause case return clauses[0].GetSpans(reader); return inOrder?(Spans) new NearSpansOrdered(this, reader, collectPayloads):(Spans) new NearSpansUnordered(this, reader); } public override Query Rewrite(IndexReader reader) { SpanNearQuery clone = null; for (int i = 0; i < clauses.Count; i++) { SpanQuery c = clauses[i]; SpanQuery query = (SpanQuery) c.Rewrite(reader); if (query != c) { // clause rewrote: must clone if (clone == null) clone = (SpanNearQuery) this.Clone(); clone.clauses[i] = query; } } if (clone != null) { return clone; // some clauses rewrote } else { return this; // no clauses rewrote } } public override System.Object Clone() { int sz = clauses.Count; SpanQuery[] newClauses = new SpanQuery[sz]; for (int i = 0; i < sz; i++) { SpanQuery clause = clauses[i]; newClauses[i] = (SpanQuery) clause.Clone(); } SpanNearQuery spanNearQuery = new SpanNearQuery(newClauses, internalSlop, inOrder); spanNearQuery.Boost = Boost; return spanNearQuery; } /// Returns true iff o is equal to this. public override bool Equals(System.Object o) { if (this == o) return true; if (!(o is SpanNearQuery)) return false; SpanNearQuery spanNearQuery = (SpanNearQuery) o; if (inOrder != spanNearQuery.inOrder) return false; if (internalSlop != spanNearQuery.internalSlop) return false; if (clauses.Count != spanNearQuery.clauses.Count) return false; System.Collections.IEnumerator iter1 = clauses.GetEnumerator(); System.Collections.IEnumerator iter2 = spanNearQuery.clauses.GetEnumerator(); while (iter1.MoveNext() && iter2.MoveNext()) { SpanQuery item1 = (SpanQuery)iter1.Current; SpanQuery item2 = (SpanQuery)iter2.Current; if (!item1.Equals(item2)) return false; } return Boost == spanNearQuery.Boost; } public override int GetHashCode() { long result = 0; //mgarski .NET uses the arraylist's location, not contents to calculate the hash // need to start with result being the hash of the contents. foreach (SpanQuery sq in clauses) { result += sq.GetHashCode(); } // Mix bits before folding in things like boost, since it could cancel the // last element of clauses. This particular mix also serves to // differentiate SpanNearQuery hashcodes from others. result ^= ((result << 14) | (Number.URShift(result, 19))); // reversible result += System.Convert.ToInt32(Boost); result += internalSlop; result ^= (inOrder ? (long) 0x99AFD3BD : 0); return (int) result; } } }