1
0
Fork 0

bug-fix/internal-issue-#574 (#8991)

* restrict number of buckets to compare

* do not use more sort buckets than requested

* cleanup
This commit is contained in:
Andrey Abramov 2019-06-04 18:55:25 +03:00 committed by GitHub
parent 704436de91
commit 0fb8b348db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 340 additions and 183 deletions

View File

@ -155,7 +155,7 @@ IResearchViewExecutorInfos::IResearchViewExecutorInfos(
RegisterId numScoreRegisters,
Query& query,
std::vector<Scorer> const& scorers,
IResearchViewSort const* sort,
std::pair<iresearch::IResearchViewSort const*, size_t> const& sort,
ExecutionPlan const& plan,
Variable const& outVariable,
aql::AstNode const& filterCondition,
@ -688,9 +688,11 @@ size_t IResearchViewExecutor<ordered>::skip(size_t limit) {
template <bool ordered>
IResearchViewMergeExecutor<ordered>::IResearchViewMergeExecutor(Fetcher& fetcher, Infos& infos)
: Base{fetcher, infos},
_heap_it{ MinHeapContext{ *infos.sort(), _segments } } {
TRI_ASSERT(infos.sort());
TRI_ASSERT(!infos.sort()->empty());
_heap_it{ MinHeapContext{ *infos.sort().first, infos.sort().second, _segments } } {
TRI_ASSERT(infos.sort().first);
TRI_ASSERT(!infos.sort().first->empty());
TRI_ASSERT(infos.sort().first->size() >= infos.sort().second);
TRI_ASSERT(infos.sort().second);
TRI_ASSERT(ordered == (infos.getNumScoreRegisters() != 0));
}

View File

@ -63,7 +63,7 @@ class IResearchViewExecutorInfos : public ExecutorInfos {
RegisterId numScoreRegisters,
Query& query,
std::vector<iresearch::Scorer> const& scorers,
iresearch::IResearchViewSort const* sort,
std::pair<iresearch::IResearchViewSort const*, size_t> const& sort,
ExecutionPlan const& plan,
Variable const& outVariable,
aql::AstNode const& filterCondition,
@ -83,7 +83,10 @@ class IResearchViewExecutorInfos : public ExecutorInfos {
int getDepth() const noexcept { return _depth; }
bool volatileSort() const noexcept { return _volatileSort; }
bool volatileFilter() const noexcept { return _volatileFilter; }
iresearch::IResearchViewSort const* sort() const noexcept { return _sort; }
// first - sort
// second - number of sort conditions to take into account
std::pair<iresearch::IResearchViewSort const*, size_t> const& sort() const noexcept { return _sort; }
bool isScoreReg(RegisterId reg) const noexcept {
return getOutputRegister() < reg && reg <= getOutputRegister() + getNumScoreRegisters();
@ -95,7 +98,7 @@ class IResearchViewExecutorInfos : public ExecutorInfos {
std::shared_ptr<iresearch::IResearchView::Snapshot const> const _reader;
Query& _query;
std::vector<iresearch::Scorer> const& _scorers;
iresearch::IResearchViewSort const* _sort{};
std::pair<iresearch::IResearchViewSort const*, size_t> _sort;
ExecutionPlan const& _plan;
Variable const& _outVariable;
aql::AstNode const& _filterCondition;
@ -449,8 +452,9 @@ class IResearchViewMergeExecutor : public IResearchViewExecutorBase<IResearchVie
public:
MinHeapContext(
iresearch::IResearchViewSort const& sort,
size_t sortBuckets,
std::vector<Segment>& segments) noexcept
: _less(sort),
: _less(sort, sortBuckets),
_segments(&segments) {
}

View File

@ -787,7 +787,31 @@ IResearchViewNode::IResearchViewNode(aql::ExecutionPlan& plan, velocypack::Slice
}
if (!primarySort.empty()) {
_sort = &primarySort; // set sort from corresponding view
size_t primarySortBuckets = primarySort.size();
auto const primarySortBucketsSlice = base.get("primarySortBuckets");
if (!primarySortBucketsSlice.isNone()) {
if (!primarySortBucketsSlice.isNumber()) {
THROW_ARANGO_EXCEPTION_MESSAGE(
TRI_ERROR_BAD_PARAMETER,
"invalid vpack format: 'primarySortBuckets' attribute is intended to be a number");
}
primarySortBuckets = primarySortBucketsSlice.getNumber<size_t>();
if (primarySortBuckets > primarySort.size()) {
THROW_ARANGO_EXCEPTION_MESSAGE(
TRI_ERROR_BAD_PARAMETER,
"invalid vpack format: value of 'primarySortBuckets' attribute '" + std::to_string(primarySortBuckets) +
"' is greater than number of buckets specified in 'primarySort' attribute '" + std::to_string(primarySort.size()) +
"' of the view '" + _view->name() + "'");
}
}
// set sort from corresponding view
_sort.first = &primarySort;
_sort.second = primarySortBuckets;
}
}
}
@ -872,11 +896,15 @@ void IResearchViewNode::toVelocyPackHelper(VPackBuilder& nodes, unsigned flags)
nodes.add("volatility", VPackValue(_volatilityMask));
// primarySort
if (_sort && !_sort->empty()) {
VPackArrayBuilder arrayScope(&nodes, "primarySort");
_sort->toVelocyPack(nodes);
if (_sort.first && !_sort.first->empty()) {
{
VPackArrayBuilder arrayScope(&nodes, "primarySort");
_sort.first->toVelocyPack(nodes);
}
nodes.add("primarySortBuckets", VPackValue(_sort.second));
}
nodes.close();
}
@ -1112,8 +1140,8 @@ std::unique_ptr<aql::ExecutionBlock> IResearchViewNode::createBlock(
getRegisterPlan()->varInfo,
getDepth()};
if (_sort) {
TRI_ASSERT(!_sort->empty()); // guaranteed by optimizer rule
if (_sort.first) {
TRI_ASSERT(!_sort.first->empty()); // guaranteed by optimizer rule
if (!ordered) {
return std::make_unique<aql::ExecutionBlockImpl<aql::IResearchViewMergeExecutor<false>>>(

View File

@ -132,13 +132,14 @@ class IResearchViewNode final : public arangodb::aql::ExecutionNode {
}
/// @return sort condition satisfied by a sorted index
IResearchViewSort const* sort() const noexcept {
std::pair<IResearchViewSort const*, size_t> const& sort() const noexcept {
return _sort;
}
/// @brief set sort condition satisfied by a sorted index
void sort(IResearchViewSort const* sort) noexcept {
_sort = sort;
void sort(IResearchViewSort const* sort, size_t size) noexcept {
_sort.first = sort;
_sort.second = sort ? std::min(size, sort->size()) : 0;
}
/// @brief getVariablesUsedHere, modifying the set in-place
@ -182,7 +183,9 @@ class IResearchViewNode final : public arangodb::aql::ExecutionNode {
aql::AstNode const* _filterCondition;
/// @brief sort condition covered by the view
IResearchViewSort const* _sort{};
/// first - sort condition
/// second - number of sort buckets to use
std::pair<IResearchViewSort const*, size_t> _sort{};
/// @brief scorers related to the view
std::vector<Scorer> _scorers;

View File

@ -248,7 +248,7 @@ bool optimizeSort(IResearchViewNode& viewNode, ExecutionPlan* plan) {
}
assert(!primarySort.empty());
viewNode.sort(&primarySort);
viewNode.sort(&primarySort, sortElements.size());
sortNode->_reinsertInCluster = false;
if (!arangodb::ServerState::instance()->isCoordinator()) {

View File

@ -147,15 +147,9 @@ arangodb::aql::AqlValue aqlFnTokens(arangodb::aql::ExpressionContext* expression
auto data = arangodb::iresearch::getStringRef(args[0].slice());
auto name = arangodb::iresearch::getStringRef(args[1].slice());
auto* analyzers =
arangodb::application_features::ApplicationServer::lookupFeature<arangodb::iresearch::IResearchAnalyzerFeature>();
arangodb::application_features::ApplicationServer::getFeature<arangodb::iresearch::IResearchAnalyzerFeature>();
if (!analyzers) {
irs::string_ref const message = "failure to find feature 'arangosearch' while "
"computing result for function 'TOKENS'";
LOG_TOPIC("fbd91", WARN, arangodb::iresearch::TOPIC) << message;
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, message);
}
TRI_ASSERT(analyzers);
arangodb::iresearch::IResearchAnalyzerFeature::AnalyzerPool::ptr pool;

View File

@ -566,15 +566,9 @@ void registerUpgradeTasks() {
void registerViewFactory() {
auto& viewType = arangodb::iresearch::DATA_SOURCE_TYPE;
auto* viewTypes =
arangodb::application_features::ApplicationServer::lookupFeature<arangodb::ViewTypesFeature>();
arangodb::application_features::ApplicationServer::getFeature<arangodb::ViewTypesFeature>();
if (!viewTypes) {
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL,
std::string("failed to find feature '") +
arangodb::ViewTypesFeature::name() +
"' while registering view type '" +
viewType.name() + "'");
}
TRI_ASSERT(viewTypes);
arangodb::Result res;

View File

@ -39,18 +39,19 @@ namespace arangodb {
namespace iresearch {
VPackComparer::VPackComparer()
: _sort(&IResearchViewMeta::DEFAULT()._primarySort) {
: VPackComparer(IResearchViewMeta::DEFAULT()._primarySort) {
}
bool VPackComparer::less(const irs::bytes_ref& lhs, const irs::bytes_ref& rhs) const {
TRI_ASSERT(_sort);
TRI_ASSERT(_sort->size() >= _size);
TRI_ASSERT(!lhs.empty());
TRI_ASSERT(!rhs.empty());
VPackSlice lhsSlice(lhs.c_str());
VPackSlice rhsSlice(rhs.c_str());
for (size_t i = 0, size = _sort->size(); i < size; ++i) {
for (size_t i = 0; i < _size; ++i) {
TRI_ASSERT(!lhsSlice.isNone());
TRI_ASSERT(!rhsSlice.isNone());

View File

@ -36,15 +36,20 @@ class VPackComparer final : public irs::comparer {
VPackComparer();
explicit VPackComparer(IResearchViewSort const& sort) noexcept
: _sort(&sort) {
: _sort(&sort), _size(sort.size()) {
}
VPackComparer(IResearchViewSort const& sort, size_t size) noexcept
: _sort(&sort), _size(std::min(sort.size(), size)) {
}
void reset(IResearchViewSort const& sort) noexcept {
_sort = &sort;
_size = sort.size();
}
bool empty() const noexcept {
return _sort->empty();
return 0 == _size;
}
protected:
@ -53,6 +58,7 @@ class VPackComparer final : public irs::comparer {
private:
IResearchViewSort const* _sort;
size_t _size; // number of buckets to compare
}; // VPackComparer
} // iresearch

View File

@ -291,16 +291,11 @@ void JS_Create(v8::FunctionCallbackInfo<v8::Value> const& args) {
PREVENT_EMBEDDED_TRANSACTION();
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature< // find feature
arangodb::iresearch::IResearchAnalyzerFeature // feature type
auto* analyzers = arangodb::application_features::ApplicationServer::getFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
if (!analyzers) {
TRI_V8_THROW_EXCEPTION_MESSAGE(
TRI_ERROR_INTERNAL,
"failure to find feature '" + arangodb::iresearch::IResearchAnalyzerFeature::name() + "'"
);
}
TRI_ASSERT(analyzers);
auto* sysDatabase = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::SystemDatabaseFeature
@ -444,16 +439,11 @@ void JS_Get(v8::FunctionCallbackInfo<v8::Value> const& args) {
PREVENT_EMBEDDED_TRANSACTION();
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature< // find feature
arangodb::iresearch::IResearchAnalyzerFeature // feature type
auto* analyzers = arangodb::application_features::ApplicationServer::getFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
if (!analyzers) {
TRI_V8_THROW_EXCEPTION_MESSAGE( // exception
TRI_ERROR_INTERNAL, // code
std::string("failure to find feature '") + arangodb::iresearch::IResearchAnalyzerFeature::name() + "'" // message
);
}
TRI_ASSERT(analyzers);
auto* sysDatabase = arangodb::application_features::ApplicationServer::lookupFeature< // find feature
arangodb::SystemDatabaseFeature // featue type
@ -515,16 +505,11 @@ void JS_List(v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND);
}
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature< // find feature
arangodb::iresearch::IResearchAnalyzerFeature // feature type
auto* analyzers = arangodb::application_features::ApplicationServer::getFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
if (!analyzers) {
TRI_V8_THROW_EXCEPTION_MESSAGE( // exception
TRI_ERROR_INTERNAL, // code
std::string("failure to find feature '") + arangodb::iresearch::IResearchAnalyzerFeature::name() + "'" // message
);
}
TRI_ASSERT(analyzers);
auto* sysDatabase = arangodb::application_features::ApplicationServer::lookupFeature< // find feature
arangodb::SystemDatabaseFeature // featue type
@ -611,16 +596,11 @@ void JS_Remove(v8::FunctionCallbackInfo<v8::Value> const& args) {
PREVENT_EMBEDDED_TRANSACTION();
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature< // find feature
arangodb::iresearch::IResearchAnalyzerFeature // feature type
auto* analyzers = arangodb::application_features::ApplicationServer::getFeature<
arangodb::iresearch::IResearchAnalyzerFeature
>();
if (!analyzers) {
TRI_V8_THROW_EXCEPTION_MESSAGE( // exception
TRI_ERROR_INTERNAL, // code
std::string("failure to find feature '") + arangodb::iresearch::IResearchAnalyzerFeature::name() + "'" // message
);
}
TRI_ASSERT(analyzers);
auto* sysDatabase = arangodb::application_features::ApplicationServer::lookupFeature< // find feature
arangodb::SystemDatabaseFeature // featue type

View File

@ -174,6 +174,151 @@ class IResearchViewNodeTest : public ::testing::Test {
} // namespace
TEST_F(IResearchViewNodeTest, constructSortedView) {
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1,
"testVocbase");
// create view
auto createJson = arangodb::velocypack::Parser::fromJson(
"{ "
" \"name\": \"testView\", "
" \"type\": \"arangosearch\", "
" \"primarySort\": [ "
" { \"field\": \"my.nested.Fields\", \"asc\": false }, "
" { \"field\": \"another.field\", \"asc\": true } ] "
"}");
auto logicalView = vocbase.createView(createJson->slice());
ASSERT_TRUE(logicalView);
// dummy query
arangodb::aql::Query query(false, vocbase, arangodb::aql::QueryString("RETURN 1"),
nullptr, arangodb::velocypack::Parser::fromJson("{}"),
arangodb::aql::PART_MAIN);
query.prepare(arangodb::QueryRegistryFeature::registry());
arangodb::aql::Variable const outVariable("variable", 0);
{
auto json = arangodb::velocypack::Parser::fromJson(
"{ \"id\":42, \"depth\":0, \"totalNrRegs\":0, \"varInfoList\":[], "
"\"nrRegs\":[], \"nrRegsHere\":[], \"regsToClear\":[], "
"\"varsUsedLater\":[], \"varsValid\":[], \"outVariable\": { "
"\"name\":\"variable\", \"id\":0 }, \"options\": { \"waitForSync\" : "
"true, \"collections\":[42] }, \"viewId\": \"" + std::to_string(logicalView->id()) + "\", "
"\"primarySort\": [ { \"field\": \"my.nested.Fields\", \"asc\": false}, { \"field\": \"another.field\", \"asc\":true } ] }");
arangodb::iresearch::IResearchViewNode node(*query.plan(), // plan
json->slice());
EXPECT_TRUE(node.empty()); // view has no links
EXPECT_TRUE(node.collections().empty()); // view has no links
EXPECT_TRUE(node.shards().empty());
EXPECT_TRUE(node.sort().first); // primary sort is set
EXPECT_EQ(2, node.sort().second);
EXPECT_TRUE(arangodb::aql::ExecutionNode::ENUMERATE_IRESEARCH_VIEW == node.getType());
EXPECT_TRUE(outVariable.id == node.outVariable().id);
EXPECT_TRUE(outVariable.name == node.outVariable().name);
EXPECT_TRUE(query.plan() == node.plan());
EXPECT_TRUE(42 == node.id());
EXPECT_TRUE(logicalView == node.view());
EXPECT_TRUE(node.scorers().empty());
EXPECT_TRUE(!node.volatility().first); // filter volatility
EXPECT_TRUE(!node.volatility().second); // sort volatility
arangodb::HashSet<arangodb::aql::Variable const*> usedHere;
node.getVariablesUsedHere(usedHere);
EXPECT_TRUE(usedHere.empty());
auto const setHere = node.getVariablesSetHere();
EXPECT_TRUE(1 == setHere.size());
EXPECT_TRUE(outVariable.id == setHere[0]->id);
EXPECT_TRUE(outVariable.name == setHere[0]->name);
EXPECT_TRUE(true == node.options().forceSync);
EXPECT_TRUE(true == node.options().restrictSources);
EXPECT_TRUE(1 == node.options().sources.size());
EXPECT_TRUE(42 == *node.options().sources.begin());
EXPECT_TRUE(0. == node.getCost().estimatedCost); // no dependencies
EXPECT_TRUE(0 == node.getCost().estimatedNrItems); // no dependencies
}
{
auto json = arangodb::velocypack::Parser::fromJson(
"{ \"id\":42, \"depth\":0, \"totalNrRegs\":0, \"varInfoList\":[], "
"\"nrRegs\":[], \"nrRegsHere\":[], \"regsToClear\":[], "
"\"varsUsedLater\":[], \"varsValid\":[], \"outVariable\": { "
"\"name\":\"variable\", \"id\":0 }, \"options\": { \"waitForSync\" : "
"true, \"collections\":[42] }, \"viewId\": \"" + std::to_string(logicalView->id()) + "\", "
"\"primarySort\": [ { \"field\": \"my.nested.Fields\", \"asc\": false}, { \"field\": \"another.field\", \"asc\":true } ], \"primarySortBuckets\": 1 }");
arangodb::iresearch::IResearchViewNode node(*query.plan(), // plan
json->slice());
EXPECT_TRUE(node.empty()); // view has no links
EXPECT_TRUE(node.collections().empty()); // view has no links
EXPECT_TRUE(node.shards().empty());
EXPECT_TRUE(node.sort().first); // primary sort is set
EXPECT_EQ(1, node.sort().second);
EXPECT_TRUE(arangodb::aql::ExecutionNode::ENUMERATE_IRESEARCH_VIEW == node.getType());
EXPECT_TRUE(outVariable.id == node.outVariable().id);
EXPECT_TRUE(outVariable.name == node.outVariable().name);
EXPECT_TRUE(query.plan() == node.plan());
EXPECT_TRUE(42 == node.id());
EXPECT_TRUE(logicalView == node.view());
EXPECT_TRUE(node.scorers().empty());
EXPECT_TRUE(!node.volatility().first); // filter volatility
EXPECT_TRUE(!node.volatility().second); // sort volatility
arangodb::HashSet<arangodb::aql::Variable const*> usedHere;
node.getVariablesUsedHere(usedHere);
EXPECT_TRUE(usedHere.empty());
auto const setHere = node.getVariablesSetHere();
EXPECT_TRUE(1 == setHere.size());
EXPECT_TRUE(outVariable.id == setHere[0]->id);
EXPECT_TRUE(outVariable.name == setHere[0]->name);
EXPECT_TRUE(true == node.options().forceSync);
EXPECT_TRUE(true == node.options().restrictSources);
EXPECT_TRUE(1 == node.options().sources.size());
EXPECT_TRUE(42 == *node.options().sources.begin());
EXPECT_TRUE(0. == node.getCost().estimatedCost); // no dependencies
EXPECT_TRUE(0 == node.getCost().estimatedNrItems); // no dependencies
}
// invalid 'primarySortBuckets' specified
{
auto json = arangodb::velocypack::Parser::fromJson(
"{ \"id\":42, \"depth\":0, \"totalNrRegs\":0, \"varInfoList\":[], "
"\"nrRegs\":[], \"nrRegsHere\":[], \"regsToClear\":[], "
"\"varsUsedLater\":[], \"varsValid\":[], \"outVariable\": { "
"\"name\":\"variable\", \"id\":0 }, \"viewId\": \"" + std::to_string(logicalView->id()) + "\", "
"\"primarySort\": [ { \"field\": \"my.nested.Fields\", \"asc\": false}, { \"field\": \"another.field\", \"asc\":true } ], \"primarySortBuckets\": false }");
try {
arangodb::iresearch::IResearchViewNode node(*query.plan(), // plan
json->slice());
EXPECT_TRUE(false);
} catch (arangodb::basics::Exception const& ex) {
EXPECT_TRUE(TRI_ERROR_BAD_PARAMETER == ex.code());
}
}
// invalid 'primarySortBuckets' specified
{
auto json = arangodb::velocypack::Parser::fromJson(
"{ \"id\":42, \"depth\":0, \"totalNrRegs\":0, \"varInfoList\":[], "
"\"nrRegs\":[], \"nrRegsHere\":[], \"regsToClear\":[], "
"\"varsUsedLater\":[], \"varsValid\":[], \"outVariable\": { "
"\"name\":\"variable\", \"id\":0 }, \"viewId\": \"" + std::to_string(logicalView->id()) + "\", "
"\"primarySort\": [ { \"field\": \"my.nested.Fields\", \"asc\": false}, { \"field\": \"another.field\", \"asc\":true } ], \"primarySortBuckets\": 3 }");
try {
arangodb::iresearch::IResearchViewNode node(*query.plan(), // plan
json->slice());
EXPECT_TRUE(false);
} catch (arangodb::basics::Exception const& ex) {
EXPECT_TRUE(TRI_ERROR_BAD_PARAMETER == ex.code());
}
}
}
TEST_F(IResearchViewNodeTest, construct) {
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1,
"testVocbase");
@ -207,7 +352,8 @@ TEST_F(IResearchViewNodeTest, construct) {
EXPECT_TRUE(node.empty()); // view has no links
EXPECT_TRUE(node.collections().empty()); // view has no links
EXPECT_TRUE(node.shards().empty());
EXPECT_TRUE(!node.sort()); // primary sort is not set by default
EXPECT_TRUE(!node.sort().first); // primary sort is not set by default
EXPECT_EQ(0, node.sort().second); // primary sort is not set by default
EXPECT_TRUE(arangodb::aql::ExecutionNode::ENUMERATE_IRESEARCH_VIEW == node.getType());
EXPECT_TRUE(&outVariable == &node.outVariable());
@ -445,7 +591,88 @@ TEST_F(IResearchViewNodeTest, constructFromVPackSingleServer) {
EXPECT_TRUE(node.empty()); // view has no links
EXPECT_TRUE(node.collections().empty()); // view has no links
EXPECT_TRUE(node.shards().empty());
EXPECT_TRUE(!node.sort());
EXPECT_TRUE(!node.sort().first); // primary sort is not set by default
EXPECT_EQ(0, node.sort().second); // primary sort is not set by default
EXPECT_TRUE(arangodb::aql::ExecutionNode::ENUMERATE_IRESEARCH_VIEW == node.getType());
EXPECT_TRUE(outVariable.id == node.outVariable().id);
EXPECT_TRUE(outVariable.name == node.outVariable().name);
EXPECT_TRUE(query.plan() == node.plan());
EXPECT_TRUE(42 == node.id());
EXPECT_TRUE(logicalView == node.view());
EXPECT_TRUE(node.scorers().empty());
EXPECT_TRUE(!node.volatility().first); // filter volatility
EXPECT_TRUE(!node.volatility().second); // sort volatility
arangodb::HashSet<arangodb::aql::Variable const*> usedHere;
node.getVariablesUsedHere(usedHere);
EXPECT_TRUE(usedHere.empty());
auto const setHere = node.getVariablesSetHere();
EXPECT_TRUE(1 == setHere.size());
EXPECT_TRUE(outVariable.id == setHere[0]->id);
EXPECT_TRUE(outVariable.name == setHere[0]->name);
EXPECT_TRUE(false == node.options().forceSync);
EXPECT_TRUE(0. == node.getCost().estimatedCost); // no dependencies
EXPECT_TRUE(0 == node.getCost().estimatedNrItems); // no dependencies
}
// no options, ignore 'primarySortBuckets'
{
auto json = arangodb::velocypack::Parser::fromJson(
"{ \"id\":42, \"depth\":0, \"totalNrRegs\":0, \"varInfoList\":[], "
"\"nrRegs\":[], \"nrRegsHere\":[], \"regsToClear\":[], "
"\"varsUsedLater\":[], \"varsValid\":[], \"outVariable\": { "
"\"name\":\"variable\", \"id\":0 }, \"viewId\": \"" +
std::to_string(logicalView->id()) + "\", \"primarySort\": [], \"primarySortBuckets\": false }");
arangodb::iresearch::IResearchViewNode node(*query.plan(), // plan
json->slice());
EXPECT_TRUE(node.empty()); // view has no links
EXPECT_TRUE(node.collections().empty()); // view has no links
EXPECT_TRUE(node.shards().empty());
EXPECT_TRUE(!node.sort().first); // primary sort is not set by default
EXPECT_EQ(0, node.sort().second); // primary sort is not set by default
EXPECT_TRUE(arangodb::aql::ExecutionNode::ENUMERATE_IRESEARCH_VIEW == node.getType());
EXPECT_TRUE(outVariable.id == node.outVariable().id);
EXPECT_TRUE(outVariable.name == node.outVariable().name);
EXPECT_TRUE(query.plan() == node.plan());
EXPECT_TRUE(42 == node.id());
EXPECT_TRUE(logicalView == node.view());
EXPECT_TRUE(node.scorers().empty());
EXPECT_TRUE(!node.volatility().first); // filter volatility
EXPECT_TRUE(!node.volatility().second); // sort volatility
arangodb::HashSet<arangodb::aql::Variable const*> usedHere;
node.getVariablesUsedHere(usedHere);
EXPECT_TRUE(usedHere.empty());
auto const setHere = node.getVariablesSetHere();
EXPECT_TRUE(1 == setHere.size());
EXPECT_TRUE(outVariable.id == setHere[0]->id);
EXPECT_TRUE(outVariable.name == setHere[0]->name);
EXPECT_TRUE(false == node.options().forceSync);
EXPECT_TRUE(0. == node.getCost().estimatedCost); // no dependencies
EXPECT_TRUE(0 == node.getCost().estimatedNrItems); // no dependencies
}
// no options
{
auto json = arangodb::velocypack::Parser::fromJson(
"{ \"id\":42, \"depth\":0, \"totalNrRegs\":0, \"varInfoList\":[], "
"\"nrRegs\":[], \"nrRegsHere\":[], \"regsToClear\":[], "
"\"varsUsedLater\":[], \"varsValid\":[], \"outVariable\": { "
"\"name\":\"variable\", \"id\":0 }, \"viewId\": \"" +
std::to_string(logicalView->id()) + "\", \"primarySort\": [], \"primarySortBuckets\": 42 }");
arangodb::iresearch::IResearchViewNode node(*query.plan(), // plan
json->slice());
EXPECT_TRUE(node.empty()); // view has no links
EXPECT_TRUE(node.collections().empty()); // view has no links
EXPECT_TRUE(node.shards().empty());
EXPECT_TRUE(!node.sort().first); // primary sort is not set by default
EXPECT_EQ(0, node.sort().second); // primary sort is not set by default
EXPECT_TRUE(arangodb::aql::ExecutionNode::ENUMERATE_IRESEARCH_VIEW == node.getType());
EXPECT_TRUE(outVariable.id == node.outVariable().id);
@ -485,7 +712,8 @@ TEST_F(IResearchViewNodeTest, constructFromVPackSingleServer) {
EXPECT_TRUE(node.empty()); // view has no links
EXPECT_TRUE(node.collections().empty()); // view has no links
EXPECT_TRUE(node.shards().empty());
EXPECT_TRUE(!node.sort());
EXPECT_TRUE(!node.sort().first); // primary sort is not set by default
EXPECT_EQ(0, node.sort().second); // primary sort is not set by default
EXPECT_TRUE(arangodb::aql::ExecutionNode::ENUMERATE_IRESEARCH_VIEW == node.getType());
EXPECT_TRUE(outVariable.id == node.outVariable().id);
@ -528,7 +756,8 @@ TEST_F(IResearchViewNodeTest, constructFromVPackSingleServer) {
EXPECT_TRUE(node.empty()); // view has no links
EXPECT_TRUE(node.collections().empty()); // view has no links
EXPECT_TRUE(node.shards().empty());
EXPECT_TRUE(!node.sort());
EXPECT_TRUE(!node.sort().first); // primary sort is not set by default
EXPECT_EQ(0, node.sort().second); // primary sort is not set by default
EXPECT_TRUE(arangodb::aql::ExecutionNode::ENUMERATE_IRESEARCH_VIEW == node.getType());
EXPECT_TRUE(outVariable.id == node.outVariable().id);
@ -570,7 +799,8 @@ TEST_F(IResearchViewNodeTest, constructFromVPackSingleServer) {
EXPECT_TRUE(node.empty()); // view has no links
EXPECT_TRUE(node.collections().empty()); // view has no links
EXPECT_TRUE(node.shards().empty());
EXPECT_TRUE(!node.sort());
EXPECT_TRUE(!node.sort().first); // primary sort is not set by default
EXPECT_EQ(0, node.sort().second); // primary sort is not set by default
EXPECT_TRUE(arangodb::aql::ExecutionNode::ENUMERATE_IRESEARCH_VIEW == node.getType());
EXPECT_TRUE(outVariable.id == node.outVariable().id);
@ -1005,7 +1235,7 @@ TEST_F(IResearchViewNodeTest, clone) {
nullptr, // no options
{} // no scorers
);
node.sort(&sort);
node.sort(&sort, 0);
EXPECT_TRUE(node.empty()); // view has no links
EXPECT_TRUE(node.collections().empty()); // view has no links
@ -1310,7 +1540,7 @@ TEST_F(IResearchViewNodeTest, serializeSortedView) {
nullptr, // no options
{} // no sort condition
);
node.sort(&viewImpl.primarySort());
node.sort(&viewImpl.primarySort(), 1);
EXPECT_TRUE(node.empty()); // view has no links
EXPECT_TRUE(node.collections().empty()); // view has no links
@ -1346,6 +1576,7 @@ TEST_F(IResearchViewNodeTest, serializeSortedView) {
EXPECT_TRUE(node.volatility() == deserialized.volatility());
EXPECT_TRUE(node.options().forceSync == deserialized.options().forceSync);
EXPECT_TRUE(node.sort() == deserialized.sort());
EXPECT_EQ(1, node.sort().second);
EXPECT_TRUE(node.getCost() == deserialized.getCost());
}
@ -1371,6 +1602,7 @@ TEST_F(IResearchViewNodeTest, serializeSortedView) {
EXPECT_TRUE(node.volatility() == deserialized.volatility());
EXPECT_TRUE(node.options().forceSync == deserialized.options().forceSync);
EXPECT_TRUE(node.sort() == deserialized.sort());
EXPECT_EQ(1, node.sort().second);
EXPECT_TRUE(node.getCost() == deserialized.getCost());
}
@ -1432,6 +1664,7 @@ TEST_F(IResearchViewNodeTest, serializeSortedView) {
EXPECT_TRUE(node.volatility() == deserialized.volatility());
EXPECT_TRUE(node.options().forceSync == deserialized.options().forceSync);
EXPECT_TRUE(node.sort() == deserialized.sort());
EXPECT_EQ(0, node.sort().second);
EXPECT_TRUE(node.getCost() == deserialized.getCost());
}
@ -1457,6 +1690,7 @@ TEST_F(IResearchViewNodeTest, serializeSortedView) {
EXPECT_TRUE(node.volatility() == deserialized.volatility());
EXPECT_TRUE(node.options().forceSync == deserialized.options().forceSync);
EXPECT_TRUE(node.sort() == deserialized.sort());
EXPECT_EQ(0, node.sort().second);
EXPECT_TRUE(node.getCost() == deserialized.getCost());
}

View File

@ -352,7 +352,8 @@ TEST_F(IResearchViewSortedTest, SingleField) {
arangodb::aql::ExecutionNode::castTo<arangodb::iresearch::IResearchViewNode*>(
nodes.front());
ASSERT_TRUE(viewNode);
EXPECT_TRUE(viewNode->sort());
EXPECT_TRUE(viewNode->sort().first);
EXPECT_EQ(1, viewNode->sort().second);
// check query execution
auto queryResult = arangodb::tests::executeQuery(vocbase, query);
@ -402,7 +403,8 @@ TEST_F(IResearchViewSortedTest, SingleField) {
arangodb::aql::ExecutionNode::castTo<arangodb::iresearch::IResearchViewNode*>(
nodes.front());
ASSERT_TRUE(viewNode);
EXPECT_TRUE(viewNode->sort());
EXPECT_TRUE(viewNode->sort().first);
EXPECT_EQ(1, viewNode->sort().second);
// check query execution
std::vector<arangodb::ManagedDocumentResult const*> expectedDocs{
@ -461,7 +463,8 @@ TEST_F(IResearchViewSortedTest, SingleField) {
arangodb::aql::ExecutionNode::castTo<arangodb::iresearch::IResearchViewNode*>(
nodes.front());
ASSERT_TRUE(viewNode);
EXPECT_TRUE(viewNode->sort());
EXPECT_TRUE(viewNode->sort().first);
EXPECT_EQ(1, viewNode->sort().second);
// check query execution
std::vector<arangodb::ManagedDocumentResult const*> expectedDocs{
@ -639,7 +642,8 @@ TEST_F(IResearchViewSortedTest, MultipleFields) {
arangodb::aql::ExecutionNode::castTo<arangodb::iresearch::IResearchViewNode*>(
nodes.front());
ASSERT_TRUE(viewNode);
EXPECT_TRUE(viewNode->sort());
EXPECT_TRUE(viewNode->sort().first);
EXPECT_EQ(4, viewNode->sort().second);
// check query execution
auto queryResult = arangodb::tests::executeQuery(vocbase, query);
@ -688,104 +692,8 @@ TEST_F(IResearchViewSortedTest, MultipleFields) {
arangodb::aql::ExecutionNode::castTo<arangodb::iresearch::IResearchViewNode*>(
nodes.front());
ASSERT_TRUE(viewNode);
EXPECT_TRUE(viewNode->sort());
// check query execution
auto queryResult = arangodb::tests::executeQuery(vocbase, query);
ASSERT_TRUE(queryResult.result.ok());
auto result = queryResult.data->slice();
EXPECT_TRUE(result.isArray());
arangodb::velocypack::ArrayIterator resultIt(result);
EXPECT_TRUE(insertedDocs.size() == resultIt.size());
auto expectedDoc = insertedDocs.rbegin();
for (auto const actualDoc : resultIt) {
auto const resolved = actualDoc.resolveExternals();
EXPECT_TRUE(0 == arangodb::basics::VelocyPackHelper::compare(
arangodb::velocypack::Slice(expectedDoc->vpack()), resolved, true));
++expectedDoc;
}
EXPECT_TRUE(expectedDoc == insertedDocs.rend());
}
// return all
{
std::string const query =
"FOR d IN testView SORT d.same, d.same DESC RETURN d";
EXPECT_TRUE(arangodb::tests::assertRules(
vocbase, query, {arangodb::aql::OptimizerRule::handleArangoSearchViewsRule}));
arangodb::SmallVector<arangodb::aql::ExecutionNode*>::allocator_type::arena_type a;
arangodb::SmallVector<arangodb::aql::ExecutionNode*> nodes{a};
auto preparedQuery = arangodb::tests::prepareQuery(vocbase, query);
auto plan = preparedQuery->plan();
ASSERT_TRUE(plan);
plan->findVarUsage();
// ensure sort node is optimized out
plan->findNodesOfType(nodes, arangodb::aql::ExecutionNode::SORT, true);
EXPECT_TRUE(nodes.empty());
// check sort is set
plan->findNodesOfType(nodes, arangodb::aql::ExecutionNode::ENUMERATE_IRESEARCH_VIEW, true);
EXPECT_TRUE(1 == nodes.size());
auto* viewNode =
arangodb::aql::ExecutionNode::castTo<arangodb::iresearch::IResearchViewNode*>(
nodes.front());
ASSERT_TRUE(viewNode);
EXPECT_TRUE(viewNode->sort());
// check query execution
auto queryResult = arangodb::tests::executeQuery(vocbase, query);
ASSERT_TRUE(queryResult.result.ok());
auto result = queryResult.data->slice();
EXPECT_TRUE(result.isArray());
arangodb::velocypack::ArrayIterator resultIt(result);
EXPECT_TRUE(insertedDocs.size() == resultIt.size());
auto expectedDoc = insertedDocs.rbegin();
for (auto const actualDoc : resultIt) {
auto const resolved = actualDoc.resolveExternals();
EXPECT_TRUE(0 == arangodb::basics::VelocyPackHelper::compare(
arangodb::velocypack::Slice(expectedDoc->vpack()), resolved, true));
++expectedDoc;
}
EXPECT_TRUE(expectedDoc == insertedDocs.rend());
}
// return all
{
std::string const query = "FOR d IN testView SORT d.same RETURN d";
EXPECT_TRUE(arangodb::tests::assertRules(
vocbase, query, {arangodb::aql::OptimizerRule::handleArangoSearchViewsRule}));
arangodb::SmallVector<arangodb::aql::ExecutionNode*>::allocator_type::arena_type a;
arangodb::SmallVector<arangodb::aql::ExecutionNode*> nodes{a};
auto preparedQuery = arangodb::tests::prepareQuery(vocbase, query);
auto plan = preparedQuery->plan();
ASSERT_TRUE(plan);
plan->findVarUsage();
// ensure sort node is optimized out
plan->findNodesOfType(nodes, arangodb::aql::ExecutionNode::SORT, true);
EXPECT_TRUE(nodes.empty());
// check sort is set
plan->findNodesOfType(nodes, arangodb::aql::ExecutionNode::ENUMERATE_IRESEARCH_VIEW, true);
EXPECT_TRUE(1 == nodes.size());
auto* viewNode =
arangodb::aql::ExecutionNode::castTo<arangodb::iresearch::IResearchViewNode*>(
nodes.front());
ASSERT_TRUE(viewNode);
EXPECT_TRUE(viewNode->sort());
EXPECT_TRUE(viewNode->sort().first);
EXPECT_EQ(3, viewNode->sort().second);
// check query execution
auto queryResult = arangodb::tests::executeQuery(vocbase, query);
@ -835,7 +743,8 @@ TEST_F(IResearchViewSortedTest, MultipleFields) {
arangodb::aql::ExecutionNode::castTo<arangodb::iresearch::IResearchViewNode*>(
nodes.front());
ASSERT_TRUE(viewNode);
EXPECT_TRUE(viewNode->sort());
EXPECT_TRUE(viewNode->sort().first);
EXPECT_EQ(3, viewNode->sort().second);
// check query execution
std::vector<arangodb::ManagedDocumentResult const*> expectedDocs{
@ -894,7 +803,8 @@ TEST_F(IResearchViewSortedTest, MultipleFields) {
arangodb::aql::ExecutionNode::castTo<arangodb::iresearch::IResearchViewNode*>(
nodes.front());
ASSERT_TRUE(viewNode);
EXPECT_TRUE(viewNode->sort());
EXPECT_TRUE(viewNode->sort().first);
EXPECT_EQ(4, viewNode->sort().second);
// check query execution
std::vector<arangodb::ManagedDocumentResult const*> expectedDocs{
@ -951,7 +861,8 @@ TEST_F(IResearchViewSortedTest, MultipleFields) {
arangodb::aql::ExecutionNode::castTo<arangodb::iresearch::IResearchViewNode*>(
nodes.front());
ASSERT_TRUE(viewNode);
EXPECT_TRUE(viewNode->sort());
EXPECT_TRUE(viewNode->sort().first);
EXPECT_EQ(2, viewNode->sort().second);
// check query execution
std::vector<arangodb::ManagedDocumentResult const*> expectedDocs{};