From db78998480573607d3f03623f4187a8b31772d36 Mon Sep 17 00:00:00 2001 From: Pavel Popov Date: Thu, 6 Jul 2017 16:59:24 +0300 Subject: [PATCH] BJ-197: Add collection support in filter (#51) * BJ-197: Add collection support * BJ-197: Review performance fix * BJ-197: Bump version to 0.5.0-SNAPSHOT * BJ-197: Rewrite split function * BJ-197: Rewrite test expected values --- .../rbkmoney/geck/common/util/StringUtil.java | 13 +++++ .../java/com/rbkmoney/geck/filter/Parser.java | 2 + .../geck/filter/PathConditionFilter.java | 53 ++++++++++++------- .../geck/filter/parser/PathParser.java | 41 ++++++-------- .../geck/filter/rule/PathConditionRule.java | 2 - .../geck/filter/PathConditionFilterTest.java | 51 ++++++++++++++++++ .../geck/filter/parser/PathParserTest.java | 29 ++++++---- filter/src/test/thrift/TestObject.thrift | 2 + pom.xml | 2 +- 9 files changed, 136 insertions(+), 59 deletions(-) diff --git a/common/src/main/java/com/rbkmoney/geck/common/util/StringUtil.java b/common/src/main/java/com/rbkmoney/geck/common/util/StringUtil.java index df7dbc2..1524e50 100644 --- a/common/src/main/java/com/rbkmoney/geck/common/util/StringUtil.java +++ b/common/src/main/java/com/rbkmoney/geck/common/util/StringUtil.java @@ -1,6 +1,7 @@ package com.rbkmoney.geck.common.util; import java.nio.charset.Charset; +import java.util.StringTokenizer; /** * Created by vpankrashkin on 03.02.17. @@ -85,6 +86,18 @@ public final class StringUtil { return true; } + public static String[] split(String value, String delimiter) { + StringTokenizer tokenizer = new StringTokenizer(value, delimiter); + String[] items = new String[tokenizer.countTokens()]; + + int itemId = 0; + while (tokenizer.hasMoreTokens()) { + items[itemId++] = tokenizer.nextToken(); + } + + return items; + } + public static String intToString(int number, int groupSize, int maxBits) { StringBuilder result = new StringBuilder(); diff --git a/filter/src/main/java/com/rbkmoney/geck/filter/Parser.java b/filter/src/main/java/com/rbkmoney/geck/filter/Parser.java index b5b5343..1ee6765 100644 --- a/filter/src/main/java/com/rbkmoney/geck/filter/Parser.java +++ b/filter/src/main/java/com/rbkmoney/geck/filter/Parser.java @@ -9,6 +9,8 @@ public interface Parser { String getItemPath(int item); + Parser getSubParser(int from); + int size(); } diff --git a/filter/src/main/java/com/rbkmoney/geck/filter/PathConditionFilter.java b/filter/src/main/java/com/rbkmoney/geck/filter/PathConditionFilter.java index b2a4375..866c0a6 100644 --- a/filter/src/main/java/com/rbkmoney/geck/filter/PathConditionFilter.java +++ b/filter/src/main/java/com/rbkmoney/geck/filter/PathConditionFilter.java @@ -5,6 +5,9 @@ import com.rbkmoney.geck.common.util.TypeUtil; import com.rbkmoney.geck.filter.rule.PathConditionRule; import org.apache.thrift.TBase; import org.apache.thrift.TFieldIdEnum; +import org.apache.thrift.TUnion; + +import java.util.Collection; /** * Created by tolkonepiu on 17/03/2017. @@ -17,35 +20,45 @@ public class PathConditionFilter implements Filter { this.rules = rules; } - @Override - public boolean match(TBase object) { + public boolean match(TBase value) { for (PathConditionRule rule : rules) { - Object value = object; Parser parser = rule.getParser(); - - for (int item = 0; item < parser.size(); item++) { - if (value == null || !(value instanceof TBase)) { - return false; - } - value = getFieldValue(parser.getItem(item), TypeUtil.convertType(TBase.class, value)); - } - - for (Condition condition : rule.getConditions()) { - if(!condition.accept(value)) { - return false; - } + if (!match(value, parser, rule.getConditions())) { + return false; } } return true; } - private Object getFieldValue(String path, TBase tBase) { - TFieldIdEnum tFieldIdEnum = TBaseUtil.getField(path, tBase); - if (tFieldIdEnum != null && tBase.isSet(tFieldIdEnum)) { - return tBase.getFieldValue(tFieldIdEnum); + public boolean match(Object value, Parser parser, Condition... conditions) { + for (int item = 0; item < parser.size(); item++) { + if (value instanceof TBase) { + TBase tBase = TypeUtil.convertType(TBase.class, value); + TFieldIdEnum tFieldIdEnum = TBaseUtil.getField(parser.getItem(item), tBase); + if (tFieldIdEnum == null + || ((tBase instanceof TUnion) && !tBase.isSet(tFieldIdEnum))) { + return false; + } + value = tBase.getFieldValue(tFieldIdEnum); + } else if (value instanceof Collection) { + Collection collection = TypeUtil.convertType(Collection.class, value); + for (Object collectionItem : collection) { + if (match(collectionItem, parser.getSubParser(item), conditions)) { + return true; + } + } + return false; + } } - return null; + + for (Condition condition : conditions) { + if (!condition.accept(value)) { + return false; + } + } + return true; } + } diff --git a/filter/src/main/java/com/rbkmoney/geck/filter/parser/PathParser.java b/filter/src/main/java/com/rbkmoney/geck/filter/parser/PathParser.java index e34f418..8c67a05 100644 --- a/filter/src/main/java/com/rbkmoney/geck/filter/parser/PathParser.java +++ b/filter/src/main/java/com/rbkmoney/geck/filter/parser/PathParser.java @@ -1,62 +1,51 @@ package com.rbkmoney.geck.filter.parser; +import com.rbkmoney.geck.common.util.StringUtil; import com.rbkmoney.geck.filter.Parser; -import java.util.ArrayList; -import java.util.List; +import java.util.Arrays; /** * Created by tolkonepiu on 16/03/2017. */ public class PathParser implements Parser { - private String fieldPath; - private List items; - private List forNameItems; + private final String[] items; + private final String delimiter; public static final String DEFAULT_DELIMITER = "."; - private String delimiter; public PathParser(String fieldPath) { this(fieldPath, DEFAULT_DELIMITER); } public PathParser(String fieldPath, String delimiter) { - this.fieldPath = fieldPath; - this.delimiter = delimiter; - init(); + this(StringUtil.split(fieldPath, delimiter), delimiter); } - private void init() { - List items = new ArrayList<>(); - List forNameItems = new ArrayList<>(); - int last = 0; - int next; - while ((next = fieldPath.indexOf(delimiter, last)) != -1) { - items.add(fieldPath.substring(last, next)); - forNameItems.add(fieldPath.substring(0, next)); - last = next + delimiter.length(); - } - forNameItems.add(fieldPath.substring(0, fieldPath.length())); - items.add(fieldPath.substring(last, fieldPath.length())); - + public PathParser(String[] items, String delimiter) { this.items = items; - this.forNameItems = forNameItems; + this.delimiter = delimiter; } @Override public String getItem(int item) { - return items.get(item); + return items[item]; } @Override public String getItemPath(int item) { - return forNameItems.get(item); + return String.join(delimiter, Arrays.copyOfRange(items, 0, item + 1)); + } + + @Override + public Parser getSubParser(int from) { + return new PathParser(Arrays.copyOfRange(items, from, items.length), delimiter); } @Override public int size() { - return items.size(); + return items.length; } } diff --git a/filter/src/main/java/com/rbkmoney/geck/filter/rule/PathConditionRule.java b/filter/src/main/java/com/rbkmoney/geck/filter/rule/PathConditionRule.java index 0399d87..156b781 100644 --- a/filter/src/main/java/com/rbkmoney/geck/filter/rule/PathConditionRule.java +++ b/filter/src/main/java/com/rbkmoney/geck/filter/rule/PathConditionRule.java @@ -5,8 +5,6 @@ import com.rbkmoney.geck.filter.Parser; import com.rbkmoney.geck.filter.Rule; import com.rbkmoney.geck.filter.parser.PathParser; -import java.util.Objects; - /** * Created by tolkonepiu on 16/03/2017. */ diff --git a/filter/src/test/java/com/rbkmoney/geck/filter/PathConditionFilterTest.java b/filter/src/test/java/com/rbkmoney/geck/filter/PathConditionFilterTest.java index 7124884..ec93937 100644 --- a/filter/src/test/java/com/rbkmoney/geck/filter/PathConditionFilterTest.java +++ b/filter/src/test/java/com/rbkmoney/geck/filter/PathConditionFilterTest.java @@ -12,6 +12,7 @@ import java.util.Arrays; import java.util.List; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; /** * Created by tolkonepiu on 17/03/2017. @@ -35,6 +36,40 @@ public class PathConditionFilterTest { testObject.setStatus(Status.ok_status(new Ok())); testObject.setType(Type.BLACK); testObject.setNumbers(Arrays.asList(new Long[]{23L, 31L, 41L})); + + List listIds = new ArrayList<>(); + listIds.add(ids); + ids = new Ids(); + ids.setMicroId((byte) 32); + ids.setMiniId((short) 33); + ids.setId(34); + listIds.add(ids); + testObject.setListIds(listIds); + + List statuses1 = new ArrayList<>(); + statuses1.add(Status.ok_status(new Ok())); + + Unknown unknownField = new Unknown(); + List resultTypes = new ArrayList<>(); + resultTypes.add(Type.BLACK); + resultTypes.add(Type.GREEN); + unknownField.setUnknown(UnknownType.resultTypes(resultTypes)); + statuses1.add(Status.unknown_status(unknownField)); + List> kebabStatus = new ArrayList<>(); + kebabStatus.add(statuses1); + + Unknown unknownField2 = new Unknown(); + List resultTypes2 = new ArrayList<>(); + resultTypes2.add(Type.BLACK); + resultTypes2.add(Type.GREEN); + unknownField2.setUnknown(UnknownType.resultTypes(resultTypes)); + statuses1.add(Status.unknown_status(unknownField)); + + List statuses2 = new ArrayList<>(); + statuses1.add(Status.ok_status(new Ok())); + kebabStatus.add(statuses2); + testObject.setStatuses(kebabStatus); + testObjectList.add(testObject); testObject = new TestObject(); @@ -111,4 +146,20 @@ public class PathConditionFilterTest { assertEquals(filter.match(testObjectList.get(0)), false); } + @Test + public void filterInCollectionTest() { + PathConditionRule rule = new PathConditionRule("list_ids.micro_id", new EqualsCondition((byte) 32)); + Filter filter = new PathConditionFilter(rule); + assertTrue(filter.match(testObjectList.get(0))); + + List resultTypes = new ArrayList<>(); + resultTypes.add(Type.BLACK); + resultTypes.add(Type.GREEN); + rule = new PathConditionRule("statuses.unknown_status.unknown.resultTypes", new EqualsCondition(resultTypes)); + filter = new PathConditionFilter(rule); + assertTrue(filter.match(testObjectList.get(0))); + + + } + } diff --git a/filter/src/test/java/com/rbkmoney/geck/filter/parser/PathParserTest.java b/filter/src/test/java/com/rbkmoney/geck/filter/parser/PathParserTest.java index 43a3d6a..c16de47 100644 --- a/filter/src/test/java/com/rbkmoney/geck/filter/parser/PathParserTest.java +++ b/filter/src/test/java/com/rbkmoney/geck/filter/parser/PathParserTest.java @@ -14,18 +14,27 @@ public class PathParserTest { public void parserInitTest() { Parser parser = new PathParser("kek.tsss.qwe"); - assertEquals(parser.getItem(0), "kek"); - assertEquals(parser.getItemPath(0), "kek"); - assertEquals(parser.getItem(1), "tsss"); - assertEquals(parser.getItemPath(1), "kek.tsss"); - assertEquals(parser.getItem(2), "qwe"); - assertEquals(parser.getItemPath(2), "kek.tsss.qwe"); - assertEquals(parser.size(), 3); + assertEquals("kek", parser.getItem(0)); + assertEquals("kek", parser.getItemPath(0)); + assertEquals("tsss", parser.getItem(1)); + assertEquals("kek.tsss", parser.getItemPath(1)); + assertEquals("qwe", parser.getItem(2)); + assertEquals("kek.tsss.qwe", parser.getItemPath(2)); + assertEquals(3, parser.size()); parser = new PathParser("kek..kek..."); - assertEquals(parser.getItem(1), ""); - assertEquals(parser.getItemPath(1), "kek."); - assertEquals(parser.size(), 6); + assertEquals("kek", parser.getItem(1)); + assertEquals("kek.kek", parser.getItemPath(1)); + assertEquals(2, parser.size()); + } + + @Test + public void subParserTest() { + Parser parser = new PathParser("1.2.3.4.5"); + assertEquals(5, parser.size()); + parser = parser.getSubParser(2); + assertEquals(3, parser.size()); + assertEquals("3.4.5", parser.getItemPath(parser.size() - 1)); } } diff --git a/filter/src/test/thrift/TestObject.thrift b/filter/src/test/thrift/TestObject.thrift index 917937c..76f7663 100644 --- a/filter/src/test/thrift/TestObject.thrift +++ b/filter/src/test/thrift/TestObject.thrift @@ -7,6 +7,8 @@ struct TestObject { 4: required Type type 5: optional slist words 6: optional list numbers + 7: optional list list_ids + 8: optional list> statuses } struct Ids { diff --git a/pom.xml b/pom.xml index 9f9a758..3215d00 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.rbkmoney.geck parent - 0.4.0 + 0.5.0-SNAPSHOT pom