diff options
author | Geir Storli <geirstorli@yahoo.no> | 2018-09-07 10:31:30 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-09-07 10:31:30 +0200 |
commit | 72f6b06551ea98dd2234ae026a52f23831f1f4ca (patch) | |
tree | faf2f4c7084bef1055778afd2cfb643e551522f5 /searchlib | |
parent | e008375a7c35c3cff83710c81cc2fab96a3f79ab (diff) | |
parent | 7f6dfbde5e22632f594a7603685ba1cac002a3a4 (diff) |
Merge pull request #6829 from vespa-engine/toregge/add-attribute-keyed-node-try2
Add AttributeKeyedNode
Diffstat (limited to 'searchlib')
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; |