diff options
author | Geir Storli <geirst@verizonmedia.com> | 2020-06-05 17:09:18 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-06-05 17:09:18 +0200 |
commit | 86c998932c894e47fe3a710d79b2f37894994947 (patch) | |
tree | 24abdcc71eb97f86fba302cc27715114b5934573 | |
parent | 2e0d48abbec7e6716ba5414e3b3368e3880118bc (diff) | |
parent | 6e3e3f2ce4322be9d9c7bbafc71236f3cb834e5a (diff) |
Merge pull request #13498 from vespa-engine/geirst/multi-value-attributes-matched-elements-only
Multi-value attributes matched elements only
17 files changed, 554 insertions, 313 deletions
diff --git a/searchsummary/CMakeLists.txt b/searchsummary/CMakeLists.txt index 2a23dd4c495..3792f2b6218 100644 --- a/searchsummary/CMakeLists.txt +++ b/searchsummary/CMakeLists.txt @@ -20,11 +20,13 @@ vespa_define_module( src/vespa/searchsummary src/vespa/searchsummary/config src/vespa/searchsummary/docsummary + src/vespa/searchsummary/test TESTS src/tests/docsumformat src/tests/docsummary src/tests/docsummary/attribute_combiner + src/tests/docsummary/attributedfw src/tests/docsummary/matched_elements_filter src/tests/docsummary/slime_summary src/tests/extractkeywords diff --git a/searchsummary/src/tests/docsummary/attribute_combiner/CMakeLists.txt b/searchsummary/src/tests/docsummary/attribute_combiner/CMakeLists.txt index cffdef25e5b..3ac95211aec 100644 --- a/searchsummary/src/tests/docsummary/attribute_combiner/CMakeLists.txt +++ b/searchsummary/src/tests/docsummary/attribute_combiner/CMakeLists.txt @@ -5,6 +5,7 @@ vespa_add_executable(searchsummary_attribute_combiner_test_app TEST attribute_combiner_test.cpp DEPENDS searchsummary + searchsummary_test GTest::GTest ) vespa_add_test(NAME searchsummary_attribute_combiner_test_app COMMAND searchsummary_attribute_combiner_test_app) diff --git a/searchsummary/src/tests/docsummary/attribute_combiner/attribute_combiner_test.cpp b/searchsummary/src/tests/docsummary/attribute_combiner/attribute_combiner_test.cpp index ae8e5f88c28..eaeaa27f053 100644 --- a/searchsummary/src/tests/docsummary/attribute_combiner/attribute_combiner_test.cpp +++ b/searchsummary/src/tests/docsummary/attribute_combiner/attribute_combiner_test.cpp @@ -1,200 +1,41 @@ // Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include <vespa/searchcommon/common/undefinedvalues.h> -#include <vespa/searchlib/attribute/attributefactory.h> -#include <vespa/searchlib/attribute/attributemanager.h> #include <vespa/searchlib/attribute/attributevector.h> -#include <vespa/searchlib/attribute/attributevector.hpp> -#include <vespa/searchlib/attribute/floatbase.h> -#include <vespa/searchlib/attribute/integerbase.h> -#include <vespa/searchlib/attribute/stringbase.h> #include <vespa/searchlib/common/matching_elements.h> #include <vespa/searchlib/common/matching_elements_fields.h> #include <vespa/searchlib/util/slime_output_raw_buf_adapter.h> +#include <vespa/searchsummary/docsummary/docsumfieldwriter.h> #include <vespa/searchsummary/docsummary/docsumstate.h> #include <vespa/searchsummary/docsummary/docsum_field_writer_state.h> #include <vespa/searchsummary/docsummary/attribute_combiner_dfw.h> +#include <vespa/searchsummary/test/mock_attribute_manager.h> +#include <vespa/searchsummary/test/mock_state_callback.h> +#include <vespa/searchsummary/test/slime_value.h> #include <vespa/vespalib/data/slime/slime.h> #include <vespa/vespalib/gtest/gtest.h> #include <vespa/log/log.h> LOG_SETUP("attribute_combiner_test"); -using search::AttributeFactory; -using search::AttributeManager; -using search::AttributeVector; -using search::IntegerAttribute; -using search::FloatingPointAttribute; -using search::MatchingElements; -using search::StringAttribute; using search::attribute::BasicType; -using search::attribute::CollectionType; -using search::attribute::Config; -using search::attribute::IAttributeVector; using search::attribute::getUndefined; using search::docsummary::AttributeCombinerDFW; using search::docsummary::GetDocsumsState; using search::docsummary::GetDocsumsStateCallback; using search::docsummary::IDocsumEnvironment; using search::docsummary::IDocsumFieldWriter; +using search::docsummary::test::MockAttributeManager; +using search::docsummary::test::MockStateCallback; +using search::docsummary::test::SlimeValue; namespace { -vespalib::string -toCompactJsonString(const vespalib::Slime &slime) -{ - vespalib::SimpleBuffer buf; - vespalib::slime::JsonFormat::encode(slime, buf, true); - return buf.get().make_string(); -} - -struct FieldBlock { - vespalib::string input; - vespalib::Slime slime; - search::RawBuf binary; - vespalib::string json; - - explicit FieldBlock(const vespalib::string &jsonInput); - ~FieldBlock(); - const char *data() const { return binary.GetDrainPos(); } - size_t dataLen() const { return binary.GetUsedLen(); } -}; - -FieldBlock::FieldBlock(const vespalib::string &jsonInput) - : input(jsonInput), slime(), binary(1024), json() -{ - size_t used = vespalib::slime::JsonFormat::decode(jsonInput, slime); - EXPECT_TRUE(used > 0); - json = toCompactJsonString(slime); - search::SlimeOutputRawBufAdapter adapter(binary); - vespalib::slime::BinaryFormat::encode(slime, adapter); -} - -FieldBlock::~FieldBlock() = default; - -struct AttributeManagerFixture -{ - AttributeManager mgr; - - AttributeManagerFixture(); - - ~AttributeManagerFixture(); - - template <typename AttributeType, typename ValueType> - void - buildAttribute(const vespalib::string &name, - BasicType type, - std::vector<std::vector<ValueType>> values); - - void - buildStringAttribute(const vespalib::string &name, - std::vector<std::vector<vespalib::string>> values); - void - buildFloatAttribute(const vespalib::string &name, - std::vector<std::vector<double>> values); - - void - buildIntegerAttribute(const vespalib::string &name, - BasicType type, - std::vector<std::vector<IAttributeVector::largeint_t>> values); -}; - -AttributeManagerFixture::AttributeManagerFixture() - : mgr() -{ - buildStringAttribute("array.name", {{"n1.1", "n1.2"}, {"n2"}, {"n3.1", "n3.2"}, {"", "n4.2"}, {}}); - buildIntegerAttribute("array.val", BasicType::Type::INT8, {{ 10, 11}, {20, 21 }, {30}, { getUndefined<int8_t>(), 41}, {}}); - buildFloatAttribute("array.fval", {{ 110.0}, { 120.0, 121.0 }, { 130.0, 131.0}, { getUndefined<double>(), 141.0 }, {}}); - buildStringAttribute("smap.key", {{"k1.1", "k1.2"}, {"k2"}, {"k3.1", "k3.2"}, {"", "k4.2"}, {}}); - buildStringAttribute("smap.value.name", {{"n1.1", "n1.2"}, {"n2"}, {"n3.1", "n3.2"}, {"", "n4.2"}, {}}); - buildIntegerAttribute("smap.value.val", BasicType::Type::INT8, {{ 10, 11}, {20, 21 }, {30}, { getUndefined<int8_t>(), 41}, {}}); - buildFloatAttribute("smap.value.fval", {{ 110.0}, { 120.0, 121.0 }, { 130.0, 131.0}, { getUndefined<double>(), 141.0 }, {}}); - buildStringAttribute("map.key", {{"k1.1", "k1.2"}, {"k2"}, {"k3.1"}, {"", "k4.2"}, {}}); - buildStringAttribute("map.value", {{"n1.1", "n1.2"}, {}, {"n3.1", "n3.2"}, {"", "n4.2"}, {}}); -} - -AttributeManagerFixture::~AttributeManagerFixture() = default; - -template <typename AttributeType, typename ValueType> -void -AttributeManagerFixture::buildAttribute(const vespalib::string &name, - BasicType type, - std::vector<std::vector<ValueType>> values) -{ - Config cfg(type, CollectionType::Type::ARRAY); - auto attrBase = AttributeFactory::createAttribute(name, cfg); - EXPECT_TRUE(attrBase); - auto attr = std::dynamic_pointer_cast<AttributeType>(attrBase); - EXPECT_TRUE(attr); - attr->addReservedDoc(); - for (const auto &docValues : values) { - uint32_t docId = 0; - EXPECT_TRUE(attr->addDoc(docId)); - EXPECT_NE(0u, docId); - for (const auto &value : docValues) { - attr->append(docId, value, 1); - } - attr->commit(); - } - EXPECT_TRUE(mgr.add(attr)); -} - -void -AttributeManagerFixture::buildStringAttribute(const vespalib::string &name, - std::vector<std::vector<vespalib::string>> values) -{ - buildAttribute<StringAttribute, vespalib::string>(name, BasicType::Type::STRING, std::move(values)); -} - -void -AttributeManagerFixture::buildFloatAttribute(const vespalib::string &name, - std::vector<std::vector<double>> values) -{ - buildAttribute<FloatingPointAttribute, double>(name, BasicType::Type::DOUBLE, std::move(values)); -} - -void -AttributeManagerFixture::buildIntegerAttribute(const vespalib::string &name, - BasicType type, - std::vector<std::vector<IAttributeVector::largeint_t>> values) -{ - buildAttribute<IntegerAttribute, IAttributeVector::largeint_t>(name, type, std::move(values)); -} - - -class DummyStateCallback : public GetDocsumsStateCallback -{ -public: - MatchingElements _matching_elements; - - DummyStateCallback(); - void FillSummaryFeatures(GetDocsumsState *, IDocsumEnvironment *) override { } - void FillRankFeatures(GetDocsumsState *, IDocsumEnvironment *) override { } - void ParseLocation(GetDocsumsState *) override { } - std::unique_ptr<MatchingElements> fill_matching_elements(const search::MatchingElementsFields &) override { return std::make_unique<MatchingElements>(_matching_elements); } - ~DummyStateCallback() override { } -}; - -DummyStateCallback::DummyStateCallback() - : GetDocsumsStateCallback(), - _matching_elements() -{ - _matching_elements.add_matching_elements(1, "array", {1}); - _matching_elements.add_matching_elements(3, "array", {0}); - _matching_elements.add_matching_elements(4, "array", {1}); - _matching_elements.add_matching_elements(1, "smap", {1}); - _matching_elements.add_matching_elements(3, "smap", {0}); - _matching_elements.add_matching_elements(4, "smap", {1}); - _matching_elements.add_matching_elements(1, "map", {1}); - _matching_elements.add_matching_elements(3, "map", {0}); - _matching_elements.add_matching_elements(4, "map", {1}); -} - struct AttributeCombinerTest : public ::testing::Test { - AttributeManagerFixture attrs; + MockAttributeManager attrs; std::unique_ptr<IDocsumFieldWriter> writer; - DummyStateCallback stateCallback; + MockStateCallback callback; GetDocsumsState state; std::shared_ptr<search::MatchingElementsFields> _matching_elems_fields; @@ -207,11 +48,31 @@ struct AttributeCombinerTest : public ::testing::Test AttributeCombinerTest::AttributeCombinerTest() : attrs(), writer(), - stateCallback(), - state(stateCallback), + callback(), + state(callback), _matching_elems_fields() { - state._attrCtx = attrs.mgr.createContext(); + attrs.build_string_attribute("array.name", {{"n1.1", "n1.2"}, {"n2"}, {"n3.1", "n3.2"}, {"", "n4.2"}, {}}); + attrs.build_int_attribute("array.val", BasicType::Type::INT8, {{ 10, 11}, {20, 21 }, {30}, { getUndefined<int8_t>(), 41}, {}}); + attrs.build_float_attribute("array.fval", {{ 110.0}, { 120.0, 121.0 }, { 130.0, 131.0}, { getUndefined<double>(), 141.0 }, {}}); + attrs.build_string_attribute("smap.key", {{"k1.1", "k1.2"}, {"k2"}, {"k3.1", "k3.2"}, {"", "k4.2"}, {}}); + attrs.build_string_attribute("smap.value.name", {{"n1.1", "n1.2"}, {"n2"}, {"n3.1", "n3.2"}, {"", "n4.2"}, {}}); + attrs.build_int_attribute("smap.value.val", BasicType::Type::INT8, {{ 10, 11}, {20, 21 }, {30}, { getUndefined<int8_t>(), 41}, {}}); + attrs.build_float_attribute("smap.value.fval", {{ 110.0}, { 120.0, 121.0 }, { 130.0, 131.0}, { getUndefined<double>(), 141.0 }, {}}); + attrs.build_string_attribute("map.key", {{"k1.1", "k1.2"}, {"k2"}, {"k3.1"}, {"", "k4.2"}, {}}); + attrs.build_string_attribute("map.value", {{"n1.1", "n1.2"}, {}, {"n3.1", "n3.2"}, {"", "n4.2"}, {}}); + + callback.add_matching_elements(1, "array", {1}); + callback.add_matching_elements(3, "array", {0}); + callback.add_matching_elements(4, "array", {1}); + callback.add_matching_elements(1, "smap", {1}); + callback.add_matching_elements(3, "smap", {0}); + callback.add_matching_elements(4, "smap", {1}); + callback.add_matching_elements(1, "map", {1}); + callback.add_matching_elements(3, "map", {0}); + callback.add_matching_elements(4, "map", {1}); + + state._attrCtx = attrs.mgr().createContext(); } AttributeCombinerTest::~AttributeCombinerTest() = default; @@ -228,83 +89,73 @@ AttributeCombinerTest::set_field(const vespalib::string &field_name, bool filter } void -AttributeCombinerTest::assertWritten(const vespalib::string &expectedJson, uint32_t docId) +AttributeCombinerTest::assertWritten(const vespalib::string &exp_slime_as_json, uint32_t docId) { - vespalib::Slime target; - vespalib::slime::SlimeInserter inserter(target); + vespalib::Slime act; + vespalib::slime::SlimeInserter inserter(act); writer->insertField(docId, nullptr, &state, search::docsummary::RES_JSONSTRING, inserter); - search::RawBuf binary(1024); - vespalib::string json = toCompactJsonString(target); - search::SlimeOutputRawBufAdapter adapter(binary); - vespalib::slime::BinaryFormat::encode(target, adapter); - FieldBlock block(expectedJson); - EXPECT_EQ(block.dataLen(), binary.GetUsedLen()); - EXPECT_EQ(0, memcmp(block.data(), binary.GetDrainPos(), block.dataLen())); - if (block.dataLen() != binary.GetUsedLen() || - memcmp(block.data(), binary.GetDrainPos(), block.dataLen()) != 0) { - LOG(error, "Expected '%s'", expectedJson.c_str()); - LOG(error, "Expected normalized '%s'", block.json.c_str()); - LOG(error, "Got '%s'", json.c_str()); - } + + SlimeValue exp(exp_slime_as_json); + EXPECT_EQ(exp.slime, act); } TEST_F(AttributeCombinerTest, require_that_attribute_combiner_dfw_generates_correct_slime_output_for_array_of_struct) { set_field("array", false); - assertWritten("[ { fval: 110.0, name: \"n1.1\", val: 10}, { name: \"n1.2\", val: 11}]", 1); - assertWritten("[ { fval: 120.0, name: \"n2\", val: 20}, { fval: 121.0, val: 21 }]", 2); - assertWritten("[ { fval: 130.0, name: \"n3.1\", val: 30}, { fval: 131.0, name: \"n3.2\"} ]", 3); - assertWritten("[ { }, { fval: 141.0, name: \"n4.2\", val: 41} ]", 4); + assertWritten("[ { fval: 110.0, name: 'n1.1', val: 10}, { name: 'n1.2', val: 11}]", 1); + assertWritten("[ { fval: 120.0, name: 'n2', val: 20}, { fval: 121.0, val: 21 }]", 2); + assertWritten("[ { fval: 130.0, name: 'n3.1', val: 30}, { fval: 131.0, name: 'n3.2'} ]", 3); + assertWritten("[ { }, { fval: 141.0, name: 'n4.2', val: 41} ]", 4); assertWritten("null", 5); } TEST_F(AttributeCombinerTest, require_that_attribute_combiner_dfw_generates_correct_slime_output_for_map_of_struct) { set_field("smap", false); - assertWritten("[ { key: \"k1.1\", value: { fval: 110.0, name: \"n1.1\", val: 10} }, { key: \"k1.2\", value: { name: \"n1.2\", val: 11} }]", 1); - assertWritten("[ { key: \"k2\", value: { fval: 120.0, name: \"n2\", val: 20} }, { key: \"\", value: { fval: 121.0, val: 21 } }]", 2); - assertWritten("[ { key: \"k3.1\", value: { fval: 130.0, name: \"n3.1\", val: 30} }, { key: \"k3.2\", value: { fval: 131.0, name: \"n3.2\"} } ]", 3); - assertWritten("[ { key: \"\", value: { } }, { key: \"k4.2\", value: { fval: 141.0, name: \"n4.2\", val: 41} } ]", 4); + assertWritten("[ { key: 'k1.1', value: { fval: 110.0, name: 'n1.1', val: 10} }, { key: 'k1.2', value: { name: 'n1.2', val: 11} }]", 1); + assertWritten("[ { key: 'k2', value: { fval: 120.0, name: 'n2', val: 20} }, { key: '', value: { fval: 121.0, val: 21 } }]", 2); + assertWritten("[ { key: 'k3.1', value: { fval: 130.0, name: 'n3.1', val: 30} }, { key: 'k3.2', value: { fval: 131.0, name: 'n3.2'} } ]", 3); + assertWritten("[ { key: '', value: { } }, { key: 'k4.2', value: { fval: 141.0, name: 'n4.2', val: 41} } ]", 4); assertWritten("null", 5); } TEST_F(AttributeCombinerTest, require_that_attribute_combiner_dfw_generates_correct_slime_output_for_map_of_string) { set_field("map", false); - assertWritten("[ { key: \"k1.1\", value: \"n1.1\" }, { key: \"k1.2\", value: \"n1.2\"}]", 1); - assertWritten("[ { key: \"k2\", value: \"\" }]", 2); - assertWritten("[ { key: \"k3.1\", value: \"n3.1\" }, { key: \"\", value: \"n3.2\"} ]", 3); - assertWritten("[ { key: \"\", value: \"\" }, { key: \"k4.2\", value: \"n4.2\" } ]", 4); + assertWritten("[ { key: 'k1.1', value: 'n1.1' }, { key: 'k1.2', value: 'n1.2'}]", 1); + assertWritten("[ { key: 'k2', value: '' }]", 2); + assertWritten("[ { key: 'k3.1', value: 'n3.1' }, { key: '', value: 'n3.2'} ]", 3); + assertWritten("[ { key: '', value: '' }, { key: 'k4.2', value: 'n4.2' } ]", 4); assertWritten("null", 5); } TEST_F(AttributeCombinerTest, require_that_attribute_combiner_dfw_generates_correct_slime_output_for_filtered_array_of_struct) { set_field("array", true); - assertWritten("[ { name: \"n1.2\", val: 11}]", 1); + assertWritten("[ { name: 'n1.2', val: 11}]", 1); assertWritten("[ ]", 2); - assertWritten("[ { fval: 130.0, name: \"n3.1\", val: 30} ]", 3); - assertWritten("[ { fval: 141.0, name: \"n4.2\", val: 41} ]", 4); + assertWritten("[ { fval: 130.0, name: 'n3.1', val: 30} ]", 3); + assertWritten("[ { fval: 141.0, name: 'n4.2', val: 41} ]", 4); assertWritten("null", 5); } TEST_F(AttributeCombinerTest, require_that_attribute_combiner_dfw_generates_correct_slime_output_for_filtered_map_of_struct) { set_field("smap", true); - assertWritten("[ { key: \"k1.2\", value: { name: \"n1.2\", val: 11} }]", 1); + assertWritten("[ { key: 'k1.2', value: { name: 'n1.2', val: 11} }]", 1); assertWritten("[ ]", 2); - assertWritten("[ { key: \"k3.1\", value: { fval: 130.0, name: \"n3.1\", val: 30} } ]", 3); - assertWritten("[ { key: \"k4.2\", value: { fval: 141.0, name: \"n4.2\", val: 41} } ]", 4); + assertWritten("[ { key: 'k3.1', value: { fval: 130.0, name: 'n3.1', val: 30} } ]", 3); + assertWritten("[ { key: 'k4.2', value: { fval: 141.0, name: 'n4.2', val: 41} } ]", 4); assertWritten("null", 5); } TEST_F(AttributeCombinerTest, require_that_attribute_combiner_dfw_generates_correct_slime_output_for_filtered_map_of_string) { set_field("map", true); - assertWritten("[ { key: \"k1.2\", value: \"n1.2\"}]", 1); + assertWritten("[ { key: 'k1.2', value: 'n1.2'}]", 1); assertWritten("[ ]", 2); - assertWritten("[ { key: \"k3.1\", value: \"n3.1\" } ]", 3); - assertWritten("[ { key: \"k4.2\", value: \"n4.2\" } ]", 4); + assertWritten("[ { key: 'k3.1', value: 'n3.1' } ]", 3); + assertWritten("[ { key: 'k4.2', value: 'n4.2' } ]", 4); assertWritten("null", 5); } diff --git a/searchsummary/src/tests/docsummary/attributedfw/CMakeLists.txt b/searchsummary/src/tests/docsummary/attributedfw/CMakeLists.txt new file mode 100644 index 00000000000..8bdf30273f2 --- /dev/null +++ b/searchsummary/src/tests/docsummary/attributedfw/CMakeLists.txt @@ -0,0 +1,11 @@ +# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +find_package(GTest REQUIRED) +vespa_add_executable(searchsummary_attributedfw_test_app TEST + SOURCES + attributedfw_test.cpp + DEPENDS + searchsummary + searchsummary_test + GTest::GTest +) +vespa_add_test(NAME searchsummary_attributedfw_test_app COMMAND searchsummary_attributedfw_test_app) diff --git a/searchsummary/src/tests/docsummary/attributedfw/attributedfw_test.cpp b/searchsummary/src/tests/docsummary/attributedfw/attributedfw_test.cpp new file mode 100644 index 00000000000..7bea92ec8f3 --- /dev/null +++ b/searchsummary/src/tests/docsummary/attributedfw/attributedfw_test.cpp @@ -0,0 +1,150 @@ +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/searchlib/common/matching_elements_fields.h> +#include <vespa/searchsummary/docsummary/attributedfw.h> +#include <vespa/searchsummary/test/mock_attribute_manager.h> +#include <vespa/searchsummary/test/mock_state_callback.h> +#include <vespa/searchsummary/test/slime_value.h> +#include <vespa/vespalib/gtest/gtest.h> + +#include <vespa/log/log.h> +LOG_SETUP("attributedfw_test"); + +using search::MatchingElements; +using search::MatchingElementsFields; +using search::attribute::BasicType; +using search::attribute::CollectionType; +using search::docsummary::AttributeDFWFactory; +using search::docsummary::GetDocsumsState; +using search::docsummary::IDocsumFieldWriter; +using search::docsummary::test::MockAttributeManager; +using search::docsummary::test::MockStateCallback; +using search::docsummary::test::SlimeValue; + +using ElementVector = std::vector<uint32_t>; + +class AttributeDFWTest : public ::testing::Test { +protected: + MockAttributeManager _attrs; + std::unique_ptr<IDocsumFieldWriter> _writer; + MockStateCallback _callback; + GetDocsumsState _state; + std::shared_ptr<search::MatchingElementsFields> _matching_elems_fields; + vespalib::string _field_name; + +public: + AttributeDFWTest() + : _attrs(), + _writer(), + _callback(), + _state(_callback), + _matching_elems_fields(), + _field_name() + { + _attrs.build_string_attribute("array_str", { {"a", "b", "c"}, {} }); + _attrs.build_int_attribute("array_int", BasicType::INT32, { {10, 20, 30}, {} }); + _attrs.build_float_attribute("array_float", { {10.5, 20.5, 30.5}, {} }); + + _attrs.build_string_attribute("wset_str", { {"a", "b", "c"}, {} }, CollectionType::WSET); + _attrs.build_int_attribute("wset_int", BasicType::INT32, { {10, 20, 30}, {} }, CollectionType::WSET); + _attrs.build_float_attribute("wset_float", { {10.5, 20.5, 30.5}, {} }, CollectionType::WSET); + + _state._attrCtx = _attrs.mgr().createContext(); + } + ~AttributeDFWTest() {} + + void setup(const vespalib::string& field_name, bool filter_elements) { + if (filter_elements) { + _matching_elems_fields = std::make_shared<MatchingElementsFields>(); + } + _writer = AttributeDFWFactory::create(_attrs.mgr(), field_name, filter_elements, _matching_elems_fields); + _writer->setIndex(0); + _field_name = field_name; + _state._attributes.resize(1); + _state._attributes[0] = _state._attrCtx->getAttribute(field_name); + } + + void expect_field(const vespalib::string& exp_slime_as_json, uint32_t docid) { + vespalib::Slime act; + vespalib::slime::SlimeInserter inserter(act); + _writer->insertField(docid, nullptr, &_state, search::docsummary::RES_JSONSTRING, inserter); + + SlimeValue exp(exp_slime_as_json); + EXPECT_EQ(exp.slime, act); + } + + void expect_filtered(const ElementVector& matching_elems, const std::string& exp_slime_as_json, uint32_t docid = 1) { + _callback.clear(); + _callback.add_matching_elements(docid, _field_name, matching_elems); + _state._matching_elements = std::unique_ptr<MatchingElements>(); + expect_field(exp_slime_as_json, docid); + } +}; + +TEST_F(AttributeDFWTest, outputs_slime_for_array_of_string) +{ + setup("array_str", false); + expect_field("[ 'a', 'b', 'c' ]", 1); + expect_field("null", 2); +} + +TEST_F(AttributeDFWTest, outputs_slime_for_array_of_int) +{ + setup("array_int", false); + expect_field("[ 10, 20, 30 ]", 1); + expect_field("null", 2); +} + +TEST_F(AttributeDFWTest, outputs_slime_for_array_of_float) +{ + setup("array_float", false); + expect_field("[ 10.5, 20.5, 30.5 ]", 1); + expect_field("null", 2); +} + +TEST_F(AttributeDFWTest, outputs_slime_for_wset_of_string) +{ + setup("wset_str", false); + expect_field("[ {'item':'a', 'weight':1}, {'item':'b', 'weight':1}, {'item':'c', 'weight':1} ]", 1); + expect_field("null", 2); +} + +TEST_F(AttributeDFWTest, outputs_slime_for_wset_of_int) +{ + setup("wset_int", false); + expect_field("[ {'item':10, 'weight':1}, {'item':20, 'weight':1}, {'item':30, 'weight':1} ]", 1); + expect_field("null", 2); +} + +TEST_F(AttributeDFWTest, outputs_slime_for_wset_of_float) +{ + setup("wset_float", false); + expect_field("[ {'item':10.5, 'weight':1}, {'item':20.5, 'weight':1}, {'item':30.5, 'weight':1} ]", 1); + expect_field("null", 2); +} + +TEST_F(AttributeDFWTest, matched_elements_fields_is_populated) +{ + setup("array_str", true); + EXPECT_TRUE(_matching_elems_fields->has_field("array_str")); +} + +TEST_F(AttributeDFWTest, filteres_matched_elements_in_array_attribute) +{ + setup("array_str", true); + expect_filtered({}, "[]"); + expect_filtered({0}, "[ 'a' ]"); + expect_filtered({1, 2}, "[ 'b', 'c' ]"); + expect_filtered({3}, "[]"); +} + +TEST_F(AttributeDFWTest, filteres_matched_elements_in_wset_attribute) +{ + setup("wset_str", true); + expect_filtered({}, "[]"); + expect_filtered({0}, "[ {'item':'a', 'weight':1} ]"); + expect_filtered({1, 2}, "[ {'item':'b', 'weight':1}, {'item':'c', 'weight':1} ]"); + expect_filtered({3}, "[]"); +} + +GTEST_MAIN_RUN_ALL_TESTS() diff --git a/searchsummary/src/tests/docsummary/matched_elements_filter/matched_elements_filter_test.cpp b/searchsummary/src/tests/docsummary/matched_elements_filter/matched_elements_filter_test.cpp index ed91d4a88eb..0ac2f09e1b0 100644 --- a/searchsummary/src/tests/docsummary/matched_elements_filter/matched_elements_filter_test.cpp +++ b/searchsummary/src/tests/docsummary/matched_elements_filter/matched_elements_filter_test.cpp @@ -15,7 +15,7 @@ #include <vespa/searchsummary/docsummary/resultconfig.h> #include <vespa/searchsummary/docsummary/resultpacker.h> #include <vespa/searchsummary/docsummary/summaryfieldconverter.h> -#include <vespa/vespalib/data/slime/json_format.h> +#include <vespa/searchsummary/test/slime_value.h> #include <vespa/vespalib/data/slime/slime.h> #include <vespa/vespalib/gtest/gtest.h> #include <iostream> @@ -32,6 +32,7 @@ using search::attribute::CollectionType; using search::attribute::Config; using search::attribute::IAttributeContext; using search::attribute::IAttributeVector; +using search::docsummary::test::SlimeValue; using vespalib::Slime; using namespace document; @@ -40,17 +41,6 @@ using namespace vespalib::slime; using ElementVector = std::vector<uint32_t>; -struct SlimeValue { - Slime slime; - - SlimeValue(const std::string& json_input) - : slime() - { - size_t used = JsonFormat::decode(json_input, slime); - EXPECT_GT(used, 0); - } -}; - StructDataType::UP make_struct_elem_type() { diff --git a/searchsummary/src/vespa/searchsummary/docsummary/attributedfw.cpp b/searchsummary/src/vespa/searchsummary/docsummary/attributedfw.cpp index f1b12d8a227..05ba12ddff9 100644 --- a/searchsummary/src/vespa/searchsummary/docsummary/attributedfw.cpp +++ b/searchsummary/src/vespa/searchsummary/docsummary/attributedfw.cpp @@ -1,25 +1,29 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -#include "docsumwriter.h" #include "attributedfw.h" #include "docsumstate.h" -#include <vespa/searchlib/attribute/stringbase.h> -#include <vespa/searchlib/attribute/integerbase.h> +#include "docsumwriter.h" +#include <vespa/eval/tensor/serialization/typed_binary_format.h> +#include <vespa/eval/tensor/tensor.h> +#include <vespa/searchcommon/attribute/iattributecontext.h> #include <vespa/searchlib/attribute/iattributemanager.h> +#include <vespa/searchlib/attribute/integerbase.h> +#include <vespa/searchlib/attribute/stringbase.h> +#include <vespa/searchlib/common/matching_elements.h> +#include <vespa/searchlib/common/matching_elements_fields.h> #include <vespa/searchlib/tensor/i_tensor_attribute.h> -#include <vespa/searchcommon/attribute/iattributecontext.h> -#include <vespa/eval/tensor/tensor.h> -#include <vespa/eval/tensor/serialization/typed_binary_format.h> -#include <vespa/vespalib/objects/nbostream.h> #include <vespa/vespalib/data/slime/slime.h> +#include <vespa/vespalib/objects/nbostream.h> #include <vespa/log/log.h> LOG_SETUP(".searchlib.docsummary.attributedfw"); using namespace search; +using search::attribute::BasicType; using search::attribute::IAttributeContext; using search::attribute::IAttributeVector; -using search::attribute::BasicType; +using vespalib::Memory; +using vespalib::slime::Cursor; using vespalib::slime::Inserter; namespace search::docsummary { @@ -30,7 +34,7 @@ AttrDFW::AttrDFW(const vespalib::string & attrName) : } const attribute::IAttributeVector & -AttrDFW::vec(const GetDocsumsState & s) const { +AttrDFW::get_attribute(const GetDocsumsState& s) const { return *s.getAttribute(getIndex()); } @@ -48,13 +52,13 @@ public: bool SingleAttrDFW::isDefaultValue(uint32_t docid, const GetDocsumsState * state) const { - return vec(*state).isUndefined(docid); + return get_attribute(*state).isUndefined(docid); } void SingleAttrDFW::insertField(uint32_t docid, GetDocsumsState * state, ResType type, Inserter &target) { - const IAttributeVector & v = vec(*state); + const auto& v = get_attribute(*state); switch (type) { case RES_INT: { uint32_t val = v.getInt(docid); @@ -133,46 +137,115 @@ SingleAttrDFW::insertField(uint32_t docid, GetDocsumsState * state, ResType type //----------------------------------------------------------------------------- -class MultiAttrDFW : public AttrDFW -{ -public: - explicit MultiAttrDFW(const vespalib::string & attrName) : AttrDFW(attrName) {} - void insertField(uint32_t docid, GetDocsumsState *state, ResType type, Inserter &target) override; +template <typename DataType> +class MultiAttrDFW : public AttrDFW { +private: + bool _is_weighted_set; + bool _filter_elements; + std::shared_ptr<MatchingElementsFields> _matching_elems_fields; +public: + explicit MultiAttrDFW(const vespalib::string& attr_name, bool is_weighted_set, + bool filter_elements, std::shared_ptr<MatchingElementsFields> matching_elems_fields) + : AttrDFW(attr_name), + _is_weighted_set(is_weighted_set), + _filter_elements(filter_elements), + _matching_elems_fields(std::move(matching_elems_fields)) + { + if (filter_elements && _matching_elems_fields) { + _matching_elems_fields->add_field(attr_name); + } + } + void insertField(uint32_t docid, GetDocsumsState* state, ResType type, Inserter& target) override; }; void -MultiAttrDFW::insertField(uint32_t docid, GetDocsumsState *state, ResType, Inserter &target) +insert_element(const std::vector<IAttributeVector::WeightedString>& elements, + size_t idx, bool is_weighted_set, Cursor& arr) +{ + const vespalib::string& sv = elements[idx].getValue(); + Memory value(sv.c_str(), sv.size()); + if (is_weighted_set) { + Cursor& elem = arr.addObject(); + elem.setString("item", value); + elem.setLong("weight", elements[idx].getWeight()); + } else { + arr.addString(value); + } +} + +void +insert_element(const std::vector<IAttributeVector::WeightedInt>& elements, + size_t idx, bool is_weighted_set, Cursor& arr) +{ + if (is_weighted_set) { + Cursor& elem = arr.addObject(); + elem.setLong("item", elements[idx].getValue()); + elem.setLong("weight", elements[idx].getWeight()); + } else { + arr.addLong(elements[idx].getValue()); + } +} + +void +insert_element(const std::vector<IAttributeVector::WeightedFloat>& elements, + size_t idx, bool is_weighted_set, Cursor& arr) { - using vespalib::slime::Cursor; - using vespalib::Memory; - const IAttributeVector & v = vec(*state); - bool isWeightedSet = v.hasWeightedSetType(); + if (is_weighted_set) { + Cursor& elem = arr.addObject(); + elem.setDouble("item", elements[idx].getValue()); + elem.setLong("weight", elements[idx].getWeight()); + } else { + arr.addDouble(elements[idx].getValue()); + } +} - uint32_t entries = v.getValueCount(docid); +template <typename DataType> +void +MultiAttrDFW<DataType>::insertField(uint32_t docid, GetDocsumsState* state, ResType, Inserter& target) +{ + const auto& attr = get_attribute(*state); + uint32_t entries = attr.getValueCount(docid); if (entries == 0) { return; // Don't insert empty fields } Cursor &arr = target.insertArray(); - BasicType::Type t = v.getBasicType(); - switch (t) { - case BasicType::NONE: - case BasicType::STRING: { - std::vector<IAttributeVector::WeightedString> elements(entries); - entries = std::min(entries, v.get(docid, &elements[0], entries)); - for (uint32_t i = 0; i < entries; ++i) { - const vespalib::string &sv = elements[i].getValue(); - Memory value(sv.c_str(), sv.size()); - if (isWeightedSet) { - Cursor &elem = arr.addObject(); - elem.setString("item", value); - elem.setLong("weight", elements[i].getWeight()); - } else { - arr.addString(value); + std::vector<DataType> elements(entries); + entries = std::min(entries, attr.get(docid, elements.data(), entries)); + + if (_filter_elements) { + const auto& matching_elems = state->get_matching_elements(*_matching_elems_fields) + .get_matching_elements(docid, getAttributeName()); + if (!matching_elems.empty() && matching_elems.back() < entries) { + for (uint32_t id_to_keep : matching_elems) { + insert_element(elements, id_to_keep, _is_weighted_set, arr); } } - return; } + } else { + for (uint32_t i = 0; i < entries; ++i) { + insert_element(elements, i, _is_weighted_set, arr); + } + } +} + +//----------------------------------------------------------------------------- + +namespace { + +std::unique_ptr<IDocsumFieldWriter> +create_multi_writer(const IAttributeVector& attr, + bool filter_elements, + std::shared_ptr<MatchingElementsFields> matching_elems_fields) +{ + auto type = attr.getBasicType(); + bool is_weighted_set = attr.hasWeightedSetType(); + switch (type) { + case BasicType::NONE: + case BasicType::STRING: { + return std::make_unique<MultiAttrDFW<IAttributeVector::WeightedString>>(attr.getName(), is_weighted_set, + filter_elements, std::move(matching_elems_fields)); + } case BasicType::BOOL: case BasicType::UINT2: case BasicType::UINT4: @@ -180,54 +253,39 @@ MultiAttrDFW::insertField(uint32_t docid, GetDocsumsState *state, ResType, Inser case BasicType::INT16: case BasicType::INT32: case BasicType::INT64: { - std::vector<IAttributeVector::WeightedInt> elements(entries); - entries = std::min(entries, v.get(docid, &elements[0], entries)); - for (uint32_t i = 0; i < entries; ++i) { - if (isWeightedSet) { - Cursor &elem = arr.addObject(); - elem.setLong("item", elements[i].getValue()); - elem.setLong("weight", elements[i].getWeight()); - } else { - arr.addLong(elements[i].getValue()); - } - } - return; } + return std::make_unique<MultiAttrDFW<IAttributeVector::WeightedInt>>(attr.getName(), is_weighted_set, + filter_elements, std::move(matching_elems_fields)); + } case BasicType::FLOAT: case BasicType::DOUBLE: { - std::vector<IAttributeVector::WeightedFloat> elements(entries); - entries = std::min(entries, v.get(docid, &elements[0], entries)); - for (uint32_t i = 0; i < entries; ++i) { - if (isWeightedSet) { - Cursor &elem = arr.addObject(); - elem.setDouble("item", elements[i].getValue()); - elem.setLong("weight", elements[i].getWeight()); - } else { - arr.addDouble(elements[i].getValue()); - } - } - return; } + return std::make_unique<MultiAttrDFW<IAttributeVector::WeightedFloat>>(attr.getName(), is_weighted_set, + filter_elements, std::move(matching_elems_fields)); + } default: // should not happen - LOG(error, "bad value for type: %u\n", t); + LOG(error, "Bad value for attribute type: %u", type); LOG_ASSERT(false); } } -//----------------------------------------------------------------------------- +} -IDocsumFieldWriter * -AttributeDFWFactory::create(IAttributeManager & vecMan, const char *vecName) +std::unique_ptr<IDocsumFieldWriter> +AttributeDFWFactory::create(IAttributeManager& attr_mgr, + const vespalib::string& attr_name, + bool filter_elements, + std::shared_ptr<MatchingElementsFields> matching_elems_fields) { - IAttributeContext::UP ctx = vecMan.createContext(); - const IAttributeVector * vec = ctx->getAttribute(vecName); - if (vec == nullptr) { - LOG(warning, "No valid attribute vector found: %s", vecName); - return nullptr; - } - if (vec->hasMultiValue()) { - return new MultiAttrDFW(vec->getName()); + auto ctx = attr_mgr.createContext(); + const auto* attr = ctx->getAttribute(attr_name); + if (attr == nullptr) { + LOG(warning, "No valid attribute vector found: '%s'", attr_name.c_str()); + return std::unique_ptr<IDocsumFieldWriter>(); + } + if (attr->hasMultiValue()) { + return create_multi_writer(*attr, filter_elements, std::move(matching_elems_fields)); } else { - return new SingleAttrDFW(vec->getName()); + return std::make_unique<SingleAttrDFW>(attr->getName()); } } diff --git a/searchsummary/src/vespa/searchsummary/docsummary/attributedfw.h b/searchsummary/src/vespa/searchsummary/docsummary/attributedfw.h index 211becea16c..55a30f0bb7b 100644 --- a/searchsummary/src/vespa/searchsummary/docsummary/attributedfw.h +++ b/searchsummary/src/vespa/searchsummary/docsummary/attributedfw.h @@ -4,16 +4,30 @@ #include "docsumfieldwriter.h" + +namespace search { class MatchingElementsFields; } namespace search::attribute { class IAttributeVector; } namespace search::docsummary { +/** + * Factory to create an IDocsumFieldWriter to write an attribute vector to slime. + */ +class AttributeDFWFactory { +public: + static std::unique_ptr<IDocsumFieldWriter> create(IAttributeManager& attr_mgr, + const vespalib::string& attr_name, + bool filter_elements = false, + std::shared_ptr<MatchingElementsFields> matching_elems_fields + = std::shared_ptr<MatchingElementsFields>()); +}; + class AttrDFW : public ISimpleDFW { private: vespalib::string _attrName; protected: - const attribute::IAttributeVector & vec(const GetDocsumsState & s) const; + const attribute::IAttributeVector& get_attribute(const GetDocsumsState& s) const; const vespalib::string & getAttributeName() const override { return _attrName; } public: AttrDFW(const vespalib::string & attrName); diff --git a/searchsummary/src/vespa/searchsummary/docsummary/docsumconfig.cpp b/searchsummary/src/vespa/searchsummary/docsummary/docsumconfig.cpp index 2c3366cb94d..f41ada8b2e8 100644 --- a/searchsummary/src/vespa/searchsummary/docsummary/docsumconfig.cpp +++ b/searchsummary/src/vespa/searchsummary/docsummary/docsumconfig.cpp @@ -88,11 +88,9 @@ DynamicDocsumConfig::createFieldWriter(const string & fieldName, const string & rc = fieldWriter.get(); } } else if (overrideName == "attribute") { - const char *vectorName = argument.c_str(); if (getEnvironment() && getEnvironment()->getAttributeManager()) { - IDocsumFieldWriter *fw = AttributeDFWFactory::create(*getEnvironment()->getAttributeManager(), vectorName); - fieldWriter.reset(fw); - rc = fw != NULL; + fieldWriter = AttributeDFWFactory::create(*getEnvironment()->getAttributeManager(), argument); + rc = static_cast<bool>(fieldWriter); } } else if (overrideName == "attributecombiner") { if (getEnvironment() && getEnvironment()->getAttributeManager()) { diff --git a/searchsummary/src/vespa/searchsummary/docsummary/docsumfieldwriter.h b/searchsummary/src/vespa/searchsummary/docsummary/docsumfieldwriter.h index 43375fb47f3..a40f105a1cb 100644 --- a/searchsummary/src/vespa/searchsummary/docsummary/docsumfieldwriter.h +++ b/searchsummary/src/vespa/searchsummary/docsummary/docsumfieldwriter.h @@ -84,14 +84,5 @@ public: vespalib::slime::Inserter &target) override; }; -//-------------------------------------------------------------------------- - -class AttributeDFWFactory -{ -private: - AttributeDFWFactory(); -public: - static IDocsumFieldWriter *create(IAttributeManager & vecMan, const char *vecName); -}; } diff --git a/searchsummary/src/vespa/searchsummary/docsummary/geoposdfw.cpp b/searchsummary/src/vespa/searchsummary/docsummary/geoposdfw.cpp index ae3d6acde43..df510d5bcbc 100644 --- a/searchsummary/src/vespa/searchsummary/docsummary/geoposdfw.cpp +++ b/searchsummary/src/vespa/searchsummary/docsummary/geoposdfw.cpp @@ -47,7 +47,7 @@ GeoPositionDFW::insertField(uint32_t docid, GetDocsumsState * dsState, ResType, using vespalib::slime::Symbol; using vespalib::slime::ArrayInserter; - const IAttributeVector & attribute = vec(*dsState); + const auto& attribute = get_attribute(*dsState); if (attribute.hasMultiValue()) { uint32_t entries = attribute.getValueCount(docid); Cursor &arr = target.insertArray(); diff --git a/searchsummary/src/vespa/searchsummary/docsummary/positionsdfw.cpp b/searchsummary/src/vespa/searchsummary/docsummary/positionsdfw.cpp index 6b003553f49..ecdde13b919 100644 --- a/searchsummary/src/vespa/searchsummary/docsummary/positionsdfw.cpp +++ b/searchsummary/src/vespa/searchsummary/docsummary/positionsdfw.cpp @@ -30,7 +30,7 @@ uint64_t AbsDistanceDFW::findMinDistance(uint32_t docid, GetDocsumsState *state) { search::common::Location &location = *state->_parsedLocation; - const IAttributeVector & attribute(vec(*state)); + const auto& attribute = get_attribute(*state); uint64_t absdist = std::numeric_limits<int64_t>::max(); int32_t docx = 0; @@ -221,10 +221,10 @@ void PositionsDFW::insertField(uint32_t docid, GetDocsumsState * dsState, ResType type, vespalib::slime::Inserter &target) { if (type == RES_XMLSTRING) { - insertFromAttr(vec(*dsState), docid, target); + insertFromAttr(get_attribute(*dsState), docid, target); return; } - vespalib::asciistream val(formatField(vec(*dsState), docid, type)); + vespalib::asciistream val(formatField(get_attribute(*dsState), docid, type)); target.insertString(vespalib::Memory(val.c_str(), val.size())); } diff --git a/searchsummary/src/vespa/searchsummary/test/CMakeLists.txt b/searchsummary/src/vespa/searchsummary/test/CMakeLists.txt new file mode 100644 index 00000000000..ae4414bb078 --- /dev/null +++ b/searchsummary/src/vespa/searchsummary/test/CMakeLists.txt @@ -0,0 +1,7 @@ +# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_library(searchsummary_test OBJECT + SOURCES + mock_attribute_manager.cpp + AFTER + searchsummary_config +) diff --git a/searchsummary/src/vespa/searchsummary/test/mock_attribute_manager.cpp b/searchsummary/src/vespa/searchsummary/test/mock_attribute_manager.cpp new file mode 100644 index 00000000000..bd7307d1624 --- /dev/null +++ b/searchsummary/src/vespa/searchsummary/test/mock_attribute_manager.cpp @@ -0,0 +1,72 @@ +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include "mock_attribute_manager.h" +#include <vespa/searchlib/attribute/attributefactory.h> +#include <vespa/searchlib/attribute/attributevector.h> +#include <vespa/searchlib/attribute/attributevector.hpp> +#include <vespa/searchlib/attribute/floatbase.h> +#include <vespa/searchlib/attribute/integerbase.h> +#include <vespa/searchlib/attribute/stringbase.h> +#include <cassert> + +using search::attribute::BasicType; +using search::attribute::CollectionType; +using search::attribute::Config; + +namespace search::docsummary::test { + +template <typename AttributeType, typename ValueType> +void +MockAttributeManager::build_attribute(const vespalib::string& name, BasicType type, + search::attribute::CollectionType col_type, + const std::vector<std::vector<ValueType>>& values) +{ + Config cfg(type, col_type); + auto attr_base = AttributeFactory::createAttribute(name, cfg); + assert(attr_base); + auto attr = std::dynamic_pointer_cast<AttributeType>(attr_base); + assert(attr); + attr->addReservedDoc(); + for (const auto& docValues : values) { + uint32_t docId = 0; + attr->addDoc(docId); + for (const auto& value : docValues) { + attr->append(docId, value, 1); + } + attr->commit(); + } + _mgr.add(attr); +} + +MockAttributeManager::MockAttributeManager() + : _mgr() +{ +} + +MockAttributeManager::~MockAttributeManager() = default; + +void +MockAttributeManager::build_string_attribute(const vespalib::string& name, + const std::vector<std::vector<vespalib::string>>& values, + CollectionType col_type) +{ + build_attribute<StringAttribute, vespalib::string>(name, BasicType::Type::STRING, col_type, values); +} + +void +MockAttributeManager::build_float_attribute(const vespalib::string& name, + const std::vector<std::vector<double>>& values, + CollectionType col_type) +{ + build_attribute<FloatingPointAttribute, double>(name, BasicType::Type::DOUBLE, col_type, values); +} + +void +MockAttributeManager::build_int_attribute(const vespalib::string& name, BasicType type, + const std::vector<std::vector<int64_t>>& values, + CollectionType col_type) +{ + build_attribute<IntegerAttribute, int64_t>(name, type, col_type, values); +} + +} diff --git a/searchsummary/src/vespa/searchsummary/test/mock_attribute_manager.h b/searchsummary/src/vespa/searchsummary/test/mock_attribute_manager.h new file mode 100644 index 00000000000..a7e425e50b6 --- /dev/null +++ b/searchsummary/src/vespa/searchsummary/test/mock_attribute_manager.h @@ -0,0 +1,37 @@ +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/searchcommon/attribute/basictype.h> +#include <vespa/searchlib/attribute/attributemanager.h> + +namespace search::docsummary::test { + +/** + * Class used to build attributes and populate a manager for testing. + */ +class MockAttributeManager { +private: + AttributeManager _mgr; + + template <typename AttributeType, typename ValueType> + void build_attribute(const vespalib::string& name, search::attribute::BasicType type, + search::attribute::CollectionType col_type, + const std::vector<std::vector<ValueType>>& values); + +public: + MockAttributeManager(); + ~MockAttributeManager(); + AttributeManager& mgr() { return _mgr; } + + void build_string_attribute(const vespalib::string& name, + const std::vector<std::vector<vespalib::string>>& values, + search::attribute::CollectionType col_type = search::attribute::CollectionType::ARRAY); + void build_float_attribute(const vespalib::string& name, + const std::vector<std::vector<double>>& values, + search::attribute::CollectionType col_type = search::attribute::CollectionType::ARRAY); + void build_int_attribute(const vespalib::string& name, search::attribute::BasicType type, + const std::vector<std::vector<int64_t>>& values, + search::attribute::CollectionType col_type = search::attribute::CollectionType::ARRAY); + +}; + +} diff --git a/searchsummary/src/vespa/searchsummary/test/mock_state_callback.h b/searchsummary/src/vespa/searchsummary/test/mock_state_callback.h new file mode 100644 index 00000000000..b3ee405c856 --- /dev/null +++ b/searchsummary/src/vespa/searchsummary/test/mock_state_callback.h @@ -0,0 +1,35 @@ +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/searchlib/common/matching_elements.h> +#include <vespa/searchsummary/docsummary/docsumstate.h> + +namespace search::docsummary::test { + +class MockStateCallback : public GetDocsumsStateCallback { +private: + MatchingElements _matching_elems; + +public: + MockStateCallback() + : GetDocsumsStateCallback(), + _matching_elems() + { + } + ~MockStateCallback() override { } + void FillSummaryFeatures(GetDocsumsState*, IDocsumEnvironment*) override { } + void FillRankFeatures(GetDocsumsState*, IDocsumEnvironment*) override { } + void ParseLocation(GetDocsumsState*) override { } + std::unique_ptr<MatchingElements> fill_matching_elements(const search::MatchingElementsFields&) override { + return std::make_unique<MatchingElements>(_matching_elems); + } + + void add_matching_elements(uint32_t docid, const vespalib::string& field_name, + const std::vector<uint32_t>& elements) { + _matching_elems.add_matching_elements(docid, field_name, elements); + } + void clear() { + _matching_elems = MatchingElements(); + } +}; + +} diff --git a/searchsummary/src/vespa/searchsummary/test/slime_value.h b/searchsummary/src/vespa/searchsummary/test/slime_value.h new file mode 100644 index 00000000000..3cc461d04ca --- /dev/null +++ b/searchsummary/src/vespa/searchsummary/test/slime_value.h @@ -0,0 +1,24 @@ +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#pragma once + +#include <vespa/vespalib/data/slime/slime.h> +#include <cassert> + +namespace search::docsummary::test { + +/** + * Utility class that wraps a slime object generated from json. + */ +struct SlimeValue { + vespalib::Slime slime; + + SlimeValue(const vespalib::string& json_input) + : slime() + { + size_t used = vespalib::slime::JsonFormat::decode(json_input, slime); + assert(used > 0); + } +}; + +} |