mirror of https://gitee.com/bigwinds/arangodb
make use of sortedness of rocksdb primary index (#7788)
This commit is contained in:
parent
63f5379115
commit
f65853e30f
18
CHANGELOG
18
CHANGELOG
|
@ -1,6 +1,19 @@
|
|||
devel
|
||||
-----
|
||||
|
||||
* RocksDB primary index can now be used by the optimizer to optimize queries
|
||||
that use `_key` or `_id` in SORT and FILTER conditions.
|
||||
|
||||
* the web UI will now by default show the documents of a collection lexicographically
|
||||
sorted by their `_key` values.
|
||||
|
||||
Previous versions of ArangoDB tried to interpret `_key` values as numeric values if
|
||||
possible and sorted by these. That previous sort strategy never used an index and
|
||||
could have caused unnecessary overhead. The new version will now use an index for
|
||||
sorting for the RocksDB engine, but may change the order in which documents are
|
||||
shown in the web UI (e.g. now a `_key` value of "10" will be shown before a `_key`
|
||||
value of "9").
|
||||
|
||||
* fixed known issue #445: ArangoSearch ignores `_id` attribute even if `includeAllFields`
|
||||
is set to `true`.
|
||||
|
||||
|
@ -8,9 +21,8 @@ devel
|
|||
|
||||
* upgraded bundled curl library to version 7.63
|
||||
|
||||
* fix issue #7900: Bind values of `null` are not replaced by
|
||||
empty string anymore, when toggling between json and table
|
||||
view in the web-ui.
|
||||
* fix issue #7900: Bind values of `null` are not replaced by empty string anymore,
|
||||
when toggling between JSON and table view in the web UI.
|
||||
|
||||
* Use base64url to encode and decode JWT parts.
|
||||
|
||||
|
|
|
@ -5,6 +5,14 @@ The following list shows in detail which features have been added or improved in
|
|||
ArangoDB 3.5. ArangoDB 3.5 also contains several bug fixes that are not listed
|
||||
here.
|
||||
|
||||
Customer Relevant
|
||||
-----------------
|
||||
|
||||
* The optimizer can now make use of the sorted-ness of primary indexes if the
|
||||
RocksDB engine is used. This means the primary index can be utilized for
|
||||
sorting by `_key` or `_id` attribute as well as for range queries (note that
|
||||
the document key is still a string).
|
||||
|
||||
Internal
|
||||
--------
|
||||
|
||||
|
|
|
@ -6,6 +6,15 @@ upgrading to ArangoDB 3.5, and adjust any client programs if necessary.
|
|||
|
||||
The following incompatible changes have been made in ArangoDB 3.5:
|
||||
|
||||
|
||||
UI
|
||||
--
|
||||
|
||||
Primary index keys will now always be sorted in lexicographical order as keys are
|
||||
strings. An exception for values representing numerical values has been removed
|
||||
when shown in the UI. Therefore a key with value "10" will be displayed before
|
||||
a key having "9" as value.
|
||||
|
||||
AQL
|
||||
---
|
||||
|
||||
|
@ -34,4 +43,3 @@ undefined.
|
|||
This change is about making queries as the above fail with a parse error, as an
|
||||
unknown variable `key1` is accessed here, avoiding the undefined behavior. This is
|
||||
also in line with what the documentation states about variable invalidation.
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
|
||||
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
|
@ -175,7 +174,7 @@ std::unordered_map<int, std::string const> const AstNode::ValueTypeNames{
|
|||
namespace {
|
||||
|
||||
/// @brief quick translation array from an AST node value type to a VPack type
|
||||
static std::array<VPackValueType, 5> const valueTypes{{
|
||||
std::array<VPackValueType, 5> const valueTypes{{
|
||||
VPackValueType::Null, // VALUE_TYPE_NULL = 0,
|
||||
VPackValueType::Bool, // VALUE_TYPE_BOOL = 1,
|
||||
VPackValueType::Int, // VALUE_TYPE_INT = 2,
|
||||
|
@ -195,7 +194,29 @@ static_assert(AstNodeValueType::VALUE_TYPE_STRING == 4,
|
|||
"incorrect ast node value types");
|
||||
|
||||
/// @brief get the node type for inter-node comparisons
|
||||
static VPackValueType getNodeCompareType(AstNode const* node) {
|
||||
inline int valueTypeOrder(VPackValueType type) {
|
||||
switch (type) {
|
||||
case VPackValueType::Null:
|
||||
return 0;
|
||||
case VPackValueType::Bool:
|
||||
return 1;
|
||||
case VPackValueType::Int:
|
||||
case VPackValueType::Double:
|
||||
return 2;
|
||||
case VPackValueType::String:
|
||||
case VPackValueType::Custom: // _id
|
||||
return 3;
|
||||
case VPackValueType::Array:
|
||||
return 4;
|
||||
case VPackValueType::Object:
|
||||
return 5;
|
||||
default:
|
||||
return 0; // null
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief get the node type for inter-node comparisons
|
||||
VPackValueType getNodeCompareType(AstNode const* node) {
|
||||
TRI_ASSERT(node != nullptr);
|
||||
|
||||
if (node->type == NODE_TYPE_VALUE) {
|
||||
|
@ -215,7 +236,7 @@ static VPackValueType getNodeCompareType(AstNode const* node) {
|
|||
return VPackValueType::Null;
|
||||
}
|
||||
|
||||
static inline int compareDoubleValues(double lhs, double rhs) {
|
||||
inline int compareDoubleValues(double lhs, double rhs) {
|
||||
if (arangodb::almostEquals(lhs, rhs)) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -258,7 +279,7 @@ int arangodb::aql::CompareAstNodes(AstNode const* lhs, AstNode const* rhs, bool
|
|||
static_cast<double>(rhs->getIntValue()));
|
||||
}
|
||||
|
||||
int diff = static_cast<int>(lType) - static_cast<int>(rType);
|
||||
int diff = valueTypeOrder(lType) - valueTypeOrder(rType);
|
||||
|
||||
TRI_ASSERT(diff != 0);
|
||||
|
||||
|
@ -816,28 +837,31 @@ uint64_t AstNode::hashValue(uint64_t hash) const noexcept {
|
|||
|
||||
/// @brief dump the node (for debugging purposes)
|
||||
#ifdef ARANGODB_ENABLE_MAINTAINER_MODE
|
||||
void AstNode::dump(int level) const {
|
||||
std::ostream& AstNode::toStream(std::ostream& os, int level) const {
|
||||
for (int i = 0; i < level; ++i) {
|
||||
std::cout << " ";
|
||||
os << " ";
|
||||
}
|
||||
std::cout << "- " << getTypeString();
|
||||
os << "- " << getTypeString();
|
||||
|
||||
if (type == NODE_TYPE_VALUE || type == NODE_TYPE_ARRAY) {
|
||||
std::cout << ": " << toVelocyPackValue().get()->toJson();
|
||||
os << ": " << toVelocyPackValue().get()->toJson();
|
||||
} else if (type == NODE_TYPE_ATTRIBUTE_ACCESS) {
|
||||
std::cout << ": " << getString();
|
||||
os << ": " << getString();
|
||||
} else if (type == NODE_TYPE_REFERENCE) {
|
||||
std::cout << ": " << static_cast<Variable const*>(getData())->name;
|
||||
os << ": " << static_cast<Variable const*>(getData())->name;
|
||||
}
|
||||
std::cout << "\n";
|
||||
os << "\n";
|
||||
|
||||
size_t const n = numMembers();
|
||||
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
auto sub = getMemberUnchecked(i);
|
||||
sub->dump(level + 1);
|
||||
sub->toStream(os, level + 1);
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
void AstNode::dump(int indent) const { toStream(std::cout, indent); }
|
||||
#endif
|
||||
|
||||
/// @brief compute the value for a constant value node
|
||||
|
|
|
@ -262,6 +262,7 @@ struct AstNode {
|
|||
|
||||
/// @brief dump the node (for debugging purposes)
|
||||
#ifdef ARANGODB_ENABLE_MAINTAINER_MODE
|
||||
std::ostream& toStream(std::ostream& os, int indent) const;
|
||||
void dump(int indent) const;
|
||||
#endif
|
||||
|
||||
|
|
|
@ -156,7 +156,7 @@ arangodb::aql::AstNode* IndexBlock::makeUnique(arangodb::aql::AstNode* node) con
|
|||
bool isSorted = false;
|
||||
bool isSparse = false;
|
||||
auto unused = trx->getIndexFeatures(_indexes[_currentIndex], isSorted, isSparse);
|
||||
if (isSparse) {
|
||||
if (isSparse || isSorted) {
|
||||
// the index is sorted. we need to use SORTED_UNIQUE to get the
|
||||
// result back in index order
|
||||
return ast->createNodeFunctionCall(TRI_CHAR_LENGTH_PAIR("SORTED_UNIQUE"), array);
|
||||
|
|
|
@ -149,7 +149,8 @@ bool ClusterIndex::isSorted() const {
|
|||
return _indexType == Index::TRI_IDX_TYPE_SKIPLIST_INDEX ||
|
||||
_indexType == Index::TRI_IDX_TYPE_PERSISTENT_INDEX;
|
||||
} else if (_engineType == ClusterEngineType::RocksDBEngine) {
|
||||
return _indexType == Index::TRI_IDX_TYPE_EDGE_INDEX ||
|
||||
return _indexType == Index::TRI_IDX_TYPE_PRIMARY_INDEX ||
|
||||
_indexType == Index::TRI_IDX_TYPE_EDGE_INDEX ||
|
||||
_indexType == Index::TRI_IDX_TYPE_HASH_INDEX ||
|
||||
_indexType == Index::TRI_IDX_TYPE_SKIPLIST_INDEX ||
|
||||
_indexType == Index::TRI_IDX_TYPE_PERSISTENT_INDEX ||
|
||||
|
@ -209,6 +210,17 @@ bool ClusterIndex::supportsFilterCondition(
|
|||
size_t itemsInIndex, size_t& estimatedItems, double& estimatedCost) const {
|
||||
switch (_indexType) {
|
||||
case TRI_IDX_TYPE_PRIMARY_INDEX: {
|
||||
if (_engineType == ClusterEngineType::RocksDBEngine) {
|
||||
std::unordered_map<size_t, std::vector<arangodb::aql::AstNode const*>> found;
|
||||
std::unordered_set<std::string> nonNullAttributes;
|
||||
std::size_t values = 0;
|
||||
SkiplistIndexAttributeMatcher::matchAttributes(this, node, reference, found,
|
||||
values, nonNullAttributes,
|
||||
/*skip evaluation (during execution)*/ false);
|
||||
estimatedItems = values;
|
||||
return !found.empty();
|
||||
}
|
||||
// MMFiles et al
|
||||
SimpleAttributeEqualityMatcher matcher(PrimaryIndexAttributes);
|
||||
return matcher.matchOne(this, node, reference, itemsInIndex, estimatedItems, estimatedCost);
|
||||
}
|
||||
|
@ -274,6 +286,15 @@ bool ClusterIndex::supportsSortCondition(arangodb::aql::SortCondition const* sor
|
|||
size_t& coveredAttributes) const {
|
||||
switch (_indexType) {
|
||||
case TRI_IDX_TYPE_PRIMARY_INDEX:
|
||||
if (_engineType == ClusterEngineType::MMFilesEngine) {
|
||||
return Index::supportsSortCondition(sortCondition, reference, itemsInIndex,
|
||||
estimatedCost, coveredAttributes);
|
||||
} else if (_engineType == ClusterEngineType::RocksDBEngine) {
|
||||
return PersistentIndexAttributeMatcher::supportsSortCondition(this, sortCondition, reference,
|
||||
itemsInIndex, estimatedCost,
|
||||
coveredAttributes);
|
||||
}
|
||||
break;
|
||||
case TRI_IDX_TYPE_GEO_INDEX:
|
||||
case TRI_IDX_TYPE_GEO1_INDEX:
|
||||
case TRI_IDX_TYPE_GEO2_INDEX:
|
||||
|
@ -333,8 +354,13 @@ aql::AstNode* ClusterIndex::specializeCondition(aql::AstNode* node,
|
|||
aql::Variable const* reference) const {
|
||||
switch (_indexType) {
|
||||
case TRI_IDX_TYPE_PRIMARY_INDEX: {
|
||||
SimpleAttributeEqualityMatcher matcher(PrimaryIndexAttributes);
|
||||
return matcher.specializeOne(this, node, reference);
|
||||
if (_engineType == ClusterEngineType::MMFilesEngine) {
|
||||
SimpleAttributeEqualityMatcher matcher(PrimaryIndexAttributes);
|
||||
return matcher.specializeOne(this, node, reference);
|
||||
} else if (_engineType == ClusterEngineType::RocksDBEngine) {
|
||||
return SkiplistIndexAttributeMatcher::specializeCondition(this, node, reference);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
// should not be called for these
|
||||
case TRI_IDX_TYPE_GEO_INDEX:
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "Basics/Common.h"
|
||||
#include "Basics/Exceptions.h"
|
||||
#include "Basics/Result.h"
|
||||
#include "Basics/StaticStrings.h"
|
||||
#include "Basics/StringRef.h"
|
||||
#include "VocBase/LocalDocumentId.h"
|
||||
#include "VocBase/voc-types.h"
|
||||
|
@ -148,12 +149,18 @@ class Index {
|
|||
}
|
||||
|
||||
/// @brief whether or not any attribute is expanded
|
||||
inline bool attributeMatches(std::vector<arangodb::basics::AttributeName> const& attribute) const {
|
||||
inline bool attributeMatches(std::vector<arangodb::basics::AttributeName> const& attribute,
|
||||
bool isPrimary = false) const {
|
||||
for (auto const& it : _fields) {
|
||||
if (arangodb::basics::AttributeName::isIdentical(attribute, it, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (isPrimary) {
|
||||
static std::vector<arangodb::basics::AttributeName> const vec_id{
|
||||
{StaticStrings::IdString, false}};
|
||||
return arangodb::basics::AttributeName::isIdentical(attribute, vec_id, true);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -378,10 +385,13 @@ class Index {
|
|||
|
||||
mutable bool _unique;
|
||||
mutable bool _sparse;
|
||||
|
||||
// use this with c++17 -- attributeMatches
|
||||
// static inline std::vector<arangodb::basics::AttributeName> const vec_id {{ StaticStrings::IdString, false }};
|
||||
};
|
||||
} // namespace arangodb
|
||||
|
||||
std::ostream& operator<<(std::ostream&, arangodb::Index const*);
|
||||
std::ostream& operator<<(std::ostream&, arangodb::Index const&);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "Aql/AstNode.h"
|
||||
#include "Aql/SortCondition.h"
|
||||
#include "Aql/Variable.h"
|
||||
#include "Basics/StaticStrings.h"
|
||||
#include "Basics/StringRef.h"
|
||||
#include "Indexes/Index.h"
|
||||
#include "Indexes/SimpleAttributeEqualityMatcher.h"
|
||||
|
@ -34,17 +35,22 @@
|
|||
using namespace arangodb;
|
||||
|
||||
bool SkiplistIndexAttributeMatcher::accessFitsIndex(
|
||||
arangodb::Index const* idx, arangodb::aql::AstNode const* access,
|
||||
arangodb::aql::AstNode const* other, arangodb::aql::AstNode const* op,
|
||||
arangodb::aql::Variable const* reference,
|
||||
std::unordered_map<size_t, std::vector<arangodb::aql::AstNode const*>>& found,
|
||||
std::unordered_set<std::string>& nonNullAttributes, bool isExecution) {
|
||||
arangodb::Index const* idx, // index
|
||||
arangodb::aql::AstNode const* access, // attribute access
|
||||
arangodb::aql::AstNode const* other, // eg const value
|
||||
arangodb::aql::AstNode const* op, // binary operation that is parent of access and other
|
||||
arangodb::aql::Variable const* reference, // variable used in access(es)
|
||||
std::unordered_map<size_t /*offset in idx->fields()*/, std::vector<arangodb::aql::AstNode const*> /*conjunct - operation*/>& found, // marks operations covered by index-fields
|
||||
std::unordered_set<std::string>& nonNullAttributes, // set of stringified op-childeren (access other) that may not be null
|
||||
bool isExecution // skip usage check in execution phase
|
||||
) {
|
||||
if (!idx->canUseConditionPart(access, other, op, reference, nonNullAttributes, isExecution)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
arangodb::aql::AstNode const* what = access;
|
||||
std::pair<arangodb::aql::Variable const*, std::vector<arangodb::basics::AttributeName>> attributeData;
|
||||
bool const isPrimaryIndex = idx->type() == arangodb::Index::IndexType::TRI_IDX_TYPE_PRIMARY_INDEX;
|
||||
|
||||
if (op->type != arangodb::aql::NODE_TYPE_OPERATOR_BINARY_IN) {
|
||||
if (!what->isAttributeAccessForVariable(attributeData) || attributeData.first != reference) {
|
||||
|
@ -63,25 +69,19 @@ bool SkiplistIndexAttributeMatcher::accessFitsIndex(
|
|||
// ok, we do have an IN here... check if it's something like 'value' IN
|
||||
// doc.value[*]
|
||||
TRI_ASSERT(op->type == arangodb::aql::NODE_TYPE_OPERATOR_BINARY_IN);
|
||||
bool canUse = false;
|
||||
|
||||
if (what->isAttributeAccessForVariable(attributeData) && attributeData.first == reference &&
|
||||
!arangodb::basics::TRI_AttributeNamesHaveExpansion(attributeData.second) &&
|
||||
idx->attributeMatches(attributeData.second)) {
|
||||
idx->attributeMatches(attributeData.second, isPrimaryIndex)) {
|
||||
// doc.value IN 'value'
|
||||
// can use this index
|
||||
canUse = true;
|
||||
} else {
|
||||
} else if (other->isAttributeAccessForVariable(attributeData) &&
|
||||
attributeData.first == reference &&
|
||||
idx->isAttributeExpanded(attributeData.second) &&
|
||||
idx->attributeMatches(attributeData.second, isPrimaryIndex)) {
|
||||
// check for 'value' IN doc.value AND 'value' IN doc.value[*]
|
||||
what = other;
|
||||
if (what->isAttributeAccessForVariable(attributeData) && attributeData.first == reference &&
|
||||
idx->isAttributeExpanded(attributeData.second) &&
|
||||
idx->attributeMatches(attributeData.second)) {
|
||||
canUse = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!canUse) {
|
||||
what = other; // if what should be used later
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -102,6 +102,15 @@ bool SkiplistIndexAttributeMatcher::accessFitsIndex(
|
|||
bool match = arangodb::basics::AttributeName::isIdentical(idx->fields()[i],
|
||||
fieldNames, true);
|
||||
|
||||
// make exception for primary index as we do not need to match "_key, _id"
|
||||
// but can go directly for "_id"
|
||||
if (!match &&
|
||||
isPrimaryIndex &&
|
||||
i == 0 &&
|
||||
fieldNames[i].name == StaticStrings::IdString) {
|
||||
match = true;
|
||||
}
|
||||
|
||||
if (match) {
|
||||
// mark ith attribute as being covered
|
||||
found[i].emplace_back(op);
|
||||
|
@ -127,6 +136,10 @@ void SkiplistIndexAttributeMatcher::matchAttributes(
|
|||
arangodb::aql::Variable const* reference,
|
||||
std::unordered_map<size_t, std::vector<arangodb::aql::AstNode const*>>& found,
|
||||
size_t& values, std::unordered_set<std::string>& nonNullAttributes, bool isExecution) {
|
||||
// assert we have a proper formed conditiona - naray conjunction
|
||||
TRI_ASSERT(node->type == arangodb::aql::NODE_TYPE_OPERATOR_NARY_AND);
|
||||
|
||||
// inspect the the conjuncts - allowed are binary comparisons and a contains check
|
||||
for (size_t i = 0; i < node->numMembers(); ++i) {
|
||||
auto op = node->getMember(i);
|
||||
|
||||
|
@ -147,7 +160,7 @@ void SkiplistIndexAttributeMatcher::matchAttributes(
|
|||
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_IN:
|
||||
if (accessFitsIndex(idx, op->getMember(0), op->getMember(1), op,
|
||||
reference, found, nonNullAttributes, isExecution)) {
|
||||
if (op->getMember(1)->isAttributeAccessForVariable(reference, false)) {
|
||||
if (op->getMember(1)->isAttributeAccessForVariable(reference, /*indexed access*/ false)) {
|
||||
// 'abc' IN doc.attr[*]
|
||||
++values;
|
||||
} else {
|
||||
|
@ -255,8 +268,7 @@ bool SkiplistIndexAttributeMatcher::supportsFilterCondition(
|
|||
}
|
||||
|
||||
estimatedItems = values;
|
||||
// ALTERNATIVE: estimatedCost = static_cast<double>(estimatedItems *
|
||||
// values);
|
||||
// ALTERNATIVE: estimatedCost = static_cast<double>(estimatedItems * values);
|
||||
estimatedCost = (std::max)(static_cast<double>(1),
|
||||
std::log2(static_cast<double>(itemsInIndex)) * values);
|
||||
|
||||
|
@ -453,6 +465,7 @@ arangodb::aql::AstNode* SkiplistIndexAttributeMatcher::specializeCondition(
|
|||
TRI_ASSERT(it->type != arangodb::aql::NODE_TYPE_OPERATOR_BINARY_NE);
|
||||
node->addMember(it);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
|
|
|
@ -105,10 +105,9 @@ class RocksDBGenericIterator {
|
|||
|
||||
~RocksDBGenericIterator() {}
|
||||
|
||||
// the following functions return if the iterator
|
||||
// is valid and in bounds on return.
|
||||
bool next(GenericCallback const& cb,
|
||||
size_t count); // number of documents the callback should be applied to
|
||||
//* The following functions returns true if the iterator is valid within bounds on return.
|
||||
// @param limit - number of documents the callback should be applied to
|
||||
bool next(GenericCallback const& cb, size_t limit);
|
||||
|
||||
// documents to skip, skipped documents
|
||||
bool skip(uint64_t count, uint64_t& skipped);
|
||||
|
|
|
@ -99,6 +99,11 @@ RocksDBKeyBounds RocksDBKeyBounds::UniqueVPackIndex(uint64_t indexId, VPackSlice
|
|||
return RocksDBKeyBounds(RocksDBEntryType::UniqueVPackIndexValue, indexId, left, right);
|
||||
}
|
||||
|
||||
RocksDBKeyBounds RocksDBKeyBounds::PrimaryIndex(uint64_t indexId, std::string const& left,
|
||||
std::string const& right) {
|
||||
return RocksDBKeyBounds(RocksDBEntryType::PrimaryIndexValue, indexId, left, right);
|
||||
}
|
||||
|
||||
/// used for point lookups
|
||||
RocksDBKeyBounds RocksDBKeyBounds::UniqueVPackIndex(uint64_t indexId, VPackSlice const& left) {
|
||||
return RocksDBKeyBounds(RocksDBEntryType::UniqueVPackIndexValue, indexId, left);
|
||||
|
@ -225,6 +230,38 @@ rocksdb::ColumnFamilyHandle* RocksDBKeyBounds::columnFamily() const {
|
|||
THROW_ARANGO_EXCEPTION(TRI_ERROR_TYPE_ERROR);
|
||||
}
|
||||
|
||||
/// bounds to iterate over specified word or edge
|
||||
RocksDBKeyBounds::RocksDBKeyBounds(RocksDBEntryType type, uint64_t id,
|
||||
std::string const& lower, std::string const& upper)
|
||||
: _type(type) {
|
||||
switch (_type) {
|
||||
case RocksDBEntryType::PrimaryIndexValue: {
|
||||
// format: id lower id upper
|
||||
// start end
|
||||
_internals.reserve(sizeof(id) + (lower.size() + sizeof(_stringSeparator)) +
|
||||
sizeof(id) + (upper.size() + sizeof(_stringSeparator)));
|
||||
|
||||
// id - lower
|
||||
uint64ToPersistent(_internals.buffer(), id);
|
||||
_internals.buffer().append(lower.data(), lower.length());
|
||||
_internals.push_back(_stringSeparator);
|
||||
|
||||
// set separator
|
||||
_internals.separate();
|
||||
|
||||
// id - upper
|
||||
uint64ToPersistent(_internals.buffer(), id);
|
||||
_internals.buffer().append(upper.data(), upper.length());
|
||||
_internals.push_back(_stringSeparator);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_BAD_PARAMETER);
|
||||
}
|
||||
}
|
||||
|
||||
// constructor for an empty bound. do not use for anything but to
|
||||
// default-construct a key bound!
|
||||
RocksDBKeyBounds::RocksDBKeyBounds()
|
||||
|
|
|
@ -70,6 +70,13 @@ class RocksDBKeyBounds {
|
|||
//////////////////////////////////////////////////////////////////////////////
|
||||
static RocksDBKeyBounds PrimaryIndex(uint64_t indexId);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief Bounds for all index-entries- within a range belonging to a
|
||||
/// specified primary index
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
static RocksDBKeyBounds PrimaryIndex(uint64_t indexId, std::string const& lower,
|
||||
std::string const& upper);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief Bounds for all index-entries belonging to a specified edge index
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -209,6 +216,8 @@ class RocksDBKeyBounds {
|
|||
RocksDBKeyBounds(RocksDBEntryType type, uint64_t first,
|
||||
VPackSlice const& second, VPackSlice const& third);
|
||||
RocksDBKeyBounds(RocksDBEntryType type, uint64_t first, uint64_t second, uint64_t third);
|
||||
RocksDBKeyBounds(RocksDBEntryType type, uint64_t id, std::string const& lower,
|
||||
std::string const& upper);
|
||||
|
||||
private:
|
||||
// private class that will hold both bounds in a single buffer (with only one
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RocksDBPrimaryIndex.h"
|
||||
#include "Aql/Ast.h"
|
||||
#include "Aql/AstNode.h"
|
||||
#include "Basics/Exceptions.h"
|
||||
#include "Basics/StaticStrings.h"
|
||||
|
@ -29,7 +30,8 @@
|
|||
#include "Cache/CachedValue.h"
|
||||
#include "Cache/TransactionalCache.h"
|
||||
#include "Cluster/ServerState.h"
|
||||
#include "Indexes/SimpleAttributeEqualityMatcher.h"
|
||||
#include "Indexes/PersistentIndexAttributeMatcher.h"
|
||||
#include "Indexes/SkiplistIndexAttributeMatcher.h"
|
||||
#include "Logger/Logger.h"
|
||||
#include "RocksDBEngine/RocksDBCollection.h"
|
||||
#include "RocksDBEngine/RocksDBCommon.h"
|
||||
|
@ -63,6 +65,116 @@
|
|||
|
||||
using namespace arangodb;
|
||||
|
||||
namespace {
|
||||
std::string const lowest; // smallest possible key
|
||||
std::string const highest = "\xFF"; // greatest possible key
|
||||
}
|
||||
|
||||
RocksDBPrimaryIndexRangeIterator::RocksDBPrimaryIndexRangeIterator(
|
||||
LogicalCollection* collection, transaction::Methods* trx,
|
||||
arangodb::RocksDBPrimaryIndex const* index, bool reverse, RocksDBKeyBounds&& bounds)
|
||||
: IndexIterator(collection, trx),
|
||||
_index(index),
|
||||
_cmp(index->comparator()),
|
||||
_reverse(reverse),
|
||||
_bounds(std::move(bounds)) {
|
||||
TRI_ASSERT(index->columnFamily() == RocksDBColumnFamily::primary());
|
||||
|
||||
RocksDBMethods* mthds = RocksDBTransactionState::toMethods(trx);
|
||||
rocksdb::ReadOptions options = mthds->iteratorReadOptions();
|
||||
// we need to have a pointer to a slice for the upper bound
|
||||
// so we need to assign the slice to an instance variable here
|
||||
if (reverse) {
|
||||
_rangeBound = _bounds.start();
|
||||
options.iterate_lower_bound = &_rangeBound;
|
||||
} else {
|
||||
_rangeBound = _bounds.end();
|
||||
options.iterate_upper_bound = &_rangeBound;
|
||||
}
|
||||
|
||||
TRI_ASSERT(options.prefix_same_as_start);
|
||||
_iterator = mthds->NewIterator(options, index->columnFamily());
|
||||
if (reverse) {
|
||||
_iterator->SeekForPrev(_bounds.end());
|
||||
} else {
|
||||
_iterator->Seek(_bounds.start());
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Reset the cursor
|
||||
void RocksDBPrimaryIndexRangeIterator::reset() {
|
||||
TRI_ASSERT(_trx->state()->isRunning());
|
||||
|
||||
if (_reverse) {
|
||||
_iterator->SeekForPrev(_bounds.end());
|
||||
} else {
|
||||
_iterator->Seek(_bounds.start());
|
||||
}
|
||||
}
|
||||
|
||||
bool RocksDBPrimaryIndexRangeIterator::outOfRange() const {
|
||||
TRI_ASSERT(_trx->state()->isRunning());
|
||||
if (_reverse) {
|
||||
return (_cmp->Compare(_iterator->key(), _bounds.start()) < 0);
|
||||
} else {
|
||||
return (_cmp->Compare(_iterator->key(), _bounds.end()) > 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool RocksDBPrimaryIndexRangeIterator::next(LocalDocumentIdCallback const& cb, size_t limit) {
|
||||
TRI_ASSERT(_trx->state()->isRunning());
|
||||
|
||||
if (limit == 0 || !_iterator->Valid() || outOfRange()) {
|
||||
// No limit no data, or we are actually done. The last call should have
|
||||
// returned false
|
||||
TRI_ASSERT(limit > 0); // Someone called with limit == 0. Api broken
|
||||
return false;
|
||||
}
|
||||
|
||||
while (limit > 0) {
|
||||
TRI_ASSERT(_index->objectId() == RocksDBKey::objectId(_iterator->key()));
|
||||
|
||||
cb(RocksDBValue::documentId(_iterator->value()));
|
||||
|
||||
--limit;
|
||||
if (_reverse) {
|
||||
_iterator->Prev();
|
||||
} else {
|
||||
_iterator->Next();
|
||||
}
|
||||
|
||||
if (!_iterator->Valid() || outOfRange()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RocksDBPrimaryIndexRangeIterator::skip(uint64_t count, uint64_t& skipped) {
|
||||
TRI_ASSERT(_trx->state()->isRunning());
|
||||
|
||||
if (!_iterator->Valid() || outOfRange()) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (count > 0) {
|
||||
TRI_ASSERT(_index->objectId() == RocksDBKey::objectId(_iterator->key()));
|
||||
|
||||
--count;
|
||||
++skipped;
|
||||
if (_reverse) {
|
||||
_iterator->Prev();
|
||||
} else {
|
||||
_iterator->Next();
|
||||
}
|
||||
|
||||
if (!_iterator->Valid() || outOfRange()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ================ Primary Index Iterator ================
|
||||
|
||||
/// @brief hard-coded vector of the index attributes
|
||||
|
@ -162,7 +274,7 @@ bool RocksDBPrimaryIndexInIterator::next(LocalDocumentIdCallback const& cb, size
|
|||
if (!_iterator.valid()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -187,7 +299,7 @@ bool RocksDBPrimaryIndexInIterator::nextCovering(DocumentCallback const& cb, siz
|
|||
if (!_iterator.valid()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} while (true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -415,8 +527,24 @@ bool RocksDBPrimaryIndex::supportsFilterCondition(
|
|||
std::vector<std::shared_ptr<arangodb::Index>> const& allIndexes,
|
||||
arangodb::aql::AstNode const* node, arangodb::aql::Variable const* reference,
|
||||
size_t itemsInIndex, size_t& estimatedItems, double& estimatedCost) const {
|
||||
SimpleAttributeEqualityMatcher matcher(IndexAttributes);
|
||||
return matcher.matchOne(this, node, reference, itemsInIndex, estimatedItems, estimatedCost);
|
||||
std::unordered_map<size_t, std::vector<arangodb::aql::AstNode const*>> found;
|
||||
std::unordered_set<std::string> nonNullAttributes;
|
||||
|
||||
std::size_t values = 0;
|
||||
SkiplistIndexAttributeMatcher::matchAttributes(this, node, reference, found,
|
||||
values, nonNullAttributes,
|
||||
/*skip evaluation (during execution)*/ false);
|
||||
estimatedItems = values;
|
||||
return !found.empty();
|
||||
}
|
||||
|
||||
bool RocksDBPrimaryIndex::supportsSortCondition(arangodb::aql::SortCondition const* sortCondition,
|
||||
arangodb::aql::Variable const* reference,
|
||||
size_t itemsInIndex, double& estimatedCost,
|
||||
size_t& coveredAttributes) const {
|
||||
return PersistentIndexAttributeMatcher::supportsSortCondition(this, sortCondition, reference,
|
||||
itemsInIndex, estimatedCost,
|
||||
coveredAttributes);
|
||||
}
|
||||
|
||||
/// @brief creates an IndexIterator for the given Condition
|
||||
|
@ -424,49 +552,215 @@ IndexIterator* RocksDBPrimaryIndex::iteratorForCondition(
|
|||
transaction::Methods* trx, ManagedDocumentResult*, arangodb::aql::AstNode const* node,
|
||||
arangodb::aql::Variable const* reference, IndexIteratorOptions const& opts) {
|
||||
TRI_ASSERT(!isSorted() || opts.sorted);
|
||||
if (node == nullptr) {
|
||||
// full range scan
|
||||
return new RocksDBPrimaryIndexRangeIterator(
|
||||
&_collection /*logical collection*/, trx, this, !opts.ascending /*reverse*/,
|
||||
RocksDBKeyBounds::PrimaryIndex(_objectId, ::lowest, ::highest));
|
||||
}
|
||||
|
||||
TRI_ASSERT(node->type == aql::NODE_TYPE_OPERATOR_NARY_AND);
|
||||
TRI_ASSERT(node->numMembers() == 1);
|
||||
|
||||
auto comp = node->getMember(0);
|
||||
// assume a.b == value
|
||||
auto attrNode = comp->getMember(0);
|
||||
auto valNode = comp->getMember(1);
|
||||
size_t const n = node->numMembers();
|
||||
TRI_ASSERT(n >= 1);
|
||||
|
||||
if (attrNode->type != aql::NODE_TYPE_ATTRIBUTE_ACCESS) {
|
||||
// value == a.b -> flip the two sides
|
||||
attrNode = comp->getMember(1);
|
||||
valNode = comp->getMember(0);
|
||||
}
|
||||
TRI_ASSERT(attrNode->type == aql::NODE_TYPE_ATTRIBUTE_ACCESS);
|
||||
if (n == 1) {
|
||||
auto comp = node->getMember(0);
|
||||
// assume a.b == value
|
||||
auto attrNode = comp->getMember(0);
|
||||
auto valNode = comp->getMember(1);
|
||||
|
||||
if (comp->type == aql::NODE_TYPE_OPERATOR_BINARY_EQ) {
|
||||
// a.b == value
|
||||
return createEqIterator(trx, attrNode, valNode);
|
||||
}
|
||||
|
||||
if (comp->type == aql::NODE_TYPE_OPERATOR_BINARY_IN) {
|
||||
// a.b IN values
|
||||
if (valNode->isArray()) {
|
||||
// a.b IN array
|
||||
return createInIterator(trx, attrNode, valNode);
|
||||
if (attrNode->type != aql::NODE_TYPE_ATTRIBUTE_ACCESS) {
|
||||
// value == a.b -> flip the two sides
|
||||
attrNode = comp->getMember(1);
|
||||
valNode = comp->getMember(0);
|
||||
}
|
||||
|
||||
TRI_ASSERT(attrNode->type == aql::NODE_TYPE_ATTRIBUTE_ACCESS);
|
||||
|
||||
if (comp->type == aql::NODE_TYPE_OPERATOR_BINARY_EQ) {
|
||||
// a.b == value
|
||||
return createEqIterator(trx, attrNode, valNode);
|
||||
}
|
||||
|
||||
if (comp->type == aql::NODE_TYPE_OPERATOR_BINARY_IN) {
|
||||
// a.b IN values
|
||||
if (valNode->isArray()) {
|
||||
// a.b IN array
|
||||
return createInIterator(trx, attrNode, valNode, opts.ascending);
|
||||
}
|
||||
}
|
||||
// fall-through intentional here
|
||||
}
|
||||
|
||||
auto removeCollectionFromString =
|
||||
[this, &trx](bool isId, std::string& value) -> int {
|
||||
if (isId) {
|
||||
char const* key = nullptr;
|
||||
size_t outLength = 0;
|
||||
std::shared_ptr<LogicalCollection> collection;
|
||||
Result res = trx->resolveId(value.data(), value.length(), collection, key, outLength);
|
||||
|
||||
if (!res.ok()) {
|
||||
// using the name of an unknown collection
|
||||
if (_isRunningInCluster) {
|
||||
// translate from our own shard name to "real" collection name
|
||||
return value.compare(trx->resolver()->getCollectionName(_collection.id()));
|
||||
}
|
||||
return value.compare(_collection.name());
|
||||
}
|
||||
|
||||
TRI_ASSERT(key);
|
||||
TRI_ASSERT(collection);
|
||||
|
||||
if (!_isRunningInCluster && collection->id() != _collection.id()) {
|
||||
// using the name of a different collection...
|
||||
return value.compare(_collection.name());
|
||||
} else if (_isRunningInCluster && collection->planId() != _collection.planId()) {
|
||||
// using a different collection
|
||||
// translate from our own shard name to "real" collection name
|
||||
return value.compare(trx->resolver()->getCollectionName(_collection.id()));
|
||||
}
|
||||
|
||||
// strip collection name prefix
|
||||
value = std::string(key, outLength);
|
||||
}
|
||||
|
||||
// usage of _key or same collection name
|
||||
return 0;
|
||||
};
|
||||
|
||||
std::string lower;
|
||||
std::string upper;
|
||||
bool lowerFound = false;
|
||||
bool upperFound = false;
|
||||
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
aql::AstNode const* comp = node->getMemberUnchecked(i);
|
||||
|
||||
if (comp == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto type = comp->type;
|
||||
|
||||
if (!(type == aql::NODE_TYPE_OPERATOR_BINARY_LE ||
|
||||
type == aql::NODE_TYPE_OPERATOR_BINARY_LT || type == aql::NODE_TYPE_OPERATOR_BINARY_GE ||
|
||||
type == aql::NODE_TYPE_OPERATOR_BINARY_GT ||
|
||||
type == aql::NODE_TYPE_OPERATOR_BINARY_EQ
|
||||
)) {
|
||||
return new EmptyIndexIterator(&_collection, trx);
|
||||
}
|
||||
|
||||
auto attrNode = comp->getMember(0);
|
||||
auto valNode = comp->getMember(1);
|
||||
|
||||
if (attrNode->type != aql::NODE_TYPE_ATTRIBUTE_ACCESS) {
|
||||
// value == a.b -> flip the two sides
|
||||
attrNode = comp->getMember(1);
|
||||
valNode = comp->getMember(0);
|
||||
type = aql::Ast::ReverseOperator(type);
|
||||
}
|
||||
|
||||
TRI_ASSERT(attrNode->type == aql::NODE_TYPE_ATTRIBUTE_ACCESS);
|
||||
bool const isId = (attrNode->stringEquals(StaticStrings::IdString));
|
||||
|
||||
std::string value; // empty string == lower bound
|
||||
if (valNode->isStringValue()) {
|
||||
value = valNode->getString();
|
||||
} else if (valNode->isObject() || valNode->isArray()) {
|
||||
// any array or object value is bigger than any potential key
|
||||
value = ::highest;
|
||||
} else if (valNode->isNullValue() || valNode->isBoolValue() || valNode->isIntValue()) {
|
||||
// any null, bool or numeric value is lower than any potential key
|
||||
// keep lower bound
|
||||
} else {
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, std::string("unhandled type for valNode: ") + valNode->getTypeString());
|
||||
}
|
||||
|
||||
// strip collection name prefix from comparison value
|
||||
int const cmpResult = removeCollectionFromString(isId, value);
|
||||
|
||||
if (type == aql::NODE_TYPE_OPERATOR_BINARY_EQ) {
|
||||
if (cmpResult != 0) {
|
||||
// doc._id == different collection
|
||||
return new EmptyIndexIterator(&_collection, trx);
|
||||
}
|
||||
if (!upperFound || value < upper) {
|
||||
upper = value;
|
||||
upperFound = true;
|
||||
}
|
||||
if (!lowerFound || value < lower) {
|
||||
lower = std::move(value);
|
||||
lowerFound = true;
|
||||
}
|
||||
} else if (type == aql::NODE_TYPE_OPERATOR_BINARY_LE || type == aql::NODE_TYPE_OPERATOR_BINARY_LT) {
|
||||
// a.b < value
|
||||
if (cmpResult > 0) {
|
||||
// doc._id < collection with "bigger" name
|
||||
upper = ::highest;
|
||||
} else if (cmpResult < 0) {
|
||||
// doc._id < collection with "lower" name
|
||||
return new EmptyIndexIterator(&_collection, trx);
|
||||
} else {
|
||||
if (type == aql::NODE_TYPE_OPERATOR_BINARY_LT && !value.empty()) {
|
||||
value.back() -= 0x01U; // modify upper bound so that it is not included
|
||||
}
|
||||
if (!upperFound || value < upper) {
|
||||
upper = std::move(value);
|
||||
}
|
||||
}
|
||||
upperFound = true;
|
||||
} else if (type == aql::NODE_TYPE_OPERATOR_BINARY_GE || type == aql::NODE_TYPE_OPERATOR_BINARY_GT) {
|
||||
// a.b > value
|
||||
if (cmpResult < 0) {
|
||||
// doc._id > collection with "smaller" name
|
||||
lower = ::lowest;
|
||||
} else if (cmpResult > 0) {
|
||||
// doc._id > collection with "bigger" name
|
||||
return new EmptyIndexIterator(&_collection, trx);
|
||||
} else {
|
||||
if (type == aql::NODE_TYPE_OPERATOR_BINARY_GE && !value.empty()) {
|
||||
value.back() -= 0x01U; // modify lower bound so it is included
|
||||
}
|
||||
if (!lowerFound || value > lower) {
|
||||
lower = std::move(value);
|
||||
}
|
||||
}
|
||||
lowerFound = true;
|
||||
}
|
||||
} // for nodes
|
||||
|
||||
// if only one bound is given select the other (lowest or highest) accordingly
|
||||
if (upperFound && !lowerFound) {
|
||||
lower = ::lowest;
|
||||
lowerFound = true;
|
||||
} else if (lowerFound && !upperFound) {
|
||||
upper = ::highest;
|
||||
upperFound = true;
|
||||
}
|
||||
|
||||
if (lowerFound && upperFound) {
|
||||
return new RocksDBPrimaryIndexRangeIterator(
|
||||
&_collection /*logical collection*/, trx, this, !opts.ascending /*reverse*/,
|
||||
RocksDBKeyBounds::PrimaryIndex(_objectId, lower, upper));
|
||||
}
|
||||
|
||||
// operator type unsupported or IN used on non-array
|
||||
return new EmptyIndexIterator(&_collection, trx);
|
||||
}
|
||||
};
|
||||
|
||||
/// @brief specializes the condition for use with the index
|
||||
arangodb::aql::AstNode* RocksDBPrimaryIndex::specializeCondition(
|
||||
arangodb::aql::AstNode* node, arangodb::aql::Variable const* reference) const {
|
||||
SimpleAttributeEqualityMatcher matcher(IndexAttributes);
|
||||
return matcher.specializeOne(this, node, reference);
|
||||
return SkiplistIndexAttributeMatcher::specializeCondition(this, node, reference);
|
||||
}
|
||||
|
||||
/// @brief create the iterator, for a single attribute, IN operator
|
||||
IndexIterator* RocksDBPrimaryIndex::createInIterator(transaction::Methods* trx,
|
||||
arangodb::aql::AstNode const* attrNode,
|
||||
arangodb::aql::AstNode const* valNode) {
|
||||
arangodb::aql::AstNode const* valNode,
|
||||
bool ascending) {
|
||||
// _key or _id?
|
||||
bool const isId = (attrNode->stringEquals(StaticStrings::IdString));
|
||||
|
||||
|
@ -480,10 +774,21 @@ IndexIterator* RocksDBPrimaryIndex::createInIterator(transaction::Methods* trx,
|
|||
size_t const n = valNode->numMembers();
|
||||
|
||||
// only leave the valid elements
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
handleValNode(trx, keys.get(), valNode->getMemberUnchecked(i), isId);
|
||||
TRI_IF_FAILURE("PrimaryIndex::iteratorValNodes") {
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||
if (ascending) {
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
handleValNode(trx, keys.get(), valNode->getMemberUnchecked(i), isId);
|
||||
TRI_IF_FAILURE("PrimaryIndex::iteratorValNodes") {
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
size_t i = n;
|
||||
while (i > 0) {
|
||||
--i;
|
||||
handleValNode(trx, keys.get(), valNode->getMemberUnchecked(i), isId);
|
||||
TRI_IF_FAILURE("PrimaryIndex::iteratorValNodes") {
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -492,7 +797,7 @@ IndexIterator* RocksDBPrimaryIndex::createInIterator(transaction::Methods* trx,
|
|||
}
|
||||
|
||||
keys->close();
|
||||
|
||||
|
||||
return new RocksDBPrimaryIndexInIterator(&_collection, trx, this, std::move(keys), !isId);
|
||||
}
|
||||
|
||||
|
|
|
@ -103,6 +103,45 @@ class RocksDBPrimaryIndexInIterator final : public IndexIterator {
|
|||
bool const _allowCoveringIndexOptimization;
|
||||
};
|
||||
|
||||
class RocksDBPrimaryIndexRangeIterator final : public IndexIterator {
|
||||
private:
|
||||
friend class RocksDBVPackIndex;
|
||||
|
||||
public:
|
||||
RocksDBPrimaryIndexRangeIterator(LogicalCollection* collection, transaction::Methods* trx,
|
||||
arangodb::RocksDBPrimaryIndex const* index,
|
||||
bool reverse, RocksDBKeyBounds&& bounds);
|
||||
|
||||
~RocksDBPrimaryIndexRangeIterator() = default;
|
||||
|
||||
public:
|
||||
char const* typeName() const override { return "rocksdb-range-index-iterator"; }
|
||||
|
||||
/// @brief Get the next limit many elements in the index
|
||||
bool next(LocalDocumentIdCallback const& cb, size_t limit) override;
|
||||
|
||||
void skip(uint64_t count, uint64_t& skipped) override;
|
||||
|
||||
/// @brief Reset the cursor
|
||||
void reset() override;
|
||||
|
||||
/// @brief we provide a method to provide the index attribute values
|
||||
/// while scanning the index
|
||||
bool hasCovering() const override { return false; }
|
||||
// bool nextCovering(DocumentCallback const& cb, size_t limit) override;
|
||||
|
||||
private:
|
||||
bool outOfRange() const;
|
||||
|
||||
arangodb::RocksDBPrimaryIndex const* _index;
|
||||
rocksdb::Comparator const* _cmp;
|
||||
std::unique_ptr<rocksdb::Iterator> _iterator;
|
||||
bool const _reverse;
|
||||
RocksDBKeyBounds _bounds;
|
||||
// used for iterate_upper_bound iterate_lower_bound
|
||||
rocksdb::Slice _rangeBound;
|
||||
};
|
||||
|
||||
class RocksDBPrimaryIndex final : public RocksDBIndex {
|
||||
friend class RocksDBPrimaryIndexEqIterator;
|
||||
friend class RocksDBPrimaryIndexInIterator;
|
||||
|
@ -125,7 +164,7 @@ class RocksDBPrimaryIndex final : public RocksDBIndex {
|
|||
|
||||
bool hasCoveringIterator() const override { return true; }
|
||||
|
||||
bool isSorted() const override { return false; }
|
||||
bool isSorted() const override { return true; }
|
||||
|
||||
bool hasSelectivityEstimate() const override { return true; }
|
||||
|
||||
|
@ -154,6 +193,10 @@ class RocksDBPrimaryIndex final : public RocksDBIndex {
|
|||
arangodb::aql::AstNode const*,
|
||||
arangodb::aql::Variable const*, size_t, size_t&,
|
||||
double&) const override;
|
||||
|
||||
bool supportsSortCondition(arangodb::aql::SortCondition const*,
|
||||
arangodb::aql::Variable const*, size_t, double&,
|
||||
size_t&) const override;
|
||||
|
||||
IndexIterator* iteratorForCondition(transaction::Methods*, ManagedDocumentResult*,
|
||||
arangodb::aql::AstNode const*,
|
||||
|
@ -184,7 +227,7 @@ class RocksDBPrimaryIndex final : public RocksDBIndex {
|
|||
private:
|
||||
/// @brief create the iterator, for a single attribute, IN operator
|
||||
IndexIterator* createInIterator(transaction::Methods*, arangodb::aql::AstNode const*,
|
||||
arangodb::aql::AstNode const*);
|
||||
arangodb::aql::AstNode const*, bool ascending);
|
||||
|
||||
/// @brief create the iterator, for a single attribute, EQ operator
|
||||
IndexIterator* createEqIterator(transaction::Methods*, arangodb::aql::AstNode const*,
|
||||
|
|
|
@ -170,6 +170,7 @@ void Task::shutdownTasks() {
|
|||
}
|
||||
|
||||
// wait for the tasks to be cleaned up
|
||||
int iterations = 0;
|
||||
while (true) {
|
||||
size_t size;
|
||||
{
|
||||
|
@ -177,12 +178,14 @@ void Task::shutdownTasks() {
|
|||
size = _tasks.size();
|
||||
}
|
||||
|
||||
if (size > 0) {
|
||||
LOG_TOPIC(INFO, Logger::FIXME) << "Waiting for " << size << " Tasks to complete.";
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
} else {
|
||||
if (size == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (++iterations % 10 == 0) {
|
||||
LOG_TOPIC(INFO, Logger::FIXME) << "waiting for " << size << " task(s) to complete";
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -201,12 +201,7 @@
|
|||
query += this.setFiltersForQuery(bindVars);
|
||||
// Sort result, only useful for a small number of docs
|
||||
if (this.getTotal() < this.MAX_SORT) {
|
||||
if (this.getSort() === '_key') {
|
||||
query += ' SORT TO_NUMBER(x.' + this.getSort() + ') == 0 ? x.' +
|
||||
this.getSort() + ' : TO_NUMBER(x.' + this.getSort() + ')';
|
||||
} else if (this.getSort() !== '') {
|
||||
query += ' SORT x.' + this.getSort();
|
||||
}
|
||||
}
|
||||
|
||||
if (bindVars.count !== 'all') {
|
||||
|
|
|
@ -296,13 +296,13 @@ function ensureIndexSuite() {
|
|||
assertTrue(idx1.unique);
|
||||
assertTrue(idx1.sparse);
|
||||
assertEqual([ "b", "c" ], idx1.fields);
|
||||
|
||||
|
||||
var idx2 = collection.ensureIndex({ type: "hash", unique: true, fields: [ "b", "c" ], sparse: false });
|
||||
assertEqual("hash", idx2.type);
|
||||
assertTrue(idx2.unique);
|
||||
assertFalse(idx2.sparse);
|
||||
assertEqual([ "b", "c" ], idx2.fields);
|
||||
|
||||
|
||||
var idx3 = collection.ensureIndex({ type: "hash", unique: false, fields: [ "b", "c" ], sparse: false });
|
||||
assertEqual("hash", idx3.type);
|
||||
assertFalse(idx3.unique);
|
||||
|
@ -315,14 +315,14 @@ function ensureIndexSuite() {
|
|||
assertTrue(idx1.sparse);
|
||||
assertEqual([ "b", "c" ], res.fields);
|
||||
assertEqual(idx1.id, res.id);
|
||||
|
||||
|
||||
res = collection.getIndexes()[collection.getIndexes().length - 2];
|
||||
assertEqual("hash", res.type);
|
||||
assertTrue(res.unique);
|
||||
assertFalse(idx2.sparse);
|
||||
assertEqual([ "b", "c" ], res.fields);
|
||||
assertEqual(idx2.id, res.id);
|
||||
|
||||
|
||||
res = collection.getIndexes()[collection.getIndexes().length - 1];
|
||||
assertEqual("hash", res.type);
|
||||
assertFalse(res.unique);
|
||||
|
@ -424,15 +424,15 @@ function ensureIndexSuite() {
|
|||
assertEqual("test" + i, doc._key);
|
||||
assertEqual(i, doc.value);
|
||||
}
|
||||
|
||||
|
||||
var query = "FOR doc IN " + collection.name() + " FILTER doc._key == 'test1' && doc.value == 1 RETURN doc";
|
||||
var st = db._createStatement({ query: query });
|
||||
|
||||
var found = false;
|
||||
st.explain().plan.nodes.forEach(function(node) {
|
||||
|
||||
var found = false;
|
||||
st.explain().plan.nodes.forEach(function(node) {
|
||||
if (node.type === "IndexNode") {
|
||||
assertTrue(node.indexes[0].type === "primary" || node.indexes[0].type === "hash");
|
||||
found = true;
|
||||
found = true;
|
||||
}
|
||||
});
|
||||
assertTrue(found);
|
||||
|
@ -566,13 +566,13 @@ function ensureIndexSuite() {
|
|||
assertTrue(idx1.unique);
|
||||
assertTrue(idx1.sparse);
|
||||
assertEqual([ "b", "c" ], idx1.fields);
|
||||
|
||||
|
||||
var idx2 = collection.ensureIndex({ type: "skiplist", unique: true, fields: [ "b", "c" ], sparse: false });
|
||||
assertEqual("skiplist", idx2.type);
|
||||
assertTrue(idx2.unique);
|
||||
assertFalse(idx2.sparse);
|
||||
assertEqual([ "b", "c" ], idx2.fields);
|
||||
|
||||
|
||||
var idx3 = collection.ensureIndex({ type: "skiplist", unique: false, fields: [ "b", "c" ], sparse: false });
|
||||
assertEqual("skiplist", idx3.type);
|
||||
assertFalse(idx3.unique);
|
||||
|
@ -585,14 +585,14 @@ function ensureIndexSuite() {
|
|||
assertTrue(idx1.sparse);
|
||||
assertEqual([ "b", "c" ], res.fields);
|
||||
assertEqual(idx1.id, res.id);
|
||||
|
||||
|
||||
res = collection.getIndexes()[collection.getIndexes().length - 2];
|
||||
assertEqual("skiplist", res.type);
|
||||
assertTrue(res.unique);
|
||||
assertFalse(idx2.sparse);
|
||||
assertEqual([ "b", "c" ], res.fields);
|
||||
assertEqual(idx2.id, res.id);
|
||||
|
||||
|
||||
res = collection.getIndexes()[collection.getIndexes().length - 1];
|
||||
assertEqual("skiplist", res.type);
|
||||
assertFalse(res.unique);
|
||||
|
@ -627,12 +627,12 @@ function ensureIndexSuite() {
|
|||
|
||||
var query = "FOR doc IN " + collection.name() + " FILTER doc._rev >= 0 SORT doc._rev RETURN doc";
|
||||
var st = db._createStatement({ query: query });
|
||||
|
||||
var found = false;
|
||||
st.explain().plan.nodes.forEach(function(node) {
|
||||
|
||||
var found = false;
|
||||
st.explain().plan.nodes.forEach(function(node) {
|
||||
if (node.type === "IndexNode") {
|
||||
assertEqual("skiplist", node.indexes[0].type);
|
||||
found = true;
|
||||
found = true;
|
||||
}
|
||||
});
|
||||
assertTrue(found);
|
||||
|
@ -671,15 +671,15 @@ function ensureIndexSuite() {
|
|||
assertEqual("test" + i, doc._key);
|
||||
assertEqual(i, doc.value);
|
||||
}
|
||||
|
||||
|
||||
var query = "FOR doc IN " + collection.name() + " FILTER doc._key >= 0 SORT doc._key RETURN doc";
|
||||
var st = db._createStatement({ query: query });
|
||||
|
||||
var found = false;
|
||||
st.explain().plan.nodes.forEach(function(node) {
|
||||
|
||||
var found = false;
|
||||
st.explain().plan.nodes.forEach(function(node) {
|
||||
if (node.type === "IndexNode") {
|
||||
assertEqual("skiplist", node.indexes[0].type);
|
||||
found = true;
|
||||
assertTrue([ "skiplist", "primary" ].includes(node.indexes[0].type), node.indexes[0].type + " is not in [ 'skiplist', 'primary' ]");
|
||||
found = true;
|
||||
}
|
||||
});
|
||||
assertTrue(found);
|
||||
|
@ -721,12 +721,12 @@ function ensureIndexSuite() {
|
|||
|
||||
var query = "FOR doc IN " + collection.name() + " FILTER doc._key == 'test1' && doc.value == 1 RETURN doc";
|
||||
var st = db._createStatement({ query: query });
|
||||
|
||||
var found = false;
|
||||
st.explain().plan.nodes.forEach(function(node) {
|
||||
|
||||
var found = false;
|
||||
st.explain().plan.nodes.forEach(function(node) {
|
||||
if (node.type === "IndexNode") {
|
||||
assertTrue(node.indexes[0].type === "primary" || node.indexes[0].type === "skiplist");
|
||||
found = true;
|
||||
found = true;
|
||||
}
|
||||
});
|
||||
assertTrue(found);
|
||||
|
@ -964,12 +964,12 @@ function ensureIndexEdgesSuite() {
|
|||
|
||||
var query = "FOR doc IN " + edge.name() + " FILTER doc._from >= 0 SORT doc._from RETURN doc";
|
||||
var st = db._createStatement({ query: query });
|
||||
|
||||
var found = false;
|
||||
st.explain().plan.nodes.forEach(function(node) {
|
||||
|
||||
var found = false;
|
||||
st.explain().plan.nodes.forEach(function(node) {
|
||||
if (node.type === "IndexNode") {
|
||||
assertEqual("skiplist", node.indexes[0].type);
|
||||
found = true;
|
||||
found = true;
|
||||
}
|
||||
});
|
||||
assertTrue(found);
|
||||
|
@ -1001,12 +1001,12 @@ function ensureIndexEdgesSuite() {
|
|||
|
||||
var query = "FOR doc IN " + edge.name() + " FILTER doc._to >= 0 SORT doc._to RETURN doc";
|
||||
var st = db._createStatement({ query: query });
|
||||
|
||||
var found = false;
|
||||
st.explain().plan.nodes.forEach(function(node) {
|
||||
|
||||
var found = false;
|
||||
st.explain().plan.nodes.forEach(function(node) {
|
||||
if (node.type === "IndexNode") {
|
||||
assertEqual("skiplist", node.indexes[0].type);
|
||||
found = true;
|
||||
found = true;
|
||||
}
|
||||
});
|
||||
assertTrue(found);
|
||||
|
@ -1036,16 +1036,16 @@ function ensureIndexEdgesSuite() {
|
|||
|
||||
assertEqual(idx.id, res.id);
|
||||
|
||||
var query = "FOR doc IN " + edge.name() +
|
||||
var query = "FOR doc IN " + edge.name() +
|
||||
" FILTER doc._from == 'UnitTestsCollectionIdxVertex/test1' && " +
|
||||
"doc._to == 'UnitTestsCollectionIdxVertex' RETURN doc";
|
||||
var st = db._createStatement({ query: query });
|
||||
|
||||
var found = false;
|
||||
st.explain().plan.nodes.forEach(function(node) {
|
||||
|
||||
var found = false;
|
||||
st.explain().plan.nodes.forEach(function(node) {
|
||||
if (node.type === "IndexNode") {
|
||||
assertTrue(node.indexes[0].type === "hash");
|
||||
found = true;
|
||||
found = true;
|
||||
}
|
||||
});
|
||||
assertTrue(found);
|
||||
|
@ -1075,16 +1075,16 @@ function ensureIndexEdgesSuite() {
|
|||
|
||||
assertEqual(idx.id, res.id);
|
||||
|
||||
var query = "FOR doc IN " + edge.name() +
|
||||
" FILTER doc._from == 'UnitTestsCollectionIdxVertex/test1' && " +
|
||||
var query = "FOR doc IN " + edge.name() +
|
||||
" FILTER doc._from == 'UnitTestsCollectionIdxVertex/test1' && " +
|
||||
"doc._to == 'UnitTestsCollectionIdxVertex' RETURN doc";
|
||||
var st = db._createStatement({ query: query });
|
||||
|
||||
var found = false;
|
||||
st.explain().plan.nodes.forEach(function(node) {
|
||||
|
||||
var found = false;
|
||||
st.explain().plan.nodes.forEach(function(node) {
|
||||
if (node.type === "IndexNode") {
|
||||
assertTrue(node.indexes[0].type === "skiplist");
|
||||
found = true;
|
||||
found = true;
|
||||
}
|
||||
});
|
||||
assertTrue(found);
|
||||
|
|
|
@ -415,7 +415,7 @@ function ahuacatlFailureSuite () {
|
|||
|
||||
testEnumerateCollectionBlock : function () {
|
||||
internal.debugSetFailAt("EnumerateCollectionBlock::moreDocuments");
|
||||
assertFailingQuery("FOR i IN " + c.name() + " FILTER i.value2 == 9 COLLECT key = i._key RETURN key");
|
||||
assertFailingQuery("FOR i IN " + c.name() + " FILTER i.value2 == 9 COLLECT key = i.value2 RETURN key");
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -46,7 +46,7 @@ function optimizerCollectMethodsTestSuite () {
|
|||
c = db._create("UnitTestsCollection", { numberOfShards: 3 });
|
||||
|
||||
for (var i = 0; i < 1500; ++i) {
|
||||
c.save({ group: "test" + (i % 10), value: i });
|
||||
c.save({ group: "test" + (i % 10), value: i, haxe: "test" + i });
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -96,7 +96,7 @@ function optimizerCollectMethodsTestSuite () {
|
|||
testNumberOfPlansWithInto : function () {
|
||||
var queries = [
|
||||
"FOR j IN " + c.name() + " COLLECT value = j INTO g RETURN g",
|
||||
"FOR j IN " + c.name() + " COLLECT value = j INTO g = j._key RETURN g",
|
||||
"FOR j IN " + c.name() + " COLLECT value = j INTO g = j.haxe RETURN g",
|
||||
"FOR j IN " + c.name() + " COLLECT value = j INTO g RETURN [ value, g ]",
|
||||
"FOR j IN " + c.name() + " COLLECT value = j INTO g KEEP j RETURN g"
|
||||
];
|
||||
|
@ -115,7 +115,7 @@ function optimizerCollectMethodsTestSuite () {
|
|||
testHashed : function () {
|
||||
var queries = [
|
||||
[ "FOR j IN " + c.name() + " COLLECT value = j RETURN value", 1500 ],
|
||||
[ "FOR j IN " + c.name() + " COLLECT value = j._key RETURN value", 1500 ],
|
||||
[ "FOR j IN " + c.name() + " COLLECT value = j.haxe RETURN value", 1500 ],
|
||||
[ "FOR j IN " + c.name() + " COLLECT value = j.group RETURN value", 10 ],
|
||||
[ "FOR j IN " + c.name() + " COLLECT value1 = j.group, value2 = j.value RETURN [ value1, value2 ]", 1500 ],
|
||||
[ "FOR j IN " + c.name() + " COLLECT value = j.group WITH COUNT INTO l RETURN [ value, l ]", 10 ],
|
||||
|
@ -159,7 +159,7 @@ function optimizerCollectMethodsTestSuite () {
|
|||
|
||||
var queries = [
|
||||
[ "FOR j IN " + c.name() + " COLLECT value = j RETURN value", 1500 ],
|
||||
[ "FOR j IN " + c.name() + " COLLECT value = j._key RETURN value", 1500 ],
|
||||
[ "FOR j IN " + c.name() + " COLLECT value = j.haxe RETURN value", 1500 ],
|
||||
[ "FOR j IN " + c.name() + " COLLECT value = j.group RETURN value", 10 ],
|
||||
[ "FOR j IN " + c.name() + " COLLECT value1 = j.group, value2 = j.value RETURN [ value1, value2 ]", 1500 ],
|
||||
[ "FOR j IN " + c.name() + " COLLECT value = j.group WITH COUNT INTO l RETURN [ value, l ]", 10 ],
|
||||
|
@ -204,7 +204,7 @@ function optimizerCollectMethodsTestSuite () {
|
|||
|
||||
var queries = [
|
||||
[ "FOR j IN " + c.name() + " COLLECT value = j RETURN value", 1500, false],
|
||||
[ "FOR j IN " + c.name() + " COLLECT value = j._key RETURN value", 1500, false],
|
||||
[ "FOR j IN " + c.name() + " COLLECT value = j.haxe RETURN value", 1500, false],
|
||||
[ "FOR j IN " + c.name() + " COLLECT value = j.group RETURN value", 10, true],
|
||||
[ "FOR j IN " + c.name() + " COLLECT value1 = j.group, value2 = j.value RETURN [ value1, value2 ]", 1500, true ],
|
||||
[ "FOR j IN " + c.name() + " COLLECT value = j.group WITH COUNT INTO l RETURN [ value, l ]", 10, true ],
|
||||
|
@ -312,7 +312,7 @@ function optimizerCollectMethodsTestSuite () {
|
|||
testSortRemoval : function () {
|
||||
var queries = [
|
||||
[ "FOR j IN " + c.name() + " COLLECT value = j SORT null RETURN value", 1500 ],
|
||||
[ "FOR j IN " + c.name() + " COLLECT value = j._key SORT null RETURN value", 1500 ],
|
||||
[ "FOR j IN " + c.name() + " COLLECT value = j.haxe SORT null RETURN value", 1500 ],
|
||||
[ "FOR j IN " + c.name() + " COLLECT value = j.group SORT null RETURN value", 10 ],
|
||||
[ "FOR j IN " + c.name() + " COLLECT value1 = j.group, value2 = j.value SORT null RETURN [ value1, value2 ]", 1500 ],
|
||||
[ "FOR j IN " + c.name() + " COLLECT value = j.group WITH COUNT INTO l SORT null RETURN [ value, l ]", 10 ],
|
||||
|
|
|
@ -0,0 +1,512 @@
|
|||
/*jshint globalstrict:false, strict:false, maxlen: 500 */
|
||||
/*global assertTrue, assertEqual, assertNotEqual, AQL_EXPLAIN, AQL_EXECUTE */
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief tests for index usage
|
||||
///
|
||||
/// @file
|
||||
///
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2010-2012 triagens GmbH, Cologne, Germany
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
/// Copyright holder is triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Jan Christoph Uhde
|
||||
/// @author Copyright 2018, triAGENS GmbH, Cologne, Germany
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var jsunity = require("jsunity");
|
||||
var db = require("@arangodb").db;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test suite
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function optimizerIndexesRangesTestSuite () {
|
||||
let c;
|
||||
|
||||
return {
|
||||
setUpAll : function () {
|
||||
db._drop("UnitTestsCollection");
|
||||
c = db._create("UnitTestsCollection");
|
||||
|
||||
for (let i = 0; i < 2000; ++i) {
|
||||
c.save({ _key: "test" + String(i).padStart(4, '0') });
|
||||
}
|
||||
},
|
||||
|
||||
tearDownAll : function () {
|
||||
db._drop("UnitTestsCollection");
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test primary index usage
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testPrimaryIndexRanges : function () {
|
||||
let queries = [ // queries[0] - query -- queries[1] - expected result
|
||||
// _key and _id mixed
|
||||
[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key >= 'test1990' && i._id <= '" + c.name() + "/test2') RETURN i._key",
|
||||
[ "test1990", "test1991", "test1992", "test1993", "test1994", "test1995", "test1996", "test1997", "test1998", "test1999" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key > 'test1990' && i._id < '" + c.name() + "/test1999') RETURN i._key",
|
||||
[ "test1991", "test1992", "test1993", "test1994", "test1995", "test1996", "test1997", "test1998" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key > 'test1990' && i._id < '" + c.name() + "/test1999' && i._key < 'test1996') RETURN i._key",
|
||||
[ "test1991", "test1992", "test1993", "test1994", "test1995" ]
|
||||
],
|
||||
|
||||
// more than one condition
|
||||
[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key >= 'test1990' && i._key <= 'test2') RETURN i._key",
|
||||
[ "test1990", "test1991", "test1992", "test1993", "test1994", "test1995", "test1996", "test1997", "test1998", "test1999" ]
|
||||
],[
|
||||
"for i in " + c.name() + " filter (i._key > 'test1990' && i._key < 'test1999') return i._key",
|
||||
[ "test1991", "test1992", "test1993", "test1994", "test1995", "test1996", "test1997", "test1998" ]
|
||||
],[
|
||||
"for i in " + c.name() + " filter (i._key > 'test1990' && i._key < 'test1999' && i._key < 'test1996') return i._key",
|
||||
[ "test1991", "test1992", "test1993", "test1994", "test1995" ]
|
||||
],
|
||||
|
||||
// tests with out-of-bounds compare keys
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key == null) RETURN i._key", [] ],
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key == false) RETURN i._key", [] ],
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key == true) RETURN i._key", [] ],
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key == 99999) RETURN i._key", [] ],
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key == '') RETURN i._key", [] ],
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key == ' ') RETURN i._key", [] ],
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key == []) RETURN i._key", [] ],
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key == {}) RETURN i._key", [] ],
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key IN [null]) RETURN i._key", [] ],
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key IN [false]) RETURN i._key", [] ],
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key IN [true]) RETURN i._key", [] ],
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key IN [99999]) RETURN i._key", [] ],
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key IN ['']) RETURN i._key", [] ],
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key IN [' ']) RETURN i._key", [] ],
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key IN [[]]) RETURN i._key", [] ],
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key IN [{}]) RETURN i._key", [] ],
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key < null) RETURN i._key", [] ],
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key < false) RETURN i._key", [] ],
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key < true) RETURN i._key", [] ],
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key < 99999) RETURN i._key", [] ],
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key < '') RETURN i._key", [] ],
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key < ' ') RETURN i._key", [] ],
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key <= null) RETURN i._key", [] ],
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key <= false) RETURN i._key", [] ],
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key <= true) RETURN i._key", [] ],
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key <= 99999) RETURN i._key", [] ],
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key <= '') RETURN i._key", [] ],
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key <= ' ') RETURN i._key", [] ],
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key > 'zzz') RETURN i._key", [] ],
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key > []) RETURN i._key", [] ],
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key > {}) RETURN i._key", [] ],
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key >= 'zzz') RETURN i._key", [] ],
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key >= []) RETURN i._key", [] ],
|
||||
[ "FOR i IN " + c.name() + " FILTER (i._key >= {}) RETURN i._key", [] ],
|
||||
|
||||
// tests for _key
|
||||
[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key < 'test0002') RETURN i._key",
|
||||
[ "test0000", "test0001" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER ('test0002' > i._key) RETURN i._key",
|
||||
[ "test0000", "test0001" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key > 'test1997') RETURN i._key",
|
||||
[ "test1998", "test1999" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER ('test1997' < i._key) RETURN i._key",
|
||||
[ "test1998", "test1999" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key <= 'test0002') RETURN i._key",
|
||||
[ "test0000", "test0001", "test0002" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER ('test0002' >= i._key) RETURN i._key",
|
||||
[ "test0000", "test0001", "test0002" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key >= 'test1997') RETURN i._key",
|
||||
[ "test1997", "test1998", "test1999" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER ('test1997' <= i._key) RETURN i._key",
|
||||
[ "test1997", "test1998", "test1999" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key >= 9 && i._key < 'test0003') RETURN i._key",
|
||||
[ "test0000", "test0001", "test0002" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key >= null && i._key < 'test0003') RETURN i._key",
|
||||
[ "test0000", "test0001", "test0002" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key > 9 && i._key < 'test0003') RETURN i._key",
|
||||
[ "test0000", "test0001", "test0002" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key > null && i._key <= 'test0003') RETURN i._key",
|
||||
[ "test0000", "test0001", "test0002", "test0003" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key >= 9 && i._key <= 'test0003') RETURN i._key",
|
||||
[ "test0000", "test0001", "test0002", "test0003" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key >= null && i._key <= 'test0003') RETURN i._key",
|
||||
[ "test0000", "test0001", "test0002", "test0003" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key > 9 && i._key <= 'test0003') RETURN i._key",
|
||||
[ "test0000", "test0001", "test0002", "test0003" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key > null && i._key <= 'test0003') RETURN i._key",
|
||||
[ "test0000", "test0001", "test0002", "test0003" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key > 'test1997' && i._key < []) RETURN i._key",
|
||||
[ "test1998", "test1999" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key > 'test1997' && i._key < {}) RETURN i._key",
|
||||
[ "test1998", "test1999" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key > 'test1997' && i._key <= []) RETURN i._key",
|
||||
[ "test1998", "test1999" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key > 'test1997' && i._key <= {}) RETURN i._key",
|
||||
[ "test1998", "test1999" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key > 'test1997' && i._key < []) RETURN i._key",
|
||||
[ "test1998", "test1999" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key > 'test1997' && i._key < {}) RETURN i._key",
|
||||
[ "test1998", "test1999" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key > 'test1997' && i._key <= []) RETURN i._key",
|
||||
[ "test1998", "test1999" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key > 'test1997' && i._key <= {}) RETURN i._key",
|
||||
[ "test1998", "test1999" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER i._key IN ['test1999', 'test1998', 'test1997'] RETURN i._key",
|
||||
[ "test1997", "test1998", "test1999" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER i._key IN ['test1999', 'test1998', 'test1997'] SORT i._key RETURN i._key",
|
||||
[ "test1997", "test1998", "test1999" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER i._key IN ['test1997', 'test1998', 'test1999'] RETURN i._key",
|
||||
[ "test1997", "test1998", "test1999" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER i._key IN ['test1997', 'test1998', 'test1999'] SORT i._key RETURN i._key",
|
||||
[ "test1997", "test1998", "test1999" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key < 'test0002') SORT i._key DESC RETURN i._key",
|
||||
[ "test0001", "test0000" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER ('test0002' > i._key) SORT i._key DESC RETURN i._key",
|
||||
[ "test0001", "test0000" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key > 'test1997') SORT i._key DESC RETURN i._key",
|
||||
[ "test1999", "test1998" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER ('test1997' < i._key) SORT i._key DESC RETURN i._key",
|
||||
[ "test1999", "test1998" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key <= 'test0002') SORT i._key DESC RETURN i._key",
|
||||
[ "test0002", "test0001", "test0000" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER ('test0002' >= i._key) SORT i._key DESC RETURN i._key",
|
||||
[ "test0002", "test0001", "test0000" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key >= 'test1997') SORT i._key DESC RETURN i._key",
|
||||
[ "test1999", "test1998", "test1997" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER ('test1997' <= i._key) SORT i._key DESC RETURN i._key",
|
||||
[ "test1999", "test1998", "test1997" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key >= 9 && i._key < 'test0003') SORT i._key DESC RETURN i._key",
|
||||
[ "test0002", "test0001", "test0000" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key >= null && i._key < 'test0003') SORT i._key DESC RETURN i._key",
|
||||
[ "test0002", "test0001", "test0000" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key > 9 && i._key < 'test0003') SORT i._key DESC RETURN i._key",
|
||||
[ "test0002", "test0001", "test0000" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key > null && i._key <= 'test0003') SORT i._key DESC RETURN i._key",
|
||||
[ "test0003", "test0002", "test0001", "test0000" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key >= 9 && i._key <= 'test0003') SORT i._key DESC RETURN i._key",
|
||||
[ "test0003", "test0002", "test0001", "test0000" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key >= null && i._key <= 'test0003') SORT i._key DESC RETURN i._key",
|
||||
[ "test0003", "test0002", "test0001", "test0000" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key > 9 && i._key <= 'test0003') SORT i._key DESC RETURN i._key",
|
||||
[ "test0003", "test0002", "test0001", "test0000" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key > null && i._key <= 'test0003') SORT i._key DESC RETURN i._key",
|
||||
[ "test0003", "test0002", "test0001", "test0000" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key > 'test1997' && i._key < []) SORT i._key DESC RETURN i._key",
|
||||
[ "test1999", "test1998" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key > 'test1997' && i._key < {}) SORT i._key DESC RETURN i._key",
|
||||
[ "test1999", "test1998" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key > 'test1997' && i._key <= []) SORT i._key DESC RETURN i._key",
|
||||
[ "test1999", "test1998" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key > 'test1997' && i._key <= {}) SORT i._key DESC RETURN i._key",
|
||||
[ "test1999", "test1998" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key > 'test1997' && i._key < []) SORT i._key DESC RETURN i._key",
|
||||
[ "test1999", "test1998" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key > 'test1997' && i._key < {}) SORT i._key DESC RETURN i._key",
|
||||
[ "test1999", "test1998" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key > 'test1997' && i._key <= []) SORT i._key DESC RETURN i._key",
|
||||
[ "test1999", "test1998" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key > 'test1997' && i._key <= {}) SORT i._key DESC RETURN i._key",
|
||||
[ "test1999", "test1998" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER i._key IN ['test1999', 'test1998', 'test1997'] SORT i._key DESC RETURN i._key",
|
||||
[ "test1999", "test1998", "test1997" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER i._key IN ['test1997', 'test1998', 'test1999'] SORT i._key DESC RETURN i._key",
|
||||
[ "test1999", "test1998", "test1997" ]
|
||||
],
|
||||
|
||||
// tests for _id
|
||||
[
|
||||
"FOR i IN " + c.name() + " FILTER (i._id < '" + c.name() + "/test0002') RETURN i._key",
|
||||
[ "test0000", "test0001" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER ('" + c.name() + "/test0002' > i._id) RETURN i._key",
|
||||
[ "test0000", "test0001" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._id > '" + c.name() + "/test1997') RETURN i._key",
|
||||
[ "test1998", "test1999" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER ('" + c.name() + "/test1997' < i._id) RETURN i._key",
|
||||
[ "test1998", "test1999" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._id <= '" + c.name() + "/test0002') RETURN i._key",
|
||||
[ "test0000", "test0001", "test0002" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER ('" + c.name() + "/test0002' >= i._id) RETURN i._key",
|
||||
[ "test0000", "test0001", "test0002" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._id >= '" + c.name() + "/test1997') RETURN i._key",
|
||||
[ "test1997", "test1998", "test1999" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER ('" + c.name() + "/test1997' <= i._id) RETURN i._key",
|
||||
[ "test1997", "test1998", "test1999" ]
|
||||
],
|
||||
|
||||
// test with sort
|
||||
[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key > 'test0002' && i._key <= 'test0005') SORT i._key RETURN i._key",
|
||||
[ "test0003", "test0004", "test0005" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key >= 'test0002' && i._key <= 'test0005') SORT i._key RETURN i._key",
|
||||
[ "test0002", "test0003", "test0004", "test0005" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key > 'test0002' && i._key < 'test0005') SORT i._key RETURN i._key",
|
||||
[ "test0003", "test0004" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key >= 'test0002' && i._key < 'test0005') SORT i._key RETURN i._key",
|
||||
[ "test0002", "test0003", "test0004" ]
|
||||
],
|
||||
|
||||
// test with sort DESC
|
||||
[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key > 'test0002' && i._key <= 'test0005') SORT i._key DESC RETURN i._key",
|
||||
[ "test0005", "test0004", "test0003" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key >= 'test0002' && i._key <= 'test0005') SORT i._key DESC RETURN i._key",
|
||||
[ "test0005", "test0004", "test0003", "test0002" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key > 'test0002' && i._key < 'test0005') SORT i._key DESC RETURN i._key",
|
||||
[ "test0004", "test0003" ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key >= 'test0002' && i._key < 'test0005') SORT i._key DESC RETURN i._key",
|
||||
[ "test0004", "test0003", "test0002" ]
|
||||
],
|
||||
|
||||
//// edge cases
|
||||
[
|
||||
// one element in range
|
||||
"FOR i IN " + c.name() + " FILTER ('test1997' <= i._key && 'test1997' >= i._key) RETURN i._key",
|
||||
[ "test1997" ]
|
||||
]
|
||||
]; //end of array
|
||||
|
||||
queries.forEach(function(query) {
|
||||
let plan = AQL_EXPLAIN(query[0]).plan;
|
||||
let nodeTypes = plan.nodes.map(function(node) {
|
||||
return node.type;
|
||||
});
|
||||
|
||||
// ensure an index is used
|
||||
assertTrue(nodeTypes.indexOf("IndexNode") !== -1 ||
|
||||
nodeTypes.indexOf("SingleRemoteOperationNode") !== -1, query);
|
||||
|
||||
// must never have a SortNode, as we use the index for sorting
|
||||
assertEqual(-1, nodeTypes.indexOf("SortNode"), query);
|
||||
|
||||
let results = AQL_EXECUTE(query[0]);
|
||||
|
||||
assertEqual(query[1].length , results.json.length, query);
|
||||
assertEqual(query[1], results.json, query);
|
||||
assertEqual(0, results.stats.scannedFull);
|
||||
});
|
||||
},
|
||||
|
||||
testPrimaryIndexRangesEdgeCases : function () {
|
||||
let queries = [ // queries[0] - query -- queries[1] - expected result
|
||||
[
|
||||
// upper is greater than lower
|
||||
"FOR i IN " + c.name() + " FILTER (i._key <= 'test1997' && i._key >= 'test1998') RETURN i._key",
|
||||
[ ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key == 'test1997' && i._key >= 'test1997') RETURN i._key",
|
||||
[ 'test1997' ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key == 'test1997' && i._key == 'test1998') RETURN i._key",
|
||||
[ ]
|
||||
],[
|
||||
"FOR i IN " + c.name() + " FILTER (i._key < true) RETURN i._key",
|
||||
[ ]
|
||||
]
|
||||
]; //end of array
|
||||
|
||||
queries.forEach(function(query) {
|
||||
let plan = AQL_EXPLAIN(query[0]).plan;
|
||||
let nodeTypes = plan.nodes.map(function(node) {
|
||||
return node.type;
|
||||
});
|
||||
|
||||
let results = AQL_EXECUTE(query[0]);
|
||||
|
||||
assertEqual(query[1].length , results.json.length, query);
|
||||
results.json.forEach(function(value) {
|
||||
assertNotEqual(-1, query[1].indexOf(value));
|
||||
});
|
||||
|
||||
assertEqual(0, results.stats.scannedFull);
|
||||
});
|
||||
},
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
function optimizerIndexesRangesCollectionsTestSuite () {
|
||||
return {
|
||||
setUp : function () {
|
||||
db._drop("UnitTestsCollection1");
|
||||
db._drop("UnitTestsCollection2");
|
||||
db._drop("UnitTestsCollection3");
|
||||
|
||||
for (let i = 1; i <= 3; ++i) {
|
||||
let c = db._create("UnitTestsCollection" + i);
|
||||
|
||||
for (let j = 0; j < 2000; ++j) {
|
||||
c.save({ _key: "test" + String(j).padStart(4, '0') });
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
tearDown : function () {
|
||||
db._drop("UnitTestsCollection1");
|
||||
db._drop("UnitTestsCollection2");
|
||||
db._drop("UnitTestsCollection3");
|
||||
},
|
||||
|
||||
testPrimaryIndexRangesIdWithDifferentCollections : function () {
|
||||
let all = [];
|
||||
for (let i = 0; i < 2000; ++i) {
|
||||
all.push("test" + String(i).padStart(4, '0'));
|
||||
}
|
||||
|
||||
let queries = [
|
||||
[
|
||||
"FOR i IN UnitTestsCollection1 FILTER i._id == 'UnitTestsCollection1/test1996' RETURN i._key",
|
||||
[ "test1996" ]
|
||||
],[
|
||||
"FOR i IN UnitTestsCollection1 FILTER i._id >= 'UnitTestsCollection1/test1996' RETURN i._key",
|
||||
[ "test1996", "test1997", "test1998", "test1999" ]
|
||||
],[
|
||||
"FOR i IN UnitTestsCollection1 FILTER i._id > 'UnitTestsCollection1/test1996' RETURN i._key",
|
||||
[ "test1997", "test1998", "test1999" ]
|
||||
],[
|
||||
"FOR i IN UnitTestsCollection1 FILTER i._id <= 'UnitTestsCollection1/test0003' RETURN i._key",
|
||||
[ "test0000", "test0001", "test0002", "test0003" ]
|
||||
],[
|
||||
"FOR i IN UnitTestsCollection1 FILTER i._id < 'UnitTestsCollection1/test0003' RETURN i._key",
|
||||
[ "test0000", "test0001", "test0002" ]
|
||||
],[
|
||||
"FOR i IN UnitTestsCollection1 FILTER i._id == 'UnitTestsCollection3/test0003' RETURN i._key",
|
||||
[ ]
|
||||
],[
|
||||
"FOR i IN UnitTestsCollection3 FILTER i._id < 'UnitTestsCollection1/test0003' RETURN i._key",
|
||||
[ ]
|
||||
],[
|
||||
"FOR i IN UnitTestsCollection3 FILTER i._id <= 'UnitTestsCollection1/test0003' RETURN i._key",
|
||||
[ ]
|
||||
],[
|
||||
"FOR i IN UnitTestsCollection3 FILTER i._id > 'UnitTestsCollection1/test0003' RETURN i._key",
|
||||
all
|
||||
],[
|
||||
"FOR i IN UnitTestsCollection3 FILTER i._id >= 'UnitTestsCollection1/test0003' RETURN i._key",
|
||||
all
|
||||
],[
|
||||
"FOR i IN UnitTestsCollection1 FILTER i._id < 'UnitTestsCollection3/test0003' RETURN i._key",
|
||||
all
|
||||
],[
|
||||
"FOR i IN UnitTestsCollection1 FILTER i._id <= 'UnitTestsCollection3/test0003' RETURN i._key",
|
||||
all
|
||||
],[
|
||||
"FOR i IN UnitTestsCollection1 FILTER i._id > 'UnitTestsCollection3/test0003' RETURN i._key",
|
||||
[ ]
|
||||
],[
|
||||
"FOR i IN UnitTestsCollection1 FILTER i._id >= 'UnitTestsCollection3/test0003' RETURN i._key",
|
||||
[ ]
|
||||
]
|
||||
];
|
||||
|
||||
queries.forEach(function(query) {
|
||||
let plan = AQL_EXPLAIN(query[0]).plan;
|
||||
let nodeTypes = plan.nodes.map(function(node) {
|
||||
return node.type;
|
||||
});
|
||||
|
||||
// ensure an index is used
|
||||
assertTrue(nodeTypes.indexOf("IndexNode") !== -1 ||
|
||||
nodeTypes.indexOf("SingleRemoteOperationNode") !== -1, query);
|
||||
|
||||
// must never have a SortNode, as we use the index for sorting
|
||||
assertEqual(-1, nodeTypes.indexOf("SortNode"), query);
|
||||
|
||||
let results = AQL_EXECUTE(query[0]);
|
||||
|
||||
assertEqual(query[1].length , results.json.length, query);
|
||||
assertEqual(query[1], results.json, query);
|
||||
assertEqual(0, results.stats.scannedFull);
|
||||
});
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
jsunity.run(optimizerIndexesRangesTestSuite);
|
||||
jsunity.run(optimizerIndexesRangesCollectionsTestSuite);
|
||||
|
||||
return jsunity.done();
|
|
@ -840,7 +840,7 @@ function arrayIndexNonArraySuite () {
|
|||
});
|
||||
assertNotEqual(-1, nodeTypes.indexOf("IndexNode"));
|
||||
|
||||
const query2 = `FOR x IN ${cName} FILTER null IN x.a[*] SORT x._key RETURN x._key`;
|
||||
const query2 = `FOR x IN ${cName} FILTER null IN x.a[*] SORT x._rev RETURN x._key`;
|
||||
plan = AQL_EXPLAIN(query2).plan;
|
||||
nodeTypes = plan.nodes.map(function(node) {
|
||||
return node.type;
|
||||
|
@ -1157,7 +1157,7 @@ function arrayIndexNonArraySuite () {
|
|||
});
|
||||
assertNotEqual(-1, nodeTypes.indexOf("IndexNode"));
|
||||
|
||||
const query2 = `FOR x IN ${cName} FILTER null IN x.a[*] SORT x._key RETURN x._key`;
|
||||
const query2 = `FOR x IN ${cName} FILTER null IN x.a[*] SORT x._rev RETURN x._key`;
|
||||
plan = AQL_EXPLAIN(query2).plan;
|
||||
nodeTypes = plan.nodes.map(function(node) {
|
||||
return node.type;
|
||||
|
|
|
@ -41,16 +41,16 @@ var db = internal.db;
|
|||
function ahuacatlQueryOptimizerInTestSuite () {
|
||||
var c = null;
|
||||
var cn = "UnitTestsAhuacatlOptimizerIn";
|
||||
|
||||
|
||||
var explain = function (query, params) {
|
||||
return helper.getCompactPlan(AQL_EXPLAIN(query, params, { optimizer: { rules: [ "-all", "+use-indexes" ] } })).map(function(node) { return node.type; });
|
||||
};
|
||||
|
||||
|
||||
var ruleIsUsed = function (query) {
|
||||
var result = AQL_EXPLAIN(query, {}, { optimizer: { rules: [ "-all", "+use-indexes" ] } });
|
||||
assertTrue(result.plan.rules.indexOf("use-indexes") !== -1, query);
|
||||
};
|
||||
|
||||
|
||||
var ruleIsNotUsed = function (query) {
|
||||
var result = AQL_EXPLAIN(query, {}, { optimizer: { rules: [ "-all", "+use-indexes" ] } });
|
||||
assertTrue(result.plan.rules.indexOf("use-indexes") === -1, query);
|
||||
|
@ -95,7 +95,7 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
var actual = getQueryResults(query);
|
||||
assertEqual(16, actual.length);
|
||||
},
|
||||
|
||||
|
||||
testSortedArrayInStatic : function () {
|
||||
var values = [
|
||||
"WJItoWBuRBMBMajh", "WJIuWmBuRBMBMbdR",
|
||||
|
@ -171,12 +171,34 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
c.save({ _key: "test" + i, parent: "test" + (i - 1), parents: [ "test" + (i - 1) ] });
|
||||
}
|
||||
|
||||
var expected = [ 'test5', 'test7' ];
|
||||
var query = "LET parents = (FOR c IN " + cn + " FILTER c._key IN [ 'test5', 'test7' ] RETURN c._key) FOR c IN " + cn + " FILTER c._key IN parents SORT c._key RETURN c._key";
|
||||
// ascending order - input ascending
|
||||
var expected = [ 'test4', 'test6' ];
|
||||
var query = "LET parents = (FOR c IN " + cn + " FILTER c._key IN [ 'test4', 'test6' ] RETURN c._key) FOR c IN " + cn + " FILTER c._key IN parents SORT c._key RETURN c._key";
|
||||
var actual = getQueryResults(query);
|
||||
assertEqual(expected, actual);
|
||||
|
||||
assertEqual([ "SingletonNode", "SubqueryNode", "IndexNode", "CalculationNode", "FilterNode", "CalculationNode", "SortNode", "CalculationNode", "ReturnNode" ], explain(query));
|
||||
|
||||
// ascending order - input descending
|
||||
expected = [ 'test5', 'test7' ];
|
||||
query = "LET parents = (FOR c IN " + cn + " FILTER c._key IN [ 'test7', 'test5' ] RETURN c._key) FOR c IN " + cn + " FILTER c._key IN parents SORT c._key RETURN c._key";
|
||||
actual = getQueryResults(query);
|
||||
assertEqual(expected, actual);
|
||||
assertEqual([ "SingletonNode", "SubqueryNode", "IndexNode", "CalculationNode", "FilterNode", "CalculationNode", "SortNode", "CalculationNode", "ReturnNode" ], explain(query));
|
||||
|
||||
// descending order - input ascending
|
||||
expected = [ 'test8', 'test6' ];
|
||||
query = "LET parents = (FOR c IN " + cn + " FILTER c._key IN [ 'test6', 'test8' ] RETURN c._key) FOR c IN " + cn + " FILTER c._key IN parents SORT c._key DESC RETURN c._key";
|
||||
actual = getQueryResults(query);
|
||||
assertEqual(expected, actual);
|
||||
assertEqual([ "SingletonNode", "SubqueryNode", "IndexNode", "CalculationNode", "FilterNode", "CalculationNode", "SortNode", "CalculationNode", "ReturnNode" ], explain(query));
|
||||
|
||||
// descending order - input descending
|
||||
expected = [ 'test9', 'test7' ];
|
||||
query = "LET parents = (FOR c IN " + cn + " FILTER c._key IN [ 'test9', 'test7' ] RETURN c._key) FOR c IN " + cn + " FILTER c._key IN parents SORT c._key DESC RETURN c._key";
|
||||
actual = getQueryResults(query);
|
||||
assertEqual(expected, actual);
|
||||
assertEqual([ "SingletonNode", "SubqueryNode", "IndexNode", "CalculationNode", "FilterNode", "CalculationNode", "SortNode", "CalculationNode", "ReturnNode" ], explain(query));
|
||||
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -224,7 +246,7 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
var query = "LET parents = [ 'test5', 'test7' ] FOR c IN " + cn + " FILTER c.code IN parents SORT c.code RETURN c.code";
|
||||
var actual = getQueryResults(query);
|
||||
assertEqual(expected, actual);
|
||||
|
||||
|
||||
assertEqual([ "SingletonNode", "CalculationNode", "IndexNode", "CalculationNode", "FilterNode", "CalculationNode", "SortNode", "CalculationNode", "ReturnNode" ], explain(query));
|
||||
},
|
||||
|
||||
|
@ -243,7 +265,7 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
var query = "LET parents = (FOR c IN " + cn + " FILTER c.code IN [ 'test5', 'test7' ] RETURN c.code) FOR c IN " + cn + " FILTER c.code IN parents SORT c.code RETURN c.code";
|
||||
var actual = getQueryResults(query);
|
||||
assertEqual(expected, actual);
|
||||
|
||||
|
||||
assertEqual([ "SingletonNode", "SubqueryNode", "IndexNode", "CalculationNode", "FilterNode", "CalculationNode", "SortNode", "CalculationNode", "ReturnNode" ], explain(query));
|
||||
},
|
||||
|
||||
|
@ -328,7 +350,7 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
var query = "LET parents = [ 'test5', 'test7' ] FOR c IN " + cn + " FILTER c.code IN parents SORT c.code RETURN c.code";
|
||||
var actual = getQueryResults(query);
|
||||
assertEqual(expected, actual);
|
||||
|
||||
|
||||
assertEqual([ "SingletonNode", "CalculationNode", "IndexNode", "CalculationNode", "FilterNode", "CalculationNode", "SortNode", "CalculationNode", "ReturnNode" ], explain(query));
|
||||
},
|
||||
|
||||
|
@ -347,7 +369,7 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
var query = "LET parents = (FOR c IN " + cn + " FILTER c.code IN [ 'test5', 'test7' ] RETURN c.code) FOR c IN " + cn + " FILTER c.code IN parents SORT c.code RETURN c.code";
|
||||
var actual = getQueryResults(query);
|
||||
assertEqual(expected, actual);
|
||||
|
||||
|
||||
assertEqual([ "SingletonNode", "SubqueryNode", "IndexNode", "CalculationNode", "FilterNode", "CalculationNode", "SortNode", "CalculationNode", "ReturnNode" ], explain(query));
|
||||
},
|
||||
|
||||
|
@ -393,11 +415,11 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
for (i = 1; i < 100; ++i) {
|
||||
c.save({ _key: "test" + i });
|
||||
}
|
||||
|
||||
|
||||
var en = cn + "Edge";
|
||||
internal.db._drop(en);
|
||||
var e = internal.db._createEdgeCollection(en);
|
||||
|
||||
|
||||
for (i = 1; i < 100; ++i) {
|
||||
e.save(cn + "/test" + i, cn + "/test" + (i - 1), { });
|
||||
}
|
||||
|
@ -406,9 +428,9 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
var query = "LET parents = [ '" + cn + "/test5', '" + cn + "/test7' ] FOR c IN " + en + " FILTER c._from IN parents SORT c._to RETURN c._to";
|
||||
var actual = getQueryResults(query);
|
||||
assertEqual(expected, actual);
|
||||
|
||||
|
||||
assertEqual([ "SingletonNode", "CalculationNode", "IndexNode", "CalculationNode", "FilterNode", "CalculationNode", "SortNode", "CalculationNode", "ReturnNode" ], explain(query));
|
||||
|
||||
|
||||
internal.db._drop(en);
|
||||
},
|
||||
|
||||
|
@ -422,11 +444,11 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
for (i = 1; i < 100; ++i) {
|
||||
c.save({ _key: "test" + i });
|
||||
}
|
||||
|
||||
|
||||
var en = cn + "Edge";
|
||||
internal.db._drop(en);
|
||||
var e = internal.db._createEdgeCollection(en);
|
||||
|
||||
|
||||
for (i = 1; i < 100; ++i) {
|
||||
e.save(cn + "/test" + i, cn + "/test" + (i - 1), { });
|
||||
}
|
||||
|
@ -435,9 +457,9 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
var query = "LET parents = (FOR c IN " + cn + " FILTER c._key IN [ 'test5', 'test7' ] RETURN c._id) FOR c IN " + en + " FILTER c._from IN parents SORT c._to RETURN c._to";
|
||||
var actual = getQueryResults(query);
|
||||
assertEqual(expected, actual);
|
||||
|
||||
|
||||
assertEqual([ "SingletonNode", "SubqueryNode", "IndexNode", "CalculationNode", "FilterNode", "CalculationNode", "SortNode", "CalculationNode", "ReturnNode" ], explain(query));
|
||||
|
||||
|
||||
internal.db._drop(en);
|
||||
},
|
||||
|
||||
|
@ -451,11 +473,11 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
for (i = 1; i < 100; ++i) {
|
||||
c.save({ _key: "test" + i });
|
||||
}
|
||||
|
||||
|
||||
var en = cn + "Edge";
|
||||
internal.db._drop(en);
|
||||
var e = internal.db._createEdgeCollection(en);
|
||||
|
||||
|
||||
for (i = 1; i < 100; ++i) {
|
||||
e.save(cn + "/test" + i, cn + "/test" + (i - 1), { });
|
||||
}
|
||||
|
@ -463,7 +485,7 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
var expected = [ { keys: [ cn + '/test4' ] }, { keys: [ cn + '/test6' ] } ];
|
||||
var actual = getQueryResults("FOR c IN " + cn + " FILTER c._key IN [ 'test5', 'test7' ] SORT c._key RETURN { keys: (FOR c2 IN " + en + " FILTER c2._from IN [ c._id ] RETURN c2._to) }");
|
||||
assertEqual(expected, actual);
|
||||
|
||||
|
||||
internal.db._drop(en);
|
||||
},
|
||||
|
||||
|
@ -477,11 +499,11 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
for (i = 1; i < 100; ++i) {
|
||||
c.save({ _key: "test" + i, ids: [ cn + "/test" + i ] });
|
||||
}
|
||||
|
||||
|
||||
var en = cn + "Edge";
|
||||
internal.db._drop(en);
|
||||
var e = internal.db._createEdgeCollection(en);
|
||||
|
||||
|
||||
for (i = 1; i < 100; ++i) {
|
||||
e.save(cn + "/test" + i, cn + "/test" + (i - 1), { });
|
||||
}
|
||||
|
@ -489,7 +511,7 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
var expected = [ { keys: [ cn + '/test4' ] }, { keys: [ cn + '/test6' ] } ];
|
||||
var actual = getQueryResults("FOR c IN " + cn + " FILTER c._key IN [ 'test5', 'test7' ] SORT c._key RETURN { keys: (FOR c2 IN " + en + " FILTER c2._from IN c.ids RETURN c2._to) }");
|
||||
assertEqual(expected, actual);
|
||||
|
||||
|
||||
internal.db._drop(en);
|
||||
},
|
||||
|
||||
|
@ -589,37 +611,37 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
c.save({ value: 12 });
|
||||
c.save({ value: false });
|
||||
c.save({ value: null });
|
||||
|
||||
|
||||
var actual = getQueryResults("FOR i IN " + cn + " FILTER i.value IN [ 'red', 'green' ] SORT i.value RETURN i.value");
|
||||
assertEqual([ "green", "red" ], actual);
|
||||
|
||||
actual = getQueryResults("FOR i IN " + cn + " FILTER i.value NOT IN [ 'red', 'green' ] SORT i.value RETURN i.value");
|
||||
assertEqual([ null, false, 12, "blue" ], actual);
|
||||
|
||||
|
||||
actual = getQueryResults("FOR i IN " + cn + " FILTER i.value IN [ 'green', 'blue' ] SORT i.value RETURN i.value");
|
||||
assertEqual([ "blue", "green" ], actual);
|
||||
|
||||
actual = getQueryResults("FOR i IN " + cn + " FILTER i.value NOT IN [ 'green', 'blue' ] SORT i.value RETURN i.value");
|
||||
assertEqual([ null, false, 12, "red" ], actual);
|
||||
|
||||
|
||||
actual = getQueryResults("FOR i IN " + cn + " FILTER i.value IN [ 'foo', 'bar' ] SORT i.value RETURN i.value");
|
||||
assertEqual([ ], actual);
|
||||
|
||||
actual = getQueryResults("FOR i IN " + cn + " FILTER i.value NOT IN [ 'foo', 'bar' ] SORT i.value RETURN i.value");
|
||||
assertEqual([ null, false, 12, "blue", "green", "red" ], actual);
|
||||
|
||||
|
||||
actual = getQueryResults("FOR i IN " + cn + " FILTER i.value IN [ 12, false ] SORT i.value RETURN i.value");
|
||||
assertEqual([ false, 12 ], actual);
|
||||
|
||||
actual = getQueryResults("FOR i IN " + cn + " FILTER i.value NOT IN [ 12, false ] SORT i.value RETURN i.value");
|
||||
assertEqual([ null, "blue", "green", "red" ], actual);
|
||||
|
||||
|
||||
actual = getQueryResults("FOR i IN " + cn + " FILTER i.value IN [ 23, 'black', 'red', null ] SORT i.value RETURN i.value");
|
||||
assertEqual([ null, 'red' ], actual);
|
||||
|
||||
actual = getQueryResults("FOR i IN " + cn + " FILTER i.value NOT IN [ 23, 'black', 'red', null ] SORT i.value RETURN i.value");
|
||||
assertEqual([ false, 12, "blue", "green" ], actual);
|
||||
|
||||
|
||||
c.truncate();
|
||||
c.save({ value: [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, "red", "blue" ]});
|
||||
actual = getQueryResults("FOR i IN " + cn + " FILTER 12 IN i.value SORT i.value RETURN LENGTH(i.value)");
|
||||
|
@ -630,28 +652,28 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
|
||||
actual = getQueryResults("FOR i IN " + cn + " FILTER 12 NOT IN i.value SORT i.value RETURN LENGTH(i.value)");
|
||||
assertEqual([ ], actual);
|
||||
|
||||
|
||||
actual = getQueryResults("FOR i IN " + cn + " FILTER 13 IN i.value SORT i.value RETURN LENGTH(i.value)");
|
||||
assertEqual([ ], actual);
|
||||
|
||||
|
||||
actual = getQueryResults("FOR i IN " + cn + " FILTER 13 NOT IN i.value SORT i.value RETURN LENGTH(i.value)");
|
||||
assertEqual([ 14 ], actual);
|
||||
|
||||
|
||||
actual = getQueryResults("FOR i IN " + cn + " FILTER 'red' IN i.value SORT i.value RETURN LENGTH(i.value)");
|
||||
assertEqual([ 14 ], actual);
|
||||
|
||||
|
||||
actual = getQueryResults("FOR i IN " + cn + " FILTER 'red' NOT IN i.value SORT i.value RETURN LENGTH(i.value)");
|
||||
assertEqual([ ], actual);
|
||||
|
||||
actual = getQueryResults("FOR i IN " + cn + " FILTER 'fuchsia' NOT IN i.value SORT i.value RETURN LENGTH(i.value)");
|
||||
assertEqual([ 14 ], actual);
|
||||
},
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief check IN
|
||||
/// @brief check IN
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testInListHashIndex : function () {
|
||||
testInListHashIndex : function () {
|
||||
for (var i = 1; i < 100; ++i) {
|
||||
c.save({ value: i });
|
||||
}
|
||||
|
@ -662,8 +684,8 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
assertEqual(expected, actual);
|
||||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
testInListSkiplist : function () {
|
||||
|
||||
testInListSkiplist : function () {
|
||||
for (var i = 1; i < 100; ++i) {
|
||||
c.save({ value: i });
|
||||
}
|
||||
|
@ -674,8 +696,8 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
assertEqual(expected, actual);
|
||||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
testInListPrimaryIndex : function () {
|
||||
|
||||
testInListPrimaryIndex : function () {
|
||||
for (var i = 1; i < 100; ++i) {
|
||||
c.save({ _key: "a" + i });
|
||||
}
|
||||
|
@ -687,10 +709,10 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief check overlapping IN
|
||||
/// @brief check overlapping IN
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testOverlappingInListHashIndex1 : function () {
|
||||
|
||||
testOverlappingInListHashIndex1 : function () {
|
||||
for (var i = 1; i < 100; ++i) {
|
||||
c.save({ value: i });
|
||||
}
|
||||
|
@ -701,8 +723,8 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
assertEqual(expected, actual);
|
||||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
testOverlappingInListSkiplist1 : function () {
|
||||
|
||||
testOverlappingInListSkiplist1 : function () {
|
||||
for (var i = 1; i < 100; ++i) {
|
||||
c.save({ value: i });
|
||||
}
|
||||
|
@ -713,8 +735,8 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
assertEqual(expected, actual);
|
||||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
testOverlappingInListSkiplist1Rev : function () {
|
||||
|
||||
testOverlappingInListSkiplist1Rev : function () {
|
||||
for (var i = 1; i < 100; ++i) {
|
||||
c.save({ value: i });
|
||||
}
|
||||
|
@ -725,8 +747,8 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
assertEqual(expected, actual);
|
||||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
testOverlappingInListHashIndex2 : function () {
|
||||
|
||||
testOverlappingInListHashIndex2 : function () {
|
||||
for (var i = 1; i < 100; ++i) {
|
||||
c.save({ value: i });
|
||||
}
|
||||
|
@ -737,8 +759,8 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
assertEqual(expected, actual);
|
||||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
testOverlappingInListSkiplist2 : function () {
|
||||
|
||||
testOverlappingInListSkiplist2 : function () {
|
||||
for (var i = 1; i < 100; ++i) {
|
||||
c.save({ value: i });
|
||||
}
|
||||
|
@ -749,8 +771,8 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
assertEqual(expected, actual);
|
||||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
testOverlappingInListSkiplist2Rev : function () {
|
||||
|
||||
testOverlappingInListSkiplist2Rev : function () {
|
||||
for (var i = 1; i < 100; ++i) {
|
||||
c.save({ value: i });
|
||||
}
|
||||
|
@ -762,7 +784,7 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
testOverlappingInListHashIndex3 : function () {
|
||||
testOverlappingInListHashIndex3 : function () {
|
||||
for (var i = 1; i < 100; ++i) {
|
||||
c.save({ value: i });
|
||||
}
|
||||
|
@ -773,8 +795,8 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
assertEqual(expected, actual);
|
||||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
testOverlappingInListSkiplist3 : function () {
|
||||
|
||||
testOverlappingInListSkiplist3 : function () {
|
||||
for (var i = 1; i < 100; ++i) {
|
||||
c.save({ value: i });
|
||||
}
|
||||
|
@ -785,8 +807,8 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
assertEqual(expected, actual);
|
||||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
testOverlappingInListSkiplist3Rev : function () {
|
||||
|
||||
testOverlappingInListSkiplist3Rev : function () {
|
||||
for (var i = 1; i < 100; ++i) {
|
||||
c.save({ value: i });
|
||||
}
|
||||
|
@ -797,8 +819,8 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
assertEqual(expected, actual);
|
||||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
testOverlappingInListHashIndex4 : function () {
|
||||
|
||||
testOverlappingInListHashIndex4 : function () {
|
||||
for (var i = 1; i < 100; ++i) {
|
||||
c.save({ value: i });
|
||||
}
|
||||
|
@ -809,8 +831,8 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
assertEqual(expected, actual);
|
||||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
testOverlappingInListSkiplist4 : function () {
|
||||
|
||||
testOverlappingInListSkiplist4 : function () {
|
||||
for (var i = 1; i < 100; ++i) {
|
||||
c.save({ value: i });
|
||||
}
|
||||
|
@ -821,8 +843,8 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
assertEqual(expected, actual);
|
||||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
testOverlappingInListSkiplist4Rev : function () {
|
||||
|
||||
testOverlappingInListSkiplist4Rev : function () {
|
||||
for (var i = 1; i < 100; ++i) {
|
||||
c.save({ value: i });
|
||||
}
|
||||
|
@ -833,8 +855,8 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
assertEqual(expected, actual);
|
||||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
testDuplicatesListHashIndex : function () {
|
||||
|
||||
testDuplicatesListHashIndex : function () {
|
||||
for (var i = 1; i < 100; ++i) {
|
||||
c.save({ value: i });
|
||||
}
|
||||
|
@ -845,8 +867,8 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
assertEqual(expected, actual);
|
||||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
testDuplicatesListSkiplist : function () {
|
||||
|
||||
testDuplicatesListSkiplist : function () {
|
||||
for (var i = 1; i < 100; ++i) {
|
||||
c.save({ value: i });
|
||||
}
|
||||
|
@ -857,8 +879,8 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
assertEqual(expected, actual);
|
||||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
testDuplicatesOrHashIndex : function () {
|
||||
|
||||
testDuplicatesOrHashIndex : function () {
|
||||
for (var i = 1; i < 100; ++i) {
|
||||
c.save({ value: i });
|
||||
}
|
||||
|
@ -869,8 +891,8 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
assertEqual(expected, actual);
|
||||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
testDuplicatesOrSkiplist : function () {
|
||||
|
||||
testDuplicatesOrSkiplist : function () {
|
||||
for (var i = 1; i < 100; ++i) {
|
||||
c.save({ value: i });
|
||||
}
|
||||
|
@ -881,8 +903,8 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
assertEqual(expected, actual);
|
||||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
testDuplicatesListHashIndexDynamic : function () {
|
||||
|
||||
testDuplicatesListHashIndexDynamic : function () {
|
||||
for (var i = 1; i < 100; ++i) {
|
||||
c.save({ value: i });
|
||||
}
|
||||
|
@ -893,8 +915,8 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
assertEqual(expected, actual);
|
||||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
testDuplicatesListSkiplistDynamic : function () {
|
||||
|
||||
testDuplicatesListSkiplistDynamic : function () {
|
||||
for (var i = 1; i < 100; ++i) {
|
||||
c.save({ value: i });
|
||||
}
|
||||
|
@ -905,8 +927,8 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
assertEqual(expected, actual);
|
||||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
testDuplicatesOrHashIndexDynamic : function () {
|
||||
|
||||
testDuplicatesOrHashIndexDynamic : function () {
|
||||
for (var i = 1; i < 100; ++i) {
|
||||
c.save({ value: i });
|
||||
}
|
||||
|
@ -917,8 +939,8 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
assertEqual(expected, actual);
|
||||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
testDuplicatesOrSkiplistDynamic : function () {
|
||||
|
||||
testDuplicatesOrSkiplistDynamic : function () {
|
||||
for (var i = 1; i < 100; ++i) {
|
||||
c.save({ value: i });
|
||||
}
|
||||
|
@ -930,7 +952,7 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
testOverlappingRangesListSkiplist1 : function () {
|
||||
testOverlappingRangesListSkiplist1 : function () {
|
||||
for (var i = 1; i < 100; ++i) {
|
||||
c.save({ value: i });
|
||||
}
|
||||
|
@ -944,8 +966,8 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
assertEqual(expected, actual);
|
||||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
testOverlappingRangesListHashIndex1 : function () {
|
||||
|
||||
testOverlappingRangesListHashIndex1 : function () {
|
||||
for (var i = 1; i < 100; ++i) {
|
||||
c.save({ value: i });
|
||||
}
|
||||
|
@ -958,7 +980,7 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
}
|
||||
},
|
||||
|
||||
testOverlappingRangesListSkiplist2 : function () {
|
||||
testOverlappingRangesListSkiplist2 : function () {
|
||||
for (var i = 1; i < 100; ++i) {
|
||||
c.save({ value: i });
|
||||
}
|
||||
|
@ -969,8 +991,8 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
assertEqual(expected, actual);
|
||||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
testOverlappingRangesListSkiplist2Rev : function () {
|
||||
|
||||
testOverlappingRangesListSkiplist2Rev : function () {
|
||||
for (var i = 1; i < 100; ++i) {
|
||||
c.save({ value: i });
|
||||
}
|
||||
|
@ -981,8 +1003,8 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
assertEqual(expected, actual);
|
||||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
testOverlappingRangesListHashIndex2 : function () {
|
||||
|
||||
testOverlappingRangesListHashIndex2 : function () {
|
||||
for (var i = 1; i < 100; ++i) {
|
||||
c.save({ value: i });
|
||||
}
|
||||
|
@ -1030,7 +1052,7 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
assertEqual(expected, actual);
|
||||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
|
||||
testNestedOrSkiplistSortedDesc : function () {
|
||||
for (var i = 1; i < 5; ++i) {
|
||||
c.save({ value: i });
|
||||
|
@ -1054,7 +1076,7 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
assertEqual(expected, actual);
|
||||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
|
||||
testOverlappingDynamicAndNonDynamic: function () {
|
||||
for (var i = 1; i <= 5; ++i) {
|
||||
c.save({ value1: i, value2: i + 5 });
|
||||
|
@ -1066,7 +1088,7 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
assertEqual(expected, actual);
|
||||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
|
||||
testSkiplistMoreThanOne1 : function () {
|
||||
for (var i = 1; i <= 10; i++) {
|
||||
for (var j = 1; j <= 10; j++) {
|
||||
|
@ -1085,8 +1107,8 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
});
|
||||
assertEqual(expected, actual);
|
||||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
testSkiplistMoreThanOne1Desc : function () {
|
||||
for (var i = 1; i <= 10; i++) {
|
||||
for (var j = 1; j <= 10; j++) {
|
||||
|
@ -1100,8 +1122,8 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
var actual = getQueryResults(query);
|
||||
assertEqual(expected, actual);
|
||||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
testSkiplistMoreThanOne2 : function () {
|
||||
for (var i = 1; i <= 10; i++) {
|
||||
for (var j = 1; j <= 10; j++) {
|
||||
|
@ -1121,7 +1143,7 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
assertEqual(expected, actual);
|
||||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
|
||||
testSkiplistMoreThanOne2Desc : function () {
|
||||
for (var i = 1; i <= 10; i++) {
|
||||
for (var j = 1; j <= 10; j++) {
|
||||
|
@ -1136,7 +1158,7 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
assertEqual(expected, actual);
|
||||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
|
||||
testSkiplistMoreThanOne3 : function () {
|
||||
for (var i = 1; i <= 10; i++) {
|
||||
for (var j = 1; j <= 10; j++) {
|
||||
|
@ -1160,7 +1182,7 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
testSkiplistMoreThanOne4 : function () {
|
||||
for (var i = 1;i <= 10;i++) {
|
||||
for (var j = 1; j <= 30; j++) {
|
||||
c.save({value1 : i, value2: j, value3: i + j,
|
||||
c.save({value1 : i, value2: j, value3: i + j,
|
||||
value4: 'somethings' + 2*j });
|
||||
}
|
||||
}
|
||||
|
@ -1168,10 +1190,10 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
c.ensureSkiplist("value1", "value2", "value3", "value4");
|
||||
|
||||
var query = "FOR x IN " + cn + " FILTER (x.value1 IN [1, 2, 3] && x.value1 IN [2, 3, 4] && x.value2 == 10 && x.value3 <= 20) || (x.value1 == 1 && x.value2 == 2 && x.value3 >= 0 && x.value3 <= 6 && x.value4 in ['somethings2', 'somethings4'] ) RETURN [x.value1, x.value2, x.value3, x.value4]";
|
||||
var expected = [
|
||||
var expected = [
|
||||
[ 1, 2, 3, "somethings4" ],
|
||||
[ 2, 10, 12, "somethings20" ],
|
||||
[ 3, 10, 13, "somethings20" ],
|
||||
[ 2, 10, 12, "somethings20" ],
|
||||
[ 3, 10, 13, "somethings20" ],
|
||||
];
|
||||
var actual = getQueryResults(query);
|
||||
// Sorting is not guaranteed any more.
|
||||
|
@ -1185,11 +1207,11 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
assertEqual(expected, actual);
|
||||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
|
||||
testSkiplistMoreThanOne4Desc : function () {
|
||||
for (var i = 1;i <= 10;i++) {
|
||||
for (var j = 1; j <= 30; j++) {
|
||||
c.save({value1 : i, value2: j, value3: i + j,
|
||||
c.save({value1 : i, value2: j, value3: i + j,
|
||||
value4: 'somethings' + 2*j });
|
||||
}
|
||||
}
|
||||
|
@ -1197,9 +1219,9 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
c.ensureSkiplist("value1", "value2", "value3", "value4");
|
||||
|
||||
var query = "FOR x IN " + cn + " FILTER (x.value1 IN [1, 2, 3] && x.value1 IN [2, 3, 4] && x.value2 == 10 && x.value3 <= 20) || (x.value1 == 1 && x.value2 == 2 && x.value3 >= 0 && x.value3 <= 6 && x.value4 in ['somethings2', 'somethings4'] ) SORT x.value1 DESC RETURN [x.value1, x.value2, x.value3, x.value4]";
|
||||
var expected = [
|
||||
[ 3, 10, 13, "somethings20" ],
|
||||
[ 2, 10, 12, "somethings20" ],
|
||||
var expected = [
|
||||
[ 3, 10, 13, "somethings20" ],
|
||||
[ 2, 10, 12, "somethings20" ],
|
||||
[ 1, 2, 3, "somethings4" ],
|
||||
];
|
||||
var actual = getQueryResults(query);
|
||||
|
@ -1212,7 +1234,7 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
for (var i = 1;i <= 10;i++) {
|
||||
for (var j = 1; j <= 10; j++) {
|
||||
for (var k = 1; k <= 10; k++) {
|
||||
c.save({value1 : i, value2: j, value3: k,
|
||||
c.save({value1 : i, value2: j, value3: k,
|
||||
value4: 'somethings' + j * 2 });
|
||||
}
|
||||
}
|
||||
|
@ -1221,12 +1243,12 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
c.ensureSkiplist("value1", "value2", "value3", "value4");
|
||||
|
||||
var query = "FOR x IN " + cn + " FILTER (x.value1 IN [PASSTHRU(1), PASSTHRU(2), PASSTHRU(3)] && x.value1 IN [2, 3, 4] && x.value2 == PASSTHRU(10) && x.value3 <= 2) || (x.value1 == 1 && x.value2 == 2 && x.value3 >= 0 && x.value3 == PASSTHRU(6) && x.value4 in ['somethings2', PASSTHRU('somethings4')] ) RETURN [x.value1, x.value2, x.value3, x.value4]";
|
||||
var expected = [
|
||||
var expected = [
|
||||
[ 1, 2, 6, "somethings4" ] ,
|
||||
[ 2, 10, 1, "somethings20" ],
|
||||
[ 2, 10, 2, "somethings20" ],
|
||||
[ 3, 10, 1, "somethings20" ],
|
||||
[ 3, 10, 2, "somethings20" ]
|
||||
[ 2, 10, 1, "somethings20" ],
|
||||
[ 2, 10, 2, "somethings20" ],
|
||||
[ 3, 10, 1, "somethings20" ],
|
||||
[ 3, 10, 2, "somethings20" ]
|
||||
];
|
||||
var actual = getQueryResults(query);
|
||||
// Sorting is not guaranteed any more.
|
||||
|
@ -1240,13 +1262,13 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
assertEqual(expected, actual);
|
||||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
|
||||
testSkiplistMoreThanOne5Desc : function () {
|
||||
|
||||
for (var i = 1;i <= 10;i++) {
|
||||
for (var j = 1; j <= 10; j++) {
|
||||
for (var k = 1; k <= 10; k++) {
|
||||
c.save({value1 : i, value2: j, value3: k,
|
||||
c.save({value1 : i, value2: j, value3: k,
|
||||
value4: 'somethings' + 2*j });
|
||||
}
|
||||
}
|
||||
|
@ -1255,24 +1277,24 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
c.ensureSkiplist("value1", "value2", "value3", "value4");
|
||||
|
||||
var query = "FOR x IN " + cn + " FILTER (x.value1 IN [PASSTHRU(1), PASSTHRU(2), PASSTHRU(3)] && x.value1 IN [2, 3, 4] && x.value2 == PASSTHRU(10) && x.value3 <= 2) || (x.value1 == 1 && x.value2 == 2 && x.value3 >= 0 && x.value3 == PASSTHRU(6) && x.value4 in ['somethings2', PASSTHRU('somethings4')] ) SORT x.value1 DESC RETURN [x.value1, x.value2, x.value3, x.value4]";
|
||||
var expected = [
|
||||
[ 3, 10, 2, "somethings20" ],
|
||||
var expected = [
|
||||
[ 3, 10, 2, "somethings20" ],
|
||||
[ 3, 10, 1, "somethings20" ],
|
||||
[ 2, 10, 2, "somethings20" ],
|
||||
[ 2, 10, 1, "somethings20" ],
|
||||
[ 2, 10, 2, "somethings20" ],
|
||||
[ 2, 10, 1, "somethings20" ],
|
||||
[ 1, 2, 6, "somethings4" ] ,
|
||||
];
|
||||
var actual = getQueryResults(query);
|
||||
assertEqual(expected, actual);
|
||||
ruleIsUsed(query);
|
||||
},
|
||||
|
||||
|
||||
testSkiplistMoreThanOne6 : function () {
|
||||
|
||||
for (var i = 1;i <= 10;i++) {
|
||||
for (var j = 1; j <= 10; j++) {
|
||||
for (var k = 1; k <= 10; k++) {
|
||||
c.save({value1 : i, value2: j, value3: k,
|
||||
c.save({value1 : i, value2: j, value3: k,
|
||||
value4: 'somethings' + 2*j });
|
||||
}
|
||||
}
|
||||
|
@ -1281,12 +1303,12 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
c.ensureSkiplist("value1", "value2", "value3", "value4");
|
||||
|
||||
var query = "FOR x IN " + cn + " FILTER (x.value1 IN [PASSTHRU(1), PASSTHRU(2), PASSTHRU(3)] && x.value1 IN PASSTHRU([2, 3, 4]) && x.value2 == PASSTHRU(10) && x.value3 <= 2) || (x.value1 == 1 && x.value2 == 2 && x.value3 >= 0 && x.value3 == PASSTHRU(6) && x.value4 in ['somethings2', PASSTHRU('somethings4')] ) RETURN [x.value1, x.value2, x.value3, x.value4]";
|
||||
var expected = [
|
||||
var expected = [
|
||||
[ 1, 2, 6, "somethings4" ] ,
|
||||
[ 2, 10, 1, "somethings20" ],
|
||||
[ 2, 10, 2, "somethings20" ],
|
||||
[ 3, 10, 1, "somethings20" ],
|
||||
[ 3, 10, 2, "somethings20" ]
|
||||
[ 2, 10, 1, "somethings20" ],
|
||||
[ 2, 10, 2, "somethings20" ],
|
||||
[ 3, 10, 1, "somethings20" ],
|
||||
[ 3, 10, 2, "somethings20" ]
|
||||
];
|
||||
var actual = getQueryResults(query);
|
||||
// Sorting is not guaranteed any more.
|
||||
|
@ -1311,7 +1333,7 @@ function ahuacatlQueryOptimizerInTestSuite () {
|
|||
function ahuacatlQueryOptimizerInWithLongArraysTestSuite () {
|
||||
var c = null;
|
||||
var cn = "UnitTestsAhuacatlOptimizerInWithLongArrays";
|
||||
|
||||
|
||||
return {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1341,11 +1363,11 @@ function ahuacatlQueryOptimizerInWithLongArraysTestSuite () {
|
|||
c.insert({ value: i });
|
||||
comp.push(i);
|
||||
}
|
||||
|
||||
|
||||
var result;
|
||||
result = AQL_EXECUTE("FOR doc IN @@cn FILTER doc.value IN @comp RETURN 1", { "@cn": cn, comp: comp }).json;
|
||||
assertEqual(1000, result.length);
|
||||
|
||||
|
||||
result = AQL_EXECUTE("FOR doc IN @@cn FILTER doc.value NOT IN @comp RETURN 1", { "@cn": cn, comp: comp }).json;
|
||||
assertEqual(0, result.length);
|
||||
},
|
||||
|
@ -1360,11 +1382,11 @@ function ahuacatlQueryOptimizerInWithLongArraysTestSuite () {
|
|||
c.insert({ value: i });
|
||||
comp.push(1000 - 1 - i);
|
||||
}
|
||||
|
||||
|
||||
var result;
|
||||
result = AQL_EXECUTE("FOR doc IN @@cn FILTER doc.value IN @comp RETURN 1", { "@cn": cn, comp: comp }).json;
|
||||
assertEqual(1000, result.length);
|
||||
|
||||
|
||||
result = AQL_EXECUTE("FOR doc IN @@cn FILTER doc.value NOT IN @comp RETURN 1", { "@cn": cn, comp: comp }).json;
|
||||
assertEqual(0, result.length);
|
||||
},
|
||||
|
@ -1379,11 +1401,11 @@ function ahuacatlQueryOptimizerInWithLongArraysTestSuite () {
|
|||
c.insert({ value: "test" + i });
|
||||
comp.push("test" + i);
|
||||
}
|
||||
|
||||
|
||||
var result;
|
||||
result = AQL_EXECUTE("FOR doc IN @@cn FILTER doc.value IN @comp RETURN 1", { "@cn": cn, comp: comp }).json;
|
||||
assertEqual(1000, result.length);
|
||||
|
||||
|
||||
result = AQL_EXECUTE("FOR doc IN @@cn FILTER doc.value NOT IN @comp RETURN 1", { "@cn": cn, comp: comp }).json;
|
||||
assertEqual(0, result.length);
|
||||
},
|
||||
|
@ -1398,11 +1420,11 @@ function ahuacatlQueryOptimizerInWithLongArraysTestSuite () {
|
|||
c.insert({ value: "test" + i });
|
||||
comp.push("test" + (1000 - 1 - i));
|
||||
}
|
||||
|
||||
|
||||
var result;
|
||||
result = AQL_EXECUTE("FOR doc IN @@cn FILTER doc.value IN @comp RETURN 1", { "@cn": cn, comp: comp }).json;
|
||||
assertEqual(1000, result.length);
|
||||
|
||||
|
||||
result = AQL_EXECUTE("FOR doc IN @@cn FILTER doc.value NOT IN @comp RETURN 1", { "@cn": cn, comp: comp }).json;
|
||||
assertEqual(0, result.length);
|
||||
},
|
||||
|
@ -1417,11 +1439,11 @@ function ahuacatlQueryOptimizerInWithLongArraysTestSuite () {
|
|||
c.insert({ value1: "test" + i, value2: i });
|
||||
comp.push({ value1: "test" + i, value2: i });
|
||||
}
|
||||
|
||||
|
||||
var result;
|
||||
result = AQL_EXECUTE("FOR doc IN @@cn FILTER { value1: doc.value1, value2: doc.value2 } IN @comp RETURN 1", { "@cn": cn, comp: comp }).json;
|
||||
assertEqual(1000, result.length);
|
||||
|
||||
|
||||
result = AQL_EXECUTE("FOR doc IN @@cn FILTER { value1: doc.value1, value2: doc.value2 } NOT IN @comp RETURN 1", { "@cn": cn, comp: comp }).json;
|
||||
assertEqual(0, result.length);
|
||||
},
|
||||
|
@ -1436,11 +1458,11 @@ function ahuacatlQueryOptimizerInWithLongArraysTestSuite () {
|
|||
c.insert({ value1: "test" + i, value2: 1000 - 1 - i });
|
||||
comp.push({ value1: "test" + i, value2: 1000 - 1 - i });
|
||||
}
|
||||
|
||||
|
||||
var result;
|
||||
result = AQL_EXECUTE("FOR doc IN @@cn FILTER { value1: doc.value1, value2: doc.value2 } IN @comp RETURN 1", { "@cn": cn, comp: comp }).json;
|
||||
assertEqual(1000, result.length);
|
||||
|
||||
|
||||
result = AQL_EXECUTE("FOR doc IN @@cn FILTER { value1: doc.value1, value2: doc.value2 } NOT IN @comp RETURN 1", { "@cn": cn, comp: comp }).json;
|
||||
assertEqual(0, result.length);
|
||||
},
|
||||
|
@ -1455,11 +1477,11 @@ function ahuacatlQueryOptimizerInWithLongArraysTestSuite () {
|
|||
c.insert({ value1: "test" + (1000 - 1 - i), value2: 1000 - 1 - i });
|
||||
comp.push({ value1: "test" + (1000 - 1 - i), value2: 1000 - 1 - i });
|
||||
}
|
||||
|
||||
|
||||
var result;
|
||||
result = AQL_EXECUTE("FOR doc IN @@cn FILTER { value1: doc.value1, value2: doc.value2 } IN @comp RETURN 1", { "@cn": cn, comp: comp }).json;
|
||||
assertEqual(1000, result.length);
|
||||
|
||||
|
||||
result = AQL_EXECUTE("FOR doc IN @@cn FILTER { value1: doc.value1, value2: doc.value2 } NOT IN @comp RETURN 1", { "@cn": cn, comp: comp }).json;
|
||||
assertEqual(0, result.length);
|
||||
},
|
||||
|
@ -1474,11 +1496,11 @@ function ahuacatlQueryOptimizerInWithLongArraysTestSuite () {
|
|||
c.insert({ value1: "test" + (1000 - 1 - i), value2: i });
|
||||
comp.push({ value1: "test" + (1000 - 1 - i), value2: i });
|
||||
}
|
||||
|
||||
|
||||
var result;
|
||||
result = AQL_EXECUTE("FOR doc IN @@cn FILTER { value1: doc.value1, value2: doc.value2 } IN @comp RETURN 1", { "@cn": cn, comp: comp }).json;
|
||||
assertEqual(1000, result.length);
|
||||
|
||||
|
||||
result = AQL_EXECUTE("FOR doc IN @@cn FILTER { value1: doc.value1, value2: doc.value2 } NOT IN @comp RETURN 1", { "@cn": cn, comp: comp }).json;
|
||||
assertEqual(0, result.length);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue