aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGeir Storli <geirst@verizonmedia.com>2020-06-05 17:09:18 +0200
committerGitHub <noreply@github.com>2020-06-05 17:09:18 +0200
commit86c998932c894e47fe3a710d79b2f37894994947 (patch)
tree24abdcc71eb97f86fba302cc27715114b5934573
parent2e0d48abbec7e6716ba5414e3b3368e3880118bc (diff)
parent6e3e3f2ce4322be9d9c7bbafc71236f3cb834e5a (diff)
Merge pull request #13498 from vespa-engine/geirst/multi-value-attributes-matched-elements-only
Multi-value attributes matched elements only
-rw-r--r--searchsummary/CMakeLists.txt2
-rw-r--r--searchsummary/src/tests/docsummary/attribute_combiner/CMakeLists.txt1
-rw-r--r--searchsummary/src/tests/docsummary/attribute_combiner/attribute_combiner_test.cpp267
-rw-r--r--searchsummary/src/tests/docsummary/attributedfw/CMakeLists.txt11
-rw-r--r--searchsummary/src/tests/docsummary/attributedfw/attributedfw_test.cpp150
-rw-r--r--searchsummary/src/tests/docsummary/matched_elements_filter/matched_elements_filter_test.cpp14
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/attributedfw.cpp208
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/attributedfw.h16
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/docsumconfig.cpp6
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/docsumfieldwriter.h9
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/geoposdfw.cpp2
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/positionsdfw.cpp6
-rw-r--r--searchsummary/src/vespa/searchsummary/test/CMakeLists.txt7
-rw-r--r--searchsummary/src/vespa/searchsummary/test/mock_attribute_manager.cpp72
-rw-r--r--searchsummary/src/vespa/searchsummary/test/mock_attribute_manager.h37
-rw-r--r--searchsummary/src/vespa/searchsummary/test/mock_state_callback.h35
-rw-r--r--searchsummary/src/vespa/searchsummary/test/slime_value.h24
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);
+ }
+};
+
+}