From ae20ddec12c463d1bbcc7817a8751c7aac355f38 Mon Sep 17 00:00:00 2001 From: Henning Baldersheim Date: Wed, 6 Jun 2018 10:01:52 +0200 Subject: Revert "Revert "Balder/sameelement in streaming"" --- document/src/tests/documenttestcase.cpp | 16 +- .../vespa/document/fieldvalue/arrayfieldvalue.cpp | 9 +- .../vespa/document/fieldvalue/iteratorhandler.cpp | 11 +- .../vespa/document/fieldvalue/iteratorhandler.h | 6 +- .../vespa/document/fieldvalue/mapfieldvalue.cpp | 10 +- searchlib/src/tests/query/query-old.cpp | 154 ++++++++++---- searchlib/src/vespa/searchlib/query/posocc.h | 35 +-- searchlib/src/vespa/searchlib/query/query.cpp | 235 ++++++++++++--------- searchlib/src/vespa/searchlib/query/query.h | 12 +- searchlib/src/vespa/searchlib/query/querynode.cpp | 89 ++++---- searchlib/src/vespa/searchlib/query/queryterm.cpp | 10 +- searchlib/src/vespa/searchlib/query/queryterm.h | 2 +- vsm/src/vespa/vsm/searcher/fieldsearcher.cpp | 6 +- vsm/src/vespa/vsm/searcher/fieldsearcher.h | 4 +- vsm/src/vespa/vsm/vsm/docsumfilter.cpp | 2 +- vsm/src/vespa/vsm/vsm/docsumfilter.h | 2 +- vsm/src/vespa/vsm/vsm/vsm-adapter.cpp | 12 +- 17 files changed, 388 insertions(+), 227 deletions(-) diff --git a/document/src/tests/documenttestcase.cpp b/document/src/tests/documenttestcase.cpp index 33d1a666d4a..ee49259e982 100644 --- a/document/src/tests/documenttestcase.cpp +++ b/document/src/tests/documenttestcase.cpp @@ -115,16 +115,22 @@ public: ~Handler(); const std::string & getResult() const { return _result; } private: - void onPrimitive(uint32_t, const Content&) override { _result += 'P'; } + void onPrimitive(uint32_t, const Content&) override { + std::ostringstream os; os << "P-" << getArrayIndex(); + _result += os.str(); + } void onCollectionStart(const Content&) override { _result += '['; } void onCollectionEnd(const Content&) override { _result += ']'; } - void onStructStart(const Content&) override { _result += '<'; } + void onStructStart(const Content&) override { + std::ostringstream os; os << "<" << getArrayIndex() << ":"; + _result += os.str(); + } void onStructEnd(const Content&) override { _result += '>'; } std::string _result; }; -Handler::Handler() { } -Handler::~Handler() { } +Handler::Handler() = default; +Handler::~Handler() = default; void DocumentTest::testTraversing() @@ -186,7 +192,7 @@ void DocumentTest::testTraversing() FieldPath empty; doc.iterateNested(empty.getFullRange(), fullTraverser); CPPUNIT_ASSERT_EQUAL(fullTraverser.getResult(), - std::string("]>>>")); + std::string("<0:P-0<0:P-0<0:P-0P-0[P-0P-1P-2][<0:P-0P-0><1:P-1P-1>]>>>")); } class VariableIteratorHandler : public IteratorHandler { diff --git a/document/src/vespa/document/fieldvalue/arrayfieldvalue.cpp b/document/src/vespa/document/fieldvalue/arrayfieldvalue.cpp index 194c9b422da..f3239553fa9 100644 --- a/document/src/vespa/document/fieldvalue/arrayfieldvalue.cpp +++ b/document/src/vespa/document/fieldvalue/arrayfieldvalue.cpp @@ -37,9 +37,7 @@ ArrayFieldValue::ArrayFieldValue(const ArrayFieldValue& other) { } -ArrayFieldValue::~ArrayFieldValue() -{ -} +ArrayFieldValue::~ArrayFieldValue() = default; ArrayFieldValue& ArrayFieldValue::operator=(const ArrayFieldValue& other) @@ -194,6 +192,7 @@ ArrayFieldValue::iterateSubset(int startPos, int endPos, std::vector indicesToRemove; for (int i = startPos; i <= endPos && i < static_cast(_array->size()); ++i) { + handler.setArrayIndex(i); if (!variable.empty()) { handler.getVariables()[variable] = IndexValue(i); } @@ -212,9 +211,7 @@ ArrayFieldValue::iterateSubset(int startPos, int endPos, handler.getVariables().erase(variable); } - for (std::vector::reverse_iterator i = indicesToRemove.rbegin(); - i != indicesToRemove.rend(); ++i) - { + for (auto i = indicesToRemove.rbegin(); i != indicesToRemove.rend(); ++i) { const_cast(*this).remove(*i); } diff --git a/document/src/vespa/document/fieldvalue/iteratorhandler.cpp b/document/src/vespa/document/fieldvalue/iteratorhandler.cpp index 2e9a525d9e6..8764e7908cd 100644 --- a/document/src/vespa/document/fieldvalue/iteratorhandler.cpp +++ b/document/src/vespa/document/fieldvalue/iteratorhandler.cpp @@ -5,7 +5,14 @@ namespace document::fieldvalue { -IteratorHandler::~IteratorHandler() { } +IteratorHandler::IteratorHandler() + : _weight(1), + _arrayIndexStack(1, 0), + _variables() +{ +} + +IteratorHandler::~IteratorHandler() = default; void @@ -18,11 +25,13 @@ IteratorHandler::handleComplex(const FieldValue & fv) { } void IteratorHandler::handleCollectionStart(const FieldValue & fv) { + _arrayIndexStack.push_back(0); onCollectionStart(Content(fv, getWeight())); } void IteratorHandler::handleCollectionEnd(const FieldValue & fv) { onCollectionEnd(Content(fv, getWeight())); + _arrayIndexStack.pop_back(); } void IteratorHandler::handleStructStart(const FieldValue & fv) { diff --git a/document/src/vespa/document/fieldvalue/iteratorhandler.h b/document/src/vespa/document/fieldvalue/iteratorhandler.h index 757cea54ef9..cf71504e7a1 100644 --- a/document/src/vespa/document/fieldvalue/iteratorhandler.h +++ b/document/src/vespa/document/fieldvalue/iteratorhandler.h @@ -4,6 +4,7 @@ #include "variablemap.h" #include "modificationstatus.h" +#include namespace document::fieldvalue { @@ -55,7 +56,7 @@ protected: int _weight; }; - IteratorHandler() : _weight(1) {} + IteratorHandler(); public: virtual ~IteratorHandler(); @@ -72,6 +73,8 @@ public: void handleStructStart(const FieldValue &fv); void handleStructEnd(const FieldValue &fv); void setWeight(int weight) { _weight = weight; } + uint32_t getArrayIndex() const { return _arrayIndexStack.back(); } + void setArrayIndex(uint32_t index) { _arrayIndexStack.back() = index; } ModificationStatus modify(FieldValue &fv) { return doModify(fv); } fieldvalue::VariableMap &getVariables() { return _variables; } void setVariables(const fieldvalue::VariableMap &vars) { _variables = vars; } @@ -93,6 +96,7 @@ private: int getWeight() const { return _weight; } int _weight; + std::vector _arrayIndexStack; fieldvalue::VariableMap _variables; }; diff --git a/document/src/vespa/document/fieldvalue/mapfieldvalue.cpp b/document/src/vespa/document/fieldvalue/mapfieldvalue.cpp index 8e5a641ab6e..ebd51c82794 100644 --- a/document/src/vespa/document/fieldvalue/mapfieldvalue.cpp +++ b/document/src/vespa/document/fieldvalue/mapfieldvalue.cpp @@ -78,9 +78,7 @@ MapFieldValue::MapFieldValue(const DataType &mapType) { } -MapFieldValue::~MapFieldValue() -{ -} +MapFieldValue::~MapFieldValue() = default; MapFieldValue::MapFieldValue(const MapFieldValue & rhs) : FieldValue(rhs), @@ -425,6 +423,7 @@ MapFieldValue::iterateNestedImpl(PathRange nested, bool wasModified = false; const bool isWSet(complexFieldValue.inherits(WeightedSetFieldValue::classId)); + uint32_t index(0); if ( ! nested.atEnd() ) { LOG(spam, "not yet at end of field path"); const FieldPathEntry & fpe = nested.cur(); @@ -451,6 +450,7 @@ MapFieldValue::iterateNestedImpl(PathRange nested, case FieldPathEntry::MAP_ALL_KEYS: LOG(spam, "MAP_ALL_KEYS"); for (const auto & entry : *this) { + handler.setArrayIndex(index++); if (isWSet) { handler.setWeight(static_cast(*entry.second).getValue()); } @@ -462,6 +462,7 @@ MapFieldValue::iterateNestedImpl(PathRange nested, case FieldPathEntry::MAP_ALL_VALUES: LOG(spam, "MAP_ALL_VALUES"); for (const auto & entry : *this) { + handler.setArrayIndex(index++); wasModified = checkAndRemove(*entry.second, entry.second->iterateNested(nested.next(), handler), wasModified, keysToRemove); @@ -482,6 +483,7 @@ MapFieldValue::iterateNestedImpl(PathRange nested, } else { PathRange next = nested.next(); for (const auto & entry : *this) { + handler.setArrayIndex(index++); LOG(spam, "key is '%s'", entry.first->toString().c_str()); handler.getVariables()[fpe.getVariableName()] = IndexValue(*entry.first); LOG(spam, "vars at this time = %s", handler.getVariables().toString().c_str()); @@ -495,6 +497,7 @@ MapFieldValue::iterateNestedImpl(PathRange nested, default: LOG(spam, "default"); for (const auto & entry : *this) { + handler.setArrayIndex(index++); if (isWSet) { handler.setWeight(static_cast(*entry.second).getValue()); } @@ -522,6 +525,7 @@ MapFieldValue::iterateNestedImpl(PathRange nested, if (handler.handleComplex(complexFieldValue)) { LOG(spam, "calling handler.handleComplex for all map keys"); for (const auto & entry : *this) { + handler.setArrayIndex(index++); if (isWSet) { handler.setWeight(static_cast(*entry.second).getValue()); } diff --git a/searchlib/src/tests/query/query-old.cpp b/searchlib/src/tests/query/query-old.cpp index 77fe813dda5..2cab4447935 100644 --- a/searchlib/src/tests/query/query-old.cpp +++ b/searchlib/src/tests/query/query-old.cpp @@ -364,65 +364,63 @@ TEST("testPhraseEvaluate") { } // field 0 - terms[0]->add(0, 0, 1); - terms[1]->add(1, 0, 1); - terms[2]->add(2, 0, 1); - terms[0]->add(7, 0, 1); - terms[1]->add(8, 0, 1); - terms[2]->add(9, 0, 1); + terms[0]->add(0, 0, 0, 1); + terms[1]->add(1, 0, 0, 1); + terms[2]->add(2, 0, 0, 1); + terms[0]->add(7, 0, 0, 1); + terms[1]->add(8, 0, 1, 1); + terms[2]->add(9, 0, 0, 1); // field 1 - terms[0]->add(4, 1, 1); - terms[1]->add(5, 1, 1); - terms[2]->add(6, 1, 1); + terms[0]->add(4, 1, 0, 1); + terms[1]->add(5, 1, 0, 1); + terms[2]->add(6, 1, 0, 1); // field 2 (not complete match) - terms[0]->add(1, 2, 1); - terms[1]->add(2, 2, 1); - terms[2]->add(4, 2, 1); + terms[0]->add(1, 2, 0, 1); + terms[1]->add(2, 2, 0, 1); + terms[2]->add(4, 2, 0, 1); // field 3 - terms[0]->add(0, 3, 1); - terms[1]->add(1, 3, 1); - terms[2]->add(2, 3, 1); + terms[0]->add(0, 3, 0, 1); + terms[1]->add(1, 3, 0, 1); + terms[2]->add(2, 3, 0, 1); // field 4 (not complete match) - terms[0]->add(1, 4, 1); - terms[1]->add(2, 4, 1); + terms[0]->add(1, 4, 0, 1); + terms[1]->add(2, 4, 0, 1); // field 5 (not complete match) - terms[0]->add(2, 5, 1); - terms[1]->add(1, 5, 1); - terms[2]->add(0, 5, 1); + terms[0]->add(2, 5, 0, 1); + terms[1]->add(1, 5, 0, 1); + terms[2]->add(0, 5, 0, 1); HitList hits; PhraseQueryNode * p = static_cast(phrases[0]); p->evaluateHits(hits); - ASSERT_TRUE(hits.size() == 4); + ASSERT_EQUAL(3u, hits.size()); EXPECT_EQUAL(hits[0].wordpos(), 2u); EXPECT_EQUAL(hits[0].context(), 0u); - EXPECT_EQUAL(hits[1].wordpos(), 9u); - EXPECT_EQUAL(hits[1].context(), 0u); - EXPECT_EQUAL(hits[2].wordpos(), 6u); - EXPECT_EQUAL(hits[2].context(), 1u); - EXPECT_EQUAL(hits[3].wordpos(), 2u); - EXPECT_EQUAL(hits[3].context(), 3u); - ASSERT_TRUE(p->getFieldInfoSize() == 4); + EXPECT_EQUAL(hits[1].wordpos(), 6u); + EXPECT_EQUAL(hits[1].context(), 1u); + EXPECT_EQUAL(hits[2].wordpos(), 2u); + EXPECT_EQUAL(hits[2].context(), 3u); + ASSERT_EQUAL(4u, p->getFieldInfoSize()); EXPECT_EQUAL(p->getFieldInfo(0).getHitOffset(), 0u); - EXPECT_EQUAL(p->getFieldInfo(0).getHitCount(), 2u); - EXPECT_EQUAL(p->getFieldInfo(1).getHitOffset(), 2u); + EXPECT_EQUAL(p->getFieldInfo(0).getHitCount(), 1u); + EXPECT_EQUAL(p->getFieldInfo(1).getHitOffset(), 1u); EXPECT_EQUAL(p->getFieldInfo(1).getHitCount(), 1u); EXPECT_EQUAL(p->getFieldInfo(2).getHitOffset(), 0u); // invalid, but will never be used EXPECT_EQUAL(p->getFieldInfo(2).getHitCount(), 0u); - EXPECT_EQUAL(p->getFieldInfo(3).getHitOffset(), 3u); + EXPECT_EQUAL(p->getFieldInfo(3).getHitOffset(), 2u); EXPECT_EQUAL(p->getFieldInfo(3).getHitCount(), 1u); } TEST("testHit") { // positions (0 - (2^24-1)) - assertHit(Hit(0, 0, 0), 0, 0, 0); - assertHit(Hit(256, 0, 1), 256, 0, 1); - assertHit(Hit(16777215, 0, -1), 16777215, 0, -1); - assertHit(Hit(16777216, 0, 1), 0, 1, 1); // overflow + assertHit(Hit(0, 0, 0, 0), 0, 0, 0); + assertHit(Hit(256, 0, 0, 1), 256, 0, 1); + assertHit(Hit(16777215, 0, 0, -1), 16777215, 0, -1); + assertHit(Hit(16777216, 0, 0, 1), 0, 1, 1); // overflow // contexts (0 - 255) - assertHit(Hit(0, 1, 1), 0, 1, 1); - assertHit(Hit(0, 255, 1), 0, 255, 1); - assertHit(Hit(0, 256, 1), 0, 0, 1); // overflow + assertHit(Hit(0, 1, 0, 1), 0, 1, 1); + assertHit(Hit(0, 255, 0, 1), 0, 255, 1); + assertHit(Hit(0, 256, 0, 1), 0, 0, 1); // overflow } void assertInt8Range(const std::string &term, bool expAdjusted, int64_t expLow, int64_t expHigh) { @@ -653,4 +651,84 @@ TEST("require that we do not break the stack on bad query") { EXPECT_FALSE(term.isValid()); } +namespace { + void verifyQueryTermNode(const vespalib::string & index, const QueryNode *node) { + EXPECT_TRUE(dynamic_cast(node) != nullptr); + EXPECT_EQUAL(index, node->getIndex()); + } +} +TEST("testSameElementEvaluate") { + QueryBuilder builder; + builder.addSameElement(3, "field"); + { + builder.addStringTerm("a", "f1", 0, Weight(0)); + builder.addStringTerm("b", "f2", 1, Weight(0)); + builder.addStringTerm("c", "f3", 2, Weight(0)); + } + Node::UP node = builder.build(); + vespalib::string stackDump = StackDumpCreator::create(*node); + QueryNodeResultFactory empty; + Query q(empty, stackDump); + SameElementQueryNode * sameElem = dynamic_cast(&q.getRoot()); + EXPECT_TRUE(sameElem != nullptr); + EXPECT_EQUAL("field", sameElem->getIndex()); + EXPECT_EQUAL(3u, sameElem->size()); + verifyQueryTermNode("field.f1", (*sameElem)[0].get()); + verifyQueryTermNode("field.f2", (*sameElem)[1].get()); + verifyQueryTermNode("field.f3", (*sameElem)[2].get()); + + QueryTermList terms; + q.getLeafs(terms); + EXPECT_EQUAL(3u, terms.size()); + for (QueryTerm * qt : terms) { + qt->resizeFieldId(3); + } + + // field 0 + terms[0]->add(1, 0, 0, 10); + terms[0]->add(2, 0, 1, 20); + terms[0]->add(3, 0, 2, 30); + terms[0]->add(4, 0, 3, 40); + terms[0]->add(5, 0, 4, 50); + terms[0]->add(6, 0, 5, 60); + + terms[1]->add(7, 1, 0, 70); + terms[1]->add(8, 1, 1, 80); + terms[1]->add(9, 1, 2, 90); + terms[1]->add(10, 1, 4, 100); + terms[1]->add(11, 1, 5, 110); + terms[1]->add(12, 1, 6, 120); + + terms[2]->add(13, 2, 0, 130); + terms[2]->add(14, 2, 2, 140); + terms[2]->add(15, 2, 4, 150); + terms[2]->add(16, 2, 5, 160); + terms[2]->add(17, 2, 6, 170); + HitList hits; + + sameElem->evaluateHits(hits); + EXPECT_EQUAL(4u, hits.size()); + EXPECT_EQUAL(0u, hits[0].wordpos()); + EXPECT_EQUAL(2u, hits[0].context()); + EXPECT_EQUAL(0u, hits[0].elemId()); + EXPECT_EQUAL(130, hits[0].weight()); + + EXPECT_EQUAL(0u, hits[1].wordpos()); + EXPECT_EQUAL(2u, hits[1].context()); + EXPECT_EQUAL(2u, hits[1].elemId()); + EXPECT_EQUAL(140, hits[1].weight()); + + EXPECT_EQUAL(0u, hits[2].wordpos()); + EXPECT_EQUAL(2u, hits[2].context()); + EXPECT_EQUAL(4u, hits[2].elemId()); + EXPECT_EQUAL(150, hits[2].weight()); + + EXPECT_EQUAL(0u, hits[3].wordpos()); + EXPECT_EQUAL(2u, hits[3].context()); + EXPECT_EQUAL(5u, hits[3].elemId()); + EXPECT_EQUAL(160, hits[3].weight()); + +} + + TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/searchlib/src/vespa/searchlib/query/posocc.h b/searchlib/src/vespa/searchlib/query/posocc.h index 1b5b6283f88..fcabef0297f 100644 --- a/searchlib/src/vespa/searchlib/query/posocc.h +++ b/searchlib/src/vespa/searchlib/query/posocc.h @@ -4,28 +4,29 @@ #include #include -namespace search -{ +namespace search { class Hit { - public: - Hit(uint32_t pos_, uint32_t context_, int32_t weight_) : - _position(pos_ | (context_<<24)), - _weight(weight_) - { } - int32_t weight() const { return _weight; } - uint32_t pos() const { return _position; } - uint32_t wordpos() const { return _position & 0xffffff; } - uint32_t context() const { return _position >> 24; } - bool operator < (const Hit & b) const { return cmp(b) < 0; } - private: - int cmp(const Hit & b) const { return _position - b._position; } - uint32_t _position; - int32_t _weight; +public: + Hit(uint32_t pos_, uint32_t context_, uint32_t elemId_, int32_t weight_) + : _position(pos_ | (context_<<24)), + _elemId(elemId_), + _weight(weight_) + { } + int32_t weight() const { return _weight; } + uint32_t pos() const { return _position; } + uint32_t wordpos() const { return _position & 0xffffff; } + uint32_t context() const { return _position >> 24; } + uint32_t elemId() const { return _elemId; } + bool operator < (const Hit & b) const { return cmp(b) < 0; } +private: + int cmp(const Hit & b) const { return _position - b._position; } + uint32_t _position; + uint32_t _elemId; + int32_t _weight; }; typedef std::vector HitList; } - diff --git a/searchlib/src/vespa/searchlib/query/query.cpp b/searchlib/src/vespa/searchlib/query/query.cpp index 984337f40ba..659e48a0662 100644 --- a/searchlib/src/vespa/searchlib/query/query.cpp +++ b/searchlib/src/vespa/searchlib/query/query.cpp @@ -16,97 +16,90 @@ QueryConnector::QueryConnector(const char * opName) : { } -QueryConnector::~QueryConnector() { } +QueryConnector::~QueryConnector() = default; const HitList & QueryConnector::evaluateHits(HitList & hl) const { - if (evaluate()) { - hl.push_back(Hit(1, 0, 1)); - } - return hl; + if (evaluate()) { + hl.push_back(Hit(1, 0, 0, 1)); + } + return hl; } void QueryConnector::reset() { - for(iterator it=begin(), mt=end(); it != mt; it++) { - QueryNode & qn = **it; - qn.reset(); - } + for(const auto & node : *this) { + node->reset(); + } } void QueryConnector::getLeafs(QueryTermList & tl) { - for(iterator it=begin(), mt=end(); it != mt; it++) { - QueryNode & qn = **it; - qn.getLeafs(tl); - } + for(const auto & node : *this) { + node->getLeafs(tl); + } } void QueryConnector::getLeafs(ConstQueryTermList & tl) const { - for(const_iterator it=begin(), mt=end(); it != mt; it++) { - const QueryNode & qn = **it; - qn.getLeafs(tl); - } + for(const auto & node : *this) { + node->getLeafs(tl); + } } void QueryConnector::getPhrases(QueryNodeRefList & tl) { - for(iterator it=begin(), mt=end(); it != mt; it++) { - QueryNode & qn = **it; - qn.getPhrases(tl); - } + for(const auto & node : *this) { + node->getPhrases(tl); + } } void QueryConnector::getPhrases(ConstQueryNodeRefList & tl) const { - for(const_iterator it=begin(), mt=end(); it != mt; it++) { - const QueryNode & qn = **it; - qn.getPhrases(tl); - } + for(const auto & node : *this) { + node->getPhrases(tl); + } } size_t QueryConnector::depth() const { - size_t d(0); - for(const_iterator it=begin(), mt=end(); (it!=mt); it++) { - const QueryNode & qn = **it; - size_t t = qn.depth(); - if (t > d) - d = t; - } - return d+1; + size_t d(0); + for(const auto & node : *this) { + size_t t = node->depth(); + if (t > d) { + d = t; + } + } + return d+1; } size_t QueryConnector::width() const { size_t w(0); - for(const_iterator it=begin(), mt=end(); (it!=mt); it++) { - const QueryNode & qn = **it; - w += qn.width(); + for(const auto & node : *this) { + w += node->width(); } return w; } -QueryConnector * +std::unique_ptr QueryConnector::create(ParseItem::ItemType type) { switch (type) { - case search::ParseItem::ITEM_AND: return new AndQueryNode(); - case search::ParseItem::ITEM_OR: return new OrQueryNode(); - case search::ParseItem::ITEM_WEAK_AND: return new OrQueryNode(); - case search::ParseItem::ITEM_EQUIV: return new EquivQueryNode(); - case search::ParseItem::ITEM_WEIGHTED_SET: return new EquivQueryNode(); - case search::ParseItem::ITEM_DOT_PRODUCT: return new OrQueryNode(); - case search::ParseItem::ITEM_WAND: return new OrQueryNode(); - case search::ParseItem::ITEM_NOT: return new AndNotQueryNode(); - case search::ParseItem::ITEM_PHRASE: return new PhraseQueryNode(); - case search::ParseItem::ITEM_SAME_ELEMENT: return new AndQueryNode(); // TODO: This needs a same element operation to work for streaming search too. - case search::ParseItem::ITEM_NEAR: return new NearQueryNode(); - case search::ParseItem::ITEM_ONEAR: return new ONearQueryNode(); - default: - return nullptr; + case search::ParseItem::ITEM_AND: return std::make_unique(); + case search::ParseItem::ITEM_OR: return std::make_unique(); + case search::ParseItem::ITEM_WEAK_AND: return std::make_unique(); + case search::ParseItem::ITEM_EQUIV: return std::make_unique(); + case search::ParseItem::ITEM_WEIGHTED_SET: return std::make_unique(); + case search::ParseItem::ITEM_DOT_PRODUCT: return std::make_unique(); + case search::ParseItem::ITEM_WAND: return std::make_unique(); + case search::ParseItem::ITEM_NOT: return std::make_unique(); + case search::ParseItem::ITEM_PHRASE: return std::make_unique(); + case search::ParseItem::ITEM_SAME_ELEMENT: return std::make_unique(); + case search::ParseItem::ITEM_NEAR: return std::make_unique(); + case search::ParseItem::ITEM_ONEAR: return std::make_unique(); + default: return nullptr; } } @@ -153,61 +146,116 @@ bool EquivQueryNode::evaluate() const return OrQueryNode::evaluate(); } +bool SameElementQueryNode::evaluate() const { + HitList hl; + return evaluateHits(hl).empty(); +} + +const HitList & +SameElementQueryNode::evaluateHits(HitList & hl) const +{ + hl.clear(); + if ( !AndQueryNode::evaluate()) return hl; + + HitList tmpHL; + unsigned int numFields = size(); + unsigned int currMatchCount = 0; + std::vector indexVector(numFields, 0); + auto curr = static_cast ((*this)[currMatchCount].get()); + bool exhausted( curr->evaluateHits(tmpHL).empty()); + for (; !exhausted; ) { + auto next = static_cast((*this)[currMatchCount+1].get()); + unsigned int & currIndex = indexVector[currMatchCount]; + unsigned int & nextIndex = indexVector[currMatchCount+1]; + + const auto & currHit = curr->evaluateHits(tmpHL)[currIndex]; + uint32_t currElemId = currHit.elemId(); + + const HitList & nextHL = next->evaluateHits(tmpHL); + + size_t nextIndexMax = nextHL.size(); + while ((nextIndex < nextIndexMax) && (nextHL[nextIndex].elemId() < currElemId)) { + nextIndex++; + } + if (nextHL[nextIndex].elemId() == currElemId) { + currMatchCount++; + if ((currMatchCount+1) == numFields) { + Hit h = nextHL[indexVector[currMatchCount]]; + hl.emplace_back(0, h.context(), h.elemId(), h.weight()); + currMatchCount = 0; + indexVector[0]++; + } + } else { + currMatchCount = 0; + indexVector[currMatchCount]++; + } + curr = static_cast((*this)[currMatchCount].get()); + exhausted = (nextIndex >= nextIndexMax) || (indexVector[currMatchCount] >= curr->evaluateHits(tmpHL).size()); + } + return hl; +} bool PhraseQueryNode::evaluate() const { - bool ok(false); HitList hl; - ok = ! evaluateHits(hl).empty(); - return ok; + return evaluateHits(hl).empty(); } void PhraseQueryNode::getPhrases(QueryNodeRefList & tl) { tl.push_back(this); } void PhraseQueryNode::getPhrases(ConstQueryNodeRefList & tl) const { tl.push_back(this); } -const HitList & PhraseQueryNode::evaluateHits(HitList & hl) const +const HitList & +PhraseQueryNode::evaluateHits(HitList & hl) const { - hl.clear(); - _fieldInfo.clear(); - bool andResult(AndQueryNode::evaluate()); - if (andResult) { + hl.clear(); + _fieldInfo.clear(); + if ( ! AndQueryNode::evaluate()) return hl; + HitList tmpHL; unsigned int fullPhraseLen = size(); unsigned int currPhraseLen = 0; std::vector indexVector(fullPhraseLen, 0); - const QueryTerm * curr = static_cast (&(*(*this)[currPhraseLen])); + auto curr = static_cast ((*this)[currPhraseLen].get()); bool exhausted( curr->evaluateHits(tmpHL).empty()); for (; !exhausted; ) { - const QueryTerm & next = static_cast(*(*this)[currPhraseLen+1]); - unsigned int & currIndex = indexVector[currPhraseLen]; - unsigned int & nextIndex = indexVector[currPhraseLen+1]; - const HitList & nextHL = next.evaluateHits(tmpHL); - - size_t firstPosition = curr->evaluateHits(tmpHL)[currIndex].pos(); - int diff(0); - size_t nextIndexMax = nextHL.size(); - while ((nextIndex < nextIndexMax) && ((diff = nextHL[nextIndex].pos()-firstPosition) < 1)) - nextIndex++; - if (diff == 1) { - currPhraseLen++; - bool ok = ((currPhraseLen+1)==fullPhraseLen); - if (ok) { - Hit h = nextHL[indexVector[currPhraseLen]]; - hl.push_back(h); - const QueryTerm::FieldInfo & fi = next.getFieldInfo(h.context()); - updateFieldInfo(h.context(), hl.size() - 1, fi.getFieldLength()); - currPhraseLen = 0; - indexVector[0]++; + auto next = static_cast((*this)[currPhraseLen+1].get()); + unsigned int & currIndex = indexVector[currPhraseLen]; + unsigned int & nextIndex = indexVector[currPhraseLen+1]; + + const auto & currHit = curr->evaluateHits(tmpHL)[currIndex]; + size_t firstPosition = currHit.pos(); + uint32_t currElemId = currHit.elemId(); + uint32_t currContext = currHit.context(); + + const HitList & nextHL = next->evaluateHits(tmpHL); + + int diff(0); + size_t nextIndexMax = nextHL.size(); + while ((nextIndex < nextIndexMax) && + ((nextHL[nextIndex].context() < currContext) || + ((nextHL[nextIndex].context() == currContext) && (nextHL[nextIndex].elemId() <= currElemId))) && + ((diff = nextHL[nextIndex].pos()-firstPosition) < 1)) + { + nextIndex++; + } + if ((diff == 1) && (nextHL[nextIndex].context() == currContext) && (nextHL[nextIndex].elemId() == currElemId)) { + currPhraseLen++; + if ((currPhraseLen+1) == fullPhraseLen) { + Hit h = nextHL[indexVector[currPhraseLen]]; + hl.push_back(h); + const QueryTerm::FieldInfo & fi = next->getFieldInfo(h.context()); + updateFieldInfo(h.context(), hl.size() - 1, fi.getFieldLength()); + currPhraseLen = 0; + indexVector[0]++; + } + } else { + currPhraseLen = 0; + indexVector[currPhraseLen]++; } - } else { - currPhraseLen = 0; - indexVector[currPhraseLen]++; - } - curr = static_cast(&*(*this)[currPhraseLen]); - exhausted = (nextIndex >= nextIndexMax) || (indexVector[currPhraseLen] >= curr->evaluateHits(tmpHL).size()); + curr = static_cast((*this)[currPhraseLen].get()); + exhausted = (nextIndex >= nextIndexMax) || (indexVector[currPhraseLen] >= curr->evaluateHits(tmpHL).size()); } - } - return hl; + return hl; } void @@ -227,9 +275,8 @@ PhraseQueryNode::updateFieldInfo(size_t fid, size_t offset, size_t fieldLength) bool NotQueryNode::evaluate() const { bool ok(false); - for (const_iterator it=begin(), mt=end(); it!=mt; it++) { - const QueryNode & qn = **it; - ok |= ! qn.evaluate(); + for (const auto & node : *this) { + ok |= ! node->evaluate(); } return ok; } @@ -253,9 +300,7 @@ bool ONearQueryNode::evaluate() const return ok; } -Query::Query() : - _root() -{ } +Query::Query() = default; Query::Query(const QueryNodeResultFactory & factory, const QueryPacketT & queryRep) : _root() @@ -273,7 +318,7 @@ bool Query::build(const QueryNodeResultFactory & factory, const QueryPacketT & q { search::SimpleQueryStackDumpIterator stack(queryRep); if (stack.next()) { - _root.reset(QueryNode::Build(NULL, factory, stack, true).release()); + _root = QueryNode::Build(nullptr, factory, stack, true); } return valid(); } diff --git a/searchlib/src/vespa/searchlib/query/query.h b/searchlib/src/vespa/searchlib/query/query.h index 9bb1b29aae5..b9bcd76d869 100644 --- a/searchlib/src/vespa/searchlib/query/query.h +++ b/searchlib/src/vespa/searchlib/query/query.h @@ -28,7 +28,7 @@ public: virtual void visitMembers(vespalib::ObjectVisitor &visitor) const; void setIndex(const vespalib::string & index) override { _index = index; } const vespalib::string & getIndex() const override { return _index; } - static QueryConnector * create(ParseItem::ItemType type); + static std::unique_ptr create(ParseItem::ItemType type); virtual bool isFlattenable(ParseItem::ItemType type) const { (void) type; return false; } private: vespalib::string _opName; @@ -123,6 +123,15 @@ private: #endif }; +class SameElementQueryNode : public AndQueryNode +{ +public: + SameElementQueryNode() : AndQueryNode("SAME_ELEMENT") { } + bool evaluate() const override; + const HitList & evaluateHits(HitList & hl) const override; + bool isFlattenable(ParseItem::ItemType type) const override { return type == ParseItem::ITEM_NOT; } +}; + /** Unary Not operator. Just inverts the nodes result. */ @@ -190,6 +199,7 @@ public: size_t width() const; bool valid() const { return _root.get() != NULL; } const QueryNode & getRoot() const { return *_root; } + QueryNode & getRoot() { return *_root; } static QueryNode::UP steal(Query && query) { return std::move(query._root); } private: QueryNode::UP _root; diff --git a/searchlib/src/vespa/searchlib/query/querynode.cpp b/searchlib/src/vespa/searchlib/query/querynode.cpp index 0d0a06de7af..fcc539658d0 100644 --- a/searchlib/src/vespa/searchlib/query/querynode.cpp +++ b/searchlib/src/vespa/searchlib/query/querynode.cpp @@ -9,38 +9,44 @@ namespace search { namespace { vespalib::stringref DEFAULT("default"); + bool isPhraseOrNear(const QueryNode * qn) { + return dynamic_cast (qn) || dynamic_cast (qn); + } } -QueryNode::UP QueryNode::Build(const QueryNode * parent, const QueryNodeResultFactory & factory, search::SimpleQueryStackDumpIterator & queryRep, bool allowRewrite) +QueryNode::UP +QueryNode::Build(const QueryNode * parent, const QueryNodeResultFactory & factory, + SimpleQueryStackDumpIterator & queryRep, bool allowRewrite) { unsigned int arity = queryRep.getArity(); - search::ParseItem::ItemType type = queryRep.getType(); + ParseItem::ItemType type = queryRep.getType(); UP qn; switch (type) { - case search::ParseItem::ITEM_AND: - case search::ParseItem::ITEM_OR: - case search::ParseItem::ITEM_WEAK_AND: - case search::ParseItem::ITEM_EQUIV: - case search::ParseItem::ITEM_WEIGHTED_SET: - case search::ParseItem::ITEM_DOT_PRODUCT: - case search::ParseItem::ITEM_WAND: - case search::ParseItem::ITEM_NOT: - case search::ParseItem::ITEM_PHRASE: - case search::ParseItem::ITEM_SAME_ELEMENT: - case search::ParseItem::ITEM_NEAR: - case search::ParseItem::ITEM_ONEAR: + case ParseItem::ITEM_AND: + case ParseItem::ITEM_OR: + case ParseItem::ITEM_WEAK_AND: + case ParseItem::ITEM_EQUIV: + case ParseItem::ITEM_WEIGHTED_SET: + case ParseItem::ITEM_DOT_PRODUCT: + case ParseItem::ITEM_WAND: + case ParseItem::ITEM_NOT: + case ParseItem::ITEM_PHRASE: + case ParseItem::ITEM_SAME_ELEMENT: + case ParseItem::ITEM_NEAR: + case ParseItem::ITEM_ONEAR: { - qn.reset(QueryConnector::create(type)); - if (qn.get()) { + qn = QueryConnector::create(type); + if (qn) { QueryConnector * qc = dynamic_cast (qn.get()); NearQueryNode * nqn = dynamic_cast (qc); if (nqn) { nqn->distance(queryRep.getArg1()); } - if ((type == search::ParseItem::ITEM_WEAK_AND) || - (type == search::ParseItem::ITEM_WEIGHTED_SET) || - (type == search::ParseItem::ITEM_DOT_PRODUCT) || - (type == search::ParseItem::ITEM_WAND)) + if ((type == ParseItem::ITEM_WEAK_AND) || + (type == ParseItem::ITEM_WEIGHTED_SET) || + (type == ParseItem::ITEM_DOT_PRODUCT) || + (type == ParseItem::ITEM_SAME_ELEMENT) || + (type == ParseItem::ITEM_WAND)) { qn->setIndex(queryRep.getIndexName()); } @@ -49,48 +55,50 @@ QueryNode::UP QueryNode::Build(const QueryNode * parent, const QueryNodeResultFa if (qc->isFlattenable(queryRep.getType())) { arity += queryRep.getArity(); } else { - UP child(Build(qc, factory, queryRep, - allowRewrite && ((dynamic_cast (qn.get()) == NULL) && (dynamic_cast (qn.get()) == NULL)))); + UP child = Build(qc, factory, queryRep, allowRewrite && !isPhraseOrNear(qn.get())); qc->push_back(std::move(child)); } } } } break; - case search::ParseItem::ITEM_NUMTERM: - case search::ParseItem::ITEM_TERM: - case search::ParseItem::ITEM_PREFIXTERM: - case search::ParseItem::ITEM_REGEXP: - case search::ParseItem::ITEM_SUBSTRINGTERM: - case search::ParseItem::ITEM_EXACTSTRINGTERM: - case search::ParseItem::ITEM_SUFFIXTERM: - case search::ParseItem::ITEM_PURE_WEIGHTED_STRING: - case search::ParseItem::ITEM_PURE_WEIGHTED_LONG: + case ParseItem::ITEM_NUMTERM: + case ParseItem::ITEM_TERM: + case ParseItem::ITEM_PREFIXTERM: + case ParseItem::ITEM_REGEXP: + case ParseItem::ITEM_SUBSTRINGTERM: + case ParseItem::ITEM_EXACTSTRINGTERM: + case ParseItem::ITEM_SUFFIXTERM: + case ParseItem::ITEM_PURE_WEIGHTED_STRING: + case ParseItem::ITEM_PURE_WEIGHTED_LONG: { - vespalib::stringref index = queryRep.getIndexName(); + vespalib::string index = queryRep.getIndexName(); if (index.empty()) { - if ((type == search::ParseItem::ITEM_PURE_WEIGHTED_STRING) || (type == search::ParseItem::ITEM_PURE_WEIGHTED_LONG)) { + if ((type == ParseItem::ITEM_PURE_WEIGHTED_STRING) || (type == ParseItem::ITEM_PURE_WEIGHTED_LONG)) { index = parent->getIndex(); } else { index = DEFAULT; } } + if (dynamic_cast(parent) != nullptr) { + index = parent->getIndex() + "." + index; + } vespalib::stringref term = queryRep.getTerm(); QueryTerm::SearchTerm sTerm(QueryTerm::WORD); switch (type) { - case search::ParseItem::ITEM_REGEXP: + case ParseItem::ITEM_REGEXP: sTerm = QueryTerm::REGEXP; break; - case search::ParseItem::ITEM_PREFIXTERM: + case ParseItem::ITEM_PREFIXTERM: sTerm = QueryTerm::PREFIXTERM; break; - case search::ParseItem::ITEM_SUBSTRINGTERM: + case ParseItem::ITEM_SUBSTRINGTERM: sTerm = QueryTerm::SUBSTRINGTERM; break; - case search::ParseItem::ITEM_EXACTSTRINGTERM: + case ParseItem::ITEM_EXACTSTRINGTERM: sTerm = QueryTerm::EXACTSTRINGTERM; break; - case search::ParseItem::ITEM_SUFFIXTERM: + case ParseItem::ITEM_SUFFIXTERM: sTerm = QueryTerm::SUFFIXTERM; break; default: @@ -107,10 +115,9 @@ QueryNode::UP QueryNode::Build(const QueryNode * parent, const QueryNodeResultFa qt->setWeight(queryRep.GetWeight()); qt->setUniqueId(queryRep.getUniqueId()); if ( qt->encoding().isBase10Integer() || ! qt->encoding().isFloat() || ! factory.getRewriteFloatTerms() || !allowRewrite || (ssTerm.find('.') == vespalib::string::npos)) { - qn.reset(qt.release()); + qn = std::move(qt); } else { std::unique_ptr phrase(new PhraseQueryNode()); - phrase->push_back(UP(new QueryTerm(factory.create(), ssTerm.substr(0, ssTerm.find('.')), ssIndex, QueryTerm::WORD))); phrase->push_back(UP(new QueryTerm(factory.create(), ssTerm.substr(ssTerm.find('.') + 1), ssIndex, QueryTerm::WORD))); std::unique_ptr orqn(new EquivQueryNode()); @@ -121,7 +128,7 @@ QueryNode::UP QueryNode::Build(const QueryNode * parent, const QueryNodeResultFa } } break; - case search::ParseItem::ITEM_RANK: + case ParseItem::ITEM_RANK: { if (arity >= 1) { queryRep.next(); diff --git a/searchlib/src/vespa/searchlib/query/queryterm.cpp b/searchlib/src/vespa/searchlib/query/queryterm.cpp index d47d942dc10..ee2e72b41a8 100644 --- a/searchlib/src/vespa/searchlib/query/queryterm.cpp +++ b/searchlib/src/vespa/searchlib/query/queryterm.cpp @@ -73,7 +73,7 @@ QueryTermBase::QueryTermBase() : _termUCS4.push_back(0); } -QueryTermBase::~QueryTermBase() { } +QueryTermBase::~QueryTermBase() = default; QueryTermBase::QueryTermBase(const string & termS, SearchTerm type) : QueryTermSimple(termS, type), @@ -104,7 +104,7 @@ QueryTerm & QueryTerm::operator = (const QueryTerm &) = default; QueryTerm::QueryTerm(QueryTerm &&) = default; QueryTerm & QueryTerm::operator = (QueryTerm &&) = default; -QueryTerm::~QueryTerm() { } +QueryTerm::~QueryTerm() = default; void QueryTermSimple::visitMembers(vespalib::ObjectVisitor & visitor) const @@ -301,9 +301,9 @@ void QueryTerm::resizeFieldId(size_t fieldNo) } } -void QueryTerm::add(unsigned pos, unsigned context, int32_t weight_) +void QueryTerm::add(unsigned pos, unsigned context, uint32_t elemId, int32_t weight_) { - _hitList.emplace_back(pos, context, weight_); + _hitList.emplace_back(pos, context, elemId, weight_); } template @@ -344,7 +344,7 @@ QueryTermSimple::QueryTermSimple() : _diversityAttribute() { } -QueryTermSimple::~QueryTermSimple() { } +QueryTermSimple::~QueryTermSimple() = default; namespace { diff --git a/searchlib/src/vespa/searchlib/query/queryterm.h b/searchlib/src/vespa/searchlib/query/queryterm.h index 423b8d28efb..457a2feeeed 100644 --- a/searchlib/src/vespa/searchlib/query/queryterm.h +++ b/searchlib/src/vespa/searchlib/query/queryterm.h @@ -173,7 +173,7 @@ public: /// Gives you all phrases of this tree. Indicating that they are all const. void getPhrases(ConstQueryNodeRefList & tl) const override; - void add(unsigned pos, unsigned context, int32_t weight); + void add(unsigned pos, unsigned context, uint32_t elemId, int32_t weight); EncodingBitMap encoding() const { return _encoding; } size_t termLen() const { return getTermLen(); } const string & index() const { return _index; } diff --git a/vsm/src/vespa/vsm/searcher/fieldsearcher.cpp b/vsm/src/vespa/vsm/searcher/fieldsearcher.cpp index 31f53fee300..78531f41cf8 100644 --- a/vsm/src/vespa/vsm/searcher/fieldsearcher.cpp +++ b/vsm/src/vespa/vsm/searcher/fieldsearcher.cpp @@ -73,6 +73,7 @@ FieldSearcher::FieldSearcher(const FieldIdT & fId, bool defaultPrefix) : _field(fId), _matchType(defaultPrefix ? PREFIX : REGULAR), _maxFieldLength(0x100000), + _currentElementId(0), _currentElementWeight(1), _pureUsAsciiCount(0), _pureUsAsciiFieldCount(0), @@ -85,9 +86,7 @@ FieldSearcher::FieldSearcher(const FieldIdT & fId, bool defaultPrefix) : zeroStat(); } -FieldSearcher::~FieldSearcher() -{ -} +FieldSearcher::~FieldSearcher() = default; bool FieldSearcher::search(const StorageDocument & doc) { @@ -275,6 +274,7 @@ FieldSearcher::IteratorHandler::onPrimitive(uint32_t, const Content & c) { LOG(spam, "onPrimitive: field value '%s'", c.getValue().toString().c_str()); _searcher.setCurrentWeight(c.getWeight()); + _searcher.setCurrentElementId(getArrayIndex()); _searcher.onValue(c.getValue()); } diff --git a/vsm/src/vespa/vsm/searcher/fieldsearcher.h b/vsm/src/vespa/vsm/searcher/fieldsearcher.h index 236382be5f9..2bf976de017 100644 --- a/vsm/src/vespa/vsm/searcher/fieldsearcher.h +++ b/vsm/src/vespa/vsm/searcher/fieldsearcher.h @@ -95,11 +95,13 @@ private: void prepareFieldId(); void setCurrentWeight(int32_t weight) { _currentElementWeight = weight; } + void setCurrentElementId(int32_t weight) { _currentElementId = weight; } bool onSearch(const StorageDocument & doc); virtual void onValue(const document::FieldValue & fv) = 0; FieldIdT _field; MatchType _matchType; unsigned _maxFieldLength; + uint32_t _currentElementId; int32_t _currentElementWeight; // Contains the weight of the current item being evaluated. /// Number of bytes in blocks containing pure us-ascii unsigned _pureUsAsciiCount; @@ -124,7 +126,7 @@ protected: * For each call to onValue() a batch of words are processed, and the position is local to this batch. **/ void addHit(search::QueryTerm & qt, uint32_t pos) const { - qt.add(_words + pos, field(), getCurrentWeight()); + qt.add(_words + pos, field(), _currentElementId, getCurrentWeight()); } public: static search::byte _foldLowCase[256]; diff --git a/vsm/src/vespa/vsm/vsm/docsumfilter.cpp b/vsm/src/vespa/vsm/vsm/docsumfilter.cpp index 21fa1d9ed02..e6af5fb6477 100644 --- a/vsm/src/vespa/vsm/vsm/docsumfilter.cpp +++ b/vsm/src/vespa/vsm/vsm/docsumfilter.cpp @@ -216,7 +216,7 @@ DocsumFilter::DocsumFilter(const DocsumToolsPtr &tools, const IDocSumCache & doc _emptyFieldPath() { } -DocsumFilter::~DocsumFilter() { } +DocsumFilter::~DocsumFilter() =default; void DocsumFilter::init(const FieldMap & fieldMap, const FieldPathMapT & fieldPathMap) { diff --git a/vsm/src/vespa/vsm/vsm/docsumfilter.h b/vsm/src/vespa/vsm/vsm/docsumfilter.h index a3f8a8539f1..2d1ab6984e4 100644 --- a/vsm/src/vespa/vsm/vsm/docsumfilter.h +++ b/vsm/src/vespa/vsm/vsm/docsumfilter.h @@ -54,7 +54,7 @@ private: public: DocsumFilter(const DocsumToolsPtr & tools, const IDocSumCache & docsumCache); - virtual ~DocsumFilter(); + ~DocsumFilter() override; const DocsumToolsPtr & getTools() const { return _tools; } /** diff --git a/vsm/src/vespa/vsm/vsm/vsm-adapter.cpp b/vsm/src/vespa/vsm/vsm/vsm-adapter.cpp index d5078102e5d..1cd35e7ca61 100644 --- a/vsm/src/vespa/vsm/vsm/vsm-adapter.cpp +++ b/vsm/src/vespa/vsm/vsm/vsm-adapter.cpp @@ -46,7 +46,7 @@ void GetDocsumsStateCallback::FillDocumentLocations(GetDocsumsState *state, IDoc } -GetDocsumsStateCallback::~GetDocsumsStateCallback() { } +GetDocsumsStateCallback::~GetDocsumsStateCallback() = default; DocsumTools::FieldSpec::FieldSpec() : _outputName(), @@ -54,7 +54,7 @@ DocsumTools::FieldSpec::FieldSpec() : _command(VsmsummaryConfig::Fieldmap::NONE) { } -DocsumTools::FieldSpec::~FieldSpec() {} +DocsumTools::FieldSpec::~FieldSpec() = default; DocsumTools::DocsumTools(std::unique_ptr writer) : _writer(std::move(writer)), @@ -64,7 +64,7 @@ DocsumTools::DocsumTools(std::unique_ptr writer) : { } -DocsumTools::~DocsumTools() { } +DocsumTools::~DocsumTools() = default; bool DocsumTools::obtainFieldNames(const FastS_VsmsummaryHandle &cfg) @@ -174,8 +174,6 @@ VSMAdapter::VSMAdapter(const vespalib::string & highlightindexes, const vespalib } -VSMAdapter::~VSMAdapter() -{ -} +VSMAdapter::~VSMAdapter() = default; -} // namespace vsm +} -- cgit v1.2.3