Skip to content

Commit

Permalink
Add support for int values in SearchQuery (Graylog2#4733)
Browse files Browse the repository at this point in the history
* Add support for int types in SearchQuery

Add new SearchQueryField.Type for int and adapt code to support them.

* Adapt default search operator to type

Using `SearchQueryOperators.REGEX` as default is fine for String types,
but it forces users to type an operator on Date and Int types.

This commit leaves `REGEX` as default for Strings, but uses `EQUALS` for
Dates and Ints, so users can still find values if they only write the
value.

* Add support for long numbers
  • Loading branch information
edmundoa authored and bernd committed Apr 19, 2018
1 parent 4e25560 commit d3f2916
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

public class SearchQueryField {
public enum Type {
STRING, DATE;
STRING, DATE, INT, LONG;
}

private final String dbField;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@

import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;
import static org.graylog2.search.SearchQueryField.Type.STRING;

/**
* Parses a simple query language for use in list filtering of data sitting in MongoDB.
Expand Down Expand Up @@ -80,7 +81,8 @@ public class SearchQueryParser {
private static final Pattern QUERY_SPLITTER_PATTERN = Pattern.compile("(\\S+:(=|=~|<|<=|>|>=)?'(?:[^'\\\\]|\\\\.)*')|(\\S+:(=|=~|<|<=|>|>=)?\"(?:[^\"\\\\]|\\\\.)*\")|\\S+|\\S+:(=|=~|<|<=|>|>=)?\\S+");
private static final String INVALID_ENTRY_MESSAGE = "Chunk [%s] is not a valid entry";
private static final String QUOTE_REPLACE_REGEX = "^[\"']|[\"']$";
public static final SearchQueryOperator DEFAULT_OPERATOR = SearchQueryOperators.REGEXP;
public static final SearchQueryOperator DEFAULT_STRING_OPERATOR = SearchQueryOperators.REGEXP;
public static final SearchQueryOperator DEFAULT_OPERATOR = SearchQueryOperators.EQUALS;

// We parse all date strings in UTC because we store and show all dates in UTC as well.
private static final List<DateTimeFormatter> DATE_TIME_FORMATTERS = ImmutableList.of(
Expand Down Expand Up @@ -116,7 +118,7 @@ public SearchQueryParser(@Nonnull String defaultField, @Nonnull Set<String> allo
public SearchQueryParser(@Nonnull String defaultField,
@Nonnull Map<String, SearchQueryField> allowedFieldsWithMapping) {
this.defaultField = requireNonNull(defaultField);
this.defaultFieldKey = SearchQueryField.create(defaultField, SearchQueryField.Type.STRING);
this.defaultFieldKey = SearchQueryField.create(defaultField, STRING);
this.dbFieldMapping = allowedFieldsWithMapping;
}

Expand Down Expand Up @@ -231,15 +233,20 @@ private DateTime parseDate(String value) {
FieldValue createFieldValue(SearchQueryField field, String quotedStringValue, boolean negate) {
// Make sure there are no quotes in the value (e.g. `"foo"' --> `foo')
final String value = quotedStringValue.replaceAll(QUOTE_REPLACE_REGEX, "");
final Pair<String, SearchQueryOperator> pair = extractOperator(value, DEFAULT_OPERATOR);
final SearchQueryField.Type fieldType = field.getFieldType();
final Pair<String, SearchQueryOperator> pair = extractOperator(value, fieldType == STRING ? DEFAULT_STRING_OPERATOR : DEFAULT_OPERATOR);

switch (field.getFieldType()) {
switch (fieldType) {
case DATE:
return new FieldValue(parseDate(pair.getLeft()), pair.getRight(), negate);
case STRING:
return new FieldValue(pair.getLeft(), pair.getRight(), negate);
case INT:
return new FieldValue(Integer.parseInt(pair.getLeft()), pair.getRight(), negate);
case LONG:
return new FieldValue(Long.parseLong(pair.getLeft()), pair.getRight(), negate);
default:
throw new IllegalArgumentException("Unhandled field type: " + field.getFieldType().toString());
throw new IllegalArgumentException("Unhandled field type: " + fieldType.toString());
}
}

Expand All @@ -249,7 +256,7 @@ public static class FieldValue {
private final boolean negate;

public FieldValue(final Object value, final boolean negate) {
this(value, DEFAULT_OPERATOR, negate);
this(value, DEFAULT_STRING_OPERATOR, negate);
}

public FieldValue(final Object value, final SearchQueryOperator operator, final boolean negate) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,31 +128,48 @@ public void extractOperator() throws Exception {
final SearchQueryParser parser = new SearchQueryParser("defaultfield",
ImmutableMap.of(
"id", SearchQueryField.create("real_id"),
"date", SearchQueryField.create("created_at", SearchQueryField.Type.DATE))
"date", SearchQueryField.create("created_at", SearchQueryField.Type.DATE),
"int", SearchQueryField.create("int", SearchQueryField.Type.INT))
);

final SearchQueryOperator defaultOp = SearchQueryOperators.REGEXP;

checkQuery(parser, "", "", defaultOp);
checkQuery(parser, "h", "h", defaultOp);
checkQuery(parser, "he", "he", defaultOp);
checkQuery(parser, "hel", "hel", defaultOp);
checkQuery(parser, "hello", "hello", defaultOp);

checkQuery(parser, ">=2017-03-23", "2017-03-23", SearchQueryOperators.GREATER_EQUALS);
checkQuery(parser, ">= 2017-03-23", "2017-03-23", SearchQueryOperators.GREATER_EQUALS);
checkQuery(parser, "<=2017-03-23", "2017-03-23", SearchQueryOperators.LESS_EQUALS);
checkQuery(parser, "<= 2017-03-23", "2017-03-23", SearchQueryOperators.LESS_EQUALS);
checkQuery(parser, ">2017-03-23", "2017-03-23", SearchQueryOperators.GREATER);
checkQuery(parser, "> 2017-03-23", "2017-03-23", SearchQueryOperators.GREATER);
checkQuery(parser, "<2017-03-23", "2017-03-23", SearchQueryOperators.LESS);
checkQuery(parser, "< 2017-03-23", "2017-03-23", SearchQueryOperators.LESS);
checkQuery(parser, "=~ hello", "hello", SearchQueryOperators.REGEXP);
checkQuery(parser, "= hello", "hello", SearchQueryOperators.EQUALS);
checkQuery(parser, "", SearchQueryField.Type.STRING, "", defaultOp);
checkQuery(parser, "h", SearchQueryField.Type.STRING, "h", defaultOp);
checkQuery(parser, "he", SearchQueryField.Type.STRING, "he", defaultOp);
checkQuery(parser, "hel", SearchQueryField.Type.STRING, "hel", defaultOp);
checkQuery(parser, "hello", SearchQueryField.Type.STRING, "hello", defaultOp);
checkQuery(parser, "=~ hello", SearchQueryField.Type.STRING, "hello", SearchQueryOperators.REGEXP);
checkQuery(parser, "= hello", SearchQueryField.Type.STRING, "hello", SearchQueryOperators.EQUALS);

checkQuery(parser, ">=2017-03-23", SearchQueryField.Type.DATE, "2017-03-23", SearchQueryOperators.GREATER_EQUALS);
checkQuery(parser, ">= 2017-03-23", SearchQueryField.Type.DATE, "2017-03-23", SearchQueryOperators.GREATER_EQUALS);
checkQuery(parser, "<=2017-03-23", SearchQueryField.Type.DATE, "2017-03-23", SearchQueryOperators.LESS_EQUALS);
checkQuery(parser, "<= 2017-03-23", SearchQueryField.Type.DATE, "2017-03-23", SearchQueryOperators.LESS_EQUALS);
checkQuery(parser, ">2017-03-23", SearchQueryField.Type.DATE, "2017-03-23", SearchQueryOperators.GREATER);
checkQuery(parser, "> 2017-03-23", SearchQueryField.Type.DATE, "2017-03-23", SearchQueryOperators.GREATER);
checkQuery(parser, "<2017-03-23", SearchQueryField.Type.DATE, "2017-03-23", SearchQueryOperators.LESS);
checkQuery(parser, "< 2017-03-23", SearchQueryField.Type.DATE, "2017-03-23", SearchQueryOperators.LESS);
checkQuery(parser, "2017-03-23", SearchQueryField.Type.DATE, "2017-03-23", SearchQueryOperators.EQUALS);

checkQuery(parser, ">=1", SearchQueryField.Type.INT, "1", SearchQueryOperators.GREATER_EQUALS);
checkQuery(parser, "<=1", SearchQueryField.Type.INT, "1", SearchQueryOperators.LESS_EQUALS);
checkQuery(parser, ">1", SearchQueryField.Type.INT, "1", SearchQueryOperators.GREATER);
checkQuery(parser, "<1", SearchQueryField.Type.INT, "1", SearchQueryOperators.LESS);
checkQuery(parser, "=1", SearchQueryField.Type.INT, "1", SearchQueryOperators.EQUALS);
checkQuery(parser, "1", SearchQueryField.Type.INT, "1", SearchQueryOperators.EQUALS);

checkQuery(parser, ">=1", SearchQueryField.Type.LONG, "1", SearchQueryOperators.GREATER_EQUALS);
checkQuery(parser, "<=1", SearchQueryField.Type.LONG, "1", SearchQueryOperators.LESS_EQUALS);
checkQuery(parser, ">1", SearchQueryField.Type.LONG, "1", SearchQueryOperators.GREATER);
checkQuery(parser, "<1", SearchQueryField.Type.LONG, "1", SearchQueryOperators.LESS);
checkQuery(parser, "=1", SearchQueryField.Type.LONG, "1", SearchQueryOperators.EQUALS);
checkQuery(parser, "1", SearchQueryField.Type.LONG, "1", SearchQueryOperators.EQUALS);
}

private void checkQuery(SearchQueryParser parser, String query, String expectedQuery, SearchQueryOperator expectedOp) {
final Pair<String, SearchQueryOperator> pair = parser.extractOperator(query, SearchQueryOperators.REGEXP);
private void checkQuery(SearchQueryParser parser, String query, SearchQueryField.Type type, String expectedQuery, SearchQueryOperator expectedOp) {
final SearchQueryOperator defaultOperator = type == SearchQueryField.Type.STRING ? SearchQueryParser.DEFAULT_STRING_OPERATOR : SearchQueryParser.DEFAULT_OPERATOR;
final Pair<String, SearchQueryOperator> pair = parser.extractOperator(query, defaultOperator);
assertThat(pair.getLeft()).isEqualTo(expectedQuery);
assertThat(pair.getRight()).isEqualTo(expectedOp);
}
Expand Down Expand Up @@ -185,7 +202,7 @@ public void createFieldValue() throws Exception {
final SearchQueryParser parser = new SearchQueryParser("defaultfield", fields);

final SearchQueryParser.FieldValue v1 = parser.createFieldValue(fields.get("id"), "abc", false);
assertThat(v1.getOperator()).isEqualTo(SearchQueryParser.DEFAULT_OPERATOR);
assertThat(v1.getOperator()).isEqualTo(SearchQueryParser.DEFAULT_STRING_OPERATOR);
assertThat(v1.getValue()).isEqualTo("abc");
assertThat(v1.isNegate()).isFalse();

Expand Down

0 comments on commit d3f2916

Please sign in to comment.