blob: 4e37f5d9a788fbbac289fe5624238bceca8b3101 [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.spatial.prefix;
import java.util.ArrayList;
import java.util.List;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermInSetQuery;
import org.apache.lucene.spatial.prefix.tree.Cell;
import org.apache.lucene.spatial.prefix.tree.CellIterator;
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
import org.apache.lucene.spatial.query.SpatialArgs;
import org.apache.lucene.spatial.query.SpatialOperation;
import org.apache.lucene.spatial.query.UnsupportedSpatialOperation;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.BytesRefBuilder;
import org.locationtech.spatial4j.shape.Point;
import org.locationtech.spatial4j.shape.Shape;
/**
* A basic implementation of {@link PrefixTreeStrategy} using a large
* {@link TermInSetQuery} of all the cells from
* {@link SpatialPrefixTree#getTreeCellIterator(org.locationtech.spatial4j.shape.Shape, int)}.
* It only supports the search of indexed Point shapes.
* <p>
* The precision of query shapes (distErrPct) is an important factor in using
* this Strategy. If the precision is too precise then it will result in many
* terms which will amount to a slower query.
*
* @lucene.experimental
*/
public class TermQueryPrefixTreeStrategy extends PrefixTreeStrategy {
protected boolean simplifyIndexedCells = false;
public TermQueryPrefixTreeStrategy(SpatialPrefixTree grid, String fieldName) {
super(grid, fieldName);
}
@Override
protected CellToBytesRefIterator newCellToBytesRefIterator() {
//Ensure we don't have leaves, as this strategy doesn't handle them.
return new CellToBytesRefIterator() {
@Override
public BytesRef next() {
if (!cellIter.hasNext()) {
return null;
}
return cellIter.next().getTokenBytesNoLeaf(bytesRef);
}
};
}
@Override
public Query makeQuery(SpatialArgs args) {
final SpatialOperation op = args.getOperation();
if (op != SpatialOperation.Intersects)
throw new UnsupportedSpatialOperation(op);
Shape shape = args.getShape();
int detailLevel = grid.getLevelForDistance(args.resolveDistErr(ctx, distErrPct));
//--get a List of BytesRef for each term we want (no parents, no leaf bytes))
final int GUESS_NUM_TERMS;
if (shape instanceof Point)
GUESS_NUM_TERMS = detailLevel;//perfect guess
else
GUESS_NUM_TERMS = 4096;//should this be a method on SpatialPrefixTree?
BytesRefBuilder masterBytes = new BytesRefBuilder();//shared byte array for all terms
List<BytesRef> terms = new ArrayList<>(GUESS_NUM_TERMS);
CellIterator cells = grid.getTreeCellIterator(shape, detailLevel);
while (cells.hasNext()) {
Cell cell = cells.next();
if (!cell.isLeaf())
continue;
BytesRef term = cell.getTokenBytesNoLeaf(null);//null because we want a new BytesRef
//We copy out the bytes because it may be re-used across the iteration. This also gives us the opportunity
// to use one contiguous block of memory for the bytes of all terms we need.
masterBytes.grow(masterBytes.length() + term.length);
masterBytes.append(term);
term.bytes = null;//don't need; will reset later
term.offset = masterBytes.length() - term.length;
terms.add(term);
}
//doing this now because if we did earlier, it's possible the bytes needed to grow()
for (BytesRef byteRef : terms) {
byteRef.bytes = masterBytes.bytes();
}
//unfortunately TermsQuery will needlessly sort & dedupe
//TODO an automatonQuery might be faster?
return new TermInSetQuery(getFieldName(), terms);
}
}