Skip to content

Commit

Permalink
public API for filter queries
Browse files Browse the repository at this point in the history
  • Loading branch information
tzaeschke committed Nov 10, 2019
1 parent f205164 commit 404aa70
Show file tree
Hide file tree
Showing 12 changed files with 179 additions and 16 deletions.
7 changes: 5 additions & 2 deletions README.md
Expand Up @@ -22,7 +22,7 @@ Maven:
<dependency>
<groupId>ch.ethz.globis.phtree</groupId>
<artifactId>phtree</artifactId>
<version>2.3.0</version>
<version>2.4.0</version>
</dependency>
```

Expand All @@ -33,12 +33,15 @@ A C++ version of the PH-Tree (with slightly different design) is available [here

# News

### 2019-11-10
Release 2.4.0
- Added missing public API for filtered queries: `PhTree.query(min, max, filter)`

### 2019-03-19
Release 2.3.0
- Added missing compute functions for `PhTreeF`, `PhTreeSolid` and `PhTreeSolidF`
- Fixed bug in `compute()`/`computeIfPresent()` in V13


### 2019-03-15

Release 2.2.0
Expand Down
15 changes: 14 additions & 1 deletion src/main/java/ch/ethz/globis/phtree/PhTree.java
Expand Up @@ -113,6 +113,18 @@ public interface PhTree<T> {
*/
PhQuery<T> query(long[] min, long[] max);

/**
* Performs a rectangular window query. The parameters are the min and max keys which
* contain the minimum respectively the maximum keys in every dimension.
* @param min Minimum values
* @param max Maximum values
* @param filter A filter function. The iterator will only return results that match the filter.
* @return Result iterator.
*/
default PhQuery<T> query(long[] min, long[] max, PhFilter filter) {
throw new UnsupportedOperationException("This is only supported in V13, V16 and V16HD.");
}

/**
*
* @return the number of dimensions of the tree
Expand Down Expand Up @@ -278,8 +290,9 @@ interface PhQuery<T> extends PhIterator<T> {
* Reset the query with the new 'min' and 'max' boundaries.
* @param min min values
* @param max max values
* @return the query itself
*/
void reset(long[] min, long[] max);
PhQuery<T> reset(long[] min, long[] max);
}

/**
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/ch/ethz/globis/phtree/v11/PhIteratorNoGC.java
Expand Up @@ -82,7 +82,7 @@ public PhIteratorNoGC(PhTree11<T> pht, PhFilter checker) {
}

@Override
public void reset(long[] rangeMin, long[] rangeMax) {
public PhIteratorNoGC<T> reset(long[] rangeMin, long[] rangeMax) {
this.rangeMin = rangeMin;
this.rangeMax = rangeMax;
this.stack.size = 0;
Expand All @@ -91,11 +91,12 @@ public void reset(long[] rangeMin, long[] rangeMax) {
if (pht.getRoot() == null) {
//empty index
isFinished = true;
return;
return this;
}

stack.prepareAndPush(pht.getRoot());
findNextElement();
return this;
}

private void findNextElement() {
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/ch/ethz/globis/phtree/v13/PhIteratorNoGC.java
Expand Up @@ -84,7 +84,7 @@ public PhIteratorNoGC(PhTree13<T> pht, PhFilter checker) {
}

@Override
public void reset(long[] rangeMin, long[] rangeMax) {
public PhIteratorNoGC<T> reset(long[] rangeMin, long[] rangeMax) {
this.rangeMin = rangeMin;
this.rangeMax = rangeMax;
this.stack.size = 0;
Expand All @@ -93,11 +93,12 @@ public void reset(long[] rangeMin, long[] rangeMax) {
if (pht.getRoot() == null) {
//empty index
isFinished = true;
return;
return this;
}

stack.prepareAndPush(pht.getRoot());
findNextElement();
return this;
}

private void findNextElement() {
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/ch/ethz/globis/phtree/v13/PhTree13.java
Expand Up @@ -556,6 +556,25 @@ public PhQuery<T> query(long[] min, long[] max) {
return q;
}

/**
* Performs a rectangular window query. The parameters are the min and max keys which
* contain the minimum respectively the maximum keys in every dimension.
* @param min Minimum values
* @param max Maximum values
* @param filter A filter function. The iterator will only return results that match the filter.
* @return Result iterator.
*/
@Override
public PhQuery<T> query(long[] min, long[] max, PhFilter filter) {
if (min.length != dims || max.length != dims) {
throw new IllegalArgumentException("Invalid number of arguments: " + min.length +
" / " + max.length + " DIM=" + dims);
}
PhQuery<T> q = new PhIteratorNoGC<>(this, filter);
q.reset(min, max);
return q;
}

/**
* Performs a rectangular window query. The parameters are the min and max keys which
* contain the minimum respectively the maximum keys in every dimension.
Expand Down
Expand Up @@ -84,7 +84,7 @@ public PhIteratorNoGC(PhTree13SP<T> pht, PhFilter checker) {
}

@Override
public void reset(long[] rangeMin, long[] rangeMax) {
public PhIteratorNoGC<T> reset(long[] rangeMin, long[] rangeMax) {
this.rangeMin = rangeMin;
this.rangeMax = rangeMax;
this.stack.size = 0;
Expand All @@ -93,11 +93,12 @@ public void reset(long[] rangeMin, long[] rangeMax) {
if (pht.getRoot() == null) {
//empty index
isFinished = true;
return;
return this;
}

stack.prepareAndPush(pht.getRoot());
findNextElement();
return this;
}

private void findNextElement() {
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/ch/ethz/globis/phtree/v16/PhIteratorNoGC.java
Expand Up @@ -94,7 +94,7 @@ public PhIteratorNoGC(PhTree16<T> pht, PhFilter checker) {
}

@Override
public void reset(long[] rangeMin, long[] rangeMax) {
public PhIteratorNoGC<T> reset(long[] rangeMin, long[] rangeMax) {
this.rangeMin = rangeMin;
this.rangeMax = rangeMax;
this.stack.size = 0;
Expand All @@ -103,11 +103,12 @@ public void reset(long[] rangeMin, long[] rangeMax) {
if (pht.getRoot() == null) {
//empty index
isFinished = true;
return;
return this;
}

stack.prepareAndPush(pht.getRoot(), null);
findNextElement();
return this;
}

private void findNextElement() {
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/ch/ethz/globis/phtree/v16/PhTree16.java
Expand Up @@ -607,6 +607,25 @@ public PhQuery<T> query(long[] min, long[] max) {
return q;
}

/**
* Performs a rectangular window query. The parameters are the min and max keys which
* contain the minimum respectively the maximum keys in every dimension.
* @param min Minimum values
* @param max Maximum values
* @param filter A filter function. The iterator will only return results that match the filter.
* @return Result iterator.
*/
@Override
public PhQuery<T> query(long[] min, long[] max, PhFilter filter) {
if (min.length != dims || max.length != dims) {
throw new IllegalArgumentException("Invalid number of arguments: " + min.length +
" / " + max.length + " DIM=" + dims);
}
PhQuery<T> q = new PhIteratorNoGC<>(this, filter);
q.reset(min, max);
return q;
}

/**
* Performs a rectangular window query. The parameters are the min and max keys which
* contain the minimum respectively the maximum keys in every dimension.
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/ch/ethz/globis/phtree/v16hd/PhIteratorNoGC.java
Expand Up @@ -93,7 +93,7 @@ public PhIteratorNoGC(PhTree16HD<T> pht, PhFilter checker) {
}

@Override
public void reset(long[] rangeMin, long[] rangeMax) {
public PhIteratorNoGC<T> reset(long[] rangeMin, long[] rangeMax) {
this.rangeMin = rangeMin;
this.rangeMax = rangeMax;
this.stack.size = 0;
Expand All @@ -102,11 +102,12 @@ public void reset(long[] rangeMin, long[] rangeMax) {
if (pht.getRoot() == null) {
//empty index
isFinished = true;
return;
return this;
}

stack.prepareAndPush(pht.getRoot(), null);
findNextElement();
return this;
}

private void findNextElement() {
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/ch/ethz/globis/phtree/v16hd/PhTree16HD.java
Expand Up @@ -455,6 +455,25 @@ public PhQuery<T> query(long[] min, long[] max) {
return q;
}

/**
* Performs a rectangular window query. The parameters are the min and max keys which
* contain the minimum respectively the maximum keys in every dimension.
* @param min Minimum values
* @param max Maximum values
* @param filter A filter function. The iterator will only return results that match the filter.
* @return Result iterator.
*/
@Override
public PhQuery<T> query(long[] min, long[] max, PhFilter filter) {
if (min.length != dims || max.length != dims) {
throw new IllegalArgumentException("Invalid number of arguments: " + min.length +
" / " + max.length + " DIM=" + dims);
}
PhQuery<T> q = new PhIteratorNoGC<>(this, filter);
q.reset(min, max);
return q;
}

/**
* Performs a rectangular window query. The parameters are the min and max keys which
* contain the minimum respectively the maximum keys in every dimension.
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/ch/ethz/globis/phtree/v8/PhIteratorNoGC.java
Expand Up @@ -95,7 +95,7 @@ public PhIteratorNoGC(PhTree8<T> pht, PhFilter checker) {
}

@Override
public void reset(long[] rangeMin, long[] rangeMax) {
public PhIteratorNoGC<T> reset(long[] rangeMin, long[] rangeMax) {
this.rangeMin = rangeMin;
this.rangeMax = rangeMax;
this.stack.size = 0;
Expand All @@ -104,14 +104,15 @@ public void reset(long[] rangeMin, long[] rangeMax) {
if (pht.getRoot() == null) {
//empty index
isFinished = true;
return;
return this;
}

if (stack.prepare(pht.getRoot())) {
findNextElement();
} else {
isFinished = true;
}
return this;
}

private void findNextElement() {
Expand Down
86 changes: 85 additions & 1 deletion src/test/java/ch/ethz/globis/phtree/test/TestRangeQuery.java
Expand Up @@ -15,14 +15,20 @@
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.function.IntFunction;

import org.junit.Test;

import ch.ethz.globis.phtree.PhDistanceL;
import ch.ethz.globis.phtree.PhFilter;
import ch.ethz.globis.phtree.PhRangeQuery;
import ch.ethz.globis.phtree.PhTree;
import ch.ethz.globis.phtree.PhTree.PhIterator;
import ch.ethz.globis.phtree.test.util.TestUtil;
import ch.ethz.globis.phtree.util.Bits;
import ch.ethz.globis.phtree.v13.PhTree13;
import ch.ethz.globis.phtree.v16.PhTree16;
import ch.ethz.globis.phtree.v16hd.PhTree16HD;

public class TestRangeQuery {

Expand Down Expand Up @@ -112,7 +118,7 @@ public void testQueryND64Random1() {
final int range = MAXV/2;
final Random R = new Random(0);
for (int d = 0; d < LOOP; d++) {
PhTree<Object> ind = TestUtil.newTree(DIM, 32);
PhTree<Object> ind = TestUtil.newTree(DIM);
PhRangeQuery<Object> q = ind.rangeQuery(1, new long[DIM]);
for (int i = 0; i < N; i++) {
long[] v = new long[DIM];
Expand Down Expand Up @@ -142,6 +148,76 @@ public void testQueryND64Random1() {
}
}

@Test
public void testQueryNDRandomFilter13() {
testQueryNDRandomFilter(dim -> new PhTree13<Object>(dim));
}

@Test
public void testQueryNDRandomFilter16() {
testQueryNDRandomFilter(dim -> new PhTree16<Object>(dim));
}

@Test
public void testQueryNDRandomFilter16HD() {
testQueryNDRandomFilter(dim -> new PhTree16HD<Object>(dim));
}

private void testQueryNDRandomFilter(IntFunction<PhTree<Object>> factory) {
final int DIM = 5;
final int LOOP = 10;
final int N = 1000;
final int NQ = 1000;
final int MAXV = 1000;
final int range = MAXV/2;
final Random R = new Random(0);

for (int d = 0; d < LOOP; d++) {
long[] v = new long[DIM];
long[] min = new long[DIM];
long[] max = new long[DIM];
PhFilter filter = filter(v, range);
PhTree<Object> ind = factory.apply(DIM);
PhTree.PhQuery<Object> q = ind.query(min, max, filter);
for (int i = 0; i < N; i++) {
long[] v2 = new long[DIM];
for (int j = 0; j < DIM; j++) {
v2[j] = Math.abs(R.nextInt(MAXV));
}
ind.put(v2, null);
}
for (int i = 0; i < NQ; i++) {
for (int j = 0; j < DIM; j++) {
v[j] = Math.abs(R.nextInt(MAXV));
min[j] = v[j]-range;
max[j] = v[j]+range;
}
long[] exp = rangeQuery(ind, range, v).get(0);
List<long[]> nnList = toList(q.reset(min, max));
assertTrue("i=" + i + " d=" + d, !nnList.isEmpty());
long[] nn = nnList.get(0);
check(v, exp, nn);
}
}
}

private PhFilter filter(long[] v, double radius) {
return new PhFilter() {
private static final long serialVersionUID = 1L;
@Override
public boolean isValid(int bitsToIgnore, long[] prefix) {
//if (true) throw new IllegalStateException();
return true;
}

@Override
public boolean isValid(long[] key) {
//return true;
return PhDistanceL.THIS.dist(v, key) <= radius;
}
};
}

/**
* This used to return an empty result set.
*/
Expand Down Expand Up @@ -473,6 +549,14 @@ private boolean contains(List<long[]> l, long ... v) {
return false;
}

private List<long[]> toList(PhTree.PhQuery<?> q) {
ArrayList<long[]> ret = new ArrayList<>();
while (q.hasNext()) {
ret.add(q.nextKey());
}
return ret;
}

private List<long[]> toList(PhRangeQuery<?> q) {
ArrayList<long[]> ret = new ArrayList<>();
while (q.hasNext()) {
Expand Down

0 comments on commit 404aa70

Please sign in to comment.