blob: c30e936677478b40a452c192ee8ab6508b6aacdc [file] [log] [blame]
/*
* 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.document;
import java.io.IOException;
import org.apache.lucene.geo.GeoEncodingUtils;
import org.apache.lucene.geo.GeoUtils;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.MultiReader;
import org.apache.lucene.index.RandomIndexWriter;
import org.apache.lucene.search.CheckHits;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.QueryUtils;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.TopScoreDocCollector;
import org.apache.lucene.store.Directory;
import org.apache.lucene.util.LuceneTestCase;
import org.apache.lucene.util.SloppyMath;
public class TestLatLonPointDistanceFeatureQuery extends LuceneTestCase {
public void testEqualsAndHashcode() {
Query q1 = LatLonPoint.newDistanceFeatureQuery("foo", 3, 10, 10, 5);
Query q2 = LatLonPoint.newDistanceFeatureQuery("foo", 3, 10, 10, 5);
QueryUtils.checkEqual(q1, q2);
Query q3 = LatLonPoint.newDistanceFeatureQuery("bar", 3, 10, 10,5);
QueryUtils.checkUnequal(q1, q3);
Query q4 = LatLonPoint.newDistanceFeatureQuery("foo", 4, 10, 10, 5);
QueryUtils.checkUnequal(q1, q4);
Query q5 = LatLonPoint.newDistanceFeatureQuery("foo", 3, 9, 10, 5);
QueryUtils.checkUnequal(q1, q5);
Query q6 = LatLonPoint.newDistanceFeatureQuery("foo", 3, 10, 9, 5);
QueryUtils.checkUnequal(q1, q6);
Query q7 = LatLonPoint.newDistanceFeatureQuery("foo", 3, 10, 10, 6);
QueryUtils.checkUnequal(q1, q7);
}
public void testBasics() throws IOException {
Directory dir = newDirectory();
RandomIndexWriter w = new RandomIndexWriter(random(), dir, newIndexWriterConfig()
.setMergePolicy(newLogMergePolicy(random().nextBoolean())));
Document doc = new Document();
LatLonPoint point = new LatLonPoint("foo", 0.0, 0.0);
doc.add(point);
LatLonDocValuesField docValue = new LatLonDocValuesField("foo",0.0, 0.0);
doc.add(docValue);
double pivotDistance = 5000;//5k
point.setLocationValue(-7, -7);
docValue.setLocationValue(-7, -7);
w.addDocument(doc);
point.setLocationValue(9, 9);
docValue.setLocationValue(9, 9);
w.addDocument(doc);
point.setLocationValue(8, 8);
docValue.setLocationValue(8, 8);
w.addDocument(doc);
point.setLocationValue(4, 4);
docValue.setLocationValue(4, 4);
w.addDocument(doc);
point.setLocationValue(-1, -1);
docValue.setLocationValue(-1, -1);
w.addDocument(doc);
DirectoryReader reader = w.getReader();
IndexSearcher searcher = newSearcher(reader);
Query q = LatLonPoint.newDistanceFeatureQuery("foo", 3, 10, 10, pivotDistance);
TopScoreDocCollector collector = TopScoreDocCollector.create(2, null, 1);
searcher.search(q, collector);
TopDocs topHits = collector.topDocs();
assertEquals(2, topHits.scoreDocs.length);
double distance1 = SloppyMath.haversinMeters(GeoEncodingUtils.decodeLatitude(GeoEncodingUtils.encodeLatitude(9)) , GeoEncodingUtils.decodeLongitude(GeoEncodingUtils.encodeLongitude(9)), 10,10);
double distance2 = SloppyMath.haversinMeters(GeoEncodingUtils.decodeLatitude(GeoEncodingUtils.encodeLatitude(8)) , GeoEncodingUtils.decodeLongitude(GeoEncodingUtils.encodeLongitude(8)), 10,10);
CheckHits.checkEqual(q,
new ScoreDoc[] {
new ScoreDoc(1, (float) (3f * (pivotDistance / (pivotDistance + distance1)))),
new ScoreDoc(2, (float) (3f * (pivotDistance / (pivotDistance + distance2))))
},
topHits.scoreDocs);
distance1 = SloppyMath.haversinMeters(GeoEncodingUtils.decodeLatitude(GeoEncodingUtils.encodeLatitude(9)) , GeoEncodingUtils.decodeLongitude(GeoEncodingUtils.encodeLongitude(9)), 9,9);
distance2 = SloppyMath.haversinMeters(GeoEncodingUtils.decodeLatitude(GeoEncodingUtils.encodeLatitude(8)) , GeoEncodingUtils.decodeLongitude(GeoEncodingUtils.encodeLongitude(8)), 9,9);
q = LatLonPoint.newDistanceFeatureQuery("foo", 3, 9, 9, pivotDistance);
collector = TopScoreDocCollector.create(2, null, 1);
searcher.search(q, collector);
topHits = collector.topDocs();
assertEquals(2, topHits.scoreDocs.length);
CheckHits.checkExplanations(q, "", searcher);
CheckHits.checkEqual(q,
new ScoreDoc[] {
new ScoreDoc(1, (float) (3f * (pivotDistance / (pivotDistance + distance1)))),
new ScoreDoc(2, (float) (3f * (pivotDistance / (pivotDistance + distance2))))
},
topHits.scoreDocs);
reader.close();
w.close();
dir.close();
}
public void testCrossesDateLine() throws IOException {
Directory dir = newDirectory();
RandomIndexWriter w = new RandomIndexWriter(random(), dir, newIndexWriterConfig()
.setMergePolicy(newLogMergePolicy(random().nextBoolean())));
Document doc = new Document();
LatLonPoint point = new LatLonPoint("foo", 0.0, 0.0);
doc.add(point);
LatLonDocValuesField docValue = new LatLonDocValuesField("foo",0.0, 0.0);
doc.add(docValue);
double pivotDistance = 5000;//5k
point.setLocationValue(0, -179);
docValue.setLocationValue(0, -179);
w.addDocument(doc);
point.setLocationValue(0, 176);
docValue.setLocationValue(0, 176);
w.addDocument(doc);
point.setLocationValue(0, -150);
docValue.setLocationValue(0, -150);
w.addDocument(doc);
point.setLocationValue(0, -140);
docValue.setLocationValue(0, -140);
w.addDocument(doc);
point.setLocationValue(0, 140);
docValue.setLocationValue(01, 140);
w.addDocument(doc);
DirectoryReader reader = w.getReader();
IndexSearcher searcher = newSearcher(reader);
Query q = LatLonPoint.newDistanceFeatureQuery("foo", 3, 0, 179, pivotDistance);
TopScoreDocCollector collector = TopScoreDocCollector.create(2, null, 1);
searcher.search(q, collector);
TopDocs topHits = collector.topDocs();
assertEquals(2, topHits.scoreDocs.length);
double distance1 = SloppyMath.haversinMeters(GeoEncodingUtils.decodeLatitude(GeoEncodingUtils.encodeLatitude(0)) , GeoEncodingUtils.decodeLongitude(GeoEncodingUtils.encodeLongitude(-179)), 0,179);
double distance2 = SloppyMath.haversinMeters(GeoEncodingUtils.decodeLatitude(GeoEncodingUtils.encodeLatitude(0)) , GeoEncodingUtils.decodeLongitude(GeoEncodingUtils.encodeLongitude(176)), 0,179);
CheckHits.checkEqual(q,
new ScoreDoc[] {
new ScoreDoc(0, (float) (3f * (pivotDistance / (pivotDistance + distance1)))),
new ScoreDoc(1, (float) (3f * (pivotDistance / (pivotDistance + distance2))))
},
topHits.scoreDocs);
reader.close();
w.close();
dir.close();
}
public void testMissingField() throws IOException {
IndexReader reader = new MultiReader();
IndexSearcher searcher = newSearcher(reader);
Query q = LatLonPoint.newDistanceFeatureQuery("foo", 3, 10, 10, 5000);
TopDocs topHits = searcher.search(q, 2);
assertEquals(0, topHits.totalHits.value);
}
public void testMissingValue() throws IOException {
Directory dir = newDirectory();
RandomIndexWriter w = new RandomIndexWriter(random(), dir, newIndexWriterConfig()
.setMergePolicy(newLogMergePolicy(random().nextBoolean())));
Document doc = new Document();
LatLonPoint point = new LatLonPoint("foo", 0, 0);
doc.add(point);
LatLonDocValuesField docValue = new LatLonDocValuesField("foo", 0, 0);
doc.add(docValue);
point.setLocationValue(3, 3);
docValue.setLocationValue(3, 3);
w.addDocument(doc);
w.addDocument(new Document());
point.setLocationValue(7, 7);
docValue.setLocationValue(7, 7);
w.addDocument(doc);
DirectoryReader reader = w.getReader();
IndexSearcher searcher = newSearcher(reader);
Query q = LatLonPoint.newDistanceFeatureQuery("foo", 3, 10, 10, 5);
TopScoreDocCollector collector = TopScoreDocCollector.create(3, null, 1);
searcher.search(q, collector);
TopDocs topHits = collector.topDocs();
assertEquals(2, topHits.scoreDocs.length);
double distance1 = SloppyMath.haversinMeters(GeoEncodingUtils.decodeLatitude(GeoEncodingUtils.encodeLatitude(7)) , GeoEncodingUtils.decodeLongitude(GeoEncodingUtils.encodeLongitude(7)), 10,10);
double distance2 = SloppyMath.haversinMeters(GeoEncodingUtils.decodeLatitude(GeoEncodingUtils.encodeLatitude(3)) , GeoEncodingUtils.decodeLongitude(GeoEncodingUtils.encodeLongitude(3)), 10,10);
CheckHits.checkEqual(q,
new ScoreDoc[] {
new ScoreDoc(2, (float) (3f * (5. / (5. + distance1)))),
new ScoreDoc(0, (float) (3f * (5. / (5. + distance2))))
},
topHits.scoreDocs);
CheckHits.checkExplanations(q, "", searcher);
reader.close();
w.close();
dir.close();
}
public void testMultiValued() throws IOException {
Directory dir = newDirectory();
RandomIndexWriter w = new RandomIndexWriter(random(), dir, newIndexWriterConfig()
.setMergePolicy(newLogMergePolicy(random().nextBoolean())));
Document doc = new Document();
for (double[] point : new double[][] {{0, 0}, {30, 30}, {60, 60}}) {
doc.add(new LatLonPoint("foo", point[0], point[1]));
doc.add(new LatLonDocValuesField("foo", point[0], point[1]));
}
w.addDocument(doc);
doc = new Document();
for (double[] point : new double[][] {{45, 0}, {-45, 0}, {-90, 0}, {90, 0}}) {
doc.add(new LatLonPoint("foo", point[0], point[1]));
doc.add(new LatLonDocValuesField("foo", point[0], point[1]));
}
w.addDocument(doc);
doc = new Document();
for (double[] point : new double[][] {{0, 90}, {0, -90}, {0, 180}, {0, -180}}) {
doc.add(new LatLonPoint("foo", point[0], point[1]));
doc.add(new LatLonDocValuesField("foo", point[0], point[1]));
}
w.addDocument(doc);
doc = new Document();
for (double[] point : new double[][] {{3, 2}}) {
doc.add(new LatLonPoint("foo", point[0], point[1]));
doc.add(new LatLonDocValuesField("foo", point[0], point[1]));
}
w.addDocument(doc);
doc = new Document();
for (double[] point : new double[][] {{45, 45}, {-45, -45}}) {
doc.add(new LatLonPoint("foo", point[0], point[1]));
doc.add(new LatLonDocValuesField("foo", point[0], point[1]));
}
w.addDocument(doc);
DirectoryReader reader = w.getReader();
IndexSearcher searcher = newSearcher(reader);
Query q = LatLonPoint.newDistanceFeatureQuery("foo", 3, 0, 0, 200);
TopScoreDocCollector collector = TopScoreDocCollector.create(2, null, 1);
searcher.search(q, collector);
TopDocs topHits = collector.topDocs();
assertEquals(2, topHits.scoreDocs.length);
double distance1 = SloppyMath.haversinMeters(GeoEncodingUtils.decodeLatitude(GeoEncodingUtils.encodeLatitude(0)) , GeoEncodingUtils.decodeLongitude(GeoEncodingUtils.encodeLongitude(0)), 0,0);
double distance2 = SloppyMath.haversinMeters(GeoEncodingUtils.decodeLatitude(GeoEncodingUtils.encodeLatitude(3)) , GeoEncodingUtils.decodeLongitude(GeoEncodingUtils.encodeLongitude(2)), 0,0);
CheckHits.checkEqual(q,
new ScoreDoc[] {
new ScoreDoc(0, (float) (3f * (200 / (200 + distance1)))),
new ScoreDoc(3, (float) (3f * (200 / (200 + distance2))))
},
topHits.scoreDocs);
q = LatLonPoint.newDistanceFeatureQuery("foo", 3, -90, 0, 10000.);
collector = TopScoreDocCollector.create(2, null, 1);
searcher.search(q, collector);
topHits = collector.topDocs();
assertEquals(2, topHits.scoreDocs.length);
CheckHits.checkExplanations(q, "", searcher);
distance1 = SloppyMath.haversinMeters(GeoEncodingUtils.decodeLatitude(GeoEncodingUtils.encodeLatitude(-90)) , GeoEncodingUtils.decodeLongitude(GeoEncodingUtils.encodeLongitude(0)), -90,0);
distance2 = SloppyMath.haversinMeters(GeoEncodingUtils.decodeLatitude(GeoEncodingUtils.encodeLatitude(-45)) , GeoEncodingUtils.decodeLongitude(GeoEncodingUtils.encodeLongitude(-45)), -90,0);
CheckHits.checkEqual(q,
new ScoreDoc[] {
new ScoreDoc(1, (float) (3f * (10000. / (10000. + distance1)))),
new ScoreDoc(4, (float) (3f * (10000. / (10000. + distance2))))
},
topHits.scoreDocs);
reader.close();
w.close();
dir.close();
}
public void testRandom() throws IOException {
Directory dir = newDirectory();
IndexWriter w = new IndexWriter(dir, newIndexWriterConfig()
.setMergePolicy(newLogMergePolicy(random().nextBoolean())));
Document doc = new Document();
LatLonPoint point = new LatLonPoint("foo", 0., 0.);
doc.add(point);
LatLonDocValuesField docValue = new LatLonDocValuesField("foo", 0., 0.);
doc.add(docValue);
int numDocs = atLeast(1000);
for (int i = 0; i < numDocs; ++i) {
double lat = random().nextDouble() * 180 - 90;
double lon = random().nextDouble() * 360 - 180;
point.setLocationValue(lat, lon);
docValue.setLocationValue(lat, lon);
w.addDocument(doc);
}
IndexReader reader = DirectoryReader.open(w);
IndexSearcher searcher = newSearcher(reader);
int numIters = atLeast(3);
for (int iter = 0; iter < numIters; ++iter) {
double lat = random().nextDouble() * 180 - 90;
double lon = random().nextDouble() * 360 - 180;
double pivotDistance = random().nextDouble() * random().nextDouble() * Math.PI * GeoUtils.EARTH_MEAN_RADIUS_METERS;
float boost = (1 + random().nextInt(10)) / 3f;
Query q = LatLonPoint.newDistanceFeatureQuery("foo", boost, lat, lon, pivotDistance);
CheckHits.checkTopScores(random(), q, searcher);
}
reader.close();
w.close();
dir.close();
}
public void testCompareSorting() throws IOException {
Directory dir = newDirectory();
RandomIndexWriter w = new RandomIndexWriter(random(), dir, newIndexWriterConfig()
.setMergePolicy(newLogMergePolicy(random().nextBoolean())));
Document doc = new Document();
LatLonPoint point = new LatLonPoint("foo", 0., 0.);
doc.add(point);
LatLonDocValuesField docValue = new LatLonDocValuesField("foo", 0., 0.);
doc.add(docValue);
int numDocs = atLeast(10000);
for (int i = 0; i < numDocs; ++i) {
double lat = random().nextDouble() * 180 - 90;
double lon = random().nextDouble() * 360 - 180;
point.setLocationValue(lat, lon);
docValue.setLocationValue(lat, lon);
w.addDocument(doc);
}
DirectoryReader reader = w.getReader();
IndexSearcher searcher = newSearcher(reader);
double lat = random().nextDouble() * 180 - 90;
double lon = random().nextDouble() * 360 - 180;
double pivotDistance = random().nextDouble() * random().nextDouble() * GeoUtils.EARTH_MEAN_RADIUS_METERS * Math.PI;
float boost = (1 + random().nextInt(10)) / 3f;
Query query1 = LatLonPoint.newDistanceFeatureQuery("foo", boost, lat, lon, pivotDistance);
Sort sort1 = new Sort(SortField.FIELD_SCORE, LatLonDocValuesField.newDistanceSort("foo", lat, lon));
Query query2 = new MatchAllDocsQuery();
Sort sort2 = new Sort(LatLonDocValuesField.newDistanceSort("foo", lat, lon));
TopDocs topDocs1 = searcher.search(query1, 10, sort1);
TopDocs topDocs2 = searcher.search(query2, 10, sort2);
for (int i =0; i< 10; i++) {
assertTrue(topDocs1.scoreDocs[i].doc == topDocs2.scoreDocs[i].doc);
}
reader.close();
w.close();
dir.close();
}
}