aboutsummaryrefslogtreecommitdiffstats
path: root/document/src
diff options
context:
space:
mode:
authorHenning Baldersheim <balder@yahoo-inc.com>2020-09-30 22:58:26 +0200
committerGitHub <noreply@github.com>2020-09-30 22:58:26 +0200
commit4386de75fc03a9b568bd5e0632f336b3557833c2 (patch)
treeb5999c0c8e351ceaacd1c7e252ceb8430e267474 /document/src
parentba161840b6583fc2a104fc5c05a32b1e6a2c19ee (diff)
parenta2f6724bf895458c119bd1dc70c2a34a21a72f11 (diff)
Merge pull request #14644 from vespa-engine/toregge/handle-reference-field-value-in-document-selection-expression
Handle ReferenceFieldValue in document selection expression.
Diffstat (limited to 'document/src')
-rw-r--r--document/src/tests/select/CMakeLists.txt9
-rw-r--r--document/src/tests/select/select_test.cpp119
-rw-r--r--document/src/vespa/document/select/valuenodes.cpp10
3 files changed, 138 insertions, 0 deletions
diff --git a/document/src/tests/select/CMakeLists.txt b/document/src/tests/select/CMakeLists.txt
new file mode 100644
index 00000000000..02f4f50b883
--- /dev/null
+++ b/document/src/tests/select/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(document_select_test_app TEST
+ SOURCES
+ select_test.cpp
+ DEPENDS
+ document
+ GTest::GTest
+)
+vespa_add_test(NAME document_select_test_app COMMAND document_select_test_app)
diff --git a/document/src/tests/select/select_test.cpp b/document/src/tests/select/select_test.cpp
new file mode 100644
index 00000000000..fa42487cd1b
--- /dev/null
+++ b/document/src/tests/select/select_test.cpp
@@ -0,0 +1,119 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/vespalib/gtest/gtest.h>
+#include <vespa/document/base/documentid.h>
+#include <vespa/document/datatype/documenttype.h>
+#include <vespa/document/datatype/referencedatatype.h>
+#include <vespa/document/fieldvalue/document.h>
+#include <vespa/document/fieldvalue/referencefieldvalue.h>
+#include <vespa/document/repo/configbuilder.h>
+#include <vespa/document/repo/documenttyperepo.h>
+#include <vespa/document/repo/document_type_repo_factory.h>
+#include <vespa/document/select/parser.h>
+
+#include <vespa/log/log.h>
+LOG_SETUP("document_select_test");
+
+using document::Document;
+using document::DocumentId;
+using document::DocumenttypesConfig;
+using document::DocumentType;
+using document::DocumentTypeRepo;
+using document::DocumentTypeRepoFactory;
+using document::Field;
+using document::ReferenceDataType;
+using document::ReferenceFieldValue;
+using document::BucketIdFactory;
+using document::select::Parser;
+using document::select::Result;
+using document::select::ResultList;
+
+std::shared_ptr<DocumenttypesConfig> make_document_types() {
+ using Struct = document::config_builder::Struct;
+ document::config_builder::DocumenttypesConfigBuilderHelper builder;
+ constexpr int parent_doctype_id = 42;
+ constexpr int child_doctype_id = 43;
+ constexpr int ref_type_id = 44;
+ builder.document(parent_doctype_id, "parent",
+ Struct("parent.header"),
+ Struct("parent.body"));
+ builder.document(child_doctype_id, "child",
+ Struct("child.header").
+ addField("ref", ref_type_id),
+ Struct("child.body")).
+ referenceType(ref_type_id, parent_doctype_id);
+ return std::make_shared<DocumenttypesConfig>(builder.config());
+}
+
+class DocumentSelectTest : public ::testing::Test
+{
+protected:
+ std::shared_ptr<DocumenttypesConfig> _document_types;
+ std::shared_ptr<const DocumentTypeRepo> _repo;
+ const DocumentType* _child_document_type;
+ const Field& _child_ref_field;
+ const ReferenceDataType& _child_ref_field_type;
+ BucketIdFactory _bucket_id_factory;
+ std::unique_ptr<Parser> _parser;
+public:
+ DocumentSelectTest();
+ ~DocumentSelectTest() override;
+ void check_select(const Document &doc, const vespalib::string &expression, const Result &exp_result);
+};
+
+DocumentSelectTest::DocumentSelectTest()
+ : ::testing::Test(),
+ _document_types(make_document_types()),
+ _repo(DocumentTypeRepoFactory::make(*_document_types)),
+ _child_document_type(_repo->getDocumentType("child")),
+ _child_ref_field(_child_document_type->getField("ref")),
+ _child_ref_field_type(dynamic_cast<const ReferenceDataType&>(_child_ref_field.getDataType())),
+ _bucket_id_factory(),
+ _parser(std::make_unique<Parser>(*_repo, _bucket_id_factory))
+{
+}
+
+DocumentSelectTest::~DocumentSelectTest() = default;
+
+void
+DocumentSelectTest::check_select(const Document& doc, const vespalib::string& expression, const Result &exp_result)
+{
+ auto node = _parser->parse(expression);
+ EXPECT_EQ(node->contains(doc), exp_result);
+}
+
+
+TEST_F(DocumentSelectTest, check_existing_reference_field)
+{
+ auto document = std::make_unique<Document>(*_child_document_type, DocumentId("id::child::0"));
+ document->setFieldValue(_child_ref_field, std::make_unique<ReferenceFieldValue>(_child_ref_field_type, DocumentId("id::parent::1")));
+ EXPECT_TRUE(document->hasValue(_child_ref_field));
+ check_select(*document, "child.ref == null", Result::False);
+ check_select(*document, "child.ref != null", Result::True);
+ check_select(*document, "child.ref == \"id::parent::1\"", Result::True);
+ check_select(*document, "child.ref != \"id::parent::1\"", Result::False);
+ check_select(*document, "child.ref == \"id::parent::2\"", Result::False);
+ check_select(*document, "child.ref != \"id::parent::2\"", Result::True);
+ check_select(*document, "child.ref < \"id::parent::0\"", Result::False);
+ check_select(*document, "child.ref < \"id::parent::2\"", Result::True);
+ check_select(*document, "child.ref > \"id::parent::0\"", Result::True);
+ check_select(*document, "child.ref > \"id::parent::2\"", Result::False);
+}
+
+TEST_F(DocumentSelectTest, check_missing_reference_field)
+{
+ auto document = std::make_unique<Document>(*_child_document_type, DocumentId("id::child::0"));
+ EXPECT_FALSE(document->hasValue(_child_ref_field));
+ check_select(*document, "child.ref == null", Result::True);
+ check_select(*document, "child.ref != null", Result::False);
+ check_select(*document, "child.ref == \"id::parent::1\"", Result::False);
+ check_select(*document, "child.ref != \"id::parent::1\"", Result::True);
+ check_select(*document, "child.ref == \"id::parent::2\"", Result::False);
+ check_select(*document, "child.ref != \"id::parent::2\"", Result::True);
+ check_select(*document, "child.ref < \"id::parent::0\"", Result::Invalid);
+ check_select(*document, "child.ref < \"id::parent::2\"", Result::Invalid);
+ check_select(*document, "child.ref > \"id::parent::0\"", Result::Invalid);
+ check_select(*document, "child.ref > \"id::parent::2\"", Result::Invalid);
+}
+
+GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/document/src/vespa/document/select/valuenodes.cpp b/document/src/vespa/document/select/valuenodes.cpp
index 026623cf83c..73fc1c6486b 100644
--- a/document/src/vespa/document/select/valuenodes.cpp
+++ b/document/src/vespa/document/select/valuenodes.cpp
@@ -6,6 +6,7 @@
#include <vespa/document/base/exceptions.h>
#include <vespa/document/update/documentupdate.h>
#include <vespa/document/fieldvalue/fieldvalues.h>
+#include <vespa/document/fieldvalue/referencefieldvalue.h>
#include <vespa/document/fieldvalue/iteratorhandler.h>
#include <vespa/document/datatype/documenttype.h>
#include <vespa/vespalib/util/md5.h>
@@ -303,6 +304,15 @@ IteratorHandler::getInternalValue(const FieldValue& fval) const
const StringFieldValue& val(dynamic_cast<const StringFieldValue&>(fval));
return std::make_unique<StringValue>(val.getAsString());
}
+ case ReferenceFieldValue::classId:
+ {
+ const ReferenceFieldValue& val(dynamic_cast<const ReferenceFieldValue&>(fval));
+ if (val.hasValidDocumentId()) {
+ return std::make_unique<StringValue>(val.getDocumentId().toString());
+ } else {
+ return std::make_unique<InvalidValue>();
+ }
+ }
case ArrayFieldValue::classId:
{
const ArrayFieldValue& val(dynamic_cast<const ArrayFieldValue&>(fval));