blob: a905260fa468c9af85d45a0f264eac6885590c77 [file] [log] [blame]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using JCG = J2N.Collections.Generic;
namespace Lucene.Net.Search.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.
*/
using AtomicReaderContext = Lucene.Net.Index.AtomicReaderContext;
using IBits = Lucene.Net.Util.IBits;
using IndexReader = Lucene.Net.Index.IndexReader;
using Term = Lucene.Net.Index.Term;
using TermContext = Lucene.Net.Index.TermContext;
using ToStringUtils = Lucene.Net.Util.ToStringUtils;
/// <summary>
/// Matches spans which are near one another. One can specify <i>slop</i>, the
/// maximum number of intervening unmatched positions, as well as whether
/// matches are required to be in-order.
/// </summary>
public class SpanNearQuery : SpanQuery
#if FEATURE_CLONEABLE
, System.ICloneable
#endif
{
protected readonly IList<SpanQuery> m_clauses;
protected int m_slop;
protected bool m_inOrder;
protected string m_field;
private bool collectPayloads;
/// <summary>
/// Construct a <see cref="SpanNearQuery"/>. Matches spans matching a span from each
/// clause, with up to <paramref name="slop"/> total unmatched positions between
/// them. * When <paramref name="inOrder"/> is <c>true</c>, the spans from each clause
/// must be * ordered as in <paramref name="clauses"/>. </summary>
/// <param name="clauses"> The clauses to find near each other </param>
/// <param name="slop"> The slop value </param>
/// <param name="inOrder"> <c>true</c> if order is important</param>
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.m_clauses = new List<SpanQuery>(clauses.Length);
for (int i = 0; i < clauses.Length; i++)
{
SpanQuery clause = clauses[i];
if (m_field == null) // check field
{
m_field = clause.Field;
}
else if (clause.Field != null && !clause.Field.Equals(m_field, StringComparison.Ordinal))
{
throw new System.ArgumentException("Clauses must have same field.");
}
this.m_clauses.Add(clause);
}
this.collectPayloads = collectPayloads;
this.m_slop = slop;
this.m_inOrder = inOrder;
}
/// <summary>
/// Return the clauses whose spans are matched. </summary>
public virtual SpanQuery[] GetClauses()
{
return m_clauses.ToArray();
}
/// <summary>
/// Return the maximum number of intervening unmatched positions permitted. </summary>
public virtual int Slop => m_slop;
/// <summary>
/// Return <c>true</c> if matches are required to be in-order. </summary>
public virtual bool IsInOrder => m_inOrder;
public override string Field => m_field;
public override void ExtractTerms(ISet<Term> terms)
{
foreach (SpanQuery clause in m_clauses)
{
clause.ExtractTerms(terms);
}
}
public override string ToString(string field)
{
StringBuilder buffer = new StringBuilder();
buffer.Append("SpanNear([");
bool isFirst = true;
foreach (SpanQuery clause in m_clauses)
{
if (!isFirst)
{
buffer.Append(", ");
}
buffer.Append(clause.ToString(field));
isFirst = false;
}
buffer.Append("], ");
buffer.Append(m_slop);
buffer.Append(", ");
buffer.Append(m_inOrder);
buffer.Append(")");
buffer.Append(ToStringUtils.Boost(Boost));
return buffer.ToString();
}
public override Spans GetSpans(AtomicReaderContext context, IBits acceptDocs, IDictionary<Term, TermContext> termContexts)
{
if (m_clauses.Count == 0) // optimize 0-clause case
{
return (new SpanOrQuery(GetClauses())).GetSpans(context, acceptDocs, termContexts);
}
if (m_clauses.Count == 1) // optimize 1-clause case
{
return m_clauses[0].GetSpans(context, acceptDocs, termContexts);
}
return m_inOrder ? (Spans)new NearSpansOrdered(this, context, acceptDocs, termContexts, collectPayloads) : (Spans)new NearSpansUnordered(this, context, acceptDocs, termContexts);
}
public override Query Rewrite(IndexReader reader)
{
SpanNearQuery clone = null;
for (int i = 0; i < m_clauses.Count; i++)
{
SpanQuery c = m_clauses[i];
SpanQuery query = (SpanQuery)c.Rewrite(reader);
if (query != c) // clause rewrote: must clone
{
if (clone == null)
{
clone = (SpanNearQuery)this.Clone();
}
clone.m_clauses[i] = query;
}
}
if (clone != null)
{
return clone; // some clauses rewrote
}
else
{
return this; // no clauses rewrote
}
}
public override object Clone()
{
int sz = m_clauses.Count;
SpanQuery[] newClauses = new SpanQuery[sz];
for (int i = 0; i < sz; i++)
{
newClauses[i] = (SpanQuery)m_clauses[i].Clone();
}
SpanNearQuery spanNearQuery = new SpanNearQuery(newClauses, m_slop, m_inOrder);
spanNearQuery.Boost = Boost;
return spanNearQuery;
}
/// <summary>
/// Returns true iff <code>o</code> is equal to this. </summary>
public override bool Equals(object o)
{
if (this == o)
{
return true;
}
if (!(o is SpanNearQuery spanNearQuery))
{
return false;
}
if (m_inOrder != spanNearQuery.m_inOrder)
{
return false;
}
if (m_slop != spanNearQuery.m_slop)
{
return false;
}
if (!JCG.ListEqualityComparer<SpanQuery>.Default.Equals(m_clauses, spanNearQuery.m_clauses))
{
return false;
}
return Boost == spanNearQuery.Boost;
}
public override int GetHashCode()
{
int result;
result = JCG.ListEqualityComparer<SpanQuery>.Default.GetHashCode(m_clauses);
// 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) | ((int)((uint)result >> 19)); // reversible
result += J2N.BitConversion.SingleToRawInt32Bits(Boost);
result += m_slop;
result ^= (m_inOrder ? unchecked((int)0x99AFD3BD) : 0);
return result;
}
}
}