summaryrefslogtreecommitdiffstats
path: root/searchlib
diff options
context:
space:
mode:
authorGeir Storli <geirstorli@yahoo.no>2018-09-07 10:31:30 +0200
committerGitHub <noreply@github.com>2018-09-07 10:31:30 +0200
commit72f6b06551ea98dd2234ae026a52f23831f1f4ca (patch)
treefaf2f4c7084bef1055778afd2cfb643e551522f5 /searchlib
parente008375a7c35c3cff83710c81cc2fab96a3f79ab (diff)
parent7f6dfbde5e22632f594a7603685ba1cac002a3a4 (diff)
Merge pull request #6829 from vespa-engine/toregge/add-attribute-keyed-node-try2
Add AttributeKeyedNode
Diffstat (limited to 'searchlib')
-rw-r--r--searchlib/src/tests/expression/attributenode/attribute_node_test.cpp85
-rw-r--r--searchlib/src/vespa/searchlib/aggregation/modifiers.cpp13
-rw-r--r--searchlib/src/vespa/searchlib/aggregation/modifiers.h6
-rw-r--r--searchlib/src/vespa/searchlib/expression/CMakeLists.txt1
-rw-r--r--searchlib/src/vespa/searchlib/expression/attribute_keyed_node.cpp334
-rw-r--r--searchlib/src/vespa/searchlib/expression/attribute_keyed_node.h43
-rw-r--r--searchlib/src/vespa/searchlib/expression/attributenode.h5
7 files changed, 456 insertions, 31 deletions
diff --git a/searchlib/src/tests/expression/attributenode/attribute_node_test.cpp b/searchlib/src/tests/expression/attributenode/attribute_node_test.cpp
index ec654e93f52..8ad53fea0f0 100644
--- a/searchlib/src/tests/expression/attributenode/attribute_node_test.cpp
+++ b/searchlib/src/tests/expression/attributenode/attribute_node_test.cpp
@@ -10,6 +10,7 @@
#include <vespa/searchlib/attribute/integerbase.h>
#include <vespa/searchlib/attribute/stringbase.h>
#include <vespa/searchlib/expression/attributenode.h>
+#include <vespa/searchlib/expression/attribute_keyed_node.h>
#include <vespa/searchlib/expression/resultvector.h>
#include <vespa/vespalib/test/insertion_operators.h>
#include <vespa/vespalib/testkit/testapp.h>
@@ -30,6 +31,7 @@ using search::attribute::Config;
using search::attribute::IAttributeVector;
using search::attribute::getUndefined;
using search::expression::AttributeNode;
+using search::expression::AttributeKeyedNode;
using search::expression::EnumResultNode;
using search::expression::EnumResultNodeVector;
using search::expression::FloatResultNode;
@@ -79,18 +81,18 @@ struct AttributeManagerFixture
AttributeManagerFixture::AttributeManagerFixture()
: mgr()
{
- buildStringAttribute("sfield", { "n1", "n2", "n3", "n4" });
- buildIntegerAttribute("ifield", BasicType::Type::INT8, { 10, 20, getUndefined<int8_t>(), 40 });
- buildFloatAttribute("ffield", { 110.0, 120.0, 130.0, 140.0 });
- buildStringArrayAttribute("array.name", {{"n1.1", "n1.2"}, {"n2"}, {"n3.1", "n3.2"}, {"", "n4.2"}, {}});
- buildIntegerArrayAttribute("array.val", BasicType::Type::INT8, {{ 10, 11}, {20, 21 }, {30}, { getUndefined<int8_t>(), 41}, {}});
- buildFloatArrayAttribute("array.fval", {{ 110.0}, { 120.0, 121.0 }, { 130.0, 131.0}, { getUndefined<double>(), 141.0 }, {}});
- buildStringArrayAttribute("smap.key", {{"k1.1", "k1.2"}, {"k2"}, {"k3.1", "k3.2"}, {"", "k4.2"}, {}});
- buildStringArrayAttribute("smap.value.name", {{"n1.1", "n1.2"}, {"n2"}, {"n3.1", "n3.2"}, {"", "n4.2"}, {}});
- buildIntegerArrayAttribute("smap.value.val", BasicType::Type::INT8, {{ 10, 11}, {20, 21 }, {30}, { getUndefined<int8_t>(), 41}, {}});
- buildFloatArrayAttribute("smap.value.fval", {{ 110.0}, { 120.0, 121.0 }, { 130.0, 131.0}, { getUndefined<double>(), 141.0 }, {}});
- buildStringArrayAttribute("map.key", {{"k1.1", "k1.2"}, {"k2"}, {"k3.1"}, {"", "k4.2"}, {}});
- buildStringArrayAttribute("map.value", {{"n1.1", "n1.2"}, {}, {"n3.1", "n3.2"}, {"", "n4.2"}, {}});
+ buildStringAttribute("sfield", { "n1", ""});
+ buildIntegerAttribute("ifield", BasicType::Type::INT8, { 10, getUndefined<int8_t>() });
+ buildFloatAttribute("ffield", { 110.0, getUndefined<double>() });
+ buildStringArrayAttribute("array.name", {{"n1.1", "n1.2"}, {"n2"}, {}});
+ buildIntegerArrayAttribute("array.val", BasicType::Type::INT8, {{ 10, 11}, {20, 21 }, {}});
+ buildFloatArrayAttribute("array.fval", {{ 110.0}, { 120.0, 121.0 }, {}});
+ buildStringArrayAttribute("smap.key", {{"k1.1", "k1.2"}, {"k2"}, {}});
+ buildStringArrayAttribute("smap.value.name", {{"n1.1", "n1.2"}, {"n2"}, {}});
+ buildIntegerArrayAttribute("smap.value.val", BasicType::Type::INT8, {{ 10, 11}, {20, 21 }, {}});
+ buildFloatArrayAttribute("smap.value.fval", {{ 110.0}, { 120.0, 121.0 }, {}});
+ buildStringArrayAttribute("map.key", {{"k1.1", "k1.2"}, {"k2"}, {}});
+ buildStringArrayAttribute("map.value", {{"n1.1", "n1.2"}, {"n2"}, {}});
}
AttributeManagerFixture::~AttributeManagerFixture() = default;
@@ -212,7 +214,12 @@ Fixture::~Fixture() = default;
std::unique_ptr<AttributeNode>
Fixture::makeNode(const vespalib::string &attributeName, bool useEnumOptimization, bool preserveAccurateTypes)
{
- auto node = std::make_unique<AttributeNode>(attributeName);
+ std::unique_ptr<AttributeNode> node;
+ if (attributeName.find('{') == vespalib::string::npos) {
+ node = std::make_unique<AttributeNode>(attributeName);
+ } else {
+ node = std::make_unique<AttributeKeyedNode>(attributeName);
+ }
if (useEnumOptimization) {
node->useEnumOptimization();
}
@@ -361,26 +368,46 @@ Fixture::assertFloatArrays(std::vector<std::vector<double>> expVals, const vespa
TEST_F("test single values", Fixture)
{
- TEST_DO(f.assertInts({ 10, 20, getUndefined<int8_t>(), 40 }, "ifield"));
- TEST_DO(f.assertInts({ 10, 20, getUndefined<int8_t>(), 40 }, "ifield", true));
- TEST_DO(f.assertStrings({ "n1", "n2", "n3", "n4" }, "sfield"));
- TEST_DO(f.assertStrings({ "n1", "n2", "n3", "n4" }, "sfield", true));
- TEST_DO(f.assertFloats({ 110.0, 120.0, 130.0, 140.0 }, "ffield"));
+ TEST_DO(f.assertInts({ 10, getUndefined<int8_t>()}, "ifield"));
+ TEST_DO(f.assertInts({ 10, getUndefined<int8_t>()}, "ifield", true));
+ TEST_DO(f.assertStrings({ "n1", "" }, "sfield"));
+ TEST_DO(f.assertStrings({ "n1", "" }, "sfield", true));
+ TEST_DO(f.assertFloats({ 110.0, getUndefined<double>() }, "ffield"));
}
TEST_F("Test array values", Fixture)
{
- TEST_DO(f.assertIntArrays({{ 10, 11}, {20, 21 }, {30}, { getUndefined<int8_t>(), 41}, {}}, "array.val"));
- TEST_DO(f.assertIntArrays({{ 10, 11}, {20, 21 }, {30}, { getUndefined<int8_t>(), 41}, {}}, "array.val", true));
- TEST_DO(f.assertStringArrays({{"n1.1", "n1.2"}, {"n2"}, {"n3.1", "n3.2"}, {"", "n4.2"}, {}}, "array.name"));
- TEST_DO(f.assertStringArrays({{"n1.1", "n1.2"}, {"n2"}, {"n3.1", "n3.2"}, {"", "n4.2"}, {}}, "array.name", true));
- TEST_DO(f.assertFloatArrays({{ 110.0}, { 120.0, 121.0 }, { 130.0, 131.0}, { getUndefined<double>(), 141.0 }, {}}, "array.fval"));
- TEST_DO(f.assertStringArrays({{"k1.1", "k1.2"}, {"k2"}, {"k3.1", "k3.2"}, {"", "k4.2"}, {}}, "smap.key"));
- TEST_DO(f.assertStringArrays({{"n1.1", "n1.2"}, {"n2"}, {"n3.1", "n3.2"}, {"", "n4.2"}, {}}, "smap.value.name"));
- TEST_DO(f.assertIntArrays({{ 10, 11}, {20, 21 }, {30}, { getUndefined<int8_t>(), 41}, {}}, "smap.value.val"));
- TEST_DO(f.assertFloatArrays({{ 110.0}, { 120.0, 121.0 }, { 130.0, 131.0}, { getUndefined<double>(), 141.0 }, {}}, "smap.value.fval"));
- TEST_DO(f.assertStringArrays({{"k1.1", "k1.2"}, {"k2"}, {"k3.1"}, {"", "k4.2"}, {}}, "map.key"));
- TEST_DO(f.assertStringArrays({{"n1.1", "n1.2"}, {}, {"n3.1", "n3.2"}, {"", "n4.2"}, {}}, "map.value"));
+ TEST_DO(f.assertIntArrays({{ 10, 11}, {20, 21 }, {}}, "array.val"));
+ TEST_DO(f.assertIntArrays({{ 10, 11}, {20, 21 }, {}}, "array.val", true));
+ TEST_DO(f.assertStringArrays({{"n1.1", "n1.2"}, {"n2"}, {}}, "array.name"));
+ TEST_DO(f.assertStringArrays({{"n1.1", "n1.2"}, {"n2"}, {}}, "array.name", true));
+ TEST_DO(f.assertFloatArrays({{ 110.0}, { 120.0, 121.0 }, {}}, "array.fval"));
+ TEST_DO(f.assertStringArrays({{"k1.1", "k1.2"}, {"k2"}, {}}, "smap.key"));
+ TEST_DO(f.assertStringArrays({{"n1.1", "n1.2"}, {"n2"}, {}}, "smap.value.name"));
+ TEST_DO(f.assertIntArrays({{ 10, 11}, {20, 21 }, {}}, "smap.value.val"));
+ TEST_DO(f.assertFloatArrays({{ 110.0}, { 120.0, 121.0 }, {}}, "smap.value.fval"));
+ TEST_DO(f.assertStringArrays({{"k1.1", "k1.2"}, {"k2"}, {}}, "map.key"));
+ TEST_DO(f.assertStringArrays({{"n1.1", "n1.2"}, {"n2"}, {}}, "map.value"));
+}
+
+TEST_F("test keyed values", Fixture)
+{
+ TEST_DO(f.assertStrings({"n1.1", "", ""}, "smap{\"k1.1\"}.name"));
+ TEST_DO(f.assertStrings({"n1.2", "", ""}, "smap{\"k1.2\"}.name"));
+ TEST_DO(f.assertStrings({"", "n2", ""}, "smap{\"k2\"}.name"));
+ TEST_DO(f.assertStrings({"", "", ""}, "smap{\"k5\"}.name"));
+ TEST_DO(f.assertFloats({ 110.0, getUndefined<double>(), getUndefined<double>()}, "smap{\"k1.1\"}.fval"));
+ TEST_DO(f.assertFloats({ getUndefined<double>(), getUndefined<double>(), getUndefined<double>()}, "smap{\"k1.2\"}.fval"));
+ TEST_DO(f.assertFloats({ getUndefined<double>(), 120.0, getUndefined<double>()}, "smap{\"k2\"}.fval"));
+ TEST_DO(f.assertFloats({ getUndefined<double>(), getUndefined<double>(), getUndefined<double>()}, "smap{\"k5\"}.fval"));
+ TEST_DO(f.assertInts({ 10, getUndefined<int8_t>(), getUndefined<int8_t>()}, "smap{\"k1.1\"}.val"));
+ TEST_DO(f.assertInts({ 11, getUndefined<int8_t>(), getUndefined<int8_t>()}, "smap{\"k1.2\"}.val"));
+ TEST_DO(f.assertInts({ getUndefined<int8_t>(), 20, getUndefined<int8_t>()}, "smap{\"k2\"}.val"));
+ TEST_DO(f.assertInts({ getUndefined<int8_t>(), getUndefined<int8_t>(), getUndefined<int8_t>()}, "smap{\"k5\"}.val"));
+ TEST_DO(f.assertStrings({"n1.1", "", ""}, "map{\"k1.1\"}"));
+ TEST_DO(f.assertStrings({"n1.2", "", ""}, "map{\"k1.2\"}"));
+ TEST_DO(f.assertStrings({"", "n2", ""}, "map{\"k2\"}"));
+ TEST_DO(f.assertStrings({"", "", ""}, "map{\"k5\"}"));
}
}
diff --git a/searchlib/src/vespa/searchlib/aggregation/modifiers.cpp b/searchlib/src/vespa/searchlib/aggregation/modifiers.cpp
index a59bbe14404..fe71484c4e6 100644
--- a/searchlib/src/vespa/searchlib/aggregation/modifiers.cpp
+++ b/searchlib/src/vespa/searchlib/aggregation/modifiers.cpp
@@ -4,6 +4,7 @@
#include "grouping.h"
#include <vespa/searchlib/expression/multiargfunctionnode.h>
#include <vespa/searchlib/expression/attributenode.h>
+#include <vespa/searchlib/expression/attribute_keyed_node.h>
#include <vespa/searchlib/expression/documentfieldnode.h>
using namespace search::expression;
@@ -63,6 +64,18 @@ Attribute2DocumentAccessor::getReplacementNode(const AttributeNode &attributeNod
return std::make_unique<DocumentFieldNode>(attributeNode.getAttributeName());
}
+std::unique_ptr<ExpressionNode>
+Attribute2AttributeKeyed::getReplacementNode(const AttributeNode &attributeNode)
+{
+ const vespalib::string &attributeName = attributeNode.getAttributeName();
+ auto lBracePos = attributeName.find('{');
+ if (attributeNode.isKeyed() || lBracePos == vespalib::string::npos) {
+ return std::unique_ptr<ExpressionNode>();
+ } else {
+ return std::make_unique<AttributeKeyedNode>(attributeName);
+ }
+}
+
}
// this function was added by ../../forcelink.sh
diff --git a/searchlib/src/vespa/searchlib/aggregation/modifiers.h b/searchlib/src/vespa/searchlib/aggregation/modifiers.h
index 0120cb4eac9..6ffda313904 100644
--- a/searchlib/src/vespa/searchlib/aggregation/modifiers.h
+++ b/searchlib/src/vespa/searchlib/aggregation/modifiers.h
@@ -28,4 +28,10 @@ private:
std::unique_ptr<search::expression::ExpressionNode> getReplacementNode(const search::expression::AttributeNode &attributeNode) override;
};
+class Attribute2AttributeKeyed : public AttributeNodeReplacer
+{
+private:
+ std::unique_ptr<search::expression::ExpressionNode> getReplacementNode(const search::expression::AttributeNode &attributeNode) override;
+};
+
}
diff --git a/searchlib/src/vespa/searchlib/expression/CMakeLists.txt b/searchlib/src/vespa/searchlib/expression/CMakeLists.txt
index 1b7a26bf621..944bc6f63df 100644
--- a/searchlib/src/vespa/searchlib/expression/CMakeLists.txt
+++ b/searchlib/src/vespa/searchlib/expression/CMakeLists.txt
@@ -1,6 +1,7 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
vespa_add_library(searchlib_expression OBJECT
SOURCES
+ attribute_keyed_node.cpp
attributenode.cpp
attributeresult.cpp
enumattributeresult.cpp
diff --git a/searchlib/src/vespa/searchlib/expression/attribute_keyed_node.cpp b/searchlib/src/vespa/searchlib/expression/attribute_keyed_node.cpp
new file mode 100644
index 00000000000..fde9e4a0b3c
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/expression/attribute_keyed_node.cpp
@@ -0,0 +1,334 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "attribute_keyed_node.h"
+#include <vespa/vespalib/stllike/asciistream.h>
+#include <vespa/vespalib/util/exceptions.h>
+#include <vespa/searchcommon/attribute/attributecontent.h>
+#include <vespa/searchcommon/attribute/iattributecontext.h>
+#include <vespa/searchcommon/common/undefinedvalues.h>
+
+using search::attribute::AttributeContent;
+using search::attribute::IAttributeVector;
+using search::attribute::BasicType;
+using search::attribute::getUndefined;
+using EnumHandle = IAttributeVector::EnumHandle;
+
+namespace search::expression {
+
+class AttributeKeyedNode::KeyHandler
+{
+protected:
+ const IAttributeVector &_attribute;
+
+ KeyHandler(const IAttributeVector &attribute)
+ : _attribute(attribute)
+ {
+ }
+public:
+ static uint32_t noKeyIdx() { return std::numeric_limits<uint32_t>::max(); }
+ virtual ~KeyHandler() = default;
+ virtual uint32_t handle(DocId docId) = 0;
+};
+
+namespace {
+
+class BadKeyHandler : public AttributeKeyedNode::KeyHandler
+{
+public:
+ BadKeyHandler(const IAttributeVector &attribute)
+ : KeyHandler(attribute)
+ {
+ }
+ uint32_t handle(DocId) override { return noKeyIdx(); }
+};
+
+template <typename KeyType>
+KeyType convertKey(const IAttributeVector &, const vespalib::string &key)
+{
+ KeyType ret;
+ vespalib::asciistream is(key);
+ is >> ret;
+ return ret;
+}
+
+template <>
+vespalib::string convertKey<vespalib::string>(const IAttributeVector &, const vespalib::string &key)
+{
+ return key;
+}
+
+template <>
+EnumHandle convertKey<EnumHandle>(const IAttributeVector &attribute, const vespalib::string &key)
+{
+ EnumHandle ret;
+ if (!attribute.findEnum(key.c_str(), ret)) {
+ ret = EnumHandle();
+ }
+ return ret;
+}
+
+template <typename T, typename KeyType = T>
+class KeyHandlerT : public AttributeKeyedNode::KeyHandler
+{
+ AttributeContent<T> _values;
+ KeyType _key;
+
+public:
+ KeyHandlerT(const IAttributeVector &attribute, const vespalib::string &key)
+ : KeyHandler(attribute),
+ _values(),
+ _key(convertKey<KeyType>(attribute, key))
+ {
+ }
+ ~KeyHandlerT() override;
+ uint32_t handle(DocId docId) override {
+ _values.fill(_attribute, docId);
+ for (uint32_t i = 0; i < _values.size(); ++i) {
+ if (_key == _values[i]) {
+ return i;
+ }
+ }
+ return noKeyIdx();
+ }
+};
+
+template <typename T, typename KeyType>
+KeyHandlerT<T,KeyType>::~KeyHandlerT()
+{
+}
+
+using IntegerKeyHandler = KeyHandlerT<IAttributeVector::largeint_t>;
+using FloatKeyHandler = KeyHandlerT<double>;
+using StringKeyHandler = KeyHandlerT<const char *, vespalib::string>;
+using EnumKeyHandler = KeyHandlerT<EnumHandle>;
+
+class ValueHandler : public AttributeNode::Handler
+{
+protected:
+ std::unique_ptr<AttributeKeyedNode::KeyHandler> _keyHandler;
+ const IAttributeVector &_attribute;
+ ValueHandler(std::unique_ptr<AttributeKeyedNode::KeyHandler> keyHandler, const IAttributeVector &attribute)
+ : _keyHandler(std::move(keyHandler)),
+ _attribute(attribute)
+ {
+ }
+};
+
+template <typename T, typename ResultNodeType>
+class ValueHandlerT : public ValueHandler
+{
+ AttributeContent<T> _values;
+ ResultNodeType &_result;
+ T _undefinedValue;
+public:
+ ValueHandlerT(std::unique_ptr<AttributeKeyedNode::KeyHandler> keyHandler, const IAttributeVector &attribute, ResultNodeType &result, T undefinedValue)
+ : ValueHandler(std::move(keyHandler), attribute),
+ _values(),
+ _result(result),
+ _undefinedValue(undefinedValue)
+ {
+ }
+ void handle(const AttributeResult & r) override {
+ uint32_t docId = r.getDocId();
+ uint32_t keyIdx = _keyHandler->handle(docId);
+ if (keyIdx != AttributeKeyedNode::KeyHandler::noKeyIdx()) {
+ _values.fill(_attribute, docId);
+ if (keyIdx < _values.size()) {
+ _result = _values[keyIdx];
+ return;
+ }
+ }
+ _result = _undefinedValue;
+ }
+};
+
+template <typename ResultNodeType>
+using IntegerValueHandler = ValueHandlerT<IAttributeVector::largeint_t, ResultNodeType>;
+using FloatValueHandler = ValueHandlerT<double, FloatResultNode>;
+using StringValueHandler = ValueHandlerT<const char *, StringResultNode>;
+using EnumValueHandler = ValueHandlerT<EnumHandle, EnumResultNode>;
+
+const IAttributeVector *findAttribute(const search::attribute::IAttributeContext &attrCtx, bool useEnumOptimization, const vespalib::string &name)
+{
+ const IAttributeVector *attribute = useEnumOptimization ? attrCtx.getAttributeStableEnum(name) : attrCtx.getAttribute(name);
+ if (attribute == nullptr) {
+ throw std::runtime_error(vespalib::make_string("Failed locating attribute vector '%s'", name.c_str()));
+ }
+ return attribute;
+}
+
+IAttributeVector::largeint_t getUndefinedValue(BasicType::Type basicType)
+{
+ switch (basicType) {
+ case BasicType::INT8:
+ return getUndefined<int8_t>();
+ case BasicType::INT16:
+ return getUndefined<int16_t>();
+ case BasicType::INT32:
+ return getUndefined<int32_t>();
+ case BasicType::INT64:
+ return getUndefined<int64_t>();
+ break;
+ default:
+ return 0;
+ }
+}
+
+}
+
+AttributeKeyedNode::AttributeKeyedNode()
+ : AttributeNode(),
+ _keyAttributeName(),
+ _valueAttributeName(),
+ _key(),
+ _keyAttribute(nullptr)
+{
+}
+
+AttributeKeyedNode::AttributeKeyedNode(const AttributeKeyedNode &) = default;
+
+AttributeKeyedNode::AttributeKeyedNode(vespalib::stringref name)
+ : AttributeNode(name),
+ _keyAttributeName(),
+ _valueAttributeName(),
+ _key(),
+ _keyAttribute(nullptr)
+{
+ setupAttributeNames();
+}
+
+AttributeKeyedNode::~AttributeKeyedNode() = default;
+
+AttributeKeyedNode &
+AttributeKeyedNode::operator=(const AttributeKeyedNode &rhs) = default;
+
+void
+AttributeKeyedNode::setupAttributeNames()
+{
+ vespalib::asciistream keyName;
+ vespalib::asciistream valueName;
+ auto leftBracePos = _attributeName.find('{');
+ auto leftQuotePos = _attributeName.find('"', leftBracePos + 1);
+ auto rightQuotePos = _attributeName.find('"', leftQuotePos + 1);
+ auto rightBracePos = _attributeName.find('}', rightQuotePos + 1);
+ auto baseName = _attributeName.substr(0, leftBracePos);
+ keyName << baseName << ".key";
+ valueName << baseName << ".value" << _attributeName.substr(rightBracePos + 1);
+ _keyAttributeName = keyName.str();
+ _valueAttributeName = valueName.str();
+ _key = _attributeName.substr(leftQuotePos + 1, rightQuotePos - leftQuotePos - 1);
+}
+
+template <typename ResultNodeType>
+void
+AttributeKeyedNode::prepareIntValues(std::unique_ptr<KeyHandler> keyHandler, const IAttributeVector &attribute, IAttributeVector::largeint_t undefinedValue)
+{
+ auto resultNode = std::make_unique<ResultNodeType>();
+ _handler = std::make_unique<IntegerValueHandler<ResultNodeType>>(std::move(keyHandler), attribute, *resultNode, undefinedValue);
+ setResultType(std::move(resultNode));
+}
+
+std::unique_ptr<AttributeKeyedNode::KeyHandler>
+AttributeKeyedNode::makeKeyHandlerHelper()
+{
+ const IAttributeVector &attribute = *_keyAttribute;
+ if (attribute.hasEnum() && _useEnumOptimization) {
+ return std::make_unique<EnumKeyHandler>(attribute, _key);
+ } else if (attribute.isIntegerType()) {
+ return std::make_unique<IntegerKeyHandler>(attribute, _key);
+ } else if (attribute.isFloatingPointType()) {
+ return std::make_unique<FloatKeyHandler>(attribute, _key);
+ } else if (attribute.isStringType()) {
+ return std::make_unique<StringKeyHandler>(attribute, _key);
+ } else {
+ return std::make_unique<BadKeyHandler>(attribute);
+ }
+}
+
+std::unique_ptr<AttributeKeyedNode::KeyHandler>
+AttributeKeyedNode::makeKeyHandler()
+{
+ try {
+ return makeKeyHandlerHelper();
+ } catch (const vespalib::IllegalArgumentException &) {
+ return std::make_unique<BadKeyHandler>(*_keyAttribute);
+ }
+}
+
+void
+AttributeKeyedNode::onPrepare(bool preserveAccurateTypes)
+{
+ auto keyHandler = makeKeyHandler();
+ const IAttributeVector * attribute = _scratchResult->getAttribute();
+ if (attribute != nullptr) {
+ BasicType::Type basicType = attribute->getBasicType();
+ if (attribute->isIntegerType()) {
+ IAttributeVector::largeint_t undefinedValue = getUndefinedValue(basicType);
+ if (preserveAccurateTypes) {
+ switch (basicType) {
+ case BasicType::INT8:
+ prepareIntValues<Int8ResultNode>(std::move(keyHandler), *attribute, undefinedValue);
+ break;
+ case BasicType::INT16:
+ prepareIntValues<Int16ResultNode>(std::move(keyHandler), *attribute, undefinedValue);
+ break;
+ case BasicType::INT32:
+ prepareIntValues<Int32ResultNode>(std::move(keyHandler), *attribute, undefinedValue);
+ break;
+ case BasicType::INT64:
+ prepareIntValues<Int64ResultNode>(std::move(keyHandler), *attribute, undefinedValue);
+ break;
+ default:
+ throw std::runtime_error("This is no valid integer attribute " + attribute->getName());
+ break;
+ }
+ } else {
+ prepareIntValues<Int64ResultNode>(std::move(keyHandler), *attribute, undefinedValue);
+ }
+ } else if (attribute->isFloatingPointType()) {
+ auto resultNode = std::make_unique<FloatResultNode>();
+ _handler = std::make_unique<FloatValueHandler>(std::move(keyHandler), *attribute, *resultNode, getUndefined<double>());
+ setResultType(std::move(resultNode));
+ } else if (attribute->isStringType()) {
+ if (_useEnumOptimization) {
+ auto resultNode = std::make_unique<EnumResultNode>();
+ _handler = std::make_unique<EnumValueHandler>(std::move(keyHandler), *attribute, *resultNode, EnumHandle());
+ setResultType(std::move(resultNode));
+ } else {
+ auto resultNode = std::make_unique<StringResultNode>();
+ _handler = std::make_unique<StringValueHandler>(std::move(keyHandler), *attribute, *resultNode, "");
+ setResultType(std::move(resultNode));
+ }
+ } else {
+ throw std::runtime_error(vespalib::make_string("Can not deduce correct resultclass for attribute vector '%s'",
+ attribute->getName().c_str()));
+ }
+ }
+}
+
+void
+AttributeKeyedNode::cleanup()
+{
+ _keyAttribute = nullptr;
+ AttributeNode::cleanup();
+}
+
+void
+AttributeKeyedNode::wireAttributes(const search::attribute::IAttributeContext &attrCtx)
+{
+ auto valueAttribute = findAttribute(attrCtx, _useEnumOptimization, _valueAttributeName);
+ _hasMultiValue = false;
+ _scratchResult = std::make_unique<AttributeResult>(valueAttribute, 0);
+ _keyAttribute = findAttribute(attrCtx, _useEnumOptimization, _keyAttributeName);
+}
+
+void
+AttributeKeyedNode::visitMembers(vespalib::ObjectVisitor &visitor) const
+{
+ AttributeNode::visitMembers(visitor);
+ visit(visitor, "keyAttributeName", _keyAttributeName);
+ visit(visitor, "valueAttributeName", _valueAttributeName);
+ visit(visitor, "key", _key);
+}
+
+}
diff --git a/searchlib/src/vespa/searchlib/expression/attribute_keyed_node.h b/searchlib/src/vespa/searchlib/expression/attribute_keyed_node.h
new file mode 100644
index 00000000000..b5a57fd5065
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/expression/attribute_keyed_node.h
@@ -0,0 +1,43 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include "attributenode.h"
+
+namespace search::expression {
+
+/**
+ * Extract map value from attribute for the map key specified in the
+ * grouping expression.
+ */
+class AttributeKeyedNode : public AttributeNode
+{
+public:
+ using IAttributeVector = search::attribute::IAttributeVector;
+ class KeyHandler;
+private:
+ vespalib::string _keyAttributeName;
+ vespalib::string _valueAttributeName;
+ vespalib::string _key;
+ const IAttributeVector *_keyAttribute;
+
+ void setupAttributeNames();
+ template <typename ResultNodeType>
+ void prepareIntValues(std::unique_ptr<KeyHandler> keyHandler, const IAttributeVector &attribute, IAttributeVector::largeint_t undefinedValue);
+ std::unique_ptr<KeyHandler> makeKeyHandlerHelper();
+ std::unique_ptr<KeyHandler> makeKeyHandler();
+ void cleanup() override;
+ void wireAttributes(const search::attribute::IAttributeContext & attrCtx) override;
+ void onPrepare(bool preserveAccurateTypes) override;
+public:
+ AttributeKeyedNode();
+ AttributeKeyedNode(vespalib::stringref name);
+ AttributeKeyedNode(const AttributeKeyedNode &);
+ AttributeKeyedNode(AttributeKeyedNode &&) = delete;
+ ~AttributeKeyedNode() override;
+ AttributeKeyedNode &operator=(const AttributeKeyedNode &rhs);
+ AttributeKeyedNode &operator=(AttributeKeyedNode &&rhs) = delete;
+ void visitMembers(vespalib::ObjectVisitor &visitor) const override;
+ bool isKeyed() const override { return true; }
+};
+
+}
diff --git a/searchlib/src/vespa/searchlib/expression/attributenode.h b/searchlib/src/vespa/searchlib/expression/attributenode.h
index 3cbccd32e60..e12b5490955 100644
--- a/searchlib/src/vespa/searchlib/expression/attributenode.h
+++ b/searchlib/src/vespa/searchlib/expression/attributenode.h
@@ -55,7 +55,8 @@ public:
void useEnumOptimization(bool use=true) { _useEnumOptimization = use; }
bool hasMultiValue() const { return _hasMultiValue; }
-protected:
+ virtual bool isKeyed() const { return false; }
+public:
class Handler
{
public:
@@ -68,7 +69,7 @@ private:
class StringHandler;
class EnumHandler;
protected:
- void cleanup();
+ virtual void cleanup();
void wireAttributes(const search::attribute::IAttributeContext & attrCtx) override;
void onPrepare(bool preserveAccurateTypes) override;
bool onExecute() const override;