aboutsummaryrefslogtreecommitdiffstats
path: root/searchcore
diff options
context:
space:
mode:
authorGeir Storli <geirst@oath.com>2018-05-15 07:33:15 +0000
committerGeir Storli <geirst@oath.com>2018-05-15 07:55:00 +0000
commitce76829dec3afd2997ea91d5edb15570137105fb (patch)
tree2bff7e98a7a9cfed08e93285c779a8df128f57b0 /searchcore
parent529ec157ed468e55de823d69753020deb421016a (diff)
Add pre-document selection than can disqualify a document without retrieving it from the document store.
This selection can be used when expression references at least one single value attribute field and we can disqualify the document if the selection evaluates to false.
Diffstat (limited to 'searchcore')
-rw-r--r--searchcore/src/tests/proton/common/cachedselect_test.cpp172
-rw-r--r--searchcore/src/vespa/searchcore/proton/common/cachedselect.cpp117
-rw-r--r--searchcore/src/vespa/searchcore/proton/common/cachedselect.h36
3 files changed, 240 insertions, 85 deletions
diff --git a/searchcore/src/tests/proton/common/cachedselect_test.cpp b/searchcore/src/tests/proton/common/cachedselect_test.cpp
index 80b80fa4338..630947b96e0 100644
--- a/searchcore/src/tests/proton/common/cachedselect_test.cpp
+++ b/searchcore/src/tests/proton/common/cachedselect_test.cpp
@@ -15,14 +15,12 @@
#include <vespa/searchlib/attribute/attributefactory.h>
#include <vespa/searchlib/attribute/attributevector.hpp>
#include <vespa/searchlib/attribute/enumcomparator.h>
-#include <vespa/searchlib/attribute/integerbase.h>
#include <vespa/searchlib/attribute/postinglistattribute.h>
#include <vespa/searchlib/attribute/singleenumattribute.hpp>
#include <vespa/searchlib/attribute/singlenumericenumattribute.hpp>
#include <vespa/searchlib/attribute/singlenumericpostattribute.h>
#include <vespa/searchlib/attribute/singlenumericpostattribute.hpp>
#include <vespa/searchlib/test/mock_attribute_manager.h>
-#include <vespa/vespalib/stllike/string.h>
#include <vespa/vespalib/testkit/testapp.h>
#include <vespa/log/log.h>
@@ -64,9 +62,10 @@ using vespalib::string;
using namespace search::index;
-typedef Node::UP NodeUP;
-typedef IntegerAttributeTemplate<int32_t> IATint32;
-typedef EnumAttribute<IATint32> IntEnumAttribute;
+using IATint32 = IntegerAttributeTemplate<int32_t>;
+using IntEnumAttribute = EnumAttribute<IATint32>;
+using NodeUP = Node::UP;
+using SessionUP = std::unique_ptr<CachedSelect::Session>;
#if 0
extern template class SingleValueNumericPostingAttribute<IntPostingAttribute>;
@@ -137,14 +136,14 @@ makeDoc(const DocumentTypeRepo &repo,
return doc;
}
-
bool
checkSelect(const NodeUP &sel,
const Context &ctx,
const Result &exp)
{
- if (EXPECT_TRUE(sel->contains(ctx) == exp))
+ if (EXPECT_TRUE(sel->contains(ctx) == exp)) {
return true;
+ }
std::ostringstream os;
EXPECT_TRUE(sel->trace(ctx, os) == exp);
LOG(info,
@@ -153,24 +152,35 @@ checkSelect(const NodeUP &sel,
return false;
}
-bool
+void
checkSelect(const CachedSelect::SP &cs,
- const Context &ctx,
+ const Document &doc,
const Result &exp)
{
- return checkSelect(cs->select(), ctx, exp);
+ bool expSessionContains = (cs->preDocOnlySelect() || (exp == Result::True));
+ EXPECT_TRUE(checkSelect(cs->docSelect(), Context(doc), exp));
+ EXPECT_EQUAL(expSessionContains, cs->createSession()->contains(doc));
}
-
-bool
+void
checkSelect(const CachedSelect::SP &cs,
uint32_t docId,
- const Result &exp)
+ const Result &exp,
+ bool expSessionContains)
{
SelectContext ctx(*cs);
ctx._docId = docId;
ctx.getAttributeGuards();
- return checkSelect(cs->attrSelect(), ctx, exp);
+ EXPECT_TRUE(checkSelect((cs->preDocOnlySelect() ? cs->preDocOnlySelect() : cs->preDocSelect()), ctx, exp));
+ EXPECT_EQUAL(expSessionContains, cs->createSession()->contains(ctx));
+}
+
+void
+checkSelect(const CachedSelect::SP &cs,
+ uint32_t docId,
+ const Result &exp)
+{
+ checkSelect(cs, docId, exp, (exp == Result::True));
}
@@ -206,7 +216,7 @@ class MyAttributeManager : public MockAttributeManager
public:
using MockAttributeManager::addAttribute;
void addAttribute(const vespalib::string &name) {
- if (findAttribute(name).get() != NULL) {
+ if (findAttribute(name).get() != nullptr) {
return;
}
AttributeVector::SP av(new MyIntAv(name));
@@ -301,6 +311,8 @@ public:
testParse(const string &selection,
const string &docTypeName);
+ MyDB &db() { return *_db; }
+
};
@@ -334,7 +346,7 @@ TestFixture::testParse(const string &selection,
CachedSelect::SP res(new CachedSelect);
const DocumentType *docType = repo.getDocumentType(docTypeName);
- ASSERT_TRUE(docType != NULL);
+ ASSERT_TRUE(docType != nullptr);
Document::UP emptyDoc(new Document(*docType, DocumentId()));
res->set(selection,
@@ -344,10 +356,59 @@ TestFixture::testParse(const string &selection,
&_amgr,
_hasFields);
- ASSERT_TRUE(res->select().get() != NULL);
+ ASSERT_TRUE(res->docSelect());
return res;
}
+class Stats {
+private:
+ bool _preDocOnlySelect;
+ bool _preDocSelect;
+ bool _allFalse;
+ bool _allTrue;
+ bool _allInvalid;
+ uint32_t _fieldNodes;
+ uint32_t _attrFieldNodes;
+ uint32_t _svAttrFieldNodes;
+
+public:
+ Stats()
+ : _preDocOnlySelect(false),
+ _preDocSelect(false),
+ _allFalse(false),
+ _allTrue(false),
+ _allInvalid(false),
+ _fieldNodes(0),
+ _attrFieldNodes(0),
+ _svAttrFieldNodes(0)
+ {}
+ Stats &preDocOnlySelect() { _preDocOnlySelect = true; return *this; }
+ Stats &preDocSelect() { _preDocSelect = true; return *this; }
+ Stats &allFalse() { _allFalse = true; return *this; }
+ Stats &allTrue() { _allTrue = true; return *this; }
+ Stats &allInvalid() { _allInvalid = true; return *this; };
+ Stats &fieldNodes(uint32_t value) { _fieldNodes = value; return *this; }
+ Stats &attrFieldNodes(uint32_t value) { _attrFieldNodes = value; return *this; }
+ Stats &svAttrFieldNodes(uint32_t value) { _svAttrFieldNodes = value; return *this; }
+
+ void assertEquals(const CachedSelect &select) const {
+ EXPECT_EQUAL(_preDocOnlySelect, (bool)select.preDocOnlySelect());
+ EXPECT_EQUAL(_preDocSelect, (bool)select.preDocSelect());
+ EXPECT_EQUAL(_allFalse, select.allFalse());
+ EXPECT_EQUAL(_allTrue, select.allTrue());
+ EXPECT_EQUAL(_allInvalid, select.allInvalid());
+ EXPECT_EQUAL(_fieldNodes, select.fieldNodes());
+ EXPECT_EQUAL(_attrFieldNodes, select.attrFieldNodes());
+ EXPECT_EQUAL(_svAttrFieldNodes, select.svAttrFieldNodes());
+ }
+};
+
+void
+assertEquals(const Stats &stats, const CachedSelect &select)
+{
+ stats.assertEquals(select);
+}
+
TEST_F("Test that test setup is OK", TestFixture)
{
@@ -413,7 +474,7 @@ TEST_F("Test that basic select works", TestFixture)
CachedSelect::SP cs;
cs = f.testParse("test.ia == \"hello\"", "test");
- EXPECT_FALSE(cs->attrSelect().get() != NULL);
+ EXPECT_TRUE(!cs->preDocOnlySelect());
EXPECT_FALSE(cs->allFalse());
EXPECT_FALSE(cs->allTrue());
EXPECT_FALSE(cs->allInvalid());
@@ -426,7 +487,7 @@ TEST_F("Test that basic select works", TestFixture)
TEST_DO(checkSelect(cs, db.getDoc(4u), Result::False));
cs = f.testParse("test.ia.foo == \"hello\"", "test");
- EXPECT_FALSE(cs->attrSelect().get() != NULL);
+ EXPECT_TRUE(!cs->preDocOnlySelect());
EXPECT_FALSE(cs->allFalse());
EXPECT_FALSE(cs->allTrue());
EXPECT_TRUE(cs->allInvalid());
@@ -439,7 +500,7 @@ TEST_F("Test that basic select works", TestFixture)
TEST_DO(checkSelect(cs, db.getDoc(4u), Result::Invalid));
cs = f.testParse("test.ia[2] == \"hello\"", "test");
- EXPECT_FALSE(cs->attrSelect().get() != NULL);
+ EXPECT_TRUE(!cs->preDocOnlySelect());
EXPECT_FALSE(cs->allFalse());
EXPECT_FALSE(cs->allTrue());
EXPECT_TRUE(cs->allInvalid());
@@ -452,7 +513,7 @@ TEST_F("Test that basic select works", TestFixture)
TEST_DO(checkSelect(cs, db.getDoc(4u), Result::Invalid));
cs = f.testParse("test.ia{foo} == \"hello\"", "test");
- EXPECT_FALSE(cs->attrSelect().get() != NULL);
+ EXPECT_TRUE(!cs->preDocOnlySelect());
EXPECT_FALSE(cs->allFalse());
EXPECT_FALSE(cs->allTrue());
EXPECT_TRUE(cs->allInvalid());
@@ -465,7 +526,7 @@ TEST_F("Test that basic select works", TestFixture)
TEST_DO(checkSelect(cs, db.getDoc(4u), Result::Invalid));
cs = f.testParse("test.ia < \"hello\"", "test");
- EXPECT_FALSE(cs->attrSelect().get() != NULL);
+ EXPECT_TRUE(!cs->preDocOnlySelect());
EXPECT_FALSE(cs->allFalse());
EXPECT_FALSE(cs->allTrue());
EXPECT_FALSE(cs->allInvalid());
@@ -478,7 +539,7 @@ TEST_F("Test that basic select works", TestFixture)
TEST_DO(checkSelect(cs, db.getDoc(4u), Result::Invalid));
cs = f.testParse("test.aa == 3", "test");
- EXPECT_TRUE(cs->attrSelect().get() != NULL);
+ EXPECT_TRUE(cs->preDocOnlySelect());
EXPECT_FALSE(cs->allFalse());
EXPECT_FALSE(cs->allTrue());
EXPECT_FALSE(cs->allInvalid());
@@ -495,7 +556,7 @@ TEST_F("Test that basic select works", TestFixture)
TEST_DO(checkSelect(cs, 4u, Result::False));
cs = f.testParse("test.aa == 3", "test");
- EXPECT_TRUE(cs->attrSelect().get() != NULL);
+ EXPECT_TRUE(cs->preDocOnlySelect());
EXPECT_FALSE(cs->allFalse());
EXPECT_FALSE(cs->allTrue());
EXPECT_FALSE(cs->allInvalid());
@@ -512,7 +573,7 @@ TEST_F("Test that basic select works", TestFixture)
TEST_DO(checkSelect(cs, 4u, Result::False));
cs = f.testParse("test.aa.foo == 3", "test");
- EXPECT_TRUE(cs->attrSelect().get() == NULL);
+ EXPECT_TRUE(!cs->preDocOnlySelect());
EXPECT_FALSE(cs->allFalse());
EXPECT_FALSE(cs->allTrue());
EXPECT_TRUE(cs->allInvalid());
@@ -525,7 +586,7 @@ TEST_F("Test that basic select works", TestFixture)
TEST_DO(checkSelect(cs, db.getDoc(4u), Result::Invalid));
cs = f.testParse("test.aa[2] == 3", "test");
- EXPECT_TRUE(cs->attrSelect().get() == NULL);
+ EXPECT_TRUE(!cs->preDocOnlySelect());
EXPECT_FALSE(cs->allFalse());
EXPECT_FALSE(cs->allTrue());
EXPECT_TRUE(cs->allInvalid());
@@ -538,7 +599,7 @@ TEST_F("Test that basic select works", TestFixture)
TEST_DO(checkSelect(cs, db.getDoc(4u), Result::Invalid));
cs = f.testParse("test.aa{4} > 3", "test");
- EXPECT_TRUE(cs->attrSelect().get() == NULL);
+ EXPECT_TRUE(!cs->preDocOnlySelect());
EXPECT_FALSE(cs->allFalse());
EXPECT_FALSE(cs->allTrue());
EXPECT_TRUE(cs->allInvalid());
@@ -551,7 +612,7 @@ TEST_F("Test that basic select works", TestFixture)
TEST_DO(checkSelect(cs, db.getDoc(4u), Result::Invalid));
cs = f.testParse("test.aaa[2] == 3", "test");
- EXPECT_TRUE(cs->attrSelect().get() == NULL);
+ EXPECT_TRUE(!cs->preDocOnlySelect());
EXPECT_FALSE(cs->allFalse());
EXPECT_FALSE(cs->allTrue());
EXPECT_FALSE(cs->allInvalid());
@@ -560,7 +621,7 @@ TEST_F("Test that basic select works", TestFixture)
EXPECT_EQUAL(0u, cs->svAttrFieldNodes());
cs = f.testParse("test.aaw{4} > 3", "test");
- EXPECT_TRUE(cs->attrSelect().get() == NULL);
+ EXPECT_TRUE(!cs->preDocOnlySelect());
EXPECT_FALSE(cs->allFalse());
EXPECT_FALSE(cs->allTrue());
EXPECT_FALSE(cs->allInvalid());
@@ -569,7 +630,7 @@ TEST_F("Test that basic select works", TestFixture)
EXPECT_EQUAL(0u, cs->svAttrFieldNodes());
cs = f.testParse("test.aa < 45", "test");
- EXPECT_TRUE(cs->attrSelect().get() != NULL);
+ EXPECT_TRUE(cs->preDocOnlySelect());
EXPECT_FALSE(cs->allFalse());
EXPECT_FALSE(cs->allTrue());
EXPECT_FALSE(cs->allInvalid());
@@ -580,16 +641,51 @@ TEST_F("Test that basic select works", TestFixture)
TEST_DO(checkSelect(cs, db.getDoc(2u), Result::True));
TEST_DO(checkSelect(cs, db.getDoc(3u), Result::Invalid));
TEST_DO(checkSelect(cs, db.getDoc(4u), Result::Invalid));
- TEST_DO(checkSelect(cs, 1u, Result::False));
- TEST_DO(checkSelect(cs, 2u, Result::True));
- TEST_DO(checkSelect(cs, 3u, Result::Invalid));
- TEST_DO(checkSelect(cs, 4u, Result::Invalid));
+ TEST_DO(checkSelect(cs, 1u, Result::False, false));
+ TEST_DO(checkSelect(cs, 2u, Result::True, true));
+ TEST_DO(checkSelect(cs, 3u, Result::Invalid, false));
+ TEST_DO(checkSelect(cs, 4u, Result::Invalid, false));
MyIntAv *v = f._amgr.getAsMyIntAttribute("aa");
- EXPECT_TRUE(v != NULL);
- EXPECT_EQUAL(6u, v->getGets());
+ EXPECT_TRUE(v != nullptr);
+ EXPECT_EQUAL(12u, v->getGets());
}
+struct PreDocSelectFixture : public TestFixture {
+ PreDocSelectFixture()
+ : TestFixture()
+ {
+ db().addDoc(1u, "doc:test:1", "foo", "null", 3, 5);
+ db().addDoc(2u, "doc:test:1", "bar", "null", 3, 5);
+ db().addDoc(3u, "doc:test:2", "foo", "null", 7, 5);
+ }
+};
+
+TEST_F("Test that single value attribute combined with non-attribute field results in pre-document select pruner", PreDocSelectFixture)
+{
+ CachedSelect::SP cs = f.testParse("test.aa == 3 AND test.ia == \"foo\"", "test");
+ TEST_DO(assertEquals(Stats().preDocSelect().fieldNodes(2).attrFieldNodes(1).svAttrFieldNodes(1), *cs));
+
+ TEST_DO(checkSelect(cs, 1u, Result::Invalid, true));
+ TEST_DO(checkSelect(cs, 2u, Result::Invalid, true));
+ TEST_DO(checkSelect(cs, 3u, Result::False, false));
+ TEST_DO(checkSelect(cs, f.db().getDoc(1u), Result::True));
+ TEST_DO(checkSelect(cs, f.db().getDoc(2u), Result::False));
+ TEST_DO(checkSelect(cs, f.db().getDoc(3u), Result::False));
+}
+
+TEST_F("Test that single value attribute with complex attribute field results in pre-document select pruner", PreDocSelectFixture)
+{
+ CachedSelect::SP cs = f.testParse("test.aa == 3 AND test.aaa[0] == 5", "test");
+ TEST_DO(assertEquals(Stats().preDocSelect().fieldNodes(2).attrFieldNodes(2).svAttrFieldNodes(1), *cs));
+
+ TEST_DO(checkSelect(cs, 1u, Result::Invalid, true));
+ TEST_DO(checkSelect(cs, 2u, Result::Invalid, true));
+ TEST_DO(checkSelect(cs, 3u, Result::False, false));
+ TEST_DO(checkSelect(cs, f.db().getDoc(1u), Result::False));
+ TEST_DO(checkSelect(cs, f.db().getDoc(2u), Result::False));
+ TEST_DO(checkSelect(cs, f.db().getDoc(3u), Result::False));
+}
TEST_F("Test performance when using attributes", TestFixture)
{
@@ -602,7 +698,7 @@ TEST_F("Test performance when using attributes", TestFixture)
CachedSelect::SP cs;
cs = f.testParse("test.aa < 45", "test");
- EXPECT_TRUE(cs->attrSelect().get() != NULL);
+ EXPECT_TRUE(cs->preDocOnlySelect());
EXPECT_FALSE(cs->allFalse());
EXPECT_FALSE(cs->allTrue());
EXPECT_FALSE(cs->allInvalid());
@@ -611,7 +707,7 @@ TEST_F("Test performance when using attributes", TestFixture)
EXPECT_EQUAL(1u, cs->svAttrFieldNodes());
SelectContext ctx(*cs);
ctx.getAttributeGuards();
- const NodeUP &sel(cs->attrSelect());
+ const NodeUP &sel(cs->preDocOnlySelect());
uint32_t i;
const uint32_t loopcnt = 30000;
LOG(info, "Starting minibm loop, %u ierations of 4 docs each", loopcnt);
diff --git a/searchcore/src/vespa/searchcore/proton/common/cachedselect.cpp b/searchcore/src/vespa/searchcore/proton/common/cachedselect.cpp
index 7965bec996d..a953d651a33 100644
--- a/searchcore/src/vespa/searchcore/proton/common/cachedselect.cpp
+++ b/searchcore/src/vespa/searchcore/proton/common/cachedselect.cpp
@@ -19,6 +19,8 @@ using search::AttributeGuard;
using document::select::FieldValueNode;
using search::attribute::CollectionType;
+using NodeUP = std::unique_ptr<document::select::Node>;
+
namespace {
class AttrVisitor : public document::select::CloningVisitor
@@ -107,35 +109,81 @@ AttrVisitor::visitFieldValueNode(const FieldValueNode &expr)
}
-CachedSelect::Session::Session(std::unique_ptr<document::select::Node> select, bool isAttrSelect)
- : _select(std::move(select)),
- _isAttrSelect(isAttrSelect)
+CachedSelect::Session::Session(std::unique_ptr<document::select::Node> docSelect,
+ std::unique_ptr<document::select::Node> preDocOnlySelect,
+ std::unique_ptr<document::select::Node> preDocSelect)
+ : _docSelect(std::move(docSelect)),
+ _preDocOnlySelect(std::move(preDocOnlySelect)),
+ _preDocSelect(std::move(preDocSelect))
{
}
bool
CachedSelect::Session::contains(const SelectContext &context) const
{
- return (!_isAttrSelect) ||
- (_isAttrSelect && (_select->contains(context) == document::select::Result::True));
+ if (_preDocSelect && (_preDocSelect->contains(context) == document::select::Result::False)) {
+ return false;
+ }
+ return (!_preDocOnlySelect) ||
+ (_preDocOnlySelect && (_preDocOnlySelect->contains(context) == document::select::Result::True));
}
bool
CachedSelect::Session::contains(const document::Document &doc) const
{
- return (_isAttrSelect || (_select->contains(doc) == document::select::Result::True));
+ return (_preDocOnlySelect) ||
+ (_docSelect && (_docSelect->contains(doc) == document::select::Result::True));
+}
+
+const document::select::Node &
+CachedSelect::Session::selectNode() const
+{
+ return (_docSelect ? *_docSelect : *_preDocOnlySelect);
+}
+
+void
+CachedSelect::setDocumentSelect(SelectPruner &docsPruner)
+{
+ _allFalse = docsPruner.isFalse();
+ _allTrue = docsPruner.isTrue();
+ _allInvalid = docsPruner.isInvalid();
+ _docSelect = std::move(docsPruner.getNode());
+ _fieldNodes = docsPruner.getFieldNodes();
+ _attrFieldNodes = docsPruner.getAttrFieldNodes();
+}
+
+void
+CachedSelect::setPreDocumentSelect(const search::IAttributeManager &attrMgr,
+ SelectPruner &noDocsPruner)
+{
+ _attributes.clear();
+ AttrVisitor allAttrVisitor(attrMgr, _attributes);
+ _docSelect->visit(allAttrVisitor);
+ assert(_fieldNodes == allAttrVisitor.getFieldNodes());
+ assert(_attrFieldNodes == (allAttrVisitor._mvAttrs + allAttrVisitor._svAttrs + allAttrVisitor._complexAttrs));
+ _svAttrFieldNodes = allAttrVisitor._svAttrs;
+
+ if (_fieldNodes == _svAttrFieldNodes) {
+ _preDocOnlySelect = std::move(allAttrVisitor.getNode());
+ } else if (_svAttrFieldNodes > 0) {
+ _attributes.clear();
+ AttrVisitor someAttrVisitor(attrMgr, _attributes);
+ noDocsPruner.getNode()->visit(someAttrVisitor);
+ _preDocSelect = std::move(someAttrVisitor.getNode());
+ }
}
CachedSelect::CachedSelect()
: _attributes(),
- _select(),
+ _docSelect(),
_fieldNodes(0u),
_attrFieldNodes(0u),
_svAttrFieldNodes(0u),
_allFalse(false),
_allTrue(false),
_allInvalid(false),
- _attrSelect()
+ _preDocOnlySelect(),
+ _preDocSelect()
{ }
CachedSelect::~CachedSelect() { }
@@ -146,11 +194,11 @@ CachedSelect::set(const vespalib::string &selection,
{
try {
document::select::Parser parser(repo, document::BucketIdFactory());
- _select = parser.parse(selection);
+ _docSelect = parser.parse(selection);
} catch (document::select::ParsingFailedException &) {
- _select.reset(nullptr);
+ _docSelect.reset(nullptr);
}
- _allFalse = !_select;
+ _allFalse = !_docSelect;
_allTrue = false;
_allInvalid = false;
}
@@ -164,44 +212,39 @@ CachedSelect::set(const vespalib::string &selection,
const search::IAttributeManager *amgr,
bool hasFields)
{
- typedef std::unique_ptr<document::select::Node> NodeUP;
-
set(selection, repo);
- NodeUP parsed(std::move(_select));
+ NodeUP parsed(std::move(_docSelect));
if (!parsed) {
return;
}
- SelectPruner pruner(docTypeName,
- amgr,
- emptyDoc,
- repo,
- hasFields,
- true);
- pruner.process(*parsed);
- _allFalse = pruner.isFalse();
- _allTrue = pruner.isTrue();
- _allInvalid = pruner.isInvalid();
- _select = std::move(pruner.getNode());
- _fieldNodes = pruner.getFieldNodes();
- _attrFieldNodes = pruner.getAttrFieldNodes();
+ SelectPruner docsPruner(docTypeName,
+ amgr,
+ emptyDoc,
+ repo,
+ hasFields,
+ true);
+ docsPruner.process(*parsed);
+ setDocumentSelect(docsPruner);
if (amgr == nullptr || _attrFieldNodes == 0u) {
return;
}
- AttrVisitor av(*amgr, _attributes);
- _select->visit(av);
- assert(_fieldNodes == av.getFieldNodes());
- assert(_attrFieldNodes == av._mvAttrs + av._svAttrs + av._complexAttrs);
- _svAttrFieldNodes = av._svAttrs;
- if (_fieldNodes == _svAttrFieldNodes) {
- _attrSelect = std::move(av.getNode());
- }
+
+ SelectPruner noDocsPruner(docTypeName,
+ amgr,
+ emptyDoc,
+ repo,
+ hasFields,
+ false);
+ noDocsPruner.process(*parsed);
+ setPreDocumentSelect(*amgr, noDocsPruner);
}
std::unique_ptr<CachedSelect::Session>
CachedSelect::createSession() const
{
- return (_attrSelect ? std::make_unique<Session>(_attrSelect->clone(), true) :
- std::make_unique<Session>(_select->clone(), false));
+ return std::make_unique<Session>((_docSelect ? _docSelect->clone() : NodeUP()),
+ (_preDocOnlySelect ? _preDocOnlySelect->clone() : NodeUP()),
+ (_preDocSelect ? _preDocSelect->clone() : NodeUP()));
}
}
diff --git a/searchcore/src/vespa/searchcore/proton/common/cachedselect.h b/searchcore/src/vespa/searchcore/proton/common/cachedselect.h
index 0c8dbe38f06..e9eb452889e 100644
--- a/searchcore/src/vespa/searchcore/proton/common/cachedselect.h
+++ b/searchcore/src/vespa/searchcore/proton/common/cachedselect.h
@@ -18,6 +18,7 @@ namespace search {
namespace proton {
class SelectContext;
+class SelectPruner;
/**
* Cached selection expression, to avoid pruning expression for each
@@ -30,14 +31,17 @@ public:
class Session {
private:
- std::unique_ptr<document::select::Node> _select;
- bool _isAttrSelect;
+ std::unique_ptr<document::select::Node> _docSelect;
+ std::unique_ptr<document::select::Node> _preDocOnlySelect;
+ std::unique_ptr<document::select::Node> _preDocSelect;
public:
- Session(std::unique_ptr<document::select::Node> select, bool isAttrSelect);
+ Session(std::unique_ptr<document::select::Node> docSelect,
+ std::unique_ptr<document::select::Node> preDocOnlySelect,
+ std::unique_ptr<document::select::Node> preDocSelect);
bool contains(const SelectContext &context) const;
bool contains(const document::Document &doc) const;
- const document::select::Node &selectNode() const { return *_select; }
+ const document::select::Node &selectNode() const;
};
using AttributeVectors = std::vector<std::shared_ptr<search::AttributeVector>>;
@@ -47,7 +51,7 @@ private:
AttributeVectors _attributes;
// Pruned selection expression, specific for a document type
- std::unique_ptr<document::select::Node> _select;
+ std::unique_ptr<document::select::Node> _docSelect;
uint32_t _fieldNodes;
uint32_t _attrFieldNodes;
uint32_t _svAttrFieldNodes;
@@ -55,13 +59,24 @@ private:
bool _allTrue;
bool _allInvalid;
- /*
+ /**
* If expression doesn't reference multi value attributes or
* non-attribute fields then this selection expression can be used
- * without retrieving document from document meta store (must use
+ * without retrieving document from document store (must use
* SelectContext class and populate _docId instead).
*/
- std::unique_ptr<document::select::Node> _attrSelect;
+ std::unique_ptr<document::select::Node> _preDocOnlySelect;
+
+ /**
+ * If expression references at least one single value attribute field
+ * then this selection expression can be used to disqualify a
+ * document without retrieving it from document store if it evaluates to false.
+ */
+ std::unique_ptr<document::select::Node> _preDocSelect;
+
+ void setDocumentSelect(SelectPruner &docsPruner);
+ void setPreDocumentSelect(const search::IAttributeManager &amgr,
+ SelectPruner &noDocsPruner);
public:
CachedSelect();
@@ -76,8 +91,9 @@ public:
bool allInvalid() const { return _allInvalid; }
// Should only be used for unit testing
- const std::unique_ptr<document::select::Node> &select() const { return _select; }
- const std::unique_ptr<document::select::Node> &attrSelect() const { return _attrSelect; }
+ const std::unique_ptr<document::select::Node> &docSelect() const { return _docSelect; }
+ const std::unique_ptr<document::select::Node> &preDocOnlySelect() const { return _preDocOnlySelect; }
+ const std::unique_ptr<document::select::Node> &preDocSelect() const { return _preDocSelect; }
void set(const vespalib::string &selection,
const document::DocumentTypeRepo &repo);