summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGeir Storli <geirst@verizonmedia.com>2019-07-08 15:40:05 +0200
committerGitHub <noreply@github.com>2019-07-08 15:40:05 +0200
commit94a3be26bb620b270fe75f4b52e67e5bbbb7332e (patch)
tree388e92a7a7fba2664fc2c7eb32b48b6266a56f21
parentca3acf93e83b664457e5d333c4f2fa064d38f08c (diff)
parent2a1e136ffbe0119d254f8feeaf84ff997a7ee480 (diff)
Merge pull request #9987 from vespa-engine/geirst/reference-attribute-search
Geirst/reference attribute search
-rw-r--r--searchlib/src/tests/attribute/reference_attribute/CMakeLists.txt1
-rw-r--r--searchlib/src/tests/attribute/reference_attribute/reference_attribute_test.cpp457
-rw-r--r--searchlib/src/vespa/searchlib/attribute/reference_attribute.cpp69
-rw-r--r--searchlib/src/vespa/searchlib/attribute/reference_attribute.h6
4 files changed, 323 insertions, 210 deletions
diff --git a/searchlib/src/tests/attribute/reference_attribute/CMakeLists.txt b/searchlib/src/tests/attribute/reference_attribute/CMakeLists.txt
index b5aa87e32f1..6638bf886b7 100644
--- a/searchlib/src/tests/attribute/reference_attribute/CMakeLists.txt
+++ b/searchlib/src/tests/attribute/reference_attribute/CMakeLists.txt
@@ -4,5 +4,6 @@ vespa_add_executable(searchlib_reference_attribute_test_app TEST
reference_attribute_test.cpp
DEPENDS
searchlib
+ gtest
)
vespa_add_test(NAME searchlib_reference_attribute_test_app COMMAND searchlib_reference_attribute_test_app)
diff --git a/searchlib/src/tests/attribute/reference_attribute/reference_attribute_test.cpp b/searchlib/src/tests/attribute/reference_attribute/reference_attribute_test.cpp
index e534153d004..d7428f02ba5 100644
--- a/searchlib/src/tests/attribute/reference_attribute/reference_attribute_test.cpp
+++ b/searchlib/src/tests/attribute/reference_attribute/reference_attribute_test.cpp
@@ -1,28 +1,39 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-#include <vespa/log/log.h>
-LOG_SETUP("reference_attribute_test");
-#include <vespa/vespalib/testkit/testapp.h>
-#include <vespa/vespalib/test/insertion_operators.h>
-#include <vespa/vespalib/util/traits.h>
-#include <vespa/vespalib/io/fileutil.h>
+
+#include <vespa/document/base/documentid.h>
#include <vespa/searchlib/attribute/attributeguard.h>
#include <vespa/searchlib/attribute/reference_attribute.h>
-#include <vespa/searchlib/common/i_gid_to_lid_mapper_factory.h>
#include <vespa/searchlib/common/i_gid_to_lid_mapper.h>
+#include <vespa/searchlib/common/i_gid_to_lid_mapper_factory.h>
+#include <vespa/searchlib/fef/termfieldmatchdata.h>
+#include <vespa/searchlib/query/queryterm.h>
+#include <vespa/searchlib/queryeval/fake_result.h>
+#include <vespa/searchlib/queryeval/searchiterator.h>
#include <vespa/searchlib/test/mock_gid_to_lid_mapping.h>
-#include <vespa/document/base/documentid.h>
+#include <vespa/vespalib/gtest/gtest.h>
+#include <vespa/vespalib/io/fileutil.h>
+#include <vespa/vespalib/test/insertion_operators.h>
+#include <vespa/vespalib/util/traits.h>
-using vespalib::MemoryUsage;
-using vespalib::ArrayRef;
+#include <vespa/log/log.h>
+LOG_SETUP("reference_attribute_test");
+
+using document::DocumentId;
+using document::GlobalId;
using generation_t = vespalib::GenerationHandler::generation_t;
+using search::AttributeGuard;
+using search::AttributeVector;
+using search::QueryTermSimple;
+using search::attribute::BasicType;
+using search::attribute::Config;
using search::attribute::Reference;
using search::attribute::ReferenceAttribute;
-using search::attribute::Config;
-using search::attribute::BasicType;
-using search::AttributeVector;
-using search::AttributeGuard;
-using document::GlobalId;
-using document::DocumentId;
+using search::attribute::SearchContextParams;
+using search::fef::TermFieldMatchData;
+using search::queryeval::FakeResult;
+using search::queryeval::SearchIterator;
+using vespalib::ArrayRef;
+using vespalib::MemoryUsage;
namespace {
@@ -36,8 +47,7 @@ vespalib::string doc3("id:test:music::3");
}
-struct MyGidToLidMapperFactory : public search::attribute::test::MockGidToLidMapperFactory
-{
+struct MyGidToLidMapperFactory : public search::attribute::test::MockGidToLidMapperFactory {
MyGidToLidMapperFactory() {
_map.insert({toGid(doc1), 10});
_map.insert({toGid(doc2), 17});
@@ -55,8 +65,7 @@ struct MyGidToLidMapperFactory : public search::attribute::test::MockGidToLidMap
}
};
-class LidCollector
-{
+class LidCollector {
std::vector<uint32_t> &_lids;
public:
LidCollector(std::vector<uint32_t> &lids)
@@ -66,16 +75,17 @@ public:
void operator()(uint32_t lid) { _lids.push_back(lid); }
};
-struct Fixture
-{
+struct ReferenceAttributeTest : public ::testing::Test {
std::shared_ptr<ReferenceAttribute> _attr;
- Fixture()
+ ReferenceAttributeTest()
: _attr()
{
resetAttr();
}
+ ~ReferenceAttributeTest() {}
+
AttributeVector &attr() {
return *_attr;
}
@@ -122,36 +132,34 @@ struct Fixture
void commit() { attr().commit(); }
- void assertNoRef(uint32_t doc)
- {
+ void assertNoRef(uint32_t doc) {
EXPECT_TRUE(get(doc) == nullptr);
}
void assertRef(vespalib::stringref str, uint32_t doc) {
const GlobalId *gid = get(doc);
- EXPECT_TRUE(gid != nullptr);
- EXPECT_EQUAL(toGid(str), *gid);
+ ASSERT_TRUE(gid != nullptr);
+ EXPECT_EQ(toGid(str), *gid);
}
void assertTargetLid(uint32_t doc, uint32_t expTargetLid) {
auto ref = getRef(doc);
- EXPECT_TRUE(ref != nullptr);
- EXPECT_EQUAL(expTargetLid, ref->lid());
- EXPECT_EQUAL(expTargetLid, _attr->getTargetLid(doc));
+ ASSERT_TRUE(ref != nullptr);
+ EXPECT_EQ(expTargetLid, ref->lid());
+ EXPECT_EQ(expTargetLid, _attr->getTargetLid(doc));
}
void assertNoTargetLid(uint32_t doc) {
auto ref = getRef(doc);
EXPECT_TRUE(ref == nullptr);
- EXPECT_EQUAL(0u, _attr->getTargetLid(doc));
+ EXPECT_EQ(0u, _attr->getTargetLid(doc));
}
- void assertLids(uint32_t targetLid, std::vector<uint32_t> expLids)
- {
+ void assertLids(uint32_t targetLid, std::vector<uint32_t> expLids) {
std::vector<uint32_t> lids;
LidCollector collector(lids);
_attr->foreach_lid(targetLid, collector);
- EXPECT_EQUAL(expLids, lids);
+ EXPECT_EQ(expLids, lids);
}
void save() {
@@ -176,9 +184,8 @@ struct Fixture
}
oldStatus = newStatus;
}
- EXPECT_GREATER(iterLimit, iter);
- LOG(info,
- "iter = %" PRIu64 ", memory usage %" PRIu64 ", -> %" PRIu64,
+ EXPECT_GT(iterLimit, iter);
+ LOG(info, "iter = %" PRIu64 ", memory usage %" PRIu64 ", -> %" PRIu64,
iter, oldStatus.getUsed(), newStatus.getUsed());
}
@@ -197,137 +204,138 @@ struct Fixture
}
};
-TEST_F("require that we can instantiate reference attribute", Fixture)
+TEST_F(ReferenceAttributeTest, reference_attribute_can_be_instantiated)
{
- f.ensureDocIdLimit(5);
- f.set(1, toGid(doc1));
- f.set(2, toGid(doc2));
- f.commit();
-
- TEST_DO(f.assertNoRef(3));
- TEST_DO(f.assertRef(doc1, 1));
- TEST_DO(f.assertRef(doc2, 2));
+ ensureDocIdLimit(5);
+ set(1, toGid(doc1));
+ set(2, toGid(doc2));
+ commit();
+
+ assertNoRef(3);
+ assertRef(doc1, 1);
+ assertRef(doc2, 2);
}
-TEST_F("require that we can set new reference for a document", Fixture)
+TEST_F(ReferenceAttributeTest, new_reference_for_a_document_can_be_set)
{
- f.ensureDocIdLimit(5);
- f.set(1, toGid(doc1));
- f.set(2, toGid(doc2));
- f.set(3, toGid(doc2));
- f.commit();
- TEST_DO(f.assertNoRef(4));
- TEST_DO(f.assertRef(doc1, 1));
- TEST_DO(f.assertRef(doc2, 2));
- TEST_DO(f.assertRef(doc2, 3));
- f.set(2, toGid(doc1));
- f.commit();
- TEST_DO(f.assertNoRef(4));
- TEST_DO(f.assertRef(doc1, 1));
- TEST_DO(f.assertRef(doc1, 2));
- TEST_DO(f.assertRef(doc2, 3));
+ ensureDocIdLimit(5);
+ set(1, toGid(doc1));
+ set(2, toGid(doc2));
+ set(3, toGid(doc2));
+ commit();
+ assertNoRef(4);
+ assertRef(doc1, 1);
+ assertRef(doc2, 2);
+ assertRef(doc2, 3);
+ set(2, toGid(doc1));
+ commit();
+ assertNoRef(4);
+ assertRef(doc1, 1);
+ assertRef(doc1, 2);
+ assertRef(doc2, 3);
}
-TEST_F("require that we can clear reference for a document", Fixture)
+TEST_F(ReferenceAttributeTest, reference_for_a_document_can_be_cleared)
{
- f.ensureDocIdLimit(5);
- f.set(2, toGid(doc2));
- f.commit();
- TEST_DO(f.assertRef(doc2, 2));
- f.clear(2);
- f.commit();
- TEST_DO(f.assertNoRef(2));
- f.clear(2);
- f.commit();
- TEST_DO(f.assertNoRef(2));
+ ensureDocIdLimit(5);
+ set(2, toGid(doc2));
+ commit();
+ assertRef(doc2, 2);
+ clear(2);
+ commit();
+ assertNoRef(2);
+ clear(2);
+ commit();
+ assertNoRef(2);
}
-TEST_F("require that read guard protects reference", Fixture)
+TEST_F(ReferenceAttributeTest, read_guard_protects_references)
{
- f.ensureDocIdLimit(5);
- f.set(2, toGid(doc2));
- f.commit();
- const GlobalId *gid = f.get(2);
- EXPECT_TRUE(gid != nullptr);
- EXPECT_EQUAL(toGid(doc2), *gid);
+ ensureDocIdLimit(5);
+ set(2, toGid(doc2));
+ commit();
+ const GlobalId *gid = get(2);
+ ASSERT_TRUE(gid != nullptr);
+ EXPECT_EQ(toGid(doc2), *gid);
{
- AttributeGuard guard(f._attr);
- f.clear(2);
- f.commit();
- EXPECT_EQUAL(toGid(doc2), *gid);
+ AttributeGuard guard(_attr);
+ clear(2);
+ commit();
+ EXPECT_EQ(toGid(doc2), *gid);
}
- f.commit();
- EXPECT_NOT_EQUAL(toGid(doc2), *gid);
+ commit();
+ EXPECT_NE(toGid(doc2), *gid);
}
-TEST_F("require that we can compact attribute", Fixture)
+TEST_F(ReferenceAttributeTest, attribute_can_be_compacted)
{
- f.ensureDocIdLimit(5);
- f.set(1, toGid(doc1));
- f.set(2, toGid(doc2));
- f.commit();
- TEST_DO(f.triggerCompaction(100000));
- TEST_DO(f.assertNoRef(3));
- TEST_DO(f.assertRef(doc1, 1));
- TEST_DO(f.assertRef(doc2, 2));
+ ensureDocIdLimit(5);
+ set(1, toGid(doc1));
+ set(2, toGid(doc2));
+ commit();
+ triggerCompaction(100000);
+ assertNoRef(3);
+ assertRef(doc1, 1);
+ assertRef(doc2, 2);
}
-TEST_F("require that we can save and load attribute", Fixture)
+TEST_F(ReferenceAttributeTest, attribute_can_be_saved_and_loaded)
{
- f.ensureDocIdLimit(5);
- f.set(1, toGid(doc1));
- f.set(2, toGid(doc2));
- f.set(4, toGid(doc1));
- f.commit();
- f.save();
- f.load();
- EXPECT_EQUAL(5u, f.attr().getNumDocs());
- TEST_DO(f.assertNoRef(3));
- TEST_DO(f.assertRef(doc1, 1));
- TEST_DO(f.assertRef(doc2, 2));
- TEST_DO(f.assertRef(doc1, 4));
+ ensureDocIdLimit(5);
+ set(1, toGid(doc1));
+ set(2, toGid(doc2));
+ set(4, toGid(doc1));
+ commit();
+ save();
+ load();
+ EXPECT_EQ(5u, attr().getNumDocs());
+ assertNoRef(3);
+ assertRef(doc1, 1);
+ assertRef(doc2, 2);
+ assertRef(doc1, 4);
EXPECT_TRUE(vespalib::unlink("test.dat"));
EXPECT_TRUE(vespalib::unlink("test.udat"));
}
-TEST_F("require that update() uses gid-mapper to set target lid", Fixture)
+TEST_F(ReferenceAttributeTest, update_uses_gid_mapper_to_set_target_lid)
{
- f.ensureDocIdLimit(6);
+ ensureDocIdLimit(6);
auto factory = std::make_shared<MyGidToLidMapperFactory>();
- f.setGidToLidMapperFactory(factory);
- f.set(1, toGid(doc1));
- f.set(2, toGid(doc2));
- f.set(4, toGid(doc1));
- f.set(5, toGid(doc3));
- f.commit();
- TEST_DO(f.assertTargetLid(1, 10));
- TEST_DO(f.assertTargetLid(2, 17));
- TEST_DO(f.assertNoTargetLid(3));
- TEST_DO(f.assertTargetLid(4, 10));
- TEST_DO(f.assertTargetLid(5, 0));
+ setGidToLidMapperFactory(factory);
+ set(1, toGid(doc1));
+ set(2, toGid(doc2));
+ set(4, toGid(doc1));
+ set(5, toGid(doc3));
+ commit();
+ assertTargetLid(1, 10);
+ assertTargetLid(2, 17);
+ assertNoTargetLid(3);
+ assertTargetLid(4, 10);
+ assertTargetLid(5, 0);
}
-TEST_F("require that notifyReferencedPut() updates lid-2-lid mapping", Fixture)
+TEST_F(ReferenceAttributeTest, notifyReferencedPut_updates_lid_2_lid_mapping)
{
- f.ensureDocIdLimit(4);
- f.set(1, toGid(doc1));
- f.set(2, toGid(doc2));
- f.set(3, toGid(doc1));
- f.commit();
- TEST_DO(f.assertTargetLid(1, 0));
- TEST_DO(f.assertTargetLid(2, 0));
- TEST_DO(f.assertTargetLid(3, 0));
- f.notifyReferencedPut(toGid(doc1), 10);
- f.notifyReferencedPut(toGid(doc2), 20);
- f.notifyReferencedPut(toGid(doc3), 30);
- TEST_DO(f.assertTargetLid(1, 10));
- TEST_DO(f.assertTargetLid(2, 20));
- TEST_DO(f.assertTargetLid(3, 10));
+ ensureDocIdLimit(4);
+ set(1, toGid(doc1));
+ set(2, toGid(doc2));
+ set(3, toGid(doc1));
+ commit();
+ assertTargetLid(1, 0);
+ assertTargetLid(2, 0);
+ assertTargetLid(3, 0);
+ notifyReferencedPut(toGid(doc1), 10);
+ notifyReferencedPut(toGid(doc2), 20);
+ notifyReferencedPut(toGid(doc3), 30);
+ assertTargetLid(1, 10);
+ assertTargetLid(2, 20);
+ assertTargetLid(3, 10);
}
namespace {
-void preparePopulateTargetLids(Fixture &f)
+void
+preparePopulateTargetLids(ReferenceAttributeTest &f)
{
f.ensureDocIdLimit(6);
f.set(1, toGid(doc1));
@@ -335,91 +343,136 @@ void preparePopulateTargetLids(Fixture &f)
f.set(3, toGid(doc1));
f.set(4, toGid(doc3));
f.commit();
- TEST_DO(f.assertTargetLid(1, 0));
- TEST_DO(f.assertTargetLid(2, 0));
- TEST_DO(f.assertTargetLid(3, 0));
- TEST_DO(f.assertTargetLid(4, 0));
- TEST_DO(f.assertNoTargetLid(5));
+ f.assertTargetLid(1, 0);
+ f.assertTargetLid(2, 0);
+ f.assertTargetLid(3, 0);
+ f.assertTargetLid(4, 0);
+ f.assertNoTargetLid(5);
}
-void checkPopulateTargetLids(Fixture &f)
+void
+checkPopulateTargetLids(ReferenceAttributeTest &f)
{
auto factory = std::make_shared<MyGidToLidMapperFactory>();
f.setGidToLidMapperFactory(factory);
- TEST_DO(f.assertTargetLid(1, 10));
- TEST_DO(f.assertTargetLid(2, 17));
- TEST_DO(f.assertTargetLid(3, 10));
- TEST_DO(f.assertTargetLid(4, 0));
- TEST_DO(f.assertNoTargetLid(5));
- TEST_DO(f.assertLids(0, { }));
- TEST_DO(f.assertLids(10, { 1, 3}));
- TEST_DO(f.assertLids(17, { 2 }));
- TEST_DO(f.assertLids(18, { }));
+ f.assertTargetLid(1, 10);
+ f.assertTargetLid(2, 17);
+ f.assertTargetLid(3, 10);
+ f.assertTargetLid(4, 0);
+ f.assertNoTargetLid(5);
+ f.assertLids(0, { });
+ f.assertLids(10, { 1, 3});
+ f.assertLids(17, { 2 });
+ f.assertLids(18, { });
}
}
-TEST_F("require that populateTargetLids() uses gid-mapper to update lid-2-lid mapping", Fixture)
+TEST_F(ReferenceAttributeTest, populateTargetLids_uses_gid_mapper_to_update_lid_2_lid_mapping)
{
- TEST_DO(preparePopulateTargetLids(f));
- TEST_DO(checkPopulateTargetLids(f));
+ preparePopulateTargetLids(*this);
+ checkPopulateTargetLids(*this);
}
-TEST_F("require that populateTargetLids() uses gid-mapper to update lid-2-lid mapping after load", Fixture)
+TEST_F(ReferenceAttributeTest, populateTargetLids_uses_gid_mapper_to_update_lid_2_lid_mapping_after_load)
{
- TEST_DO(preparePopulateTargetLids(f));
- f.save();
- f.load();
- TEST_DO(checkPopulateTargetLids(f));
+ preparePopulateTargetLids(*this);
+ save();
+ load();
+ checkPopulateTargetLids(*this);
EXPECT_TRUE(vespalib::unlink("test.dat"));
EXPECT_TRUE(vespalib::unlink("test.udat"));
}
-TEST_F("Require that notifyReferencedPut and notifyReferencedRemove changes reverse mapping", Fixture)
+TEST_F(ReferenceAttributeTest, notifyReferencedPut_and_notifyReferencedRemove_changes_reverse_mapping)
{
- TEST_DO(preparePopulateTargetLids(f));
- TEST_DO(f.assertLids(10, { }));
- TEST_DO(f.assertLids(11, { }));
- f.notifyReferencedPut(toGid(doc1), 10);
- TEST_DO(f.assertLids(10, { 1, 3}));
- TEST_DO(f.assertLids(11, { }));
- f.notifyReferencedPut(toGid(doc1), 11);
- TEST_DO(f.assertLids(10, { }));
- TEST_DO(f.assertLids(11, { 1, 3}));
- f.notifyReferencedRemove(toGid(doc1));
- TEST_DO(f.assertLids(10, { }));
- TEST_DO(f.assertLids(11, { }));
+ preparePopulateTargetLids(*this);
+ assertLids(10, { });
+ assertLids(11, { });
+ notifyReferencedPut(toGid(doc1), 10);
+ assertLids(10, { 1, 3});
+ assertLids(11, { });
+ notifyReferencedPut(toGid(doc1), 11);
+ assertLids(10, { });
+ assertLids(11, { 1, 3});
+ notifyReferencedRemove(toGid(doc1));
+ assertLids(10, { });
+ assertLids(11, { });
}
-TEST_F("Require that we track unique gids", Fixture)
+TEST_F(ReferenceAttributeTest, unique_gids_are_tracked)
{
- EXPECT_EQUAL(0u, f.getUniqueGids());
- f.notifyReferencedPut(toGid(doc1), 10);
- EXPECT_EQUAL(1u, f.getUniqueGids());
- f.ensureDocIdLimit(3);
- f.set(1, toGid(doc1));
- f.commit();
- EXPECT_EQUAL(1u, f.getUniqueGids());
- TEST_DO(f.assertTargetLid(1, 10));
- TEST_DO(f.assertLids(10, { 1 }));
- f.set(2, toGid(doc2));
- f.commit();
- EXPECT_EQUAL(2u, f.getUniqueGids());
- TEST_DO(f.assertTargetLid(2, 0));
- f.notifyReferencedPut(toGid(doc2), 17);
- EXPECT_EQUAL(2u, f.getUniqueGids());
- TEST_DO(f.assertTargetLid(2, 17));
- TEST_DO(f.assertLids(17, { 2 }));
- f.clear(1);
- f.notifyReferencedRemove(toGid(doc2));
- EXPECT_EQUAL(2u, f.getUniqueGids());
- TEST_DO(f.assertNoTargetLid(1));
- TEST_DO(f.assertTargetLid(2, 0));
- TEST_DO(f.assertLids(10, { }));
- TEST_DO(f.assertLids(17, { }));
- f.clear(2);
- f.notifyReferencedRemove(toGid(doc1));
- EXPECT_EQUAL(0u, f.getUniqueGids());
+ EXPECT_EQ(0u, getUniqueGids());
+ notifyReferencedPut(toGid(doc1), 10);
+ EXPECT_EQ(1u, getUniqueGids());
+ ensureDocIdLimit(3);
+ set(1, toGid(doc1));
+ commit();
+ EXPECT_EQ(1u, getUniqueGids());
+ assertTargetLid(1, 10);
+ assertLids(10, { 1 });
+ set(2, toGid(doc2));
+ commit();
+ EXPECT_EQ(2u, getUniqueGids());
+ assertTargetLid(2, 0);
+ notifyReferencedPut(toGid(doc2), 17);
+ EXPECT_EQ(2u, getUniqueGids());
+ assertTargetLid(2, 17);
+ assertLids(17, { 2 });
+ clear(1);
+ notifyReferencedRemove(toGid(doc2));
+ EXPECT_EQ(2u, getUniqueGids());
+ assertNoTargetLid(1);
+ assertTargetLid(2, 0);
+ assertLids(10, { });
+ assertLids(17, { });
+ clear(2);
+ notifyReferencedRemove(toGid(doc1));
+ EXPECT_EQ(0u, getUniqueGids());
+}
+
+struct ReferenceAttributeSearchTest : public ReferenceAttributeTest {
+
+ constexpr static uint32_t doc_id_limit = 6;
+
+ ReferenceAttributeSearchTest()
+ : ReferenceAttributeTest()
+ {
+ ensureDocIdLimit(doc_id_limit);
+ set(1, toGid(doc1));
+ set(3, toGid(doc2));
+ set(4, toGid(doc1));
+ commit();
+ }
+
+ FakeResult perform_search(SearchIterator& itr) {
+ FakeResult result;
+ itr.initFullRange();
+ for (uint32_t doc_id = 1; doc_id < doc_id_limit; ++doc_id) {
+ if (itr.seek(doc_id)) {
+ result.doc(doc_id);
+ }
+ }
+ return result;
+ }
+
+ void expect_search_result(const std::string& term, const FakeResult& expected) {
+ auto ctx = _attr->getSearch(std::make_unique<QueryTermSimple>(term, QueryTermSimple::WORD),
+ SearchContextParams());
+ TermFieldMatchData tfmd;
+ auto itr = ctx->createIterator(&tfmd, false);
+ FakeResult actual = perform_search(*itr);
+ EXPECT_EQ(expected, actual);
+ }
+
+};
+
+TEST_F(ReferenceAttributeSearchTest, can_be_searched_by_document_id)
+{
+ expect_search_result(doc1, FakeResult().doc(1).doc(4));
+ expect_search_result(doc2, FakeResult().doc(3));
+ expect_search_result(doc3, FakeResult());
+ expect_search_result("invalid document id", FakeResult());
}
-TEST_MAIN() { TEST_RUN_ALL(); }
+GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/searchlib/src/vespa/searchlib/attribute/reference_attribute.cpp b/searchlib/src/vespa/searchlib/attribute/reference_attribute.cpp
index f91108d066b..82539214ea9 100644
--- a/searchlib/src/vespa/searchlib/attribute/reference_attribute.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/reference_attribute.cpp
@@ -1,21 +1,28 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-#include "reference_attribute.h"
-#include "reference_attribute_saver.h"
#include "attributesaver.h"
#include "readerbase.h"
-#include <vespa/searchlib/common/i_gid_to_lid_mapper_factory.h>
+#include "reference_attribute.h"
+#include "reference_attribute_saver.h"
+#include <vespa/document/base/documentid.h>
+#include <vespa/document/base/idstringexception.h>
#include <vespa/searchlib/common/i_gid_to_lid_mapper.h>
-#include <vespa/vespalib/datastore/unique_store_builder.h>
+#include <vespa/searchlib/common/i_gid_to_lid_mapper_factory.h>
+#include <vespa/searchlib/query/queryterm.h>
+#include <vespa/vespalib/data/fileheader.h>
#include <vespa/vespalib/datastore/datastore.hpp>
#include <vespa/vespalib/datastore/unique_store.hpp>
-#include <vespa/vespalib/data/fileheader.h>
+#include <vespa/vespalib/datastore/unique_store_builder.h>
#include <vespa/log/log.h>
LOG_SETUP(".searchlib.attribute.reference_attribute");
namespace search::attribute {
+using document::DocumentId;
+using document::GlobalId;
+using document::IdParseException;
+
namespace {
// minimum dead bytes in unique store before consider compaction
@@ -265,7 +272,7 @@ ReferenceAttribute::update(DocId doc, const GlobalId &gid)
}
const Reference *
-ReferenceAttribute::getReference(DocId doc)
+ReferenceAttribute::getReference(DocId doc) const
{
assert(doc < _indices.size());
EntryRef ref = _indices[doc];
@@ -411,6 +418,56 @@ ReferenceAttribute::onShrinkLidSpace()
setNumDocs(committedDocIdLimit);
}
+namespace {
+
+class ReferenceSearchContext : public AttributeVector::SearchContext {
+private:
+ const ReferenceAttribute& _ref_attr;
+ GlobalId _term;
+
+public:
+ ReferenceSearchContext(const ReferenceAttribute& ref_attr, const GlobalId& term)
+ : AttributeVector::SearchContext(ref_attr),
+ _ref_attr(ref_attr),
+ _term(term)
+ {
+ }
+ bool valid() const override {
+ return _term != GlobalId();
+ }
+ int32_t onFind(DocId docId, int32_t elementId, int32_t& weight) const override {
+ if (elementId != 0) {
+ return -1;
+ }
+ auto* ref = _ref_attr.getReference(docId);
+ if (ref == nullptr) {
+ return -1;
+ }
+ weight = 1;
+ return (_term == ref->gid()) ? 0 : -1;
+ }
+ int32_t onFind(DocId docId, int32_t elementId) const override {
+ int32_t weight;
+ return onFind(docId, elementId, weight);
+ }
+};
+
+}
+
+AttributeVector::SearchContext::UP
+ReferenceAttribute::getSearch(QueryTermSimpleUP term, const attribute::SearchContextParams& params) const
+{
+ (void) params;
+ GlobalId gid;
+ try {
+ DocumentId docId(term->getTerm());
+ gid = docId.getGlobalId();
+ } catch (const IdParseException&) {
+ // The query term is not valid, which will result in an empty search iterator.
+ }
+ return std::make_unique<ReferenceSearchContext>(*this, gid);
+}
+
IMPLEMENT_IDENTIFIABLE_ABSTRACT(ReferenceAttribute, AttributeVector);
}
diff --git a/searchlib/src/vespa/searchlib/attribute/reference_attribute.h b/searchlib/src/vespa/searchlib/attribute/reference_attribute.h
index 87d5a5c27bb..87d624eb21f 100644
--- a/searchlib/src/vespa/searchlib/attribute/reference_attribute.h
+++ b/searchlib/src/vespa/searchlib/attribute/reference_attribute.h
@@ -3,8 +3,8 @@
#pragma once
#include "not_implemented_attribute.h"
-#include "reference_mappings.h"
#include "reference.h"
+#include "reference_mappings.h"
#include <vespa/vespalib/datastore/unique_store.h>
#include <vespa/vespalib/util/rcuvector.h>
@@ -71,7 +71,7 @@ public:
bool addDoc(DocId &doc) override;
uint32_t clearDoc(DocId doc) override;
void update(DocId doc, const GlobalId &gid);
- const Reference *getReference(DocId doc);
+ const Reference *getReference(DocId doc) const;
void setGidToLidMapperFactory(std::shared_ptr<IGidToLidMapperFactory> gidToLidMapperFactory);
std::shared_ptr<IGidToLidMapperFactory> getGidToLidMapperFactory() const { return _gidToLidMapperFactory; }
TargetLids getTargetLids() const { return _referenceMappings.getTargetLids(); }
@@ -91,6 +91,8 @@ public:
foreach_lid(uint32_t targetLid, FunctionType &&func) const {
_referenceMappings.foreach_lid(targetLid, std::forward<FunctionType>(func));
}
+
+ SearchContext::UP getSearch(QueryTermSimpleUP term, const attribute::SearchContextParams& params) const override;
};
}