mirror of https://github.com/apache/druid.git
Add option for select query to get next page without modifying returned paging identifiers
This commit is contained in:
parent
2fc5918e69
commit
29bb00535b
|
@ -167,5 +167,15 @@ This can be used with the next query's pagingSpec:
|
||||||
"pagingSpec":{"pagingIdentifiers": {"wikipedia_2012-12-29T00:00:00.000Z_2013-01-10T08:00:00.000Z_2013-01-10T08:13:47.830Z_v9" : 5}, "threshold":5}
|
"pagingSpec":{"pagingIdentifiers": {"wikipedia_2012-12-29T00:00:00.000Z_2013-01-10T08:00:00.000Z_2013-01-10T08:13:47.830Z_v9" : 5}, "threshold":5}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Note that in the second query, an offset is specified and that it is 1 greater than the largest offset found in the initial results. To return the next "page", this offset must be incremented by 1 (should be decremented by 1 for descending query), with each new query. When an empty results set is received, the very last page has been returned.
|
Note that in the second query, an offset is specified and that it is 1 greater than the largest offset found in the initial results. To return the next "page", this offset must be incremented by 1 (should be decremented by 1 for descending query), with each new query, but with option `fromNext` enabled, this operation is not needed. When an empty results set is received, the very last page has been returned.
|
||||||
|
|
||||||
|
`fromNext` options is in pagingSpec:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
...
|
||||||
|
"pagingSpec":{"pagingIdentifiers": {}, "threshold":5, "fromNext": true}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
|
@ -21,28 +21,60 @@ package io.druid.query.select;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.primitives.Ints;
|
import com.google.common.primitives.Ints;
|
||||||
import com.metamx.common.StringUtils;
|
import com.metamx.common.StringUtils;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
public class PagingSpec
|
public class PagingSpec
|
||||||
{
|
{
|
||||||
private final LinkedHashMap<String, Integer> pagingIdentifiers;
|
public static PagingSpec newSpec(int threshold)
|
||||||
|
{
|
||||||
|
return new PagingSpec(null, threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, Integer> merge(Iterable<Map<String, Integer>> cursors)
|
||||||
|
{
|
||||||
|
Map<String, Integer> next = Maps.newHashMap();
|
||||||
|
for (Map<String, Integer> cursor : cursors) {
|
||||||
|
for (Map.Entry<String, Integer> entry : cursor.entrySet()) {
|
||||||
|
next.put(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, Integer> next(Map<String, Integer> cursor, boolean descending)
|
||||||
|
{
|
||||||
|
for (Map.Entry<String, Integer> entry : cursor.entrySet()) {
|
||||||
|
entry.setValue(descending ? entry.getValue() - 1 : entry.getValue() + 1);
|
||||||
|
}
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Map<String, Integer> pagingIdentifiers;
|
||||||
private final int threshold;
|
private final int threshold;
|
||||||
|
private final boolean fromNext;
|
||||||
|
|
||||||
@JsonCreator
|
@JsonCreator
|
||||||
public PagingSpec(
|
public PagingSpec(
|
||||||
@JsonProperty("pagingIdentifiers") LinkedHashMap<String, Integer> pagingIdentifiers,
|
@JsonProperty("pagingIdentifiers") Map<String, Integer> pagingIdentifiers,
|
||||||
@JsonProperty("threshold") int threshold
|
@JsonProperty("threshold") int threshold,
|
||||||
|
@JsonProperty("fromNext") boolean fromNext
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
this.pagingIdentifiers = pagingIdentifiers == null ? new LinkedHashMap<String, Integer>() : pagingIdentifiers;
|
this.pagingIdentifiers = pagingIdentifiers == null ? Maps.<String, Integer>newHashMap() : pagingIdentifiers;
|
||||||
this.threshold = threshold;
|
this.threshold = threshold;
|
||||||
|
this.fromNext = fromNext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PagingSpec(Map<String, Integer> pagingIdentifiers, int threshold)
|
||||||
|
{
|
||||||
|
this(pagingIdentifiers, threshold, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
|
@ -57,6 +89,12 @@ public class PagingSpec
|
||||||
return threshold;
|
return threshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
public boolean isFromNext()
|
||||||
|
{
|
||||||
|
return fromNext;
|
||||||
|
}
|
||||||
|
|
||||||
public byte[] getCacheKey()
|
public byte[] getCacheKey()
|
||||||
{
|
{
|
||||||
final byte[][] pagingKeys = new byte[pagingIdentifiers.size()][];
|
final byte[][] pagingKeys = new byte[pagingIdentifiers.size()][];
|
||||||
|
@ -75,7 +113,7 @@ public class PagingSpec
|
||||||
|
|
||||||
final byte[] thresholdBytes = ByteBuffer.allocate(Ints.BYTES).putInt(threshold).array();
|
final byte[] thresholdBytes = ByteBuffer.allocate(Ints.BYTES).putInt(threshold).array();
|
||||||
|
|
||||||
final ByteBuffer queryCacheKey = ByteBuffer.allocate(pagingKeysSize + pagingValuesSize + thresholdBytes.length);
|
final ByteBuffer queryCacheKey = ByteBuffer.allocate(pagingKeysSize + pagingValuesSize + thresholdBytes.length + 1);
|
||||||
|
|
||||||
for (byte[] pagingKey : pagingKeys) {
|
for (byte[] pagingKey : pagingKeys) {
|
||||||
queryCacheKey.put(pagingKey);
|
queryCacheKey.put(pagingKey);
|
||||||
|
@ -86,22 +124,37 @@ public class PagingSpec
|
||||||
}
|
}
|
||||||
|
|
||||||
queryCacheKey.put(thresholdBytes);
|
queryCacheKey.put(thresholdBytes);
|
||||||
|
queryCacheKey.put(isFromNext() ? (byte) 0x01 : 0x00);
|
||||||
|
|
||||||
return queryCacheKey.array();
|
return queryCacheKey.array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PagingOffset getOffset(String identifier, boolean descending)
|
||||||
|
{
|
||||||
|
Integer offset = pagingIdentifiers.get(identifier);
|
||||||
|
if (offset == null) {
|
||||||
|
offset = PagingOffset.toOffset(0, descending);
|
||||||
|
} else if (fromNext) {
|
||||||
|
offset = descending ? offset - 1 : offset + 1;
|
||||||
|
}
|
||||||
|
return PagingOffset.of(offset, threshold);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o)
|
public boolean equals(Object o)
|
||||||
{
|
{
|
||||||
if (this == o) {
|
if (this == o) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!(o instanceof PagingSpec)) {
|
if (o == null || getClass() != o.getClass()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
PagingSpec that = (PagingSpec) o;
|
PagingSpec that = (PagingSpec) o;
|
||||||
|
|
||||||
|
if (fromNext != that.fromNext) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (threshold != that.threshold) {
|
if (threshold != that.threshold) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -117,6 +170,7 @@ public class PagingSpec
|
||||||
{
|
{
|
||||||
int result = pagingIdentifiers.hashCode();
|
int result = pagingIdentifiers.hashCode();
|
||||||
result = 31 * result + threshold;
|
result = 31 * result + threshold;
|
||||||
|
result = 31 * result + (fromNext ? 1 : 0);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,16 +180,7 @@ public class PagingSpec
|
||||||
return "PagingSpec{" +
|
return "PagingSpec{" +
|
||||||
"pagingIdentifiers=" + pagingIdentifiers +
|
"pagingIdentifiers=" + pagingIdentifiers +
|
||||||
", threshold=" + threshold +
|
", threshold=" + threshold +
|
||||||
|
", fromNext=" + fromNext +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
public PagingOffset getOffset(String identifier, boolean descending)
|
|
||||||
{
|
|
||||||
Integer offset = pagingIdentifiers.get(identifier);
|
|
||||||
if (offset == null) {
|
|
||||||
offset = PagingOffset.toOffset(0, descending);
|
|
||||||
}
|
|
||||||
return PagingOffset.of(offset, threshold);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,6 @@ package io.druid.query.select;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import com.google.common.io.CharSource;
|
import com.google.common.io.CharSource;
|
||||||
import com.metamx.common.guava.Sequences;
|
import com.metamx.common.guava.Sequences;
|
||||||
import io.druid.granularity.QueryGranularity;
|
import io.druid.granularity.QueryGranularity;
|
||||||
|
@ -53,14 +52,17 @@ import org.junit.AfterClass;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.Parameterized;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
|
@RunWith(Parameterized.class)
|
||||||
public class MultiSegmentSelectQueryTest
|
public class MultiSegmentSelectQueryTest
|
||||||
{
|
{
|
||||||
private static final SelectQueryQueryToolChest toolChest = new SelectQueryQueryToolChest(
|
private static final SelectQueryQueryToolChest toolChest = new SelectQueryQueryToolChest(
|
||||||
|
@ -188,51 +190,52 @@ public class MultiSegmentSelectQueryTest
|
||||||
IOUtils.closeQuietly(segment_override);
|
IOUtils.closeQuietly(segment_override);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Druids.SelectQueryBuilder builder =
|
@Parameterized.Parameters(name = "fromNext={0}")
|
||||||
Druids.newSelectQueryBuilder()
|
public static Iterable<Object[]> constructorFeeder() throws IOException
|
||||||
|
{
|
||||||
|
return QueryRunnerTestHelper.cartesian(Arrays.asList(false, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
private final boolean fromNext;
|
||||||
|
|
||||||
|
public MultiSegmentSelectQueryTest(boolean fromNext)
|
||||||
|
{
|
||||||
|
this.fromNext = fromNext;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Druids.SelectQueryBuilder newBuilder()
|
||||||
|
{
|
||||||
|
return Druids.newSelectQueryBuilder()
|
||||||
.dataSource(new TableDataSource(QueryRunnerTestHelper.dataSource))
|
.dataSource(new TableDataSource(QueryRunnerTestHelper.dataSource))
|
||||||
.intervals(SelectQueryRunnerTest.I_0112_0114)
|
.intervals(SelectQueryRunnerTest.I_0112_0114)
|
||||||
.granularity(QueryRunnerTestHelper.allGran)
|
.granularity(QueryRunnerTestHelper.allGran)
|
||||||
.dimensionSpecs(DefaultDimensionSpec.toSpec(QueryRunnerTestHelper.dimensions))
|
.dimensionSpecs(DefaultDimensionSpec.toSpec(QueryRunnerTestHelper.dimensions))
|
||||||
.pagingSpec(new PagingSpec(null, 3));
|
.pagingSpec(PagingSpec.newSpec(3));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAllGranularityAscending()
|
public void testAllGranularity()
|
||||||
{
|
{
|
||||||
SelectQuery query = builder.build();
|
runAllGranularityTest(
|
||||||
|
newBuilder().build(),
|
||||||
for (int[] expected : new int[][]{
|
new int[][]{
|
||||||
{2, -1, -1, -1, 3}, {3, 1, -1, -1, 3}, {-1, 3, 0, -1, 3}, {-1, -1, 3, -1, 3}, {-1, -1, 4, 1, 3},
|
{2, -1, -1, -1, 3}, {3, 1, -1, -1, 3}, {-1, 3, 0, -1, 3}, {-1, -1, 3, -1, 3}, {-1, -1, 4, 1, 3},
|
||||||
{-1, -1, -1, 4, 3}, {-1, -1, -1, 7, 3}, {-1, -1, -1, 10, 3}, {-1, -1, -1, 12, 2}, {-1, -1, -1, 13, 0}
|
{-1, -1, -1, 4, 3}, {-1, -1, -1, 7, 3}, {-1, -1, -1, 10, 3}, {-1, -1, -1, 12, 2}, {-1, -1, -1, 13, 0}
|
||||||
}) {
|
}
|
||||||
List<Result<SelectResultValue>> results = Sequences.toList(
|
|
||||||
runner.run(query, ImmutableMap.of()),
|
|
||||||
Lists.<Result<SelectResultValue>>newArrayList()
|
|
||||||
);
|
);
|
||||||
Assert.assertEquals(1, results.size());
|
|
||||||
|
|
||||||
SelectResultValue value = results.get(0).getValue();
|
runAllGranularityTest(
|
||||||
Map<String, Integer> pagingIdentifiers = value.getPagingIdentifiers();
|
newBuilder().descending(true).build(),
|
||||||
for (int i = 0; i < expected.length - 1; i++) {
|
new int[][]{
|
||||||
if (expected[i] >= 0) {
|
|
||||||
Assert.assertEquals(expected[i], pagingIdentifiers.get(segmentIdentifiers.get(i)).intValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Assert.assertEquals(expected[expected.length - 1], value.getEvents().size());
|
|
||||||
|
|
||||||
query = query.withPagingSpec(toNextPager(3, query.isDescending(), pagingIdentifiers));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAllGranularityDescending()
|
|
||||||
{
|
|
||||||
SelectQuery query = builder.descending(true).build();
|
|
||||||
|
|
||||||
for (int[] expected : new int[][]{
|
|
||||||
{0, 0, 0, -3, 3}, {0, 0, 0, -6, 3}, {0, 0, 0, -9, 3}, {0, 0, 0, -12, 3}, {0, 0, -2, -13, 3},
|
{0, 0, 0, -3, 3}, {0, 0, 0, -6, 3}, {0, 0, 0, -9, 3}, {0, 0, 0, -12, 3}, {0, 0, -2, -13, 3},
|
||||||
{0, 0, -5, 0, 3}, {0, -3, 0, 0, 3}, {-2, -4, 0, 0, 3}, {-4, 0, 0, 0, 2}, {-5, 0, 0, 0, 0}
|
{0, 0, -5, 0, 3}, {0, -3, 0, 0, 3}, {-2, -4, 0, 0, 3}, {-4, 0, 0, 0, 2}, {-5, 0, 0, 0, 0}
|
||||||
}) {
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runAllGranularityTest(SelectQuery query, int[][] expectedOffsets)
|
||||||
|
{
|
||||||
|
for (int[] expected : expectedOffsets) {
|
||||||
List<Result<SelectResultValue>> results = Sequences.toList(
|
List<Result<SelectResultValue>> results = Sequences.toList(
|
||||||
runner.run(query, ImmutableMap.of()),
|
runner.run(query, ImmutableMap.of()),
|
||||||
Lists.<Result<SelectResultValue>>newArrayList()
|
Lists.<Result<SelectResultValue>>newArrayList()
|
||||||
|
@ -242,59 +245,44 @@ public class MultiSegmentSelectQueryTest
|
||||||
SelectResultValue value = results.get(0).getValue();
|
SelectResultValue value = results.get(0).getValue();
|
||||||
Map<String, Integer> pagingIdentifiers = value.getPagingIdentifiers();
|
Map<String, Integer> pagingIdentifiers = value.getPagingIdentifiers();
|
||||||
|
|
||||||
for (int i = 0; i < expected.length - 1; i++) {
|
Map<String, Integer> merged = PagingSpec.merge(Arrays.asList(pagingIdentifiers));
|
||||||
if (expected[i] < 0) {
|
|
||||||
Assert.assertEquals(expected[i], pagingIdentifiers.get(segmentIdentifiers.get(i)).intValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Assert.assertEquals(expected[expected.length - 1], value.getEvents().size());
|
|
||||||
|
|
||||||
query = query.withPagingSpec(toNextPager(3, query.isDescending(), pagingIdentifiers));
|
for (int i = 0; i < 4; i++) {
|
||||||
|
if (query.isDescending() ^ expected[i] >= 0) {
|
||||||
|
Assert.assertEquals(
|
||||||
|
expected[i], pagingIdentifiers.get(segmentIdentifiers.get(i)).intValue()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assert.assertEquals(expected[4], value.getEvents().size());
|
||||||
|
|
||||||
|
query = query.withPagingSpec(toNextCursor(merged, query, 3));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDayGranularityAscending()
|
public void testDayGranularity()
|
||||||
{
|
{
|
||||||
SelectQuery query = builder.granularity(QueryRunnerTestHelper.dayGran).build();
|
runDayGranularityTest(
|
||||||
|
newBuilder().granularity(QueryRunnerTestHelper.dayGran).build(),
|
||||||
for (int[] expected : new int[][]{
|
new int[][]{
|
||||||
{2, -1, -1, 2, 3, 0, 0, 3}, {3, 1, -1, 5, 1, 2, 0, 3}, {-1, 3, 0, 8, 0, 2, 1, 3},
|
{2, -1, -1, 2, 3, 0, 0, 3}, {3, 1, -1, 5, 1, 2, 0, 3}, {-1, 3, 0, 8, 0, 2, 1, 3},
|
||||||
{-1, -1, 3, 11, 0, 0, 3, 3}, {-1, -1, 4, 12, 0, 0, 1, 1}, {-1, -1, 5, 13, 0, 0, 0, 0}
|
{-1, -1, 3, 11, 0, 0, 3, 3}, {-1, -1, 4, 12, 0, 0, 1, 1}, {-1, -1, 5, 13, 0, 0, 0, 0}
|
||||||
}) {
|
}
|
||||||
List<Result<SelectResultValue>> results = Sequences.toList(
|
|
||||||
runner.run(query, ImmutableMap.of()),
|
|
||||||
Lists.<Result<SelectResultValue>>newArrayList()
|
|
||||||
);
|
);
|
||||||
Assert.assertEquals(2, results.size());
|
|
||||||
|
|
||||||
SelectResultValue value0 = results.get(0).getValue();
|
runDayGranularityTest(
|
||||||
SelectResultValue value1 = results.get(1).getValue();
|
newBuilder().granularity(QueryRunnerTestHelper.dayGran).descending(true).build(),
|
||||||
|
new int[][]{
|
||||||
Map<String, Integer> pagingIdentifiers0 = value0.getPagingIdentifiers();
|
|
||||||
Map<String, Integer> pagingIdentifiers1 = value1.getPagingIdentifiers();
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++) {
|
|
||||||
if (expected[i] >= 0) {
|
|
||||||
Map<String, Integer> paging = i < 3 ? pagingIdentifiers0 : pagingIdentifiers1;
|
|
||||||
Assert.assertEquals(expected[i], paging.get(segmentIdentifiers.get(i)).intValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
query = query.withPagingSpec(toNextPager(3, query.isDescending(), pagingIdentifiers0, pagingIdentifiers1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDayGranularityDescending()
|
|
||||||
{
|
|
||||||
QueryGranularity granularity = QueryRunnerTestHelper.dayGran;
|
|
||||||
SelectQuery query = builder.granularity(granularity).descending(true).build();
|
|
||||||
|
|
||||||
for (int[] expected : new int[][]{
|
|
||||||
{0, 0, -3, -3, 0, 0, 3, 3}, {0, -1, -5, -6, 0, 1, 2, 3}, {0, -4, 0, -9, 0, 3, 0, 3},
|
{0, 0, -3, -3, 0, 0, 3, 3}, {0, -1, -5, -6, 0, 1, 2, 3}, {0, -4, 0, -9, 0, 3, 0, 3},
|
||||||
{-3, 0, 0, -12, 3, 0, 0, 3}, {-4, 0, 0, -13, 1, 0, 0, 1}, {-5, 0, 0, -14, 0, 0, 0, 0}
|
{-3, 0, 0, -12, 3, 0, 0, 3}, {-4, 0, 0, -13, 1, 0, 0, 1}, {-5, 0, 0, -14, 0, 0, 0, 0}
|
||||||
}) {
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runDayGranularityTest(SelectQuery query, int[][] expectedOffsets)
|
||||||
|
{
|
||||||
|
for (int[] expected : expectedOffsets) {
|
||||||
List<Result<SelectResultValue>> results = Sequences.toList(
|
List<Result<SelectResultValue>> results = Sequences.toList(
|
||||||
runner.run(query, ImmutableMap.of()),
|
runner.run(query, ImmutableMap.of()),
|
||||||
Lists.<Result<SelectResultValue>>newArrayList()
|
Lists.<Result<SelectResultValue>>newArrayList()
|
||||||
|
@ -307,26 +295,23 @@ public class MultiSegmentSelectQueryTest
|
||||||
Map<String, Integer> pagingIdentifiers0 = value0.getPagingIdentifiers();
|
Map<String, Integer> pagingIdentifiers0 = value0.getPagingIdentifiers();
|
||||||
Map<String, Integer> pagingIdentifiers1 = value1.getPagingIdentifiers();
|
Map<String, Integer> pagingIdentifiers1 = value1.getPagingIdentifiers();
|
||||||
|
|
||||||
|
Map<String, Integer> merged = PagingSpec.merge(Arrays.asList(pagingIdentifiers0, pagingIdentifiers1));
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
if (expected[i] < 0) {
|
if (query.isDescending() ^ expected[i] >= 0) {
|
||||||
Map<String, Integer> paging = i < 3 ? pagingIdentifiers1 : pagingIdentifiers0;
|
Assert.assertEquals(expected[i], merged.get(segmentIdentifiers.get(i)).intValue());
|
||||||
Assert.assertEquals(expected[i], paging.get(segmentIdentifiers.get(i)).intValue());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
query = query.withPagingSpec(toNextPager(3, query.isDescending(), pagingIdentifiers0, pagingIdentifiers1));
|
query = query.withPagingSpec(toNextCursor(merged, query, 3));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SafeVarargs
|
private PagingSpec toNextCursor(Map<String, Integer> merged, SelectQuery query, int threshold)
|
||||||
private final PagingSpec toNextPager(int threshold, boolean descending, Map<String, Integer>... pagers)
|
|
||||||
{
|
{
|
||||||
LinkedHashMap<String, Integer> next = Maps.newLinkedHashMap();
|
if (!fromNext) {
|
||||||
for (Map<String, Integer> pager : pagers) {
|
merged = PagingSpec.next(merged, query.isDescending());
|
||||||
for (Map.Entry<String, Integer> entry : pager.entrySet()) {
|
}
|
||||||
next.put(entry.getKey(), descending ? entry.getValue() - 1 : entry.getValue() + 1);
|
return new PagingSpec(merged, threshold, fromNext);
|
||||||
}
|
|
||||||
}
|
|
||||||
return new PagingSpec(next, threshold);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import com.google.common.collect.ObjectArrays;
|
||||||
import com.metamx.common.ISE;
|
import com.metamx.common.ISE;
|
||||||
import com.metamx.common.guava.Sequences;
|
import com.metamx.common.guava.Sequences;
|
||||||
import io.druid.jackson.DefaultObjectMapper;
|
import io.druid.jackson.DefaultObjectMapper;
|
||||||
|
import io.druid.query.Druids;
|
||||||
import io.druid.query.QueryRunner;
|
import io.druid.query.QueryRunner;
|
||||||
import io.druid.query.QueryRunnerTestHelper;
|
import io.druid.query.QueryRunnerTestHelper;
|
||||||
import io.druid.query.Result;
|
import io.druid.query.Result;
|
||||||
|
@ -53,7 +54,6 @@ import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -126,20 +126,24 @@ public class SelectQueryRunnerTest
|
||||||
this.descending = descending;
|
this.descending = descending;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Druids.SelectQueryBuilder newTestQuery() {
|
||||||
|
return Druids.newSelectQueryBuilder()
|
||||||
|
.dataSource(new TableDataSource(QueryRunnerTestHelper.dataSource))
|
||||||
|
.dimensionSpecs(DefaultDimensionSpec.toSpec(Arrays.<String>asList()))
|
||||||
|
.metrics(Arrays.<String>asList())
|
||||||
|
.intervals(QueryRunnerTestHelper.fullOnInterval)
|
||||||
|
.granularity(QueryRunnerTestHelper.allGran)
|
||||||
|
.pagingSpec(PagingSpec.newSpec(3))
|
||||||
|
.descending(descending);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFullOnSelect()
|
public void testFullOnSelect()
|
||||||
{
|
{
|
||||||
SelectQuery query = new SelectQuery(
|
SelectQuery query = newTestQuery()
|
||||||
new TableDataSource(QueryRunnerTestHelper.dataSource),
|
.intervals(I_0112_0114)
|
||||||
I_0112_0114,
|
.build();
|
||||||
descending,
|
|
||||||
null,
|
|
||||||
QueryRunnerTestHelper.allGran,
|
|
||||||
DefaultDimensionSpec.toSpec(Arrays.<String>asList()),
|
|
||||||
Arrays.<String>asList(),
|
|
||||||
new PagingSpec(null, 3),
|
|
||||||
null
|
|
||||||
);
|
|
||||||
HashMap<String, Object> context = new HashMap<String, Object>();
|
HashMap<String, Object> context = new HashMap<String, Object>();
|
||||||
Iterable<Result<SelectResultValue>> results = Sequences.toList(
|
Iterable<Result<SelectResultValue>> results = Sequences.toList(
|
||||||
runner.run(query, context),
|
runner.run(query, context),
|
||||||
|
@ -155,6 +159,48 @@ public class SelectQueryRunnerTest
|
||||||
verify(expectedResults, results);
|
verify(expectedResults, results);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSequentialPaging()
|
||||||
|
{
|
||||||
|
int[] asc = {2, 5, 8, 11, 14, 17, 20, 23, 25};
|
||||||
|
int[] dsc = {-3, -6, -9, -12, -15, -18, -21, -24, -26};
|
||||||
|
int[] expected = descending ? dsc : asc;
|
||||||
|
|
||||||
|
SelectQuery query = newTestQuery().intervals(I_0112_0114).build();
|
||||||
|
for (int offset : expected) {
|
||||||
|
List<Result<SelectResultValue>> results = Sequences.toList(
|
||||||
|
runner.run(query, ImmutableMap.of()),
|
||||||
|
Lists.<Result<SelectResultValue>>newArrayList()
|
||||||
|
);
|
||||||
|
|
||||||
|
Assert.assertEquals(1, results.size());
|
||||||
|
|
||||||
|
SelectResultValue result = results.get(0).getValue();
|
||||||
|
Map<String, Integer> pagingIdentifiers = result.getPagingIdentifiers();
|
||||||
|
Assert.assertEquals(offset, pagingIdentifiers.get(QueryRunnerTestHelper.segmentId).intValue());
|
||||||
|
|
||||||
|
Map<String, Integer> next = PagingSpec.next(pagingIdentifiers, descending);
|
||||||
|
query = query.withPagingSpec(new PagingSpec(next, 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
query = newTestQuery().intervals(I_0112_0114).build();
|
||||||
|
for (int offset : expected) {
|
||||||
|
List<Result<SelectResultValue>> results = Sequences.toList(
|
||||||
|
runner.run(query, ImmutableMap.of()),
|
||||||
|
Lists.<Result<SelectResultValue>>newArrayList()
|
||||||
|
);
|
||||||
|
|
||||||
|
Assert.assertEquals(1, results.size());
|
||||||
|
|
||||||
|
SelectResultValue result = results.get(0).getValue();
|
||||||
|
Map<String, Integer> pagingIdentifiers = result.getPagingIdentifiers();
|
||||||
|
Assert.assertEquals(offset, pagingIdentifiers.get(QueryRunnerTestHelper.segmentId).intValue());
|
||||||
|
|
||||||
|
// use identifier as-is but with fromNext=true
|
||||||
|
query = query.withPagingSpec(new PagingSpec(pagingIdentifiers, 3, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFullOnSelectWithDimensionSpec()
|
public void testFullOnSelectWithDimensionSpec()
|
||||||
{
|
{
|
||||||
|
@ -169,12 +215,8 @@ public class SelectQueryRunnerTest
|
||||||
map.put("technology", "technology0");
|
map.put("technology", "technology0");
|
||||||
map.put("travel", "travel0");
|
map.put("travel", "travel0");
|
||||||
|
|
||||||
SelectQuery query = new SelectQuery(
|
SelectQuery query = newTestQuery()
|
||||||
new TableDataSource(QueryRunnerTestHelper.dataSource),
|
.dimensionSpecs(
|
||||||
QueryRunnerTestHelper.fullOnInterval,
|
|
||||||
descending,
|
|
||||||
null,
|
|
||||||
QueryRunnerTestHelper.allGran,
|
|
||||||
Arrays.<DimensionSpec>asList(
|
Arrays.<DimensionSpec>asList(
|
||||||
new DefaultDimensionSpec(QueryRunnerTestHelper.marketDimension, "mar"),
|
new DefaultDimensionSpec(QueryRunnerTestHelper.marketDimension, "mar"),
|
||||||
new ExtractionDimensionSpec(
|
new ExtractionDimensionSpec(
|
||||||
|
@ -183,9 +225,10 @@ public class SelectQueryRunnerTest
|
||||||
new LookupExtractionFn(new MapLookupExtractor(map, true), false, null, true, false)
|
new LookupExtractionFn(new MapLookupExtractor(map, true), false, null, true, false)
|
||||||
),
|
),
|
||||||
new DefaultDimensionSpec(QueryRunnerTestHelper.placementDimension, "place")
|
new DefaultDimensionSpec(QueryRunnerTestHelper.placementDimension, "place")
|
||||||
), Lists.<String>newArrayList(), new PagingSpec(null, 3),
|
)
|
||||||
null
|
)
|
||||||
);
|
.build();
|
||||||
|
|
||||||
HashMap<String, Object> context = new HashMap<String, Object>();
|
HashMap<String, Object> context = new HashMap<String, Object>();
|
||||||
Iterable<Result<SelectResultValue>> results = Sequences.toList(
|
Iterable<Result<SelectResultValue>> results = Sequences.toList(
|
||||||
runner.run(query, context),
|
runner.run(query, context),
|
||||||
|
@ -286,17 +329,12 @@ public class SelectQueryRunnerTest
|
||||||
@Test
|
@Test
|
||||||
public void testSelectWithDimsAndMets()
|
public void testSelectWithDimsAndMets()
|
||||||
{
|
{
|
||||||
SelectQuery query = new SelectQuery(
|
SelectQuery query = newTestQuery()
|
||||||
new TableDataSource(QueryRunnerTestHelper.dataSource),
|
.intervals(I_0112_0114)
|
||||||
I_0112_0114,
|
.dimensionSpecs(DefaultDimensionSpec.toSpec(QueryRunnerTestHelper.marketDimension))
|
||||||
descending,
|
.metrics(Arrays.asList(QueryRunnerTestHelper.indexMetric))
|
||||||
null,
|
.build();
|
||||||
QueryRunnerTestHelper.allGran,
|
|
||||||
DefaultDimensionSpec.toSpec(Arrays.asList(QueryRunnerTestHelper.marketDimension)),
|
|
||||||
Arrays.asList(QueryRunnerTestHelper.indexMetric),
|
|
||||||
new PagingSpec(null, 3),
|
|
||||||
null
|
|
||||||
);
|
|
||||||
HashMap<String, Object> context = new HashMap<String, Object>();
|
HashMap<String, Object> context = new HashMap<String, Object>();
|
||||||
Iterable<Result<SelectResultValue>> results = Sequences.toList(
|
Iterable<Result<SelectResultValue>> results = Sequences.toList(
|
||||||
runner.run(query, context),
|
runner.run(query, context),
|
||||||
|
@ -325,17 +363,12 @@ public class SelectQueryRunnerTest
|
||||||
@Test
|
@Test
|
||||||
public void testSelectPagination()
|
public void testSelectPagination()
|
||||||
{
|
{
|
||||||
SelectQuery query = new SelectQuery(
|
SelectQuery query = newTestQuery()
|
||||||
new TableDataSource(QueryRunnerTestHelper.dataSource),
|
.intervals(I_0112_0114)
|
||||||
I_0112_0114,
|
.dimensionSpecs(DefaultDimensionSpec.toSpec(QueryRunnerTestHelper.qualityDimension))
|
||||||
descending,
|
.metrics(Arrays.asList(QueryRunnerTestHelper.indexMetric))
|
||||||
null,
|
.pagingSpec(new PagingSpec(toPagingIdentifier(3, descending), 3))
|
||||||
QueryRunnerTestHelper.allGran,
|
.build();
|
||||||
DefaultDimensionSpec.toSpec(Arrays.asList(QueryRunnerTestHelper.qualityDimension)),
|
|
||||||
Arrays.asList(QueryRunnerTestHelper.indexMetric),
|
|
||||||
new PagingSpec(toPagingIdentifier(3, descending), 3),
|
|
||||||
null
|
|
||||||
);
|
|
||||||
|
|
||||||
Iterable<Result<SelectResultValue>> results = Sequences.toList(
|
Iterable<Result<SelectResultValue>> results = Sequences.toList(
|
||||||
runner.run(query, Maps.newHashMap()),
|
runner.run(query, Maps.newHashMap()),
|
||||||
|
@ -363,17 +396,15 @@ public class SelectQueryRunnerTest
|
||||||
{
|
{
|
||||||
// startDelta + threshold pairs
|
// startDelta + threshold pairs
|
||||||
for (int[] param : new int[][]{{3, 3}, {0, 1}, {5, 5}, {2, 7}, {3, 0}}) {
|
for (int[] param : new int[][]{{3, 3}, {0, 1}, {5, 5}, {2, 7}, {3, 0}}) {
|
||||||
SelectQuery query = new SelectQuery(
|
SelectQuery query = newTestQuery()
|
||||||
new TableDataSource(QueryRunnerTestHelper.dataSource),
|
.intervals(I_0112_0114)
|
||||||
I_0112_0114,
|
.filters(new SelectorDimFilter(QueryRunnerTestHelper.marketDimension, "spot"))
|
||||||
descending,
|
.granularity(QueryRunnerTestHelper.dayGran)
|
||||||
new SelectorDimFilter(QueryRunnerTestHelper.marketDimension, "spot"),
|
.dimensionSpecs(DefaultDimensionSpec.toSpec(QueryRunnerTestHelper.qualityDimension))
|
||||||
QueryRunnerTestHelper.dayGran,
|
.metrics(Lists.<String>newArrayList(QueryRunnerTestHelper.indexMetric))
|
||||||
DefaultDimensionSpec.toSpec(Lists.<String>newArrayList(QueryRunnerTestHelper.qualityDimension)),
|
.pagingSpec(new PagingSpec(toPagingIdentifier(param[0], descending), param[1]))
|
||||||
Lists.<String>newArrayList(QueryRunnerTestHelper.indexMetric),
|
.build();
|
||||||
new PagingSpec(toPagingIdentifier(param[0], descending), param[1]),
|
|
||||||
null
|
|
||||||
);
|
|
||||||
HashMap<String, Object> context = new HashMap<String, Object>();
|
HashMap<String, Object> context = new HashMap<String, Object>();
|
||||||
Iterable<Result<SelectResultValue>> results = Sequences.toList(
|
Iterable<Result<SelectResultValue>> results = Sequences.toList(
|
||||||
runner.run(query, context),
|
runner.run(query, context),
|
||||||
|
@ -427,20 +458,17 @@ public class SelectQueryRunnerTest
|
||||||
@Test
|
@Test
|
||||||
public void testFullSelectNoResults()
|
public void testFullSelectNoResults()
|
||||||
{
|
{
|
||||||
SelectQuery query = new SelectQuery(
|
SelectQuery query = newTestQuery()
|
||||||
new TableDataSource(QueryRunnerTestHelper.dataSource),
|
.intervals(I_0112_0114)
|
||||||
I_0112_0114,
|
.filters(
|
||||||
descending,
|
|
||||||
new AndDimFilter(
|
new AndDimFilter(
|
||||||
Arrays.<DimFilter>asList(
|
Arrays.<DimFilter>asList(
|
||||||
new SelectorDimFilter(QueryRunnerTestHelper.marketDimension, "spot"),
|
new SelectorDimFilter(QueryRunnerTestHelper.marketDimension, "spot"),
|
||||||
new SelectorDimFilter(QueryRunnerTestHelper.marketDimension, "foo")
|
new SelectorDimFilter(QueryRunnerTestHelper.marketDimension, "foo")
|
||||||
)
|
)
|
||||||
),
|
)
|
||||||
QueryRunnerTestHelper.allGran,
|
)
|
||||||
DefaultDimensionSpec.toSpec(Lists.<String>newArrayList()), Lists.<String>newArrayList(), new PagingSpec(null, 3),
|
.build();
|
||||||
null
|
|
||||||
);
|
|
||||||
|
|
||||||
Iterable<Result<SelectResultValue>> results = Sequences.toList(
|
Iterable<Result<SelectResultValue>> results = Sequences.toList(
|
||||||
runner.run(query, Maps.newHashMap()),
|
runner.run(query, Maps.newHashMap()),
|
||||||
|
@ -463,17 +491,11 @@ public class SelectQueryRunnerTest
|
||||||
@Test
|
@Test
|
||||||
public void testFullSelectNoDimensionAndMetric()
|
public void testFullSelectNoDimensionAndMetric()
|
||||||
{
|
{
|
||||||
SelectQuery query = new SelectQuery(
|
SelectQuery query = newTestQuery()
|
||||||
new TableDataSource(QueryRunnerTestHelper.dataSource),
|
.intervals(I_0112_0114)
|
||||||
I_0112_0114,
|
.dimensionSpecs(DefaultDimensionSpec.toSpec("foo"))
|
||||||
descending,
|
.metrics(Lists.<String>newArrayList("foo2"))
|
||||||
null,
|
.build();
|
||||||
QueryRunnerTestHelper.allGran,
|
|
||||||
DefaultDimensionSpec.toSpec(Lists.<String>newArrayList("foo")),
|
|
||||||
Lists.<String>newArrayList("foo2"),
|
|
||||||
new PagingSpec(null, 3),
|
|
||||||
null
|
|
||||||
);
|
|
||||||
|
|
||||||
Iterable<Result<SelectResultValue>> results = Sequences.toList(
|
Iterable<Result<SelectResultValue>> results = Sequences.toList(
|
||||||
runner.run(query, Maps.newHashMap()),
|
runner.run(query, Maps.newHashMap()),
|
||||||
|
@ -498,13 +520,11 @@ public class SelectQueryRunnerTest
|
||||||
verify(expectedResults, results);
|
verify(expectedResults, results);
|
||||||
}
|
}
|
||||||
|
|
||||||
private LinkedHashMap<String, Integer> toPagingIdentifier(int startDelta, boolean descending)
|
private Map<String, Integer> toPagingIdentifier(int startDelta, boolean descending)
|
||||||
{
|
{
|
||||||
return Maps.newLinkedHashMap(
|
return ImmutableMap.of(
|
||||||
ImmutableMap.of(
|
|
||||||
QueryRunnerTestHelper.segmentId,
|
QueryRunnerTestHelper.segmentId,
|
||||||
PagingOffset.toOffset(startDelta, descending)
|
PagingOffset.toOffset(startDelta, descending)
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,7 @@ public class SelectQuerySpecTest
|
||||||
+ "\"granularity\":{\"type\":\"all\"},"
|
+ "\"granularity\":{\"type\":\"all\"},"
|
||||||
+ "\"dimensions\":[{\"type\":\"default\",\"dimension\":\"market\",\"outputName\":\"market\"},{\"type\":\"default\",\"dimension\":\"quality\",\"outputName\":\"quality\"}],"
|
+ "\"dimensions\":[{\"type\":\"default\",\"dimension\":\"market\",\"outputName\":\"market\"},{\"type\":\"default\",\"dimension\":\"quality\",\"outputName\":\"quality\"}],"
|
||||||
+ "\"metrics\":[\"index\"],"
|
+ "\"metrics\":[\"index\"],"
|
||||||
+ "\"pagingSpec\":{\"pagingIdentifiers\":{},\"threshold\":3},"
|
+ "\"pagingSpec\":{\"pagingIdentifiers\":{},\"threshold\":3,\"fromNext\":false},"
|
||||||
+ "\"context\":null}";
|
+ "\"context\":null}";
|
||||||
|
|
||||||
SelectQuery query = new SelectQuery(
|
SelectQuery query = new SelectQuery(
|
||||||
|
|
Loading…
Reference in New Issue