-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial, beta version of field type list pagination with querying and…
… filtering.
- Loading branch information
1 parent
a95c74c
commit e61d518
Showing
12 changed files
with
325 additions
and
107 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
55 changes: 55 additions & 0 deletions
55
...rc/main/java/org/graylog2/database/filtering/inmemory/InMemoryFilterExpressionParser.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
/* | ||
* Copyright (C) 2020 Graylog, Inc. | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the Server Side Public License, version 1, | ||
* as published by MongoDB, Inc. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* Server Side Public License for more details. | ||
* | ||
* You should have received a copy of the Server Side Public License | ||
* along with this program. If not, see | ||
* <http://www.mongodb.com/licensing/server-side-public-license>. | ||
*/ | ||
package org.graylog2.database.filtering.inmemory; | ||
|
||
import org.bson.conversions.Bson; | ||
import org.graylog2.database.filtering.Filter; | ||
import org.graylog2.rest.resources.entities.EntityAttribute; | ||
|
||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.function.Predicate; | ||
import java.util.stream.Collectors; | ||
|
||
import static java.util.stream.Collectors.groupingBy; | ||
|
||
public class InMemoryFilterExpressionParser { | ||
|
||
private final SingleFilterParser singleFilterParser = new SingleFilterParser(); | ||
|
||
public Predicate<InMemoryFilterable> parsePredicate(final List<String> filterExpressions, | ||
final List<EntityAttribute> attributes) { | ||
if (filterExpressions == null || filterExpressions.isEmpty()) { | ||
return o -> true; | ||
} | ||
final Map<String, List<Filter>> groupedByField = filterExpressions.stream() | ||
.map(expr -> singleFilterParser.parseSingleExpression(expr, attributes)) | ||
.collect(groupingBy(Filter::field)); | ||
|
||
return groupedByField.values().stream() | ||
.map(grouped -> grouped.stream() | ||
.map(Filter::toPredicate) | ||
.collect(Collectors.toList())) | ||
.map(groupedPredicates -> groupedPredicates.stream().reduce(Predicate::or).orElse(o -> true)) | ||
.reduce(Predicate::and).orElse(o -> true); | ||
} | ||
|
||
public Bson parseSingleExpression(final String filterExpression, final List<EntityAttribute> attributes) { | ||
final Filter filter = singleFilterParser.parseSingleExpression(filterExpression, attributes); | ||
return filter.toBson(); | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
...og2-server/src/main/java/org/graylog2/database/filtering/inmemory/InMemoryFilterable.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/* | ||
* Copyright (C) 2020 Graylog, Inc. | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the Server Side Public License, version 1, | ||
* as published by MongoDB, Inc. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* Server Side Public License for more details. | ||
* | ||
* You should have received a copy of the Server Side Public License | ||
* along with this program. If not, see | ||
* <http://www.mongodb.com/licensing/server-side-public-license>. | ||
*/ | ||
package org.graylog2.database.filtering.inmemory; | ||
|
||
import java.util.Optional; | ||
|
||
public interface InMemoryFilterable { | ||
|
||
Optional<Object> extractFieldValue(final String fieldName); | ||
} |
114 changes: 114 additions & 0 deletions
114
...og2-server/src/main/java/org/graylog2/database/filtering/inmemory/SingleFilterParser.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
/* | ||
* Copyright (C) 2020 Graylog, Inc. | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the Server Side Public License, version 1, | ||
* as published by MongoDB, Inc. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* Server Side Public License for more details. | ||
* | ||
* You should have received a copy of the Server Side Public License | ||
* along with this program. If not, see | ||
* <http://www.mongodb.com/licensing/server-side-public-license>. | ||
*/ | ||
package org.graylog2.database.filtering.inmemory; | ||
|
||
import org.graylog2.database.filtering.Filter; | ||
import org.graylog2.database.filtering.RangeFilter; | ||
import org.graylog2.database.filtering.SingleValueFilter; | ||
import org.graylog2.rest.resources.entities.EntityAttribute; | ||
import org.graylog2.search.SearchQueryField; | ||
import org.joda.time.DateTime; | ||
|
||
import java.util.List; | ||
|
||
public class SingleFilterParser { | ||
|
||
public static final String FIELD_AND_VALUE_SEPARATOR = ":"; | ||
public static final String RANGE_VALUES_SEPARATOR = "><"; | ||
static final String WRONG_FILTER_EXPR_FORMAT_ERROR_MSG = | ||
"Wrong filter expression, <field_name>" + FIELD_AND_VALUE_SEPARATOR + "<field_value> format should be used"; | ||
|
||
public Filter parseSingleExpression(final String filterExpression, final List<EntityAttribute> attributes) { | ||
if (!filterExpression.contains(FIELD_AND_VALUE_SEPARATOR)) { | ||
throw new IllegalArgumentException(WRONG_FILTER_EXPR_FORMAT_ERROR_MSG); | ||
} | ||
final String[] split = filterExpression.split(FIELD_AND_VALUE_SEPARATOR, 2); | ||
|
||
final String fieldPart = split[0]; | ||
if (fieldPart == null || fieldPart.isEmpty()) { | ||
throw new IllegalArgumentException(WRONG_FILTER_EXPR_FORMAT_ERROR_MSG); | ||
} | ||
final String valuePart = split[1]; | ||
if (valuePart == null || valuePart.isEmpty()) { | ||
throw new IllegalArgumentException(WRONG_FILTER_EXPR_FORMAT_ERROR_MSG); | ||
} | ||
|
||
final EntityAttribute attributeMetaData = getAttributeMetaData(attributes, fieldPart); | ||
|
||
final SearchQueryField.Type fieldType = attributeMetaData.type(); | ||
if (isRangeValueExpression(valuePart, fieldType)) { | ||
if (valuePart.startsWith(RANGE_VALUES_SEPARATOR)) { | ||
return new RangeFilter(attributeMetaData.id(), | ||
null, | ||
extractValue(fieldType, valuePart.substring(RANGE_VALUES_SEPARATOR.length())) | ||
); | ||
} else if (valuePart.endsWith(RANGE_VALUES_SEPARATOR)) { | ||
return new RangeFilter(attributeMetaData.id(), | ||
extractValue(fieldType, valuePart.substring(0, valuePart.length() - RANGE_VALUES_SEPARATOR.length())), | ||
null | ||
); | ||
} else { | ||
final String[] ranges = valuePart.split(RANGE_VALUES_SEPARATOR); | ||
return new RangeFilter(attributeMetaData.id(), | ||
extractValue(fieldType, ranges[0]), | ||
extractValue(fieldType, ranges[1]) | ||
); | ||
} | ||
} else { | ||
return new SingleValueFilter(attributeMetaData.id(), extractValue(fieldType, valuePart)); | ||
} | ||
|
||
} | ||
|
||
private Object extractValue(SearchQueryField.Type fieldType, String valuePart) { | ||
final Object converted = fieldType.getMongoValueConverter().apply(valuePart); | ||
if (converted instanceof DateTime && fieldType == SearchQueryField.Type.DATE) { | ||
return ((DateTime) converted).toDate(); //MongoDB does not like Joda | ||
} else { | ||
return converted; | ||
} | ||
|
||
} | ||
|
||
private EntityAttribute getAttributeMetaData(final List<EntityAttribute> attributes, | ||
final String attributeName) { | ||
EntityAttribute matchingByTitle = null; | ||
|
||
for (EntityAttribute attr : attributes) { | ||
if (attributeName.equals(attr.id()) && isFilterable(attr)) { | ||
return attr; | ||
} else if (attributeName.equalsIgnoreCase(attr.title()) && isFilterable(attr)) { | ||
matchingByTitle = attr; | ||
} | ||
} | ||
|
||
if (matchingByTitle != null) { | ||
return matchingByTitle; | ||
} else { | ||
throw new IllegalArgumentException(attributeName + " is not a field that can be used for filtering"); | ||
} | ||
|
||
} | ||
|
||
private boolean isRangeValueExpression(String valuePart, SearchQueryField.Type fieldType) { | ||
return SearchQueryField.Type.NUMERIC_TYPES.contains(fieldType) && valuePart.contains(RANGE_VALUES_SEPARATOR); | ||
} | ||
|
||
private boolean isFilterable(final EntityAttribute attr) { | ||
return Boolean.TRUE.equals(attr.filterable()); | ||
} | ||
} |
Oops, something went wrong.