1
0
Fork 0

added edge index iterator

Conflicts:
	arangod/Aql/ExecutionBlock.h
This commit is contained in:
Jan Steemann 2015-03-27 16:21:04 +01:00
parent 50b531666c
commit 3057cf7a74
7 changed files with 446 additions and 66 deletions

View File

@ -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) {

View File

@ -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

View File

@ -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
// -----------------------------------------------------------------------------

View 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
// -----------------------------------------------------------------------------

View File

@ -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);

View File

@ -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
////////////////////////////////////////////////////////////////////////////////

View File

@ -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
////////////////////////////////////////////////////////////////////////////////