mirror of https://gitee.com/bigwinds/arangodb
added edge index iterator
Conflicts: arangod/Aql/ExecutionBlock.h
This commit is contained in:
parent
50b531666c
commit
3057cf7a74
|
@ -797,12 +797,13 @@ bool EnumerateCollectionBlock::moreDocuments (size_t hint) {
|
|||
hint = DefaultBatchSize;
|
||||
}
|
||||
|
||||
std::vector<TRI_doc_mptr_copy_t> newDocs;
|
||||
|
||||
throwIfKilled(); // check if we were aborted
|
||||
|
||||
TRI_IF_FAILURE("EnumerateCollectionBlock::moreDocuments") {
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||
}
|
||||
|
||||
std::vector<TRI_doc_mptr_copy_t> newDocs;
|
||||
newDocs.reserve(hint);
|
||||
|
||||
int res = _scanner->scan(newDocs, hint);
|
||||
|
@ -992,6 +993,7 @@ IndexRangeBlock::IndexRangeBlock (ExecutionEngine* engine,
|
|||
_posInDocs(0),
|
||||
_anyBoundVariable(false),
|
||||
_skiplistIterator(nullptr),
|
||||
_edgeIndexIterator(nullptr),
|
||||
_hashIndexSearchValue({ 0, nullptr }),
|
||||
_hashNextElement(nullptr),
|
||||
_condition(new IndexOrCondition()),
|
||||
|
@ -1047,6 +1049,8 @@ IndexRangeBlock::~IndexRangeBlock () {
|
|||
if (_skiplistIterator != nullptr) {
|
||||
TRI_FreeSkiplistIterator(_skiplistIterator);
|
||||
}
|
||||
|
||||
delete _edgeIndexIterator;
|
||||
}
|
||||
|
||||
int IndexRangeBlock::initialize () {
|
||||
|
@ -1344,10 +1348,19 @@ bool IndexRangeBlock::initRanges () {
|
|||
removeOverlapsIndexOr(*_condition);
|
||||
}
|
||||
|
||||
if (en->_index->type == TRI_IDX_TYPE_PRIMARY_INDEX ||
|
||||
en->_index->type == TRI_IDX_TYPE_EDGE_INDEX) {
|
||||
if (en->_index->type == TRI_IDX_TYPE_PRIMARY_INDEX) {
|
||||
return true; //no initialization here!
|
||||
}
|
||||
|
||||
if (en->_index->type == TRI_IDX_TYPE_EDGE_INDEX) {
|
||||
if (_condition->empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_posInRanges = 0;
|
||||
getEdgeIndexIterator(_condition->at(_posInRanges));
|
||||
return (_edgeIndexIterator != nullptr);
|
||||
}
|
||||
|
||||
if (en->_index->type == TRI_IDX_TYPE_HASH_INDEX) {
|
||||
if (_condition->empty()) {
|
||||
|
@ -1559,11 +1572,11 @@ void IndexRangeBlock::freeCondition () {
|
|||
bool IndexRangeBlock::readIndex (size_t atMost) {
|
||||
ENTER_BLOCK;
|
||||
// this is called every time we want more in _documents.
|
||||
// For non-skiplist indexes (currently hash, primary, edge), this
|
||||
// only reads the index once, and never again (although there might be
|
||||
// multiple calls to this function). For skiplists indexes, initRanges creates
|
||||
// a skiplistIterator and readIndex just reads from the iterator until it is
|
||||
// done. Then initRanges is read again and so on. This is to avoid reading the
|
||||
// For the primary key index, this only reads the index once, and never
|
||||
// again (although there might be multiple calls to this function).
|
||||
// For the edge, hash or skiplists indexes, initRanges creates an iterator
|
||||
// and read*Index just reads from the iterator until it is done.
|
||||
// Then initRanges is read again and so on. This is to avoid reading the
|
||||
// entire index when we only want a small number of documents.
|
||||
|
||||
if (_documents.empty()) {
|
||||
|
@ -1583,17 +1596,15 @@ bool IndexRangeBlock::readIndex (size_t atMost) {
|
|||
readPrimaryIndex(*_condition);
|
||||
}
|
||||
}
|
||||
else if (en->_index->type == TRI_IDX_TYPE_EDGE_INDEX) {
|
||||
readEdgeIndex(atMost);
|
||||
}
|
||||
else if (en->_index->type == TRI_IDX_TYPE_HASH_INDEX) {
|
||||
readHashIndex(atMost);
|
||||
}
|
||||
else if (en->_index->type == TRI_IDX_TYPE_SKIPLIST_INDEX) {
|
||||
readSkiplistIndex(atMost);
|
||||
}
|
||||
else if (en->_index->type == TRI_IDX_TYPE_EDGE_INDEX) {
|
||||
if (_flag) {
|
||||
readEdgeIndex(*_condition);
|
||||
}
|
||||
}
|
||||
else {
|
||||
TRI_ASSERT(false);
|
||||
}
|
||||
|
@ -1867,62 +1878,113 @@ void IndexRangeBlock::readPrimaryIndex (IndexOrCondition const& ranges) {
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief read documents using the edges index
|
||||
/// @brief build search values for edge index lookup
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void IndexRangeBlock::readEdgeIndex (IndexOrCondition const& ranges) {
|
||||
void IndexRangeBlock::getEdgeIndexIterator (IndexAndCondition const& ranges) {
|
||||
ENTER_BLOCK;
|
||||
TRI_document_collection_t* document = _collection->documentCollection();
|
||||
|
||||
std::string key;
|
||||
TRI_edge_direction_e direction = TRI_EDGE_IN; // must set a default to satisfy compiler
|
||||
for (size_t i = 0; i < ranges.size(); i++) {
|
||||
for (auto x : ranges[i]) {
|
||||
if (x._attr == std::string(TRI_VOC_ATTRIBUTE_FROM)) {
|
||||
// we can use lower bound because only equality is supported
|
||||
TRI_ASSERT(x.is1ValueRangeInfo());
|
||||
auto const json = x._lowConst.bound().json();
|
||||
if (TRI_IsStringJson(json)) {
|
||||
// no error will be thrown if _from is not a string
|
||||
key = std::string(json->_value._string.data, json->_value._string.length - 1);
|
||||
direction = TRI_EDGE_OUT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (x._attr == std::string(TRI_VOC_ATTRIBUTE_TO)) {
|
||||
// we can use lower bound because only equality is supported
|
||||
TRI_ASSERT(x.is1ValueRangeInfo());
|
||||
auto const json = x._lowConst.bound().json();
|
||||
if (TRI_IsStringJson(json)) {
|
||||
// no error will be thrown if _to is not a string
|
||||
key = std::string(json->_value._string.data, json->_value._string.length - 1);
|
||||
direction = TRI_EDGE_IN;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
_edgeNextElement = nullptr;
|
||||
|
||||
if (_edgeIndexIterator != nullptr) {
|
||||
delete _edgeIndexIterator;
|
||||
_edgeIndexIterator = nullptr;
|
||||
}
|
||||
|
||||
auto buildIterator = [this] (TRI_edge_direction_e direction, TRI_json_t const* key) -> void {
|
||||
TRI_ASSERT(_edgeIndexIterator == nullptr);
|
||||
|
||||
TRI_voc_cid_t documentCid;
|
||||
std::string documentKey;
|
||||
|
||||
int errorCode = resolve(key->_value._string.data, documentCid, documentKey);
|
||||
|
||||
if (errorCode == TRI_ERROR_NO_ERROR) {
|
||||
_edgeIndexIterator = new TRI_edge_index_iterator_t(direction, documentCid, (TRI_voc_key_t) documentKey.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
if (! key.empty()) {
|
||||
TRI_voc_cid_t documentCid;
|
||||
std::string documentKey;
|
||||
for (auto x : ranges) {
|
||||
if (x._attr == std::string(TRI_VOC_ATTRIBUTE_FROM)) {
|
||||
// we can use lower bound because only equality is supported
|
||||
TRI_ASSERT(x.is1ValueRangeInfo());
|
||||
auto const json = x._lowConst.bound().json();
|
||||
if (TRI_IsStringJson(json)) {
|
||||
// no error will be thrown if _from is not a string
|
||||
buildIterator(TRI_EDGE_OUT, json);
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (x._attr == std::string(TRI_VOC_ATTRIBUTE_TO)) {
|
||||
// we can use lower bound because only equality is supported
|
||||
TRI_ASSERT(x.is1ValueRangeInfo());
|
||||
auto const json = x._lowConst.bound().json();
|
||||
if (TRI_IsStringJson(json)) {
|
||||
// no error will be thrown if _to is not a string
|
||||
buildIterator(TRI_EDGE_IN, json);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LEAVE_BLOCK;
|
||||
}
|
||||
|
||||
int errorCode = resolve(key.c_str(), documentCid, documentKey);
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief actually read from the edge index
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
if (errorCode == TRI_ERROR_NO_ERROR) {
|
||||
// silently ignore all errors due to wrong _from / _to specifications
|
||||
auto&& result = TRI_LookupEdgesDocumentCollection(document, direction,
|
||||
documentCid, (TRI_voc_key_t) documentKey.c_str());
|
||||
for (auto it : result) {
|
||||
_documents.emplace_back(it);
|
||||
}
|
||||
void IndexRangeBlock::readEdgeIndex (size_t atMost) {
|
||||
ENTER_BLOCK;
|
||||
|
||||
if (_edgeIndexIterator == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto en = static_cast<IndexRangeNode const*>(getPlanNode());
|
||||
TRI_index_t* idx = en->_index->getInternals();
|
||||
TRI_ASSERT(idx != nullptr);
|
||||
|
||||
try {
|
||||
size_t nrSent = 0;
|
||||
while (nrSent < atMost && _edgeIndexIterator != nullptr) {
|
||||
size_t const n = _documents.size();
|
||||
|
||||
TRI_IF_FAILURE("IndexRangeBlock::readEdgeIndex") {
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||
}
|
||||
|
||||
TRI_LookupEdgeIndex(idx, _edgeIndexIterator, _documents, _edgeNextElement, atMost);
|
||||
|
||||
_engine->_stats.scannedIndex += static_cast<int64_t>(result.size());
|
||||
size_t const numRead = _documents.size() - n;
|
||||
|
||||
_engine->_stats.scannedIndex += static_cast<int64_t>(numRead);
|
||||
nrSent += numRead;
|
||||
|
||||
if (_edgeNextElement == nullptr) {
|
||||
delete _edgeIndexIterator;
|
||||
_edgeIndexIterator = nullptr;
|
||||
|
||||
if (++_posInRanges < _condition->size()) {
|
||||
getEdgeIndexIterator(_condition->at(_posInRanges));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
if (_edgeIndexIterator != nullptr) {
|
||||
delete _edgeIndexIterator;
|
||||
_edgeIndexIterator = nullptr;
|
||||
}
|
||||
throw;
|
||||
}
|
||||
LEAVE_BLOCK;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief destroy the search values for the hash index lookup
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void IndexRangeBlock::destroyHashIndexSearchValues () {
|
||||
if (_hashIndexSearchValue._values != nullptr) {
|
||||
TRI_shaper_t* shaper = _collection->documentCollection()->getShaper();
|
||||
|
@ -1936,6 +1998,10 @@ void IndexRangeBlock::destroyHashIndexSearchValues () {
|
|||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief set up search values for the hash index lookup
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool IndexRangeBlock::setupHashIndexSearchValue (IndexAndCondition const& range) {
|
||||
auto en = static_cast<IndexRangeNode const*>(getPlanNode());
|
||||
TRI_index_t* idx = en->_index->getInternals();
|
||||
|
@ -2008,6 +2074,10 @@ void IndexRangeBlock::getHashIndexIterator (IndexAndCondition const& ranges) {
|
|||
LEAVE_BLOCK;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief actually read from the hash index
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void IndexRangeBlock::readHashIndex (size_t atMost) {
|
||||
ENTER_BLOCK;
|
||||
|
||||
|
@ -2166,6 +2236,10 @@ void IndexRangeBlock::getSkiplistIterator (IndexAndCondition const& ranges) {
|
|||
LEAVE_BLOCK;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief actually read from the skiplist index
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void IndexRangeBlock::readSkiplistIndex (size_t atMost) {
|
||||
ENTER_BLOCK;
|
||||
if (_skiplistIterator == nullptr) {
|
||||
|
|
|
@ -38,13 +38,14 @@
|
|||
#include "Aql/Range.h"
|
||||
#include "Aql/WalkerWorker.h"
|
||||
#include "Aql/ExecutionStats.h"
|
||||
#include "Cluster/ClusterComm.h"
|
||||
#include "Utils/AqlTransaction.h"
|
||||
#include "Utils/transactions.h"
|
||||
#include "Utils/V8TransactionContext.h"
|
||||
#include "Cluster/ClusterComm.h"
|
||||
|
||||
struct TRI_doc_mptr_copy_t;
|
||||
struct TRI_df_marker_s;
|
||||
struct TRI_doc_mptr_copy_t;
|
||||
struct TRI_edge_index_iterator_t;
|
||||
struct TRI_hash_index_element_multi_s;
|
||||
struct TRI_json_t;
|
||||
|
||||
|
@ -619,12 +620,6 @@ namespace triagens {
|
|||
|
||||
void readPrimaryIndex (IndexOrCondition const&);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief read using the edges index
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void readEdgeIndex (IndexOrCondition const&);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief destroy the hash index search value
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -649,6 +644,18 @@ namespace triagens {
|
|||
|
||||
void readHashIndex (size_t);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief this tries to create an edge iterator to read from the index.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void getEdgeIndexIterator (IndexAndCondition const&);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief read using an edge index
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void readEdgeIndex (size_t atMost);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief this tries to create a skiplistIterator to read from the index.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -769,6 +776,14 @@ namespace triagens {
|
|||
|
||||
TRI_skiplist_iterator_t* _skiplistIterator;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief _edgeIterator: holds the edge iterator found using
|
||||
/// getEdgeIndexIterator (if any) so that it can be read in chunks and not
|
||||
/// necessarily all at once.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct TRI_edge_index_iterator_t* _edgeIndexIterator;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief current search value for hash index lookup
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -781,6 +796,12 @@ namespace triagens {
|
|||
|
||||
struct TRI_hash_index_element_multi_s* _hashNextElement;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief reentrant edge index iterator state
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void* _edgeNextElement;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief _condition: holds the IndexAndCondition for the current incoming block,
|
||||
/// this is just the _ranges[_rangesPos] member of the plan node if _allBoundsConstant
|
||||
|
|
|
@ -215,6 +215,44 @@ std::vector<TRI_doc_mptr_copy_t> TRI_LookupEdgesDocumentCollection (
|
|||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief looks up edges using the index, restarting at the edge pointed at
|
||||
/// by next
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TRI_LookupEdgeIndex (TRI_index_t* idx,
|
||||
TRI_edge_index_iterator_t const* edgeIndexIterator,
|
||||
std::vector<TRI_doc_mptr_copy_t>& result,
|
||||
void*& next,
|
||||
size_t batchSize) {
|
||||
|
||||
std::function<void(void*)> callback = [&result] (void* data) -> void {
|
||||
TRI_doc_mptr_t* doc = static_cast<TRI_doc_mptr_t*>(data);
|
||||
|
||||
result.emplace_back(*(doc));
|
||||
};
|
||||
|
||||
TRI_edge_index_t* edgesIndex = (TRI_edge_index_t*) idx;
|
||||
|
||||
if (edgeIndexIterator->_direction == TRI_EDGE_OUT) {
|
||||
TRI_LookupByKeyMultiPointer(&edgesIndex->_edges_from,
|
||||
&edgeIndexIterator->_edge,
|
||||
callback,
|
||||
next,
|
||||
batchSize);
|
||||
}
|
||||
else if (edgeIndexIterator->_direction == TRI_EDGE_IN) {
|
||||
TRI_LookupByKeyMultiPointer(&edgesIndex->_edges_to,
|
||||
&edgeIndexIterator->_edge,
|
||||
callback,
|
||||
next,
|
||||
batchSize);
|
||||
}
|
||||
else {
|
||||
TRI_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- END-OF-FILE
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -31,9 +31,12 @@
|
|||
#define ARANGODB_VOC_BASE_EDGE__COLLECTION_H 1
|
||||
|
||||
#include "Basics/Common.h"
|
||||
#include "Basics/Exceptions.h"
|
||||
#include "VocBase/voc-types.h"
|
||||
#include "VocBase/document-collection.h"
|
||||
|
||||
struct TRI_index_s;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- EDGE COLLECTION
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -63,6 +66,34 @@ typedef struct TRI_edge_header_s {
|
|||
}
|
||||
TRI_edge_header_t;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief edge index iterator
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct TRI_edge_index_iterator_t {
|
||||
TRI_edge_index_iterator_t (TRI_edge_direction_e direction,
|
||||
TRI_voc_cid_t cid,
|
||||
TRI_voc_key_t key)
|
||||
: _direction(direction),
|
||||
_edge({ cid, nullptr }) {
|
||||
|
||||
TRI_ASSERT(key != nullptr);
|
||||
_edge._key = TRI_DuplicateStringZ(TRI_UNKNOWN_MEM_ZONE, key);
|
||||
if (_edge._key == nullptr) {
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
}
|
||||
|
||||
~TRI_edge_index_iterator_t () {
|
||||
if (_edge._key != nullptr) {
|
||||
TRI_FreeString(TRI_UNKNOWN_MEM_ZONE, _edge._key);
|
||||
}
|
||||
}
|
||||
|
||||
TRI_edge_direction_e const _direction;
|
||||
TRI_edge_header_t _edge;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- EDGES INDEX
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -81,6 +112,17 @@ std::vector<TRI_doc_mptr_copy_t> TRI_LookupEdgesDocumentCollection (
|
|||
TRI_voc_cid_t,
|
||||
TRI_voc_key_t);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief looks up edges using the index, restarting at the edge pointed at
|
||||
/// by next
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TRI_LookupEdgeIndex (struct TRI_index_s*,
|
||||
TRI_edge_index_iterator_t const*,
|
||||
std::vector<TRI_doc_mptr_copy_t>&,
|
||||
void*&,
|
||||
size_t);
|
||||
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -2425,6 +2425,134 @@ function optimizerIndexesTestSuite () {
|
|||
/// @brief test suite
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function optimizerEdgeIndexTestSuite () {
|
||||
var c;
|
||||
var e;
|
||||
|
||||
return {
|
||||
setUp : function () {
|
||||
db._drop("UnitTestsCollection");
|
||||
db._drop("UnitTestsEdgeCollection");
|
||||
c = db._create("UnitTestsCollection");
|
||||
e = db._createEdgeCollection("UnitTestsEdgeCollection");
|
||||
|
||||
for (var i = 0; i < 2000; i += 100) {
|
||||
var j;
|
||||
|
||||
for (j = 0; j < i; ++j) {
|
||||
e.save("UnitTestsCollection/from" + i, "UnitTestsCollection/nono", { value: i + "-" + j });
|
||||
}
|
||||
for (j = 0; j < i; ++j) {
|
||||
e.save("UnitTestsCollection/nono", "UnitTestsCollection/to" + i, { value: i + "-" + j });
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
tearDown : function () {
|
||||
db._drop("UnitTestsCollection");
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test index usage
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testFindNone : function () {
|
||||
var queries = [
|
||||
"FOR i IN " + e.name() + " FILTER i._from == '' RETURN i._key",
|
||||
"FOR i IN " + e.name() + " FILTER i._from == 'UnitTestsCollection/from0' RETURN i._key",
|
||||
"FOR i IN " + e.name() + " FILTER i._from == 'UnitTestsCollection/from1' RETURN i._key",
|
||||
"FOR i IN " + e.name() + " FILTER i._from == 'UnitTestsCollection/from2' RETURN i._key",
|
||||
"FOR i IN " + e.name() + " FILTER i._from == 'UnitTestsCollection/' RETURN i._key",
|
||||
"FOR i IN " + e.name() + " FILTER i._from == 'UnitTestsCollection' RETURN i._key",
|
||||
"FOR i IN " + e.name() + " FILTER i._from == '/' RETURN i._key",
|
||||
"FOR i IN " + e.name() + " FILTER i._from == '--foobar--' RETURN i._key",
|
||||
"FOR i IN " + e.name() + " FILTER i._to == '' RETURN i._key",
|
||||
"FOR i IN " + e.name() + " FILTER i._to == 'UnitTestsCollection/from0' RETURN i._key",
|
||||
"FOR i IN " + e.name() + " FILTER i._to == 'UnitTestsCollection/from1' RETURN i._key",
|
||||
"FOR i IN " + e.name() + " FILTER i._to == 'UnitTestsCollection/from2' RETURN i._key",
|
||||
"FOR i IN " + e.name() + " FILTER i._to == 'UnitTestsCollection/' RETURN i._key",
|
||||
"FOR i IN " + e.name() + " FILTER i._to == 'UnitTestsCollection' RETURN i._key",
|
||||
"FOR i IN " + e.name() + " FILTER i._to == '/' RETURN i._key",
|
||||
"FOR i IN " + e.name() + " FILTER i._to == '--foobar--' RETURN i._key"
|
||||
];
|
||||
|
||||
queries.forEach(function(query) {
|
||||
var results = AQL_EXECUTE(query);
|
||||
assertEqual([ ], results.json, query);
|
||||
assertEqual(0, results.stats.scannedFull);
|
||||
});
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test index usage
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testFindFrom : function () {
|
||||
var queries = [
|
||||
[ "FOR i IN " + e.name() + " FILTER i._from == 'UnitTestsCollection/from100' RETURN i._key", 100 ],
|
||||
[ "FOR i IN " + e.name() + " FILTER i._from == 'UnitTestsCollection/from200' RETURN i._key", 200 ],
|
||||
[ "FOR i IN " + e.name() + " FILTER i._from == 'UnitTestsCollection/from1000' RETURN i._key", 1000 ],
|
||||
[ "FOR i IN " + e.name() + " FILTER i._from == 'UnitTestsCollection/from1100' RETURN i._key", 1100 ],
|
||||
[ "FOR i IN " + e.name() + " FILTER i._from == 'UnitTestsCollection/from1900' RETURN i._key", 1900 ]
|
||||
];
|
||||
|
||||
queries.forEach(function(query) {
|
||||
var results = AQL_EXECUTE(query[0]);
|
||||
assertEqual(query[1], results.json.length, query[0]);
|
||||
assertEqual(0, results.stats.scannedFull);
|
||||
assertEqual(query[1], results.stats.scannedIndex);
|
||||
});
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test index usage
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testFindTo : function () {
|
||||
var queries = [
|
||||
[ "FOR i IN " + e.name() + " FILTER i._to == 'UnitTestsCollection/to100' RETURN i._key", 100 ],
|
||||
[ "FOR i IN " + e.name() + " FILTER i._to == 'UnitTestsCollection/to200' RETURN i._key", 200 ],
|
||||
[ "FOR i IN " + e.name() + " FILTER i._to == 'UnitTestsCollection/to1000' RETURN i._key", 1000 ],
|
||||
[ "FOR i IN " + e.name() + " FILTER i._to == 'UnitTestsCollection/to1100' RETURN i._key", 1100 ],
|
||||
[ "FOR i IN " + e.name() + " FILTER i._to == 'UnitTestsCollection/to1900' RETURN i._key", 1900 ]
|
||||
];
|
||||
|
||||
queries.forEach(function(query) {
|
||||
var results = AQL_EXECUTE(query[0]);
|
||||
assertEqual(query[1], results.json.length, query[0]);
|
||||
assertEqual(0, results.stats.scannedFull);
|
||||
assertEqual(query[1], results.stats.scannedIndex);
|
||||
});
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test index usage
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testFindFromTo : function () {
|
||||
var queries = [
|
||||
[ "FOR i IN " + e.name() + " FILTER i._from == 'UnitTestsCollection/from100' && i._to == 'UnitTestsCollection/nono' RETURN i._key", 100 ],
|
||||
[ "FOR i IN " + e.name() + " FILTER i._from == 'UnitTestsCollection/from200' && i._to == 'UnitTestsCollection/nono' RETURN i._key", 200 ],
|
||||
[ "FOR i IN " + e.name() + " FILTER i._from == 'UnitTestsCollection/from1000' && i._to == 'UnitTestsCollection/nono' RETURN i._key", 1000 ],
|
||||
[ "FOR i IN " + e.name() + " FILTER i._from == 'UnitTestsCollection/from1100' && i._to == 'UnitTestsCollection/nono' RETURN i._key", 1100 ],
|
||||
[ "FOR i IN " + e.name() + " FILTER i._from == 'UnitTestsCollection/from1900' && i._to == 'UnitTestsCollection/nono' RETURN i._key", 1900 ]
|
||||
];
|
||||
|
||||
queries.forEach(function(query) {
|
||||
var results = AQL_EXECUTE(query[0]);
|
||||
assertEqual(query[1], results.json.length, query[0]);
|
||||
assertEqual(0, results.stats.scannedFull);
|
||||
assertEqual(query[1], results.stats.scannedIndex);
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test suite
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function optimizerIndexesInOrTestSuite () {
|
||||
var c;
|
||||
|
||||
|
@ -3871,6 +3999,7 @@ function optimizerIndexesSortTestSuite () {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
jsunity.run(optimizerIndexesTestSuite);
|
||||
jsunity.run(optimizerEdgeIndexTestSuite);
|
||||
jsunity.run(optimizerIndexesInOrTestSuite);
|
||||
jsunity.run(optimizerIndexesRangesTestSuite);
|
||||
jsunity.run(optimizerIndexesSortTestSuite);
|
||||
|
|
|
@ -563,6 +563,72 @@ TRI_vector_pointer_t TRI_LookupByKeyMultiPointer (TRI_memory_zone_t* zone,
|
|||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief lookups an element given a key and the last element
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TRI_LookupByKeyMultiPointer (TRI_multi_pointer_t* array,
|
||||
void const* key,
|
||||
std::function<void(void*)> callback,
|
||||
void*& next,
|
||||
size_t batchSize) {
|
||||
size_t total = 0;
|
||||
TRI_ASSERT(batchSize > 0);
|
||||
|
||||
if (next == nullptr) {
|
||||
// compute the hash
|
||||
uint64_t hash = array->hashKey(array, key);
|
||||
uint64_t i = hash % array->_nrAlloc;
|
||||
|
||||
#ifdef TRI_INTERNAL_STATS
|
||||
// update statistics
|
||||
array->_nrFinds++;
|
||||
#endif
|
||||
|
||||
// search the table
|
||||
while (array->_table[i].ptr != nullptr &&
|
||||
(! array->isEqualKeyElement(array, key, array->_table[i].ptr) ||
|
||||
array->_table[i].prev != TRI_MULTI_POINTER_INVALID_INDEX)) {
|
||||
i = TRI_IncModU64(i, array->_nrAlloc);
|
||||
#ifdef TRI_INTERNAL_STATS
|
||||
array->_nrProbesF++;
|
||||
#endif
|
||||
}
|
||||
|
||||
// insert and set next
|
||||
if (array->_table[i].ptr != nullptr) {
|
||||
callback(array->_table[i].ptr);
|
||||
++total;
|
||||
|
||||
auto nextIndex = array->_table[i].next;
|
||||
if (nextIndex == TRI_MULTI_POINTER_INVALID_INDEX) {
|
||||
next = nullptr;
|
||||
}
|
||||
else {
|
||||
next = &(array->_table[nextIndex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (next != nullptr) {
|
||||
// we already had a state
|
||||
while (total < batchSize) {
|
||||
auto current = static_cast<TRI_multi_pointer_entry_t*>(next);
|
||||
callback(current->ptr);
|
||||
++total;
|
||||
|
||||
auto nextIndex = current->next;
|
||||
if (nextIndex == TRI_MULTI_POINTER_INVALID_INDEX) {
|
||||
next = nullptr;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
next = &(array->_table[nextIndex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief lookups an element given an element
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -34,10 +34,10 @@
|
|||
#define ARANGODB_BASICS_C_ASSOCIATIVE__MULTI_H 1
|
||||
|
||||
#include "Basics/Common.h"
|
||||
|
||||
#include "Basics/locks.h"
|
||||
#include "Basics/vector.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- MULTI ASSOCIATIVE POINTERS
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -191,6 +191,16 @@ TRI_vector_pointer_t TRI_LookupByKeyMultiPointer (TRI_memory_zone_t*,
|
|||
TRI_multi_pointer_t*,
|
||||
void const* key);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief lookups an element given a key and the last element
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TRI_LookupByKeyMultiPointer (TRI_multi_pointer_t*,
|
||||
void const* key,
|
||||
std::function<void(void*)>,
|
||||
void*&,
|
||||
size_t);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief lookups an element given an element
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
Loading…
Reference in New Issue