diff options
4 files changed, 124 insertions, 11 deletions
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/AttributeMapLookupValue.java b/container-search/src/main/java/com/yahoo/search/grouping/request/AttributeMapLookupValue.java new file mode 100644 index 00000000000..fb9702cc1cc --- /dev/null +++ b/container-search/src/main/java/com/yahoo/search/grouping/request/AttributeMapLookupValue.java @@ -0,0 +1,57 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.search.grouping.request; + +/** + * This class represents a lookup in a map attribute in a {@link GroupingExpression}. + * + * It evaluates to the value found using the given key for the lookup in that attribute. + * The key is either specified explicitly or found via a key source attribute. + * + * @author geirst + */ +public class AttributeMapLookupValue extends AttributeValue { + + private final String prefix; + private final String suffix; + private final String key; + private final String keySourceAttribute; + + private AttributeMapLookupValue(String attributeValue, String prefix, String suffix, String key, String keySourceAttribute) { + super(attributeValue); + this.prefix = prefix; + this.suffix = suffix; + this.key = key; + this.keySourceAttribute = keySourceAttribute; + } + + public static AttributeMapLookupValue fromKey(String prefix, String key, String suffix) { + return new AttributeMapLookupValue(prefix + "{\"" + key + "\"}" + suffix, + prefix, suffix, key, ""); + } + + public static AttributeMapLookupValue fromKeySourceAttribute(String prefix, String keySourceAttribute, String suffix) { + return new AttributeMapLookupValue(prefix + "{attribute(" + keySourceAttribute + ")}" + suffix, + prefix, suffix, "", keySourceAttribute); + } + + @Override + public AttributeMapLookupValue copy() { + return new AttributeMapLookupValue(getAttributeName(), prefix, suffix, key, keySourceAttribute); + } + + public String getKeyAttribute() { + return prefix + ".key"; + } + + public String getValueAttribute() { + return prefix + ".value" + suffix; + } + + public String getKey() { + return key; + } + + public String getKeySourceAttribute() { + return keySourceAttribute; + } +} diff --git a/container-search/src/main/java/com/yahoo/search/grouping/vespa/ExpressionConverter.java b/container-search/src/main/java/com/yahoo/search/grouping/vespa/ExpressionConverter.java index d2dfb3c0ee7..95384fb12d3 100644 --- a/container-search/src/main/java/com/yahoo/search/grouping/vespa/ExpressionConverter.java +++ b/container-search/src/main/java/com/yahoo/search/grouping/vespa/ExpressionConverter.java @@ -6,6 +6,7 @@ import com.yahoo.search.grouping.request.AggregatorNode; import com.yahoo.search.grouping.request.AndFunction; import com.yahoo.search.grouping.request.ArrayAtLookup; import com.yahoo.search.grouping.request.AttributeFunction; +import com.yahoo.search.grouping.request.AttributeMapLookupValue; import com.yahoo.search.grouping.request.AttributeValue; import com.yahoo.search.grouping.request.AvgAggregator; import com.yahoo.search.grouping.request.BucketValue; @@ -263,6 +264,9 @@ class ExpressionConverter { if (exp instanceof AndFunction) { return addArguments(new AndFunctionNode(), (AndFunction)exp); } + if (exp instanceof AttributeMapLookupValue) { + return new AttributeNode(((AttributeMapLookupValue)exp).getAttributeName()); + } if (exp instanceof AttributeValue) { return new AttributeNode(((AttributeValue)exp).getAttributeName()); } diff --git a/container-search/src/main/javacc/com/yahoo/search/grouping/request/parser/GroupingParser.jj b/container-search/src/main/javacc/com/yahoo/search/grouping/request/parser/GroupingParser.jj index 0678b030bc5..6a55a32eb8a 100644 --- a/container-search/src/main/javacc/com/yahoo/search/grouping/request/parser/GroupingParser.jj +++ b/container-search/src/main/javacc/com/yahoo/search/grouping/request/parser/GroupingParser.jj @@ -404,14 +404,31 @@ AndFunction andFunction(GroupingOperation grp) : AttributeValue attributeValue() : { - StringBuilder ret = new StringBuilder(); + StringBuilder prefix = new StringBuilder(); + StringBuilder suffix = new StringBuilder(); String str; + String key = null; + AttributeFunction keySourceAttr = null; } { - ( str = identifier() { ret.append(str); } - ( ( <DOT> { ret.append(token.image); } ( str = identifier() { ret.append(str); } ) ) | - ( lcurly() str = string() { ret.append("{\"").append(str).append("\"}"); } rcurly() ) )* ) - { return new AttributeValue(ret.toString()); } + ( str = identifier() { prefix.append(str); } + ( LOOKAHEAD(2) <DOT> { prefix.append(token.image); } ( str = identifier() { prefix.append(str); } ) )* + ( LOOKAHEAD(3) + ( lcurly() key = string() rcurly() ) | + ( lcurly() keySourceAttr = attributeFunction() rcurly() ) + )? + ( <DOT> { suffix.append(token.image); } ( str = identifier() { suffix.append(str); } ) )* + ) + { + if (key != null) { + return AttributeMapLookupValue.fromKey(prefix.toString(), key, suffix.toString()); + } else if (keySourceAttr != null) { + return AttributeMapLookupValue.fromKeySourceAttribute(prefix.toString(), keySourceAttr.getAttributeName(), suffix.toString()); + } else { + prefix.append(suffix.toString()); + return new AttributeValue(prefix.toString()); + } + } } AttributeFunction attributeFunction() : diff --git a/container-search/src/test/java/com/yahoo/search/grouping/request/parser/GroupingParserTestCase.java b/container-search/src/test/java/com/yahoo/search/grouping/request/parser/GroupingParserTestCase.java index 2c43873036e..afbad73f982 100644 --- a/container-search/src/test/java/com/yahoo/search/grouping/request/parser/GroupingParserTestCase.java +++ b/container-search/src/test/java/com/yahoo/search/grouping/request/parser/GroupingParserTestCase.java @@ -2,6 +2,7 @@ package com.yahoo.search.grouping.request.parser; import com.yahoo.search.grouping.request.AllOperation; +import com.yahoo.search.grouping.request.AttributeMapLookupValue; import com.yahoo.search.grouping.request.EachOperation; import com.yahoo.search.grouping.request.GroupingOperation; import com.yahoo.search.query.parser.Parsable; @@ -24,12 +25,6 @@ import static org.junit.Assert.fail; */ public class GroupingParserTestCase { - // -------------------------------------------------------------------------------- - // - // Tests. - // - // -------------------------------------------------------------------------------- - @Test public void requireThatMathAllowsWhitespace() { for (String op : Arrays.asList("+", " +", " + ", "+ ", @@ -448,6 +443,46 @@ public class GroupingParserTestCase { assertParse("all(group(my.little{key }))", "all(group(my.little{\"key\"}))"); assertParse("all(group(my.little{\"key\"}))", "all(group(my.little{\"key\"}))"); assertParse("all(group(my.little{\"key{}%\"}))", "all(group(my.little{\"key{}%\"}))"); + assertParse("all(group(my.little{key}.name))", "all(group(my.little{\"key\"}.name))"); + assertParse("all(group(my.little{key }.name))", "all(group(my.little{\"key\"}.name))"); + assertParse("all(group(my.little{\"key\"}.name))", "all(group(my.little{\"key\"}.name))"); + assertParse("all(group(my.little{\"key{}%\"}.name))", "all(group(my.little{\"key{}%\"}.name))"); + + assertAttributeMapLookup("all(group(my_map{\"my_key\"}))", + "my_map.key", "my_map.value", "my_key", ""); + assertAttributeMapLookup("all(group(my_map{\"my_key\"}.name))", + "my_map.key", "my_map.value.name", "my_key", ""); + assertAttributeMapLookup("all(group(my.map{\"my_key\"}))", + "my.map.key", "my.map.value", "my_key", ""); + } + + @Test + public void testMapSyntaxWithKeySourceAttribute() { + assertAttributeMapLookup("all(group(my_map{attribute(my_attr)}))", + "my_map.key", "my_map.value", "", "my_attr"); + assertAttributeMapLookup("all(group(my_map{attribute(my_attr)}.name))", + "my_map.key", "my_map.value.name", "", "my_attr"); + assertAttributeMapLookup("all(group(my.map{attribute(my_attr.name)}))", + "my.map.key", "my.map.value", "", "my_attr.name"); + + assertIllegalArgument("all(group(my_map{attribute(\"my_attr\")}))", + "Encountered \" <STRING> \"\\\"my_attr\\\" \"\" at line 1, column 28"); + + } + + private static void assertAttributeMapLookup(String request, + String expKeyAttribute, + String expValueAttribute, + String expKey, + String expKeySourceAttribute) { + assertParse(request, request); + List<GroupingOperation> operations = GroupingOperation.fromStringAsList(request); + assertEquals(1, operations.size()); + AttributeMapLookupValue mapLookup = (AttributeMapLookupValue)operations.get(0).getGroupBy(); + assertEquals(expKeyAttribute, mapLookup.getKeyAttribute()); + assertEquals(expValueAttribute, mapLookup.getValueAttribute()); + assertEquals(expKey, mapLookup.getKey()); + assertEquals(expKeySourceAttribute, mapLookup.getKeySourceAttribute()); } @Test |