blob: b2b3022bf0b005c6b8234a6d65065deffecca389 [file] [log] [blame]
// Lucene version compatibility level 4.8.1
using System.Collections.Generic;
using Lucene.Net.Index;
using Lucene.Net.Search;
using Lucene.Net.Util;
namespace Lucene.Net.Join
{
/*
* 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.
*/
/// <summary>
/// A query that has an array of terms from a specific field. This query will match documents have one or more terms in
/// the specified field that match with the terms specified in the array.
///
/// @lucene.experimental
/// </summary>
internal class TermsQuery : MultiTermQuery
{
private readonly BytesRefHash _terms;
private readonly int[] _ords;
private readonly Query _fromQuery; // Used for equals() only
/// <summary>
///
/// </summary>
/// <param name="field">The field that should contain terms that are specified in the previous parameter.</param>
/// <param name="fromQuery"></param>
/// <param name="terms">The terms that matching documents should have. The terms must be sorted by natural order.</param>
internal TermsQuery(string field, Query fromQuery, BytesRefHash terms)
: base(field)
{
_fromQuery = fromQuery;
_terms = terms;
_ords = terms.Sort(BytesRef.UTF8SortedAsUnicodeComparer);
}
protected override TermsEnum GetTermsEnum(Terms terms, AttributeSource atts)
{
if (_terms.Count == 0)
{
return TermsEnum.EMPTY;
}
return new SeekingTermSetTermsEnum(terms.GetEnumerator(), _terms, _ords);
}
public override string ToString(string field)
{
return string.Format("TermsQuery{{field={0}}}", field);
}
public override bool Equals(object obj)
{
if (this == obj)
{
return true;
}
if (!base.Equals(obj))
{
return false;
}
if (GetType() != obj.GetType())
{
return false;
}
TermsQuery other = (TermsQuery)obj;
if (!_fromQuery.Equals(other._fromQuery))
{
return false;
}
return true;
}
public override int GetHashCode()
{
int prime = 31;
int result = base.GetHashCode();
result += prime * _fromQuery.GetHashCode();
return result;
}
private class SeekingTermSetTermsEnum : FilteredTermsEnum
{
private readonly BytesRefHash terms;
private readonly int[] ords;
private readonly int _lastElement;
private readonly BytesRef _lastTerm;
private readonly BytesRef _spare = new BytesRef();
private readonly IComparer<BytesRef> _comparer;
private BytesRef _seekTerm;
private int _upto;
internal SeekingTermSetTermsEnum(TermsEnum tenum, BytesRefHash terms, int[] ords)
: base(tenum)
{
this.terms = terms;
this.ords = ords;
_comparer = BytesRef.UTF8SortedAsUnicodeComparer;
_lastElement = terms.Count - 1;
_lastTerm = terms.Get(ords[_lastElement], new BytesRef());
_seekTerm = terms.Get(ords[_upto], _spare);
}
protected override BytesRef NextSeekTerm(BytesRef currentTerm)
{
BytesRef temp = _seekTerm;
_seekTerm = null;
return temp;
}
protected override AcceptStatus Accept(BytesRef term)
{
if (_comparer.Compare(term, _lastTerm) > 0)
{
return AcceptStatus.END;
}
BytesRef currentTerm = terms.Get(ords[_upto], _spare);
if (_comparer.Compare(term, currentTerm) == 0)
{
if (_upto == _lastElement)
{
return AcceptStatus.YES;
}
_seekTerm = terms.Get(ords[++_upto], _spare);
return AcceptStatus.YES_AND_SEEK;
}
if (_upto == _lastElement)
{
return AcceptStatus.NO;
} // Our current term doesn't match the the given term.
int cmp;
do // We maybe are behind the given term by more than one step. Keep incrementing till we're the same or higher.
{
if (_upto == _lastElement)
{
return AcceptStatus.NO;
}
// typically the terms dict is a superset of query's terms so it's unusual that we have to skip many of
// our terms so we don't do a binary search here
_seekTerm = terms.Get(ords[++_upto], _spare);
} while ((cmp = _comparer.Compare(_seekTerm, term)) < 0);
if (cmp == 0)
{
if (_upto == _lastElement)
{
return AcceptStatus.YES;
}
_seekTerm = terms.Get(ords[++_upto], _spare);
return AcceptStatus.YES_AND_SEEK;
}
return AcceptStatus.NO_AND_SEEK;
}
}
}
}