Skip to content

Commit

Permalink
Support alphanumeric sort in search query (apache#2593)
Browse files Browse the repository at this point in the history
* support alphanumeric sort in search query

* address a comment about handling equals() and hashCode()

* address comments

* add Ut for string comparators

* address a comment about space indentations.
  • Loading branch information
jaehc authored and fjy committed Jun 28, 2016
1 parent 1d40df4 commit efbcbf5
Show file tree
Hide file tree
Showing 10 changed files with 409 additions and 30 deletions.
3 changes: 2 additions & 1 deletion docs/content/querying/limitspec.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ OrderByColumnSpecs indicate how to do order by operations. Each order-by conditi
```json
{
"dimension" : "<Any dimension or metric name>",
"direction" : <"ascending"|"descending">
"direction" : <"ascending"|"descending">,
"dimensionOrder" : <"lexicographic(default)"|"alphanumeric"|"strlen">
}
```

Expand Down
2 changes: 1 addition & 1 deletion docs/content/querying/searchquery.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ There are several main parts to a search query:
|intervals|A JSON Object representing ISO-8601 Intervals. This defines the time ranges to run the query over.|yes|
|searchDimensions|The dimensions to run the search over. Excluding this means the search is run over all dimensions.|no|
|query|See [SearchQuerySpec](../querying/searchqueryspec.html).|yes|
|sort|An object specifying how the results of the search should be sorted. Two possible types here are "lexicographic" (the default sort) and "strlen".|no|
|sort|An object specifying how the results of the search should be sorted. Possible types here are "lexicographic" (the default sort), "alphanumeric" and "strlen".|no|
|context|See [Context](../querying/query-context.html)|no|

The format of the result is:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
import com.google.common.collect.Ordering;
import com.google.common.primitives.Ints;
import com.google.common.primitives.UnsignedBytes;
import com.metamx.common.IAE;
import com.metamx.common.StringUtils;
Expand All @@ -34,40 +36,43 @@ public class StringComparators
{
public static final String LEXICOGRAPHIC_NAME = "lexicographic";
public static final String ALPHANUMERIC_NAME = "alphanumeric";

public static final String STRLEN_NAME = "strlen";

public static final LexicographicComparator LEXICOGRAPHIC = new LexicographicComparator();
public static final AlphanumericComparator ALPHANUMERIC = new AlphanumericComparator();

public static final StrlenComparator STRLEN = new StrlenComparator();

@JsonTypeInfo(use=Id.NAME, include=As.PROPERTY, property="type", defaultImpl = LexicographicComparator.class)
@JsonSubTypes(value = {
@JsonSubTypes.Type(name = StringComparators.LEXICOGRAPHIC_NAME, value = LexicographicComparator.class),
@JsonSubTypes.Type(name = StringComparators.ALPHANUMERIC_NAME, value = AlphanumericComparator.class)
@JsonSubTypes.Type(name = StringComparators.ALPHANUMERIC_NAME, value = AlphanumericComparator.class),
@JsonSubTypes.Type(name = StringComparators.STRLEN_NAME, value = StrlenComparator.class)
})
public static interface StringComparator extends Comparator<String>
{
}

public static class LexicographicComparator implements StringComparator
{
private static final Ordering<String> ORDERING = Ordering.from(new Comparator<String>()
{
@Override
public int compare(String s, String s2)
{
return UnsignedBytes.lexicographicalComparator().compare(
StringUtils.toUtf8(s), StringUtils.toUtf8(s2));
}
}).nullsFirst();

@Override
public int compare(String s, String s2)
{
// Avoid conversion to bytes for equal references
if(s == s2){
return 0;
}
// null first
if (s == null) {
return -1;
}
if (s2 == null) {
return 1;
}

return UnsignedBytes.lexicographicalComparator().compare(
StringUtils.toUtf8(s),
StringUtils.toUtf8(s2)
);
return ORDERING.compare(s, s2);
}

@Override
Expand Down Expand Up @@ -101,6 +106,9 @@ public int compare(String str1, String str2)

if (str1 == null)
{
if (str2 == null) {
return 0;
}
return -1;
} else if (str2 == null)
{
Expand Down Expand Up @@ -272,7 +280,7 @@ private int compareNonNumeric(String str0, String str1, int[] pos)
// compare the substrings
return String.CASE_INSENSITIVE_ORDER.compare(str0.substring(start0, pos[0]), str1.substring(start1, pos[1]));
}

@Override
public boolean equals(Object o)
{
Expand All @@ -282,23 +290,66 @@ public boolean equals(Object o)
if (o == null || getClass() != o.getClass()) {
return false;
}

return true;
}

@Override
public String toString()
{
return StringComparators.ALPHANUMERIC_NAME;
}
}

public static class StrlenComparator implements StringComparator
{
private static final Ordering<String> ORDERING = Ordering.from(new Comparator<String>()
{
@Override
public int compare(String s, String s2)
{
return Ints.compare(s.length(), s2.length());
}
}).nullsFirst().compound(Ordering.natural());

@Override
public int compare(String s, String s2)
{
if (s == s2) {
return 0;
}

return ORDERING.compare(s, s2);
}

@Override
public boolean equals(Object o)
{
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}

return true;
}

@Override
public String toString()
{
return StringComparators.STRLEN_NAME;
}
}

public static StringComparator makeComparator(String type)
{
if (type.equals(StringComparators.LEXICOGRAPHIC_NAME)) {
return LEXICOGRAPHIC;
} else if (type.equals(StringComparators.ALPHANUMERIC_NAME)) {
return ALPHANUMERIC;
} else if (type.equals(StringComparators.STRLEN_NAME)) {
return STRLEN;
} else {
throw new IAE("Unknown string comparator[%s]", type);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Licensed to Metamarkets Group Inc. (Metamarkets) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Metamarkets 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 io.druid.query.search.search;

import com.fasterxml.jackson.annotation.JsonCreator;

import io.druid.query.ordering.StringComparators;

import java.util.Comparator;

/**
*/
public class AlphanumericSearchSortSpec implements SearchSortSpec
{
@JsonCreator
public AlphanumericSearchSortSpec(
)
{
}

@Override
public Comparator<SearchHit> getComparator()
{
return new Comparator<SearchHit>()
{
@Override
public int compare(SearchHit searchHit1, SearchHit searchHit2)
{
int retVal = StringComparators.ALPHANUMERIC.compare(
searchHit1.getValue(), searchHit2.getValue());
if (retVal == 0) {
retVal = StringComparators.LEXICOGRAPHIC.compare(
searchHit1.getDimension(), searchHit2.getDimension());
}
return retVal;
}
};
}

public String toString()
{
return "alphanumericSort";
}

@Override
public boolean equals(Object other) {
return this == other || other instanceof AlphanumericSearchSortSpec;
}

@Override
public int hashCode()
{
return 0;
}

@Override
public byte[] getCacheKey()
{
return toString().getBytes();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

import com.fasterxml.jackson.annotation.JsonCreator;

import io.druid.query.ordering.StringComparators;

import java.util.Comparator;

/**
Expand All @@ -41,9 +43,12 @@ public Comparator<SearchHit> getComparator()
@Override
public int compare(SearchHit searchHit, SearchHit searchHit1)
{
int retVal = searchHit.getValue().compareTo(searchHit1.getValue());
int retVal = StringComparators.LEXICOGRAPHIC.compare(
searchHit.getValue(), searchHit1.getValue());

if (retVal == 0) {
retVal = searchHit.getDimension().compareTo(searchHit1.getDimension());
retVal = StringComparators.LEXICOGRAPHIC.compare(
searchHit.getDimension(), searchHit1.getDimension());
}
return retVal;
}
Expand All @@ -63,6 +68,12 @@ public String toString()

@Override
public boolean equals(Object other) {
return (other instanceof LexicographicSearchSortSpec);
return this == other || other instanceof LexicographicSearchSortSpec;
}

@Override
public int hashCode()
{
return 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = LexicographicSearchSortSpec.class)
@JsonSubTypes(value = {
@JsonSubTypes.Type(name = "lexicographic", value = LexicographicSearchSortSpec.class),
@JsonSubTypes.Type(name = "alphanumeric", value = AlphanumericSearchSortSpec.class),
@JsonSubTypes.Type(name = "strlen", value = StrlenSearchSortSpec.class)
})
public interface SearchSortSpec
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

package io.druid.query.search.search;

import com.google.common.primitives.Ints;
import io.druid.query.ordering.StringComparators;

import java.util.Comparator;

Expand All @@ -38,14 +38,10 @@ public Comparator<SearchHit> getComparator()
@Override
public int compare(SearchHit s, SearchHit s1)
{
final String v1 = s.getValue();
final String v2 = s1.getValue();
int res = Ints.compare(v1.length(), v2.length());
int res = StringComparators.STRLEN.compare(s.getValue(), s1.getValue());
if (res == 0) {
res = v1.compareTo(v2);
}
if (res == 0) {
res = s.getDimension().compareTo(s1.getDimension());
res = StringComparators.LEXICOGRAPHIC.compare(
s.getDimension(), s1.getDimension());
}
return res;
}
Expand All @@ -62,4 +58,15 @@ public String toString()
{
return "stringLengthSort";
}

@Override
public boolean equals(Object other) {
return this == other || other instanceof StrlenSearchSortSpec;
}

@Override
public int hashCode()
{
return 0;
}
}
Loading

0 comments on commit efbcbf5

Please sign in to comment.