You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucenenet.apache.org by sy...@apache.org on 2015/02/09 02:27:54 UTC
[4/6] lucenenet git commit: Moving the 3.0 spatial module to serve as
a basis to Lucene.Net.Spatial
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/c9f901b2/src/Lucene.Net.Spatial/Util/CachingDoubleValueSource.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Util/CachingDoubleValueSource.cs b/src/Lucene.Net.Spatial/Util/CachingDoubleValueSource.cs
new file mode 100644
index 0000000..ef7a174
--- /dev/null
+++ b/src/Lucene.Net.Spatial/Util/CachingDoubleValueSource.cs
@@ -0,0 +1,101 @@
+/*
+ * 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.Collections.Generic;
+using Lucene.Net.Index;
+using Lucene.Net.Search.Function;
+
+namespace Lucene.Net.Spatial.Util
+{
+ public class CachingDoubleValueSource : ValueSource
+ {
+ protected readonly ValueSource source;
+ protected readonly Dictionary<int, double> cache;
+
+ public CachingDoubleValueSource(ValueSource source)
+ {
+ this.source = source;
+ cache = new Dictionary<int, double>();
+ }
+
+ public class CachingDoubleDocValue : DocValues
+ {
+ private readonly int docBase;
+ private readonly DocValues values;
+ private readonly Dictionary<int, double> cache;
+
+ public CachingDoubleDocValue(int docBase, DocValues vals, Dictionary<int, double> cache)
+ {
+ this.docBase = docBase;
+ this.values = vals;
+ this.cache = cache;
+ }
+
+ public override double DoubleVal(int doc)
+ {
+ var key = docBase + doc;
+ double v;
+ if (!cache.TryGetValue(key, out v))
+ {
+ v = values.DoubleVal(doc);
+ cache[key] = v;
+ }
+ return v;
+ }
+
+ public override float FloatVal(int doc)
+ {
+ return (float)DoubleVal(doc);
+ }
+
+ public override string ToString(int doc)
+ {
+ return DoubleVal(doc) + string.Empty;
+ }
+ }
+
+ public override DocValues GetValues(IndexReader reader)
+ {
+ var @base = 0; //reader.DocBase;
+ var vals = source.GetValues(reader);
+ return new CachingDoubleDocValue(@base, vals, cache);
+
+ }
+
+ public override string Description()
+ {
+ return "Cached[" + source.Description() + "]";
+ }
+
+ public override bool Equals(object o)
+ {
+ if (this == o) return true;
+
+ var that = o as CachingDoubleValueSource;
+
+ if (that == null) return false;
+ if (source != null ? !source.Equals(that.source) : that.source != null) return false;
+
+ return true;
+ }
+
+ public override int GetHashCode()
+ {
+ return source != null ? source.GetHashCode() : 0;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/c9f901b2/src/Lucene.Net.Spatial/Util/CompatibilityExtensions.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Util/CompatibilityExtensions.cs b/src/Lucene.Net.Spatial/Util/CompatibilityExtensions.cs
new file mode 100644
index 0000000..26a1462
--- /dev/null
+++ b/src/Lucene.Net.Spatial/Util/CompatibilityExtensions.cs
@@ -0,0 +1,296 @@
+/*
+ * 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;
+#if NET35
+using Lucene.Net.Support.Compatibility;
+#else
+using System.Collections.Concurrent;
+#endif
+using System.Collections.Generic;
+using System.Diagnostics;
+using Lucene.Net.Analysis.Tokenattributes;
+using Lucene.Net.Index;
+using Lucene.Net.Search;
+
+namespace Lucene.Net.Spatial.Util
+{
+ public static class CompatibilityExtensions
+ {
+ public static void Append(this ITermAttribute termAtt, string str)
+ {
+ termAtt.SetTermBuffer(termAtt.Term + str); // TODO: Not optimal, but works
+ }
+
+ public static void Append(this ITermAttribute termAtt, char ch)
+ {
+ termAtt.SetTermBuffer(termAtt.Term + new string(new[] {ch})); // TODO: Not optimal, but works
+ }
+
+ private static readonly ConcurrentDictionary<Key<string, IndexReader>, IBits> _docsWithFieldCache =
+ new ConcurrentDictionary<Key<string, IndexReader>, IBits>();
+
+ internal static IBits GetDocsWithField(this FieldCache fc, IndexReader reader, String field)
+ {
+ return _docsWithFieldCache.GetOrAdd(new Key<string, IndexReader>(field, reader),
+ key =>
+ DocsWithFieldCacheEntry_CreateValue(key.Item2,
+ new Entry(key.Item1, null), false));
+ }
+
+ /// <summary> <p/>
+ /// EXPERT: Instructs the FieldCache to forcibly expunge all entries
+ /// from the underlying caches. This is intended only to be used for
+ /// test methods as a way to ensure a known base state of the Cache
+ /// (with out needing to rely on GC to free WeakReferences).
+ /// It should not be relied on for "Cache maintenance" in general
+ /// application code.
+ /// <p/>
+ /// <p/>
+ /// <b>EXPERIMENTAL API:</b> This API is considered extremely advanced
+ /// and experimental. It may be removed or altered w/o warning in future
+ /// releases
+ /// of Lucene.
+ /// <p/>
+ /// </summary>
+ public static void PurgeSpatialCaches(this FieldCache fc)
+ {
+ _docsWithFieldCache.Clear();
+ }
+
+ private static IBits DocsWithFieldCacheEntry_CreateValue(IndexReader reader, Entry entryKey,
+ bool setDocsWithField /* ignored */)
+ {
+ var field = entryKey.field;
+ FixedBitSet res = null;
+ var terms = new TermsEnumCompatibility(reader, field);
+ var maxDoc = reader.MaxDoc;
+
+ var term = terms.Next();
+ if (term != null)
+ {
+ int termsDocCount = terms.GetDocCount();
+ Debug.Assert(termsDocCount <= maxDoc);
+ if (termsDocCount == maxDoc)
+ {
+ // Fast case: all docs have this field:
+ return new MatchAllBits(maxDoc);
+ }
+
+ while (true)
+ {
+ if (res == null)
+ {
+ // lazy init
+ res = new FixedBitSet(maxDoc);
+ }
+
+ var termDocs = reader.TermDocs(term);
+ while (termDocs.Next())
+ {
+ res.Set(termDocs.Doc);
+ }
+
+ term = terms.Next();
+ if (term == null)
+ {
+ break;
+ }
+ }
+ }
+ if (res == null)
+ {
+ return new MatchNoBits(maxDoc);
+ }
+ int numSet = res.Cardinality();
+ if (numSet >= maxDoc)
+ {
+ // The cardinality of the BitSet is maxDoc if all documents have a value.
+ Debug.Assert(numSet == maxDoc);
+ return new MatchAllBits(maxDoc);
+ }
+ return res;
+ }
+
+ /* table of number of leading zeros in a byte */
+
+ public static readonly byte[] nlzTable =
+ {
+ 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3
+ , 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1
+ , 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ , 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+
+ /// <summary>
+ /// Returns the number of leading zero bits.
+ /// </summary>
+ /// <param name="x"></param>
+ /// <returns></returns>
+ public static int BitUtilNlz(long x)
+ {
+ int n = 0;
+ // do the first step as a long
+ var y = (int) ((ulong) x >> 32);
+ if (y == 0)
+ {
+ n += 32;
+ y = (int) (x);
+ }
+ if ((y & 0xFFFF0000) == 0)
+ {
+ n += 16;
+ y <<= 16;
+ }
+ if ((y & 0xFF000000) == 0)
+ {
+ n += 8;
+ y <<= 8;
+ }
+ return n + nlzTable[(uint) y >> 24];
+ /* implementation without table:
+ if ((y & 0xF0000000) == 0) { n+=4; y<<=4; }
+ if ((y & 0xC0000000) == 0) { n+=2; y<<=2; }
+ if ((y & 0x80000000) == 0) { n+=1; y<<=1; }
+ if ((y & 0x80000000) == 0) { n+=1;}
+ return n;
+ */
+ }
+ }
+
+ public static class Arrays
+ {
+ public static void Fill<T>(T[] array, int fromIndex, int toIndex, T value)
+ {
+ if (array == null)
+ {
+ throw new ArgumentNullException("array");
+ }
+ if (fromIndex < 0 || fromIndex > toIndex)
+ {
+ throw new ArgumentOutOfRangeException("fromIndex");
+ }
+ if (toIndex > array.Length)
+ {
+ throw new ArgumentOutOfRangeException("toIndex");
+ }
+ for (var i = fromIndex; i < toIndex; i++)
+ {
+ array[i] = value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Expert: Every composite-key in the internal cache is of this type.
+ /// </summary>
+ internal class Entry
+ {
+ internal readonly String field; // which Fieldable
+ internal readonly Object custom; // which custom comparator or parser
+
+ /* Creates one of these objects for a custom comparator/parser. */
+
+ public Entry(String field, Object custom)
+ {
+ this.field = field;
+ this.custom = custom;
+ }
+
+ /* Two of these are equal iff they reference the same field and type. */
+
+ public override bool Equals(Object o)
+ {
+ var other = o as Entry;
+ if (other != null)
+ {
+ if (other.field.Equals(field))
+ {
+ if (other.custom == null)
+ {
+ if (custom == null) return true;
+ }
+ else if (other.custom.Equals(custom))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /* Composes a hashcode based on the field and type. */
+
+ public override int GetHashCode()
+ {
+ return field.GetHashCode() ^ (custom == null ? 0 : custom.GetHashCode());
+ }
+ }
+
+ internal struct Key<T1, T2> : IEquatable<Key<T1, T2>>
+ {
+ public bool Equals(Key<T1, T2> other)
+ {
+ return EqualityComparer<T1>.Default.Equals(Item1, other.Item1) &&
+ EqualityComparer<T2>.Default.Equals(Item2, other.Item2);
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (ReferenceEquals(null, obj))
+ {
+ return false;
+ }
+ return obj is Key<T1, T2> && Equals((Key<T1, T2>) obj);
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ return (EqualityComparer<T1>.Default.GetHashCode(Item1)*397) ^
+ EqualityComparer<T2>.Default.GetHashCode(Item2);
+ }
+ }
+
+ public static bool operator ==(Key<T1, T2> left, Key<T1, T2> right)
+ {
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(Key<T1, T2> left, Key<T1, T2> right)
+ {
+ return !left.Equals(right);
+ }
+
+ public readonly T1 Item1;
+ public readonly T2 Item2;
+
+ public Key(T1 item1, T2 item2)
+ {
+ Item1 = item1;
+ Item2 = item2;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/c9f901b2/src/Lucene.Net.Spatial/Util/FixedBitSet.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Util/FixedBitSet.cs b/src/Lucene.Net.Spatial/Util/FixedBitSet.cs
new file mode 100644
index 0000000..bfc5c45
--- /dev/null
+++ b/src/Lucene.Net.Spatial/Util/FixedBitSet.cs
@@ -0,0 +1,454 @@
+/*
+ * 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.Collections;
+using Lucene.Net.Search;
+using Lucene.Net.Util;
+
+namespace Lucene.Net.Spatial.Util
+{
+ /* BitSet of fixed length (numBits), backed by accessible
+ * ({@link #getBits}) long[], accessed with an int index,
+ * implementing Bits and DocIdSet. Unlike {@link
+ * OpenBitSet} this bit set does not auto-expand, cannot
+ * handle long index, and does not have fastXX/XX variants
+ * (just X).
+ *
+ * @lucene.internal
+ **/
+ public class FixedBitSet : DocIdSet, IBits
+ {
+ private readonly BitArray bits;
+
+ /// <summary>
+ /// returns the number of 64 bit words it would take to hold numBits
+ /// </summary>
+ /// <param name="numBits"></param>
+ /// <returns></returns>
+ public static int bits2words(int numBits)
+ {
+ var numLong = (int)((uint)numBits >> 6);
+ if ((numBits & 63) != 0)
+ {
+ numLong++;
+ }
+ return numLong;
+ }
+
+ public FixedBitSet(int numBits)
+ {
+ bits = new BitArray(numBits);
+ }
+
+ /// <summary>
+ /// Makes full copy.
+ /// </summary>
+ /// <param name="other"></param>
+ public FixedBitSet(FixedBitSet other)
+ {
+ bits = new BitArray(other.bits);
+ }
+
+ public IBits Bits()
+ {
+ return this;
+ }
+
+ public int Length()
+ {
+ return bits.Length;
+ }
+
+ public override bool IsCacheable
+ {
+ get { return true; }
+ }
+
+ /// <summary>
+ /// Returns number of set bits. NOTE: this visits every
+ /// long in the backing bits array, and the result is not
+ /// internally cached!
+ /// </summary>
+ /// <returns></returns>
+ public int Cardinality()
+ {
+ int ret = 0;
+ for (var i = 0; i < bits.Length; i++)
+ {
+ if (bits[i]) ret++;
+ }
+ return ret;
+ }
+
+ public bool Get(int index)
+ {
+ return bits[index];
+ }
+
+ public void Set(int index)
+ {
+ bits.Set(index, true);
+ }
+
+ public bool GetAndSet(int index)
+ {
+ var ret = bits[index];
+ bits.Set(index, true);
+ return ret;
+ }
+
+ public void Clear(int index)
+ {
+ bits.Set(index, false);
+ }
+
+ public bool GetAndClear(int index)
+ {
+ var ret = bits[index];
+ bits.Set(index, false);
+ return ret;
+ }
+
+ /// <summary>
+ /// Returns the index of the first set bit starting at the index specified.
+ /// -1 is returned if there are no more set bits.
+ /// </summary>
+ /// <param name="index"></param>
+ /// <returns></returns>
+ public int NextSetBit(int index)
+ {
+ if (index >= bits.Length || index < 0)
+ throw new ArgumentException("Invalid index", "index");
+
+ for (var i = index; i < bits.Length; i++)
+ {
+ if (bits[i]) return i;
+ }
+
+ return -1;
+ }
+
+ /* Returns the index of the last set bit before or on the index specified.
+ * -1 is returned if there are no more set bits.
+ */
+ public int PrevSetBit(int index)
+ {
+ if (index >= bits.Length || index < 0)
+ throw new ArgumentException("Invalid index", "index");
+
+ for (var i = index; i >= 0; i--)
+ {
+ if (bits[i]) return i;
+ }
+
+ return -1;
+ }
+
+ /* Does in-place OR of the bits provided by the
+ * iterator. */
+ //public void Or(DocIdSetIterator iter)
+ //{
+ // if (iter is OpenBitSetIterator && iter.DocID() == -1)
+ // {
+ // var obs = (OpenBitSetIterator)iter;
+ // Or(obs.arr, obs.words);
+ // // advance after last doc that would be accepted if standard
+ // // iteration is used (to exhaust it):
+ // obs.Advance(bits.Length);
+ // }
+ // else
+ // {
+ // int doc;
+ // while ((doc = iter.NextDoc()) < bits.Length)
+ // {
+ // Set(doc);
+ // }
+ // }
+ //}
+
+ /* this = this OR other */
+ public void Or(FixedBitSet other)
+ {
+ Or(other.bits, other.bits.Length);
+ }
+
+ private void Or(BitArray otherArr, int otherLen)
+ {
+ var thisArr = this.bits;
+ int pos = Math.Min(thisArr.Length, otherLen);
+ while (--pos >= 0)
+ {
+ thisArr[pos] |= otherArr[pos];
+ }
+ }
+
+ /* Does in-place AND of the bits provided by the
+ * iterator. */
+ //public void And(DocIdSetIterator iter)
+ //{
+ // if (iter is OpenBitSetIterator && iter.DocID() == -1)
+ // {
+ // var obs = (OpenBitSetIterator)iter;
+ // And(obs.arr, obs.words);
+ // // advance after last doc that would be accepted if standard
+ // // iteration is used (to exhaust it):
+ // obs.Advance(bits.Length);
+ // }
+ // else
+ // {
+ // if (bits.Length == 0) return;
+ // int disiDoc, bitSetDoc = NextSetBit(0);
+ // while (bitSetDoc != -1 && (disiDoc = iter.Advance(bitSetDoc)) < bits.Length)
+ // {
+ // Clear(bitSetDoc, disiDoc);
+ // disiDoc++;
+ // bitSetDoc = (disiDoc < bits.Length) ? NextSetBit(disiDoc) : -1;
+ // }
+ // if (bitSetDoc != -1)
+ // {
+ // Clear(bitSetDoc, bits.Length);
+ // }
+ // }
+ //}
+
+ /* this = this AND other */
+ public void And(FixedBitSet other)
+ {
+ And(other.bits, other.bits.Length);
+ }
+
+ private void And(BitArray otherArr, int otherLen)
+ {
+ var thisArr = this.bits;
+ int pos = Math.Min(thisArr.Length, otherLen);
+ while (--pos >= 0)
+ {
+ thisArr[pos] &= otherArr[pos];
+ }
+ if (thisArr.Length > otherLen)
+ {
+ for (var i = otherLen; i < thisArr.Length; i++)
+ {
+ thisArr[i] = false;
+ }
+ }
+ }
+
+ /* Does in-place AND NOT of the bits provided by the
+ * iterator. */
+ //public void AndNot(DocIdSetIterator iter)
+ //{
+ // var obs = iter as OpenBitSetIterator;
+ // if (obs != null && iter.DocID() == -1)
+ // {
+ // AndNot(obs.arr, obs.words);
+ // // advance after last doc that would be accepted if standard
+ // // iteration is used (to exhaust it):
+ // obs.Advance(bits.Length);
+ // }
+ // else
+ // {
+ // int doc;
+ // while ((doc = iter.NextDoc()) < bits.Length)
+ // {
+ // Clear(doc);
+ // }
+ // }
+ //}
+
+ /* this = this AND NOT other */
+ public void AndNot(FixedBitSet other)
+ {
+ AndNot(other.bits, other.bits.Length);
+ }
+
+ private void AndNot(BitArray otherArr, int otherLen)
+ {
+ var thisArr = this.bits;
+ int pos = Math.Min(thisArr.Length, otherLen);
+ while (--pos >= 0)
+ {
+ thisArr[pos] &= !otherArr[pos];
+ }
+ }
+
+ // NOTE: no .isEmpty() here because that's trappy (ie,
+ // typically isEmpty is low cost, but this one wouldn't
+ // be)
+
+ /* Flips a range of bits
+ *
+ * @param startIndex lower index
+ * @param endIndex one-past the last bit to flip
+ */
+ // public void Flip(int startIndex, int endIndex) {
+ // Debug.Assert(startIndex >= 0 && startIndex < numBits);
+ // Debug.Assert(endIndex >= 0 && endIndex <= numBits);
+ // if (endIndex <= startIndex) {
+ // return;
+ // }
+
+ // int startWord = startIndex >> 6;
+ // int endWord = (endIndex-1) >> 6;
+
+ // /* Grrr, java shifting wraps around so -1L>>>64 == -1
+ // * for that reason, make sure not to use endmask if the bits to flip will
+ // * be zero in the last word (redefine endWord to be the last changed...)
+ // long startmask = -1L << (startIndex & 0x3f); // example: 11111...111000
+ // long endmask = -1L >>> (64-(endIndex & 0x3f)); // example: 00111...111111
+ // ***/
+
+ // long startmask = -1L << startIndex;
+ // long endmask = -1L >>> -endIndex; // 64-(endIndex&0x3f) is the same as -endIndex due to wrap
+
+ // if (startWord == endWord) {
+ // bits[startWord] ^= (startmask & endmask);
+ // return;
+ // }
+
+ // bits[startWord] ^= startmask;
+
+ // for (var i=startWord+1; i<endWord; i++) {
+ // bits[i] = ~bits[i];
+ // }
+
+ // bits[endWord] ^= endmask;
+ //}
+
+ /* Sets a range of bits
+ *
+ * @param startIndex lower index
+ * @param endIndex one-past the last bit to set
+ */
+ public void Set(int startIndex, int endIndex)
+ {
+ // Naive implementation
+ for (int i = startIndex; i < endIndex; i++)
+ {
+ Set(i);
+ }
+ }
+
+ // public void Set(int startIndex, int endIndex) {
+ // Debug.Assert(startIndex >= 0 && startIndex < numBits);
+ // Debug.Assert(endIndex >= 0 && endIndex <= numBits);
+ // if (endIndex <= startIndex) {
+ // return;
+ // }
+
+ // int startWord = startIndex >> 6;
+ // int endWord = (endIndex-1) >> 6;
+
+ // long startmask = -1L << startIndex;
+ // long endmask = -1L >>> -endIndex; // 64-(endIndex&0x3f) is the same as -endIndex due to wrap
+
+ // if (startWord == endWord) {
+ // bits[startWord] |= (startmask & endmask);
+ // return;
+ // }
+
+ // bits[startWord] |= startmask;
+ // Arrays.Fill(bits, startWord+1, endWord, -1L);
+ // bits[endWord] |= endmask;
+ //}
+
+ /* Clears a range of bits.
+ *
+ * @param startIndex lower index
+ * @param endIndex one-past the last bit to clear
+ */
+ public void Clear(int startIndex, int endIndex)
+ {
+ for (int i = startIndex; i < endIndex; i++)
+ {
+ Clear(i);
+ }
+ }
+
+ //@Override
+ public FixedBitSet Clone()
+ {
+ return new FixedBitSet(this);
+ }
+
+ /* returns true if both sets have the same bits set */
+ public override bool Equals(Object o)
+ {
+ if (this == o)
+ {
+ return true;
+ }
+
+ var other = o as FixedBitSet;
+ if (other == null)
+ {
+ return false;
+ }
+
+ return bits.Equals(other.bits);
+ }
+
+ public override int GetHashCode()
+ {
+ return bits.GetHashCode();
+ }
+
+ public override DocIdSetIterator Iterator()
+ {
+ return new FixedBitSetIterator(this);
+ }
+
+ /// <summary>
+ /// A FixedBitSet Iterator implementation
+ /// </summary>
+ public class FixedBitSetIterator : DocIdSetIterator
+ {
+ private int curDocId = -1;
+ private readonly IEnumerator enumerator;
+
+ public FixedBitSetIterator(FixedBitSet bitset)
+ {
+ enumerator = bitset.bits.GetEnumerator();
+ }
+
+ public override int DocID()
+ {
+ return curDocId;
+ }
+
+ public override int NextDoc()
+ {
+ while (enumerator.MoveNext())
+ {
+ ++curDocId;
+ if ((bool)enumerator.Current) return curDocId;
+ }
+ return curDocId = NO_MORE_DOCS;
+ }
+
+ public override int Advance(int target)
+ {
+ int doc;
+ while ((doc = NextDoc()) < target)
+ {
+ }
+ return doc;
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/c9f901b2/src/Lucene.Net.Spatial/Util/FunctionQuery.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Util/FunctionQuery.cs b/src/Lucene.Net.Spatial/Util/FunctionQuery.cs
new file mode 100644
index 0000000..64eda65
--- /dev/null
+++ b/src/Lucene.Net.Spatial/Util/FunctionQuery.cs
@@ -0,0 +1,215 @@
+/*
+ * 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 Lucene.Net.Index;
+using Lucene.Net.Search;
+using Lucene.Net.Search.Function;
+
+namespace Lucene.Net.Spatial.Util
+{
+ /// <summary>
+ /// Port of Solr's FunctionQuery (v1.4)
+ ///
+ /// Returns a score for each document based on a ValueSource,
+ /// often some function of the value of a field.
+ ///
+ /// <b>Note: This API is experimental and may change in non backward-compatible ways in the future</b>
+ /// </summary>
+ public class FunctionQuery : Query
+ {
+ protected readonly ValueSource func;
+
+ public FunctionQuery(ValueSource func)
+ {
+ this.func = func;
+ }
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <returns>The associated ValueSource</returns>
+ public ValueSource GetValueSource()
+ {
+ return func;
+ }
+
+ public override Query Rewrite(Index.IndexReader reader)
+ {
+ return this;
+ }
+
+ public override void ExtractTerms(System.Collections.Generic.ISet<Term> terms)
+ {
+ //base.ExtractTerms(terms);
+ }
+
+ protected class FunctionWeight : Weight
+ {
+ protected Searcher searcher;
+ protected float queryNorm;
+ protected float queryWeight;
+ protected readonly FunctionQuery enclosingInstance;
+
+ public FunctionWeight(Searcher searcher, FunctionQuery q)
+ {
+ enclosingInstance = q;
+ this.searcher = searcher;
+ //q.func.CreateWeight(searcher);
+ }
+
+ internal float GetQueryNorm()
+ {
+ return queryNorm;
+ }
+
+ public override Query Query
+ {
+ get { return enclosingInstance; }
+ }
+
+ public override float Value
+ {
+ get { return queryWeight; }
+ }
+
+ public override float GetSumOfSquaredWeights()
+ {
+ queryWeight = enclosingInstance.Boost;
+ return queryWeight * queryWeight;
+ }
+
+ public override void Normalize(float norm)
+ {
+ this.queryNorm = norm;
+ queryWeight *= this.queryNorm;
+ }
+
+ public override Scorer Scorer(IndexReader reader, bool scoreDocsInOrder, bool topScorer)
+ {
+ return new AllScorer(enclosingInstance.GetSimilarity(searcher), reader, this);
+ }
+
+ public override Explanation Explain(IndexReader reader, int doc)
+ {
+ return ((AllScorer)Scorer(reader, true, true)).Explain(doc);
+ }
+ }
+
+ protected class AllScorer : Scorer
+ {
+ readonly IndexReader reader;
+ readonly FunctionWeight weight;
+ readonly int maxDoc;
+ readonly float qWeight;
+ int doc = -1;
+ readonly DocValues vals;
+ readonly bool hasDeletions;
+
+ public AllScorer(Similarity similarity, IndexReader reader, FunctionWeight w)
+ : base(similarity)
+ {
+ this.weight = w;
+ this.qWeight = w.Value;
+ this.reader = reader;
+ this.maxDoc = reader.MaxDoc;
+ this.hasDeletions = reader.HasDeletions;
+ vals = ((FunctionQuery)w.Query).func.GetValues(reader);
+ }
+
+ public override int DocID()
+ {
+ return doc;
+ }
+
+ // instead of matching all docs, we could also embed a query.
+ // the score could either ignore the subscore, or boost it.
+ // Containment: floatline(foo:myTerm, "myFloatField", 1.0, 0.0f)
+ // Boost: foo:myTerm^floatline("myFloatField",1.0,0.0f)
+ public override int NextDoc()
+ {
+ for (; ; )
+ {
+ ++doc;
+ if (doc >= maxDoc)
+ {
+ return doc = NO_MORE_DOCS;
+ }
+ if (hasDeletions && reader.IsDeleted(doc)) continue;
+ return doc;
+ }
+ }
+
+ public override int Advance(int target)
+ {
+ // this will work even if target==NO_MORE_DOCS
+ doc = target - 1;
+ return NextDoc();
+ }
+
+ public override float Score()
+ {
+ float score = qWeight * vals.FloatVal(doc);
+
+ // Current Lucene priority queues can't handle NaN and -Infinity, so
+ // map to -Float.MAX_VALUE. This conditional handles both -infinity
+ // and NaN since comparisons with NaN are always false.
+ return score > float.NegativeInfinity ? score : -float.MaxValue;
+ }
+
+ public /*override*/ Explanation Explain(int doc)
+ {
+ float sc = qWeight * vals.FloatVal(doc);
+
+ Explanation result = new ComplexExplanation
+ (true, sc, "FunctionQuery(" + ((FunctionQuery)weight.Query).func + "), product of:");
+
+ result.AddDetail(vals.Explain(doc));
+ result.AddDetail(new Explanation(weight.Query.Boost, "boost"));
+ result.AddDetail(new Explanation(weight.GetQueryNorm(), "queryNorm"));
+ return result;
+ }
+ }
+
+ public override Weight CreateWeight(Searcher searcher)
+ {
+ return new FunctionQuery.FunctionWeight(searcher, this);
+ }
+
+ public override string ToString(string field)
+ {
+ float boost = Boost;
+ return (boost != 1.0 ? "(" : "") + func.ToString()
+ + (boost == 1.0 ? "" : ")^" + boost);
+ }
+
+ public override bool Equals(object o)
+ {
+ var other = o as FunctionQuery;
+
+ if (other == null) return false;
+
+ return this.Boost == other.Boost && this.func.Equals(other.func);
+ }
+
+ public override int GetHashCode()
+ {
+ return (int) (func.GetHashCode() * 31 + BitConverter.DoubleToInt64Bits(Boost));
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/c9f901b2/src/Lucene.Net.Spatial/Util/ReciprocalFloatFunction.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Util/ReciprocalFloatFunction.cs b/src/Lucene.Net.Spatial/Util/ReciprocalFloatFunction.cs
new file mode 100644
index 0000000..5789df4
--- /dev/null
+++ b/src/Lucene.Net.Spatial/Util/ReciprocalFloatFunction.cs
@@ -0,0 +1,100 @@
+/*
+ * 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 Lucene.Net.Index;
+using Lucene.Net.Search.Function;
+
+namespace Lucene.Net.Spatial.Util
+{
+ public class ReciprocalFloatFunction : ValueSource
+ {
+ protected readonly ValueSource source;
+ protected readonly float m;
+ protected readonly float a;
+ protected readonly float b;
+
+ /// <summary>
+ /// f(source) = a/(m*float(source)+b)
+ /// </summary>
+ /// <param name="source"></param>
+ /// <param name="m"></param>
+ /// <param name="a"></param>
+ /// <param name="b"></param>
+ public ReciprocalFloatFunction(ValueSource source, float m, float a, float b)
+ {
+ this.source = source;
+ this.m = m;
+ this.a = a;
+ this.b = b;
+ }
+
+ public class FloatDocValues : DocValues
+ {
+ private readonly ReciprocalFloatFunction _enclosingInstance;
+ private readonly DocValues vals;
+
+ public FloatDocValues(ReciprocalFloatFunction enclosingInstance, DocValues vals)
+ {
+ _enclosingInstance = enclosingInstance;
+ this.vals = vals;
+ }
+
+ public override float FloatVal(int doc)
+ {
+ return _enclosingInstance.a / (_enclosingInstance.m * vals.FloatVal(doc) + _enclosingInstance.b);
+ }
+
+ public override string ToString(int doc)
+ {
+ return _enclosingInstance.a + "/("
+ + _enclosingInstance.m + "*float(" + vals.ToString(doc) + ')'
+ + '+' + _enclosingInstance.b + ')';
+ }
+ }
+
+ public override DocValues GetValues(IndexReader reader)
+ {
+ var vals = source.GetValues(reader);
+ return new FloatDocValues(this, vals);
+ }
+
+ public override string Description()
+ {
+ return a + "/("
+ + m + "*float(" + source.Description() + ")"
+ + "+" + b + ')';
+ }
+
+ public override bool Equals(object o)
+ {
+ if (typeof(ReciprocalFloatFunction) != o.GetType()) return false;
+ var other = (ReciprocalFloatFunction)o;
+ return this.m == other.m
+ && this.a == other.a
+ && this.b == other.b
+ && this.source.Equals(other.source);
+ }
+
+ public override int GetHashCode()
+ {
+ int h = (int) BitConverter.DoubleToInt64Bits(a) + (int) BitConverter.DoubleToInt64Bits(m);
+ h ^= (h << 13) | (int)((uint)h >> 20);
+ return h + ((int) BitConverter.DoubleToInt64Bits(b)) + source.GetHashCode();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/c9f901b2/src/Lucene.Net.Spatial/Util/ShapeFieldCache.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Util/ShapeFieldCache.cs b/src/Lucene.Net.Spatial/Util/ShapeFieldCache.cs
new file mode 100644
index 0000000..59db379
--- /dev/null
+++ b/src/Lucene.Net.Spatial/Util/ShapeFieldCache.cs
@@ -0,0 +1,55 @@
+/*
+ * 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.Collections.Generic;
+using Spatial4n.Core.Shapes;
+
+namespace Lucene.Net.Spatial.Util
+{
+ /// <summary>
+ /// Bounded Cache of Shapes associated with docIds. Note, multiple Shapes can be
+ /// associated with a given docId
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ public class ShapeFieldCache<T> where T : Shape
+ {
+ private readonly IList<T>[] cache;
+ public int defaultLength;
+
+ public ShapeFieldCache(int length, int defaultLength)
+ {
+ cache = new IList<T>[length];
+ this.defaultLength = defaultLength;
+ }
+
+ public void Add(int docid, T s)
+ {
+ IList<T> list = cache[docid];
+ if (list == null)
+ {
+ list = cache[docid] = new List<T>(defaultLength);
+ }
+ list.Add(s);
+ }
+
+ public IList<T> GetShapes(int docid)
+ {
+ return cache[docid];
+ }
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/c9f901b2/src/Lucene.Net.Spatial/Util/ShapeFieldCacheDistanceValueSource.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Util/ShapeFieldCacheDistanceValueSource.cs b/src/Lucene.Net.Spatial/Util/ShapeFieldCacheDistanceValueSource.cs
new file mode 100644
index 0000000..d806767
--- /dev/null
+++ b/src/Lucene.Net.Spatial/Util/ShapeFieldCacheDistanceValueSource.cs
@@ -0,0 +1,116 @@
+/*
+ * 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 Lucene.Net.Index;
+using Lucene.Net.Search.Function;
+using Spatial4n.Core.Context;
+using Spatial4n.Core.Distance;
+using Spatial4n.Core.Shapes;
+
+namespace Lucene.Net.Spatial.Util
+{
+ /// <summary>
+ /// An implementation of the Lucene ValueSource model to support spatial relevance ranking.
+ /// </summary>
+ public class ShapeFieldCacheDistanceValueSource : ValueSource
+ {
+ private readonly ShapeFieldCacheProvider<Point> provider;
+ private readonly SpatialContext ctx;
+ private readonly Point from;
+
+ public ShapeFieldCacheDistanceValueSource(SpatialContext ctx, ShapeFieldCacheProvider<Point> provider, Point from)
+ {
+ this.ctx = ctx;
+ this.from = from;
+ this.provider = provider;
+ }
+
+ public class CachedDistanceDocValues : DocValues
+ {
+ private readonly ShapeFieldCacheDistanceValueSource enclosingInstance;
+ private readonly ShapeFieldCache<Point> cache;
+ private readonly Point from;
+ private readonly DistanceCalculator calculator;
+ private readonly double nullValue;
+
+ public CachedDistanceDocValues(IndexReader reader, ShapeFieldCacheDistanceValueSource enclosingInstance)
+ {
+ cache = enclosingInstance.provider.GetCache(reader);
+ this.enclosingInstance = enclosingInstance;
+
+ from = enclosingInstance.from;
+ calculator = enclosingInstance.ctx.GetDistCalc();
+ nullValue = (enclosingInstance.ctx.IsGeo() ? 180 : double.MaxValue);
+ }
+
+ public override float FloatVal(int doc)
+ {
+ return (float)DoubleVal(doc);
+ }
+
+ public override double DoubleVal(int doc)
+ {
+ var vals = cache.GetShapes(doc);
+ if (vals != null)
+ {
+ double v = calculator.Distance(from, vals[0]);
+ for (int i = 1; i < vals.Count; i++)
+ {
+ v = Math.Min(v, calculator.Distance(from, vals[i]));
+ }
+ return v;
+ }
+ return nullValue;
+ }
+
+ public override string ToString(int doc)
+ {
+ return enclosingInstance.Description() + "=" + FloatVal(doc);
+ }
+ }
+
+ public override DocValues GetValues(IndexReader reader)
+ {
+ return new CachedDistanceDocValues(reader, this);
+ }
+
+ public override string Description()
+ {
+ return GetType().Name + "(" + provider + ", " + from + ")";
+ }
+
+ public override bool Equals(object o)
+ {
+ if (this == o) return true;
+
+ var that = o as ShapeFieldCacheDistanceValueSource;
+
+ if (that == null) return false;
+ if (!ctx.Equals(that.ctx)) return false;
+ if (!from.Equals(that.from)) return false;
+ if (!provider.Equals(that.provider)) return false;
+
+ return true;
+ }
+
+ public override int GetHashCode()
+ {
+ return from.GetHashCode();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/c9f901b2/src/Lucene.Net.Spatial/Util/ShapeFieldCacheProvider.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Util/ShapeFieldCacheProvider.cs b/src/Lucene.Net.Spatial/Util/ShapeFieldCacheProvider.cs
new file mode 100644
index 0000000..782ebc9
--- /dev/null
+++ b/src/Lucene.Net.Spatial/Util/ShapeFieldCacheProvider.cs
@@ -0,0 +1,105 @@
+/*
+ * 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.Runtime.CompilerServices;
+using Lucene.Net.Index;
+using Spatial4n.Core.Shapes;
+#if NET35
+using Lucene.Net.Support;
+#endif
+
+namespace Lucene.Net.Spatial.Util
+{
+ /// <summary>
+ /// Provides access to a {@link ShapeFieldCache} for a given {@link AtomicReader}.
+ ///
+ /// If a Cache does not exist for the Reader, then it is built by iterating over
+ /// the all terms for a given field, reconstructing the Shape from them, and adding
+ /// them to the Cache.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ public abstract class ShapeFieldCacheProvider<T> where T : Shape
+ {
+ //private Logger log = Logger.getLogger(getClass().getName());
+
+ // it may be a List<T> or T
+#if !NET35
+ private readonly ConditionalWeakTable<IndexReader, ShapeFieldCache<T>> sidx =
+ new ConditionalWeakTable<IndexReader, ShapeFieldCache<T>>(); // WeakHashMap
+#else
+ private readonly WeakDictionary<IndexReader, ShapeFieldCache<T>> sidx =
+ new WeakDictionary<IndexReader, ShapeFieldCache<T>>();
+#endif
+
+
+ protected readonly int defaultSize;
+ protected readonly String shapeField;
+
+ protected ShapeFieldCacheProvider(String shapeField, int defaultSize)
+ {
+ this.shapeField = shapeField;
+ this.defaultSize = defaultSize;
+ }
+
+ protected abstract T ReadShape(/*BytesRef*/ Term term);
+
+ private readonly object locker = new object();
+
+ public ShapeFieldCache<T> GetCache(IndexReader reader)
+ {
+ lock (locker)
+ {
+ ShapeFieldCache<T> idx;
+ if (sidx.TryGetValue(reader, out idx) && idx != null)
+ {
+ return idx;
+ }
+
+ //long startTime = System.CurrentTimeMillis();
+ //log.fine("Building Cache [" + reader.MaxDoc() + "]");
+
+ idx = new ShapeFieldCache<T>(reader.MaxDoc, defaultSize);
+ var count = 0;
+ var tec = new TermsEnumCompatibility(reader, shapeField);
+
+ var term = tec.Next();
+ while (term != null)
+ {
+ var shape = ReadShape(term);
+ if (shape != null)
+ {
+ var docs = reader.TermDocs(new Term(shapeField, tec.Term().Text));
+ while (docs.Next())
+ {
+ idx.Add(docs.Doc, shape);
+ count++;
+ }
+ }
+ term = tec.Next();
+ }
+
+ sidx.Add(reader, idx);
+ tec.Close();
+
+ //long elapsed = System.CurrentTimeMillis() - startTime;
+ //log.fine("Cached: [" + count + " in " + elapsed + "ms] " + idx);
+ return idx;
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/c9f901b2/src/Lucene.Net.Spatial/Util/TermsEnumCompatibility.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Util/TermsEnumCompatibility.cs b/src/Lucene.Net.Spatial/Util/TermsEnumCompatibility.cs
new file mode 100644
index 0000000..7b6809e
--- /dev/null
+++ b/src/Lucene.Net.Spatial/Util/TermsEnumCompatibility.cs
@@ -0,0 +1,140 @@
+/*
+ *
+ * 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.Collections.Generic;
+using Lucene.Net.Index;
+using Lucene.Net.Util;
+
+namespace Lucene.Net.Spatial.Util
+{
+ /// <summary>
+ /// Wraps Lucene 3 TermEnum to make it look like a Lucene 4 TermsEnum
+ /// SOLR-2155
+ /// @author dsmiley
+ /// </summary>
+ public class TermsEnumCompatibility
+ {
+ private readonly IndexReader reader;
+ private readonly String fieldName;
+ private TermEnum termEnum;
+ private bool initialState = true;
+
+ public TermsEnumCompatibility(IndexReader reader, String fieldName)
+ {
+ this.reader = reader;
+ this.fieldName = string.Intern(fieldName);
+ this.termEnum = reader.Terms(new Term(this.fieldName));
+ }
+
+ public TermEnum GetTermEnum()
+ {
+ return termEnum;
+ }
+
+ public Term Term()
+ {
+ Term t = termEnum.Term;
+ return t != null && t.Field == fieldName ? t : null;
+ }
+
+ public Term Next()
+ {
+ //in Lucene 3, a call to reader.terms(term) is already pre-positioned, you don't call next first
+ if (initialState)
+ {
+ initialState = false;
+ return Term();
+ }
+ else
+ {
+ return termEnum.Next() ? Term() : null;
+ }
+ }
+
+ public void Close()
+ {
+ termEnum.Close();
+ }
+
+ public enum SeekStatus
+ {
+ END,
+ FOUND,
+ NOT_FOUND
+ }
+
+ public SeekStatus Seek(String value)
+ {
+ termEnum = reader.Terms(new Term(this.fieldName, value));
+ Term t = Term();
+ if (t == null)
+ return SeekStatus.END;
+ return (t.Text.Equals(value)) ? SeekStatus.FOUND : SeekStatus.NOT_FOUND;
+ }
+
+ /// <summary>
+ /// Seeks to the specified term, if it exists, or to the
+ /// next (ceiling) term. Returns SeekStatus to
+ /// indicate whether exact term was found, a different
+ /// term was found, or EOF was hit. The target term may
+ /// be before or after the current term. If this returns
+ /// SeekStatus.END, the enum is unpositioned.
+ /// </summary>
+ /// <param name="value"></param>
+ /// <returns></returns>
+ public SeekStatus SeekCeil(String value)
+ {
+ return Seek(value);
+ }
+
+ /// <summary>
+ /// Returns the number of documents that have at least one
+ /// term for this field, or -1 if this measure isn't
+ /// stored by the codec. Note that, just like other term
+ /// measures, this measure does not take deleted documents
+ /// into account.
+ /// </summary>
+ /// <returns></returns>
+ public int GetDocCount()
+ {
+ return -1; // TODO find a way to efficiently determine this
+ }
+
+ public void Docs(OpenBitSet bits)
+ {
+ var termDocs = reader.TermDocs(new Term(fieldName, Term().Text));
+ while (termDocs.Next())
+ {
+ bits.FastSet(termDocs.Doc);
+ }
+ }
+
+ public void Docs(FixedBitSet bits)
+ {
+ var termDocs = reader.TermDocs(new Term(fieldName, Term().Text));
+ while (termDocs.Next())
+ {
+ bits.Set(termDocs.Doc);
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/c9f901b2/src/Lucene.Net.Spatial/Util/TermsFilter.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Util/TermsFilter.cs b/src/Lucene.Net.Spatial/Util/TermsFilter.cs
new file mode 100644
index 0000000..4acfcef
--- /dev/null
+++ b/src/Lucene.Net.Spatial/Util/TermsFilter.cs
@@ -0,0 +1,117 @@
+/*
+ * 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.Collections.Generic;
+using System.Diagnostics;
+using Lucene.Net.Index;
+using Lucene.Net.Search;
+
+namespace Lucene.Net.Spatial.Util
+{
+ /// <summary>
+ /// Constructs a filter for docs matching any of the terms added to this class.
+ /// Unlike a RangeFilter this can be used for filtering on multiple terms that are not necessarily in
+ /// a sequence. An example might be a collection of primary keys from a database query result or perhaps
+ /// a choice of "category" labels picked by the end user. As a filter, this is much faster than the
+ /// equivalent query (a BooleanQuery with many "should" TermQueries)
+ /// </summary>
+ public class TermsFilter : Filter
+ {
+ private readonly SortedSet<Term> terms = new SortedSet<Term>();
+
+ /// <summary>
+ /// Adds a term to the list of acceptable terms
+ /// </summary>
+ /// <param name="term"></param>
+ public void AddTerm(Term term)
+ {
+ terms.Add(term);
+ }
+
+ public override DocIdSet GetDocIdSet(IndexReader reader)
+ {
+ var result = new FixedBitSet(reader.MaxDoc);
+ var fields = reader.GetFieldNames(IndexReader.FieldOption.ALL);
+
+ if (fields == null || fields.Count == 0)
+ {
+ return result;
+ }
+
+ String lastField = null;
+ TermsEnumCompatibility termsEnum = null;
+ foreach (Term term in terms)
+ {
+ if (!term.Field.Equals(lastField))
+ {
+ var termsC = new TermsEnumCompatibility(reader, term.Field);
+ if (termsC.Term() == null)
+ {
+ return result;
+ }
+ termsEnum = termsC;
+ lastField = term.Field;
+ }
+
+ if (terms != null)
+ {
+ // TODO this check doesn't make sense, decide which variable its supposed to be for
+ Debug.Assert(termsEnum != null);
+ if (termsEnum.SeekCeil(term.Text) == TermsEnumCompatibility.SeekStatus.FOUND)
+ {
+ termsEnum.Docs(result);
+ }
+ }
+ }
+ return result;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (this == obj)
+ return true;
+
+ if ((obj == null) || (obj.GetType() != this.GetType()))
+ return false;
+
+ var test = (TermsFilter)obj;
+ if (terms == test.terms)
+ return true;
+ if (terms == null || terms.Count != test.terms.Count)
+ return false;
+
+ var e1 = terms.GetEnumerator();
+ var e2 = test.terms.GetEnumerator();
+ while (e1.MoveNext() && e2.MoveNext())
+ {
+ if (!e1.Current.Equals(e2.Current)) return false;
+ }
+ return true;
+ }
+
+ public override int GetHashCode()
+ {
+ int hash = 9;
+ foreach (Term term in terms)
+ {
+ hash = 31 * hash + term.GetHashCode();
+ }
+ return hash;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/c9f901b2/src/Lucene.Net.Spatial/Util/ValueSourceFilter.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Util/ValueSourceFilter.cs b/src/Lucene.Net.Spatial/Util/ValueSourceFilter.cs
new file mode 100644
index 0000000..e92c120
--- /dev/null
+++ b/src/Lucene.Net.Spatial/Util/ValueSourceFilter.cs
@@ -0,0 +1,72 @@
+/*
+ * 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 Lucene.Net.Search;
+using Lucene.Net.Search.Function;
+
+namespace Lucene.Net.Spatial.Util
+{
+ /// <summary>
+ /// Filter that matches all documents where a valuesource is
+ /// in between a range of <c>min</c> and <c>max</c> inclusive.
+ /// </summary>
+ public class ValueSourceFilter : Filter
+ {
+ readonly Filter startingFilter;
+ readonly ValueSource source;
+ public readonly double min;
+ public readonly double max;
+
+ public ValueSourceFilter(Filter startingFilter, ValueSource source, double min, double max)
+ {
+ if (startingFilter == null)
+ {
+ throw new ArgumentException("please provide a non-null startingFilter; you can use QueryWrapperFilter(MatchAllDocsQuery) as a no-op filter", "startingFilter");
+ }
+ this.startingFilter = startingFilter;
+ this.source = source;
+ this.min = min;
+ this.max = max;
+ }
+
+ public override DocIdSet GetDocIdSet(Index.IndexReader reader)
+ {
+ var values = source.GetValues(reader);
+ return new ValueSourceFilteredDocIdSet(startingFilter.GetDocIdSet(reader), values, this);
+ }
+
+ public class ValueSourceFilteredDocIdSet : FilteredDocIdSet
+ {
+ private readonly ValueSourceFilter enclosingFilter;
+ private readonly DocValues values;
+
+ public ValueSourceFilteredDocIdSet(DocIdSet innerSet, DocValues values, ValueSourceFilter caller)
+ : base(innerSet)
+ {
+ this.enclosingFilter = caller;
+ this.values = values;
+ }
+
+ public override bool Match(int doc)
+ {
+ double val = values.DoubleVal(doc);
+ return val >= enclosingFilter.min && val <= enclosingFilter.max;
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/c9f901b2/src/Lucene.Net.Spatial/Vector/DistanceValueSource.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Vector/DistanceValueSource.cs b/src/Lucene.Net.Spatial/Vector/DistanceValueSource.cs
new file mode 100644
index 0000000..19e2ea0
--- /dev/null
+++ b/src/Lucene.Net.Spatial/Vector/DistanceValueSource.cs
@@ -0,0 +1,118 @@
+/*
+ * 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.Diagnostics;
+using Lucene.Net.Index;
+using Lucene.Net.Search;
+using Lucene.Net.Search.Function;
+using Lucene.Net.Spatial.Util;
+using Spatial4n.Core.Distance;
+using Spatial4n.Core.Shapes;
+using Spatial4n.Core.Shapes.Impl;
+
+namespace Lucene.Net.Spatial.Vector
+{
+ /// <summary>
+ /// An implementation of the Lucene ValueSource model that returns the distance.
+ /// </summary>
+ public class DistanceValueSource : ValueSource
+ {
+ private readonly PointVectorStrategy strategy;
+ private readonly Point from;
+
+ public DistanceValueSource(PointVectorStrategy strategy, Point from)
+ {
+ this.strategy = strategy;
+ this.from = from;
+ }
+
+ public class DistanceDocValues : DocValues
+ {
+ private readonly DistanceValueSource enclosingInstance;
+
+ private readonly double[] ptX, ptY;
+ private readonly IBits validX, validY;
+
+ private readonly Point from;
+ private readonly DistanceCalculator calculator;
+ private readonly double nullValue;
+
+ public DistanceDocValues(DistanceValueSource enclosingInstance, IndexReader reader)
+ {
+ this.enclosingInstance = enclosingInstance;
+
+ ptX = FieldCache_Fields.DEFAULT.GetDoubles(reader, enclosingInstance.strategy.GetFieldNameX()/*, true*/);
+ ptY = FieldCache_Fields.DEFAULT.GetDoubles(reader, enclosingInstance.strategy.GetFieldNameY()/*, true*/);
+ validX = FieldCache_Fields.DEFAULT.GetDocsWithField(reader, enclosingInstance.strategy.GetFieldNameX());
+ validY = FieldCache_Fields.DEFAULT.GetDocsWithField(reader, enclosingInstance.strategy.GetFieldNameY());
+
+ from = enclosingInstance.from;
+ calculator = enclosingInstance.strategy.GetSpatialContext().GetDistCalc();
+ nullValue = (enclosingInstance.strategy.GetSpatialContext().IsGeo() ? 180 : double.MaxValue);
+ }
+
+ public override float FloatVal(int doc)
+ {
+ return (float)DoubleVal(doc);
+ }
+
+ public override double DoubleVal(int doc)
+ {
+ // make sure it has minX and area
+ if (validX.Get(doc))
+ {
+ Debug.Assert(validY.Get(doc));
+ return calculator.Distance(from, ptX[doc], ptY[doc]);
+ }
+ return nullValue;
+ }
+
+ public override string ToString(int doc)
+ {
+ return enclosingInstance.Description() + "=" + FloatVal(doc);
+ }
+ }
+
+ public override DocValues GetValues(IndexReader reader)
+ {
+ return new DistanceDocValues(this, reader);
+ }
+
+ public override string Description()
+ {
+ return "DistanceValueSource(" + strategy + ", " + from + ")";
+ }
+
+ public override bool Equals(object o)
+ {
+ if (this == o) return true;
+
+ var that = o as DistanceValueSource;
+ if (that == null) return false;
+
+ if (!from.Equals(that.from)) return false;
+ if (!strategy.Equals(that.strategy)) return false;
+
+ return true;
+ }
+
+ public override int GetHashCode()
+ {
+ return from.GetHashCode();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/c9f901b2/src/Lucene.Net.Spatial/Vector/PointVectorStrategy.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Vector/PointVectorStrategy.cs b/src/Lucene.Net.Spatial/Vector/PointVectorStrategy.cs
new file mode 100644
index 0000000..f3ef815
--- /dev/null
+++ b/src/Lucene.Net.Spatial/Vector/PointVectorStrategy.cs
@@ -0,0 +1,260 @@
+/*
+ * 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 Lucene.Net.Documents;
+using Lucene.Net.Search;
+using Lucene.Net.Search.Function;
+using Lucene.Net.Spatial.Queries;
+using Lucene.Net.Spatial.Util;
+using Spatial4n.Core.Context;
+using Spatial4n.Core.Shapes;
+
+namespace Lucene.Net.Spatial.Vector
+{
+ /// <summary>
+ /// Simple {@link SpatialStrategy} which represents Points in two numeric {@link DoubleField}s.
+ ///
+ /// Note, currently only Points can be indexed by this Strategy. At query time, the bounding
+ /// box of the given Shape is used to create {@link NumericRangeQuery}s to efficiently
+ /// find Points within the Shape.
+ ///
+ /// Due to the simple use of numeric fields, this Strategy provides support for sorting by
+ /// distance through {@link DistanceValueSource}
+ /// </summary>
+ public class PointVectorStrategy : SpatialStrategy
+ {
+ public static String SUFFIX_X = "__x";
+ public static String SUFFIX_Y = "__y";
+
+ private readonly String fieldNameX;
+ private readonly String fieldNameY;
+
+ public int precisionStep = 8; // same as solr default
+
+ public PointVectorStrategy(SpatialContext ctx, String fieldNamePrefix)
+ : base(ctx, fieldNamePrefix)
+ {
+ this.fieldNameX = fieldNamePrefix + SUFFIX_X;
+ this.fieldNameY = fieldNamePrefix + SUFFIX_Y;
+ }
+
+ public void SetPrecisionStep(int p)
+ {
+ precisionStep = p;
+ if (precisionStep <= 0 || precisionStep >= 64)
+ precisionStep = int.MaxValue;
+ }
+
+ public string GetFieldNameX()
+ {
+ return fieldNameX;
+ }
+
+ public string GetFieldNameY()
+ {
+ return fieldNameY;
+ }
+
+ public override AbstractField[] CreateIndexableFields(Shape shape)
+ {
+ var point = shape as Point;
+ if (point != null)
+ return CreateIndexableFields(point);
+
+ throw new InvalidOperationException("Can only index Point, not " + shape);
+ }
+
+ public AbstractField[] CreateIndexableFields(Point point)
+ {
+ var f = new AbstractField[2];
+
+ var f0 = new NumericField(fieldNameX, precisionStep, Field.Store.NO, true)
+ {OmitNorms = true, OmitTermFreqAndPositions = true};
+ f0.SetDoubleValue(point.GetX());
+ f[0] = f0;
+
+ var f1 = new NumericField(fieldNameY, precisionStep, Field.Store.NO, true)
+ {OmitNorms = true, OmitTermFreqAndPositions = true};
+ f1.SetDoubleValue(point.GetY());
+ f[1] = f1;
+
+ return f;
+ }
+
+ public override ValueSource MakeDistanceValueSource(Point queryPoint)
+ {
+ return new DistanceValueSource(this, queryPoint);
+ }
+
+ public override ConstantScoreQuery MakeQuery(SpatialArgs args)
+ {
+ if (!SpatialOperation.Is(args.Operation,
+ SpatialOperation.Intersects,
+ SpatialOperation.IsWithin))
+ throw new UnsupportedSpatialOperation(args.Operation);
+
+ Shape shape = args.Shape;
+ var bbox = shape as Rectangle;
+ if (bbox != null)
+ return new ConstantScoreQuery(new QueryWrapperFilter(MakeWithin(bbox)));
+
+ var circle = shape as Circle;
+ if (circle != null)
+ {
+ bbox = circle.GetBoundingBox();
+ var vsf = new ValueSourceFilter(
+ new QueryWrapperFilter(MakeWithin(bbox)),
+ MakeDistanceValueSource(circle.GetCenter()),
+ 0,
+ circle.GetRadius());
+ return new ConstantScoreQuery(vsf);
+ }
+
+ throw new InvalidOperationException("Only Rectangles and Circles are currently supported, " +
+ "found [" + shape.GetType().Name + "]"); //TODO
+ }
+
+ //TODO this is basically old code that hasn't been verified well and should probably be removed
+ public Query MakeQueryDistanceScore(SpatialArgs args)
+ {
+ // For starters, just limit the bbox
+ var shape = args.Shape;
+ if (!(shape is Rectangle || shape is Circle))
+ throw new InvalidOperationException("Only Rectangles and Circles are currently supported, found ["
+ + shape.GetType().Name + "]");//TODO
+
+ Rectangle bbox = shape.GetBoundingBox();
+ if (bbox.GetCrossesDateLine())
+ {
+ throw new InvalidOperationException("Crossing dateline not yet supported");
+ }
+
+ ValueSource valueSource = null;
+
+ Query spatial = null;
+ SpatialOperation op = args.Operation;
+
+ if (SpatialOperation.Is(op,
+ SpatialOperation.BBoxWithin,
+ SpatialOperation.BBoxIntersects))
+ {
+ spatial = MakeWithin(bbox);
+ }
+ else if (SpatialOperation.Is(op,
+ SpatialOperation.Intersects,
+ SpatialOperation.IsWithin))
+ {
+ spatial = MakeWithin(bbox);
+ var circle = args.Shape as Circle;
+ if (circle != null)
+ {
+ // Make the ValueSource
+ valueSource = MakeDistanceValueSource(shape.GetCenter());
+
+ var vsf = new ValueSourceFilter(
+ new QueryWrapperFilter(spatial), valueSource, 0, circle.GetRadius());
+
+ spatial = new FilteredQuery(new MatchAllDocsQuery(), vsf);
+ }
+ }
+ else if (op == SpatialOperation.IsDisjointTo)
+ {
+ spatial = MakeDisjoint(bbox);
+ }
+
+ if (spatial == null)
+ {
+ throw new UnsupportedSpatialOperation(args.Operation);
+ }
+
+ if (valueSource != null)
+ {
+ valueSource = new CachingDoubleValueSource(valueSource);
+ }
+ else
+ {
+ valueSource = MakeDistanceValueSource(shape.GetCenter());
+ }
+ Query spatialRankingQuery = new FunctionQuery(valueSource);
+ var bq = new BooleanQuery();
+ bq.Add(spatial, Occur.MUST);
+ bq.Add(spatialRankingQuery, Occur.MUST);
+ return bq;
+
+ }
+
+ public override Filter MakeFilter(SpatialArgs args)
+ {
+ //unwrap the CSQ from makeQuery
+ ConstantScoreQuery csq = MakeQuery(args);
+ Filter filter = csq.Filter;
+ if (filter != null)
+ return filter;
+ else
+ return new QueryWrapperFilter(csq);
+ }
+
+ /// <summary>
+ /// Constructs a query to retrieve documents that fully contain the input envelope.
+ /// </summary>
+ /// <param name="bbox"></param>
+ private Query MakeWithin(Rectangle bbox)
+ {
+ var bq = new BooleanQuery();
+ const Occur MUST = Occur.MUST;
+ if (bbox.GetCrossesDateLine())
+ {
+ //use null as performance trick since no data will be beyond the world bounds
+ bq.Add(RangeQuery(fieldNameX, null /*-180*/, bbox.GetMaxX()), Occur.SHOULD);
+ bq.Add(RangeQuery(fieldNameX, bbox.GetMinX(), null /*+180*/), Occur.SHOULD);
+ bq.MinimumNumberShouldMatch = 1; //must match at least one of the SHOULD
+ }
+ else
+ {
+ bq.Add(RangeQuery(fieldNameX, bbox.GetMinX(), bbox.GetMaxX()), MUST);
+ }
+ bq.Add(RangeQuery(fieldNameY, bbox.GetMinY(), bbox.GetMaxY()), MUST);
+ return bq;
+ }
+
+ private NumericRangeQuery<Double> RangeQuery(String fieldName, double? min, double? max)
+ {
+ return NumericRangeQuery.NewDoubleRange(
+ fieldName,
+ precisionStep,
+ min,
+ max,
+ true,
+ true); //inclusive
+ }
+
+ /// <summary>
+ /// Constructs a query to retrieve documents that fully contain the input envelope.
+ /// </summary>
+ /// <param name="bbox"></param>
+ private Query MakeDisjoint(Rectangle bbox)
+ {
+ if (bbox.GetCrossesDateLine())
+ throw new InvalidOperationException("MakeDisjoint doesn't handle dateline cross");
+ Query qX = RangeQuery(fieldNameX, bbox.GetMinX(), bbox.GetMaxX());
+ Query qY = RangeQuery(fieldNameY, bbox.GetMinY(), bbox.GetMaxY());
+ var bq = new BooleanQuery {{qX, Occur.MUST_NOT}, {qY, Occur.MUST_NOT}};
+ return bq;
+ }
+ }
+}