mirror of https://github.com/apache/druid.git
addressed comments
This commit is contained in:
parent
032c3e986d
commit
69c86716d6
|
@ -147,4 +147,13 @@ Search filters can be used to filter on partial string matches.
|
||||||
|property|description|required?|
|
|property|description|required?|
|
||||||
|--------|-----------|---------|
|
|--------|-----------|---------|
|
||||||
|type|This String should always be "fragment".|yes|
|
|type|This String should always be "fragment".|yes|
|
||||||
|values|A JSON array of String values to run the search over. Case insensitive.|yes|
|
|values|A JSON array of String values to run the search over.|yes|
|
||||||
|
|caseSensitive|Whether strings should be compared as case sensitive or not. Default: false(insensitive)|no|
|
||||||
|
|
||||||
|
##### Contains
|
||||||
|
|
||||||
|
|property|description|required?|
|
||||||
|
|--------|-----------|---------|
|
||||||
|
|type|This String should always be "contains".|yes|
|
||||||
|
|value|A String value to run the search over.|yes|
|
||||||
|
|caseSensitive|Whether two string should be compared as case sensitive or not|yes|
|
||||||
|
|
|
@ -19,11 +19,25 @@ If any part of a dimension value contains the value specified in this search que
|
||||||
FragmentSearchQuerySpec
|
FragmentSearchQuerySpec
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
If any part of a dimension value contains any of the values specified in this search query spec, regardless of case, a "match" occurs. The grammar is:
|
If any part of a dimension value contains all of the values specified in this search query spec, regardless of case by default, a "match" occurs. The grammar is:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type" : "fragment",
|
"type" : "fragment",
|
||||||
|
"case_sensitive" : false,
|
||||||
"values" : ["fragment1", "fragment2"]
|
"values" : ["fragment1", "fragment2"]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
ContainsSearchQuerySpec
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
If any part of a dimension value contains the value specified in this search query spec, a "match" occurs. The grammar is:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type" : "contains",
|
||||||
|
"case_sensitive" : true,
|
||||||
|
"value" : "some_value"
|
||||||
|
}
|
||||||
|
```
|
|
@ -75,6 +75,10 @@
|
||||||
<groupId>org.mapdb</groupId>
|
<groupId>org.mapdb</groupId>
|
||||||
<artifactId>mapdb</artifactId>
|
<artifactId>mapdb</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-lang</groupId>
|
||||||
|
<artifactId>commons-lang</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Tests -->
|
<!-- Tests -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
package io.druid.query;
|
package io.druid.query;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
@ -683,25 +684,29 @@ public class Druids
|
||||||
|
|
||||||
public SearchQueryBuilder query(String q)
|
public SearchQueryBuilder query(String q)
|
||||||
{
|
{
|
||||||
|
Preconditions.checkNotNull(q, "no value");
|
||||||
querySpec = new InsensitiveContainsSearchQuerySpec(q);
|
querySpec = new InsensitiveContainsSearchQuerySpec(q);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SearchQueryBuilder query(Map<String, Object> q)
|
public SearchQueryBuilder query(Map<String, Object> q)
|
||||||
{
|
{
|
||||||
querySpec = new InsensitiveContainsSearchQuerySpec((String) q.get("value"));
|
String value = Preconditions.checkNotNull(q.get("value"), "no value").toString();
|
||||||
|
querySpec = new InsensitiveContainsSearchQuerySpec(value);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SearchQueryBuilder query(String q, boolean caseSensitive)
|
public SearchQueryBuilder query(String q, boolean caseSensitive)
|
||||||
{
|
{
|
||||||
|
Preconditions.checkNotNull(q, "no value");
|
||||||
querySpec = new ContainsSearchQuerySpec(q, caseSensitive);
|
querySpec = new ContainsSearchQuerySpec(q, caseSensitive);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SearchQueryBuilder query(Map<String, Object> q, boolean caseSensitive)
|
public SearchQueryBuilder query(Map<String, Object> q, boolean caseSensitive)
|
||||||
{
|
{
|
||||||
querySpec = new ContainsSearchQuerySpec((String) q.get("value"), caseSensitive);
|
String value = Preconditions.checkNotNull(q.get("value"), "no value").toString();
|
||||||
|
querySpec = new ContainsSearchQuerySpec(value, caseSensitive);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -712,6 +717,7 @@ public class Druids
|
||||||
|
|
||||||
public SearchQueryBuilder fragments(List<String> q, boolean caseSensitive)
|
public SearchQueryBuilder fragments(List<String> q, boolean caseSensitive)
|
||||||
{
|
{
|
||||||
|
Preconditions.checkNotNull(q, "no value");
|
||||||
querySpec = new FragmentSearchQuerySpec(q, caseSensitive);
|
querySpec = new FragmentSearchQuerySpec(q, caseSensitive);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,20 @@
|
||||||
|
/*
|
||||||
|
* Druid - a distributed column store.
|
||||||
|
* Copyright 2012 - 2015 Metamarkets Group Inc.
|
||||||
|
*
|
||||||
|
* Licensed 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 io.druid.query.search.search;
|
package io.druid.query.search.search;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
|
@ -16,8 +33,6 @@ public class ContainsSearchQuerySpec implements SearchQuerySpec
|
||||||
private final String value;
|
private final String value;
|
||||||
private final boolean caseSensitive;
|
private final boolean caseSensitive;
|
||||||
|
|
||||||
private final String target;
|
|
||||||
|
|
||||||
@JsonCreator
|
@JsonCreator
|
||||||
public ContainsSearchQuerySpec(
|
public ContainsSearchQuerySpec(
|
||||||
@JsonProperty("value") String value,
|
@JsonProperty("value") String value,
|
||||||
|
@ -26,7 +41,6 @@ public class ContainsSearchQuerySpec implements SearchQuerySpec
|
||||||
{
|
{
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.caseSensitive = caseSensitive;
|
this.caseSensitive = caseSensitive;
|
||||||
this.target = value == null || caseSensitive ? value : value.toLowerCase();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
|
@ -44,21 +58,29 @@ public class ContainsSearchQuerySpec implements SearchQuerySpec
|
||||||
@Override
|
@Override
|
||||||
public boolean accept(String dimVal)
|
public boolean accept(String dimVal)
|
||||||
{
|
{
|
||||||
if (dimVal == null) {
|
if (dimVal == null || value == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final String input = caseSensitive ? dimVal : dimVal.toLowerCase();
|
if (caseSensitive) {
|
||||||
return input.contains(target);
|
return dimVal.contains(value);
|
||||||
|
}
|
||||||
|
return org.apache.commons.lang.StringUtils.containsIgnoreCase(dimVal, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] getCacheKey()
|
public byte[] getCacheKey()
|
||||||
{
|
{
|
||||||
|
if (value == null) {
|
||||||
|
return ByteBuffer.allocate(2)
|
||||||
|
.put(CACHE_TYPE_ID)
|
||||||
|
.put(caseSensitive ? (byte) 1 : 0).array();
|
||||||
|
}
|
||||||
|
|
||||||
byte[] valueBytes = StringUtils.toUtf8(value);
|
byte[] valueBytes = StringUtils.toUtf8(value);
|
||||||
|
|
||||||
return ByteBuffer.allocate(2 + valueBytes.length)
|
return ByteBuffer.allocate(2 + valueBytes.length)
|
||||||
.put(caseSensitive ? (byte)1 : 0)
|
|
||||||
.put(CACHE_TYPE_ID)
|
.put(CACHE_TYPE_ID)
|
||||||
|
.put(caseSensitive ? (byte) 1 : 0)
|
||||||
.put(valueBytes)
|
.put(valueBytes)
|
||||||
.array();
|
.array();
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,8 @@ public class FragmentSearchQuerySpec implements SearchQuerySpec
|
||||||
@JsonCreator
|
@JsonCreator
|
||||||
public FragmentSearchQuerySpec(
|
public FragmentSearchQuerySpec(
|
||||||
@JsonProperty("values") List<String> values
|
@JsonProperty("values") List<String> values
|
||||||
) {
|
)
|
||||||
|
{
|
||||||
this(values, false);
|
this(values, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +56,7 @@ public class FragmentSearchQuerySpec implements SearchQuerySpec
|
||||||
Set<String> set = new HashSet();
|
Set<String> set = new HashSet();
|
||||||
if (values != null) {
|
if (values != null) {
|
||||||
for (String value : values) {
|
for (String value : values) {
|
||||||
set.add(caseSensitive ? value : value.toLowerCase());
|
set.add(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
target = set.toArray(new String[set.size()]);
|
target = set.toArray(new String[set.size()]);
|
||||||
|
@ -76,10 +77,22 @@ public class FragmentSearchQuerySpec implements SearchQuerySpec
|
||||||
@Override
|
@Override
|
||||||
public boolean accept(String dimVal)
|
public boolean accept(String dimVal)
|
||||||
{
|
{
|
||||||
if (dimVal == null) {
|
if (dimVal == null || values == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final String input = caseSensitive ? dimVal : dimVal.toLowerCase();
|
if (caseSensitive) {
|
||||||
|
return containsAny(target, dimVal);
|
||||||
|
}
|
||||||
|
for (String search : target) {
|
||||||
|
if (!org.apache.commons.lang.StringUtils.containsIgnoreCase(dimVal, search)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean containsAny(String[] target, String input)
|
||||||
|
{
|
||||||
for (String value : target) {
|
for (String value : target) {
|
||||||
if (!input.contains(value)) {
|
if (!input.contains(value)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -91,6 +104,12 @@ public class FragmentSearchQuerySpec implements SearchQuerySpec
|
||||||
@Override
|
@Override
|
||||||
public byte[] getCacheKey()
|
public byte[] getCacheKey()
|
||||||
{
|
{
|
||||||
|
if (values == null) {
|
||||||
|
return ByteBuffer.allocate(2)
|
||||||
|
.put(CACHE_TYPE_ID)
|
||||||
|
.put(caseSensitive ? (byte) 1 : 0).array();
|
||||||
|
}
|
||||||
|
|
||||||
final byte[][] valuesBytes = new byte[values.size()][];
|
final byte[][] valuesBytes = new byte[values.size()][];
|
||||||
int valuesBytesSize = 0;
|
int valuesBytesSize = 0;
|
||||||
int index = 0;
|
int index = 0;
|
||||||
|
@ -101,8 +120,8 @@ public class FragmentSearchQuerySpec implements SearchQuerySpec
|
||||||
}
|
}
|
||||||
|
|
||||||
final ByteBuffer queryCacheKey = ByteBuffer.allocate(2 + valuesBytesSize)
|
final ByteBuffer queryCacheKey = ByteBuffer.allocate(2 + valuesBytesSize)
|
||||||
.put(caseSensitive ? (byte) 1 : 0)
|
.put(CACHE_TYPE_ID)
|
||||||
.put(CACHE_TYPE_ID);
|
.put(caseSensitive ? (byte) 1 : 0);
|
||||||
|
|
||||||
for (byte[] bytes : valuesBytes) {
|
for (byte[] bytes : valuesBytes) {
|
||||||
queryCacheKey.put(bytes);
|
queryCacheKey.put(bytes);
|
||||||
|
|
|
@ -329,10 +329,19 @@ public class QueryRunnerTestHelper
|
||||||
QueryRunnerFactory<T, QueryType> factory,
|
QueryRunnerFactory<T, QueryType> factory,
|
||||||
Segment adapter
|
Segment adapter
|
||||||
)
|
)
|
||||||
|
{
|
||||||
|
return makeQueryRunner(factory, segmentId, adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, QueryType extends Query<T>> QueryRunner<T> makeQueryRunner(
|
||||||
|
QueryRunnerFactory<T, QueryType> factory,
|
||||||
|
String segmentId,
|
||||||
|
Segment adapter
|
||||||
|
)
|
||||||
{
|
{
|
||||||
return new FinalizeResultsQueryRunner<T>(
|
return new FinalizeResultsQueryRunner<T>(
|
||||||
new BySegmentQueryRunner<T>(
|
new BySegmentQueryRunner<T>(
|
||||||
adapter.getIdentifier(), adapter.getDataInterval().getStart(),
|
segmentId, adapter.getDataInterval().getStart(),
|
||||||
factory.createRunner(adapter)
|
factory.createRunner(adapter)
|
||||||
),
|
),
|
||||||
(QueryToolChest<T, Query<T>>)factory.getToolchest()
|
(QueryToolChest<T, Query<T>>)factory.getToolchest()
|
||||||
|
|
|
@ -68,7 +68,8 @@ public class SearchQueryRunnerWithCaseTest
|
||||||
"2011-01-12T00:00:00.000Z\tspot\tAutoMotive\tPREFERRED\ta\u0001preferred\t100.000000\n" +
|
"2011-01-12T00:00:00.000Z\tspot\tAutoMotive\tPREFERRED\ta\u0001preferred\t100.000000\n" +
|
||||||
"2011-01-12T00:00:00.000Z\tSPot\tbusiness\tpreferred\tb\u0001Preferred\t100.000000\n" +
|
"2011-01-12T00:00:00.000Z\tSPot\tbusiness\tpreferred\tb\u0001Preferred\t100.000000\n" +
|
||||||
"2011-01-12T00:00:00.000Z\tspot\tentertainment\tPREFERRed\te\u0001preferred\t100.000000\n" +
|
"2011-01-12T00:00:00.000Z\tspot\tentertainment\tPREFERRed\te\u0001preferred\t100.000000\n" +
|
||||||
"2011-01-13T00:00:00.000Z\tspot\tautomotive\tpreferred\ta\u0001preferred\t94.874713");
|
"2011-01-13T00:00:00.000Z\tspot\tautomotive\tpreferred\ta\u0001preferred\t94.874713"
|
||||||
|
);
|
||||||
|
|
||||||
IncrementalIndex index1 = TestIndex.makeRealtimeIndex(input, true);
|
IncrementalIndex index1 = TestIndex.makeRealtimeIndex(input, true);
|
||||||
IncrementalIndex index2 = TestIndex.makeRealtimeIndex(input, false);
|
IncrementalIndex index2 = TestIndex.makeRealtimeIndex(input, false);
|
||||||
|
@ -76,12 +77,14 @@ public class SearchQueryRunnerWithCaseTest
|
||||||
QueryableIndex index3 = TestIndex.persistRealtimeAndLoadMMapped(index1);
|
QueryableIndex index3 = TestIndex.persistRealtimeAndLoadMMapped(index1);
|
||||||
QueryableIndex index4 = TestIndex.persistRealtimeAndLoadMMapped(index2);
|
QueryableIndex index4 = TestIndex.persistRealtimeAndLoadMMapped(index2);
|
||||||
|
|
||||||
return transformToConstructionFeeder(Arrays.asList(
|
return transformToConstructionFeeder(
|
||||||
makeQueryRunner(factory, new IncrementalIndexSegment(index1, "index1")),
|
Arrays.asList(
|
||||||
makeQueryRunner(factory, new IncrementalIndexSegment(index2, "index2")),
|
makeQueryRunner(factory, "index1", new IncrementalIndexSegment(index1, "index1")),
|
||||||
makeQueryRunner(factory, new QueryableIndexSegment("index3", index3)),
|
makeQueryRunner(factory, "index2", new IncrementalIndexSegment(index2, "index2")),
|
||||||
makeQueryRunner(factory, new QueryableIndexSegment("index4", index4))
|
makeQueryRunner(factory, "index3", new QueryableIndexSegment("index3", index3)),
|
||||||
));
|
makeQueryRunner(factory, "index4", new QueryableIndexSegment("index4", index4))
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final QueryRunner runner;
|
private final QueryRunner runner;
|
||||||
|
@ -93,7 +96,8 @@ public class SearchQueryRunnerWithCaseTest
|
||||||
this.runner = runner;
|
this.runner = runner;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Druids.SearchQueryBuilder testBuilder() {
|
private Druids.SearchQueryBuilder testBuilder()
|
||||||
|
{
|
||||||
return Druids.newSearchQueryBuilder()
|
return Druids.newSearchQueryBuilder()
|
||||||
.dataSource(dataSource)
|
.dataSource(dataSource)
|
||||||
.granularity(allGran)
|
.granularity(allGran)
|
||||||
|
|
|
@ -162,7 +162,8 @@ public class TestIndex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IncrementalIndex makeRealtimeIndex(final String resourceFilename, final boolean useOffheap) {
|
private static IncrementalIndex makeRealtimeIndex(final String resourceFilename, final boolean useOffheap)
|
||||||
|
{
|
||||||
final URL resource = TestIndex.class.getClassLoader().getResource(resourceFilename);
|
final URL resource = TestIndex.class.getClassLoader().getResource(resourceFilename);
|
||||||
if (resource == null) {
|
if (resource == null) {
|
||||||
throw new IllegalArgumentException("cannot find resource " + resourceFilename);
|
throw new IllegalArgumentException("cannot find resource " + resourceFilename);
|
||||||
|
@ -198,7 +199,8 @@ public class TestIndex
|
||||||
int lineCount;
|
int lineCount;
|
||||||
try {
|
try {
|
||||||
lineCount = source.readLines(
|
lineCount = source.readLines(
|
||||||
new LineProcessor<Integer>() {
|
new LineProcessor<Integer>()
|
||||||
|
{
|
||||||
StringInputRowParser parser = new StringInputRowParser(
|
StringInputRowParser parser = new StringInputRowParser(
|
||||||
new DelimitedParseSpec(
|
new DelimitedParseSpec(
|
||||||
new TimestampSpec("ts", "iso", null),
|
new TimestampSpec("ts", "iso", null),
|
||||||
|
|
Loading…
Reference in New Issue