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;
|
hint = DefaultBatchSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<TRI_doc_mptr_copy_t> newDocs;
|
throwIfKilled(); // check if we were aborted
|
||||||
|
|
||||||
TRI_IF_FAILURE("EnumerateCollectionBlock::moreDocuments") {
|
TRI_IF_FAILURE("EnumerateCollectionBlock::moreDocuments") {
|
||||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<TRI_doc_mptr_copy_t> newDocs;
|
||||||
newDocs.reserve(hint);
|
newDocs.reserve(hint);
|
||||||
|
|
||||||
int res = _scanner->scan(newDocs, hint);
|
int res = _scanner->scan(newDocs, hint);
|
||||||
|
@ -992,6 +993,7 @@ IndexRangeBlock::IndexRangeBlock (ExecutionEngine* engine,
|
||||||
_posInDocs(0),
|
_posInDocs(0),
|
||||||
_anyBoundVariable(false),
|
_anyBoundVariable(false),
|
||||||
_skiplistIterator(nullptr),
|
_skiplistIterator(nullptr),
|
||||||
|
_edgeIndexIterator(nullptr),
|
||||||
_hashIndexSearchValue({ 0, nullptr }),
|
_hashIndexSearchValue({ 0, nullptr }),
|
||||||
_hashNextElement(nullptr),
|
_hashNextElement(nullptr),
|
||||||
_condition(new IndexOrCondition()),
|
_condition(new IndexOrCondition()),
|
||||||
|
@ -1047,6 +1049,8 @@ IndexRangeBlock::~IndexRangeBlock () {
|
||||||
if (_skiplistIterator != nullptr) {
|
if (_skiplistIterator != nullptr) {
|
||||||
TRI_FreeSkiplistIterator(_skiplistIterator);
|
TRI_FreeSkiplistIterator(_skiplistIterator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delete _edgeIndexIterator;
|
||||||
}
|
}
|
||||||
|
|
||||||
int IndexRangeBlock::initialize () {
|
int IndexRangeBlock::initialize () {
|
||||||
|
@ -1344,11 +1348,20 @@ bool IndexRangeBlock::initRanges () {
|
||||||
removeOverlapsIndexOr(*_condition);
|
removeOverlapsIndexOr(*_condition);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (en->_index->type == TRI_IDX_TYPE_PRIMARY_INDEX ||
|
if (en->_index->type == TRI_IDX_TYPE_PRIMARY_INDEX) {
|
||||||
en->_index->type == TRI_IDX_TYPE_EDGE_INDEX) {
|
|
||||||
return true; //no initialization here!
|
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 (en->_index->type == TRI_IDX_TYPE_HASH_INDEX) {
|
||||||
if (_condition->empty()) {
|
if (_condition->empty()) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -1559,11 +1572,11 @@ void IndexRangeBlock::freeCondition () {
|
||||||
bool IndexRangeBlock::readIndex (size_t atMost) {
|
bool IndexRangeBlock::readIndex (size_t atMost) {
|
||||||
ENTER_BLOCK;
|
ENTER_BLOCK;
|
||||||
// this is called every time we want more in _documents.
|
// this is called every time we want more in _documents.
|
||||||
// For non-skiplist indexes (currently hash, primary, edge), this
|
// For the primary key index, this only reads the index once, and never
|
||||||
// only reads the index once, and never again (although there might be
|
// again (although there might be multiple calls to this function).
|
||||||
// multiple calls to this function). For skiplists indexes, initRanges creates
|
// For the edge, hash or skiplists indexes, initRanges creates an iterator
|
||||||
// a skiplistIterator and readIndex just reads from the iterator until it is
|
// and read*Index just reads from the iterator until it is done.
|
||||||
// done. Then initRanges is read again and so on. This is to avoid reading the
|
// 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.
|
// entire index when we only want a small number of documents.
|
||||||
|
|
||||||
if (_documents.empty()) {
|
if (_documents.empty()) {
|
||||||
|
@ -1583,17 +1596,15 @@ bool IndexRangeBlock::readIndex (size_t atMost) {
|
||||||
readPrimaryIndex(*_condition);
|
readPrimaryIndex(*_condition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (en->_index->type == TRI_IDX_TYPE_EDGE_INDEX) {
|
||||||
|
readEdgeIndex(atMost);
|
||||||
|
}
|
||||||
else if (en->_index->type == TRI_IDX_TYPE_HASH_INDEX) {
|
else if (en->_index->type == TRI_IDX_TYPE_HASH_INDEX) {
|
||||||
readHashIndex(atMost);
|
readHashIndex(atMost);
|
||||||
}
|
}
|
||||||
else if (en->_index->type == TRI_IDX_TYPE_SKIPLIST_INDEX) {
|
else if (en->_index->type == TRI_IDX_TYPE_SKIPLIST_INDEX) {
|
||||||
readSkiplistIndex(atMost);
|
readSkiplistIndex(atMost);
|
||||||
}
|
}
|
||||||
else if (en->_index->type == TRI_IDX_TYPE_EDGE_INDEX) {
|
|
||||||
if (_flag) {
|
|
||||||
readEdgeIndex(*_condition);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
TRI_ASSERT(false);
|
TRI_ASSERT(false);
|
||||||
}
|
}
|
||||||
|
@ -1867,25 +1878,40 @@ 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;
|
ENTER_BLOCK;
|
||||||
TRI_document_collection_t* document = _collection->documentCollection();
|
|
||||||
|
|
||||||
std::string key;
|
_edgeNextElement = nullptr;
|
||||||
TRI_edge_direction_e direction = TRI_EDGE_IN; // must set a default to satisfy compiler
|
|
||||||
for (size_t i = 0; i < ranges.size(); i++) {
|
if (_edgeIndexIterator != nullptr) {
|
||||||
for (auto x : ranges[i]) {
|
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());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto x : ranges) {
|
||||||
if (x._attr == std::string(TRI_VOC_ATTRIBUTE_FROM)) {
|
if (x._attr == std::string(TRI_VOC_ATTRIBUTE_FROM)) {
|
||||||
// we can use lower bound because only equality is supported
|
// we can use lower bound because only equality is supported
|
||||||
TRI_ASSERT(x.is1ValueRangeInfo());
|
TRI_ASSERT(x.is1ValueRangeInfo());
|
||||||
auto const json = x._lowConst.bound().json();
|
auto const json = x._lowConst.bound().json();
|
||||||
if (TRI_IsStringJson(json)) {
|
if (TRI_IsStringJson(json)) {
|
||||||
// no error will be thrown if _from is not a string
|
// no error will be thrown if _from is not a string
|
||||||
key = std::string(json->_value._string.data, json->_value._string.length - 1);
|
buildIterator(TRI_EDGE_OUT, json);
|
||||||
direction = TRI_EDGE_OUT;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1895,34 +1921,70 @@ void IndexRangeBlock::readEdgeIndex (IndexOrCondition const& ranges) {
|
||||||
auto const json = x._lowConst.bound().json();
|
auto const json = x._lowConst.bound().json();
|
||||||
if (TRI_IsStringJson(json)) {
|
if (TRI_IsStringJson(json)) {
|
||||||
// no error will be thrown if _to is not a string
|
// no error will be thrown if _to is not a string
|
||||||
key = std::string(json->_value._string.data, json->_value._string.length - 1);
|
buildIterator(TRI_EDGE_IN, json);
|
||||||
direction = TRI_EDGE_IN;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! key.empty()) {
|
LEAVE_BLOCK;
|
||||||
TRI_voc_cid_t documentCid;
|
}
|
||||||
std::string documentKey;
|
|
||||||
|
|
||||||
int errorCode = resolve(key.c_str(), documentCid, documentKey);
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief actually read from the edge index
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
if (errorCode == TRI_ERROR_NO_ERROR) {
|
void IndexRangeBlock::readEdgeIndex (size_t atMost) {
|
||||||
// silently ignore all errors due to wrong _from / _to specifications
|
ENTER_BLOCK;
|
||||||
auto&& result = TRI_LookupEdgesDocumentCollection(document, direction,
|
|
||||||
documentCid, (TRI_voc_key_t) documentKey.c_str());
|
if (_edgeIndexIterator == nullptr) {
|
||||||
for (auto it : result) {
|
return;
|
||||||
_documents.emplace_back(it);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_engine->_stats.scannedIndex += static_cast<int64_t>(result.size());
|
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);
|
||||||
|
|
||||||
|
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;
|
LEAVE_BLOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief destroy the search values for the hash index lookup
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
void IndexRangeBlock::destroyHashIndexSearchValues () {
|
void IndexRangeBlock::destroyHashIndexSearchValues () {
|
||||||
if (_hashIndexSearchValue._values != nullptr) {
|
if (_hashIndexSearchValue._values != nullptr) {
|
||||||
TRI_shaper_t* shaper = _collection->documentCollection()->getShaper();
|
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) {
|
bool IndexRangeBlock::setupHashIndexSearchValue (IndexAndCondition const& range) {
|
||||||
auto en = static_cast<IndexRangeNode const*>(getPlanNode());
|
auto en = static_cast<IndexRangeNode const*>(getPlanNode());
|
||||||
TRI_index_t* idx = en->_index->getInternals();
|
TRI_index_t* idx = en->_index->getInternals();
|
||||||
|
@ -2008,6 +2074,10 @@ void IndexRangeBlock::getHashIndexIterator (IndexAndCondition const& ranges) {
|
||||||
LEAVE_BLOCK;
|
LEAVE_BLOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief actually read from the hash index
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
void IndexRangeBlock::readHashIndex (size_t atMost) {
|
void IndexRangeBlock::readHashIndex (size_t atMost) {
|
||||||
ENTER_BLOCK;
|
ENTER_BLOCK;
|
||||||
|
|
||||||
|
@ -2166,6 +2236,10 @@ void IndexRangeBlock::getSkiplistIterator (IndexAndCondition const& ranges) {
|
||||||
LEAVE_BLOCK;
|
LEAVE_BLOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief actually read from the skiplist index
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
void IndexRangeBlock::readSkiplistIndex (size_t atMost) {
|
void IndexRangeBlock::readSkiplistIndex (size_t atMost) {
|
||||||
ENTER_BLOCK;
|
ENTER_BLOCK;
|
||||||
if (_skiplistIterator == nullptr) {
|
if (_skiplistIterator == nullptr) {
|
||||||
|
|
|
@ -38,13 +38,14 @@
|
||||||
#include "Aql/Range.h"
|
#include "Aql/Range.h"
|
||||||
#include "Aql/WalkerWorker.h"
|
#include "Aql/WalkerWorker.h"
|
||||||
#include "Aql/ExecutionStats.h"
|
#include "Aql/ExecutionStats.h"
|
||||||
|
#include "Cluster/ClusterComm.h"
|
||||||
#include "Utils/AqlTransaction.h"
|
#include "Utils/AqlTransaction.h"
|
||||||
#include "Utils/transactions.h"
|
#include "Utils/transactions.h"
|
||||||
#include "Utils/V8TransactionContext.h"
|
#include "Utils/V8TransactionContext.h"
|
||||||
#include "Cluster/ClusterComm.h"
|
|
||||||
|
|
||||||
struct TRI_doc_mptr_copy_t;
|
|
||||||
struct TRI_df_marker_s;
|
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_hash_index_element_multi_s;
|
||||||
struct TRI_json_t;
|
struct TRI_json_t;
|
||||||
|
|
||||||
|
@ -619,12 +620,6 @@ namespace triagens {
|
||||||
|
|
||||||
void readPrimaryIndex (IndexOrCondition const&);
|
void readPrimaryIndex (IndexOrCondition const&);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief read using the edges index
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void readEdgeIndex (IndexOrCondition const&);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief destroy the hash index search value
|
/// @brief destroy the hash index search value
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -649,6 +644,18 @@ namespace triagens {
|
||||||
|
|
||||||
void readHashIndex (size_t);
|
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.
|
/// @brief this tries to create a skiplistIterator to read from the index.
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -769,6 +776,14 @@ namespace triagens {
|
||||||
|
|
||||||
TRI_skiplist_iterator_t* _skiplistIterator;
|
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
|
/// @brief current search value for hash index lookup
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -781,6 +796,12 @@ namespace triagens {
|
||||||
|
|
||||||
struct TRI_hash_index_element_multi_s* _hashNextElement;
|
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,
|
/// @brief _condition: holds the IndexAndCondition for the current incoming block,
|
||||||
/// this is just the _ranges[_rangesPos] member of the plan node if _allBoundsConstant
|
/// 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;
|
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
|
// --SECTION-- END-OF-FILE
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
|
@ -31,9 +31,12 @@
|
||||||
#define ARANGODB_VOC_BASE_EDGE__COLLECTION_H 1
|
#define ARANGODB_VOC_BASE_EDGE__COLLECTION_H 1
|
||||||
|
|
||||||
#include "Basics/Common.h"
|
#include "Basics/Common.h"
|
||||||
|
#include "Basics/Exceptions.h"
|
||||||
#include "VocBase/voc-types.h"
|
#include "VocBase/voc-types.h"
|
||||||
#include "VocBase/document-collection.h"
|
#include "VocBase/document-collection.h"
|
||||||
|
|
||||||
|
struct TRI_index_s;
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// --SECTION-- EDGE COLLECTION
|
// --SECTION-- EDGE COLLECTION
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
@ -63,6 +66,34 @@ typedef struct TRI_edge_header_s {
|
||||||
}
|
}
|
||||||
TRI_edge_header_t;
|
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
|
// --SECTION-- EDGES INDEX
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
@ -81,6 +112,17 @@ std::vector<TRI_doc_mptr_copy_t> TRI_LookupEdgesDocumentCollection (
|
||||||
TRI_voc_cid_t,
|
TRI_voc_cid_t,
|
||||||
TRI_voc_key_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
|
#endif
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
|
@ -2425,6 +2425,134 @@ function optimizerIndexesTestSuite () {
|
||||||
/// @brief test suite
|
/// @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 () {
|
function optimizerIndexesInOrTestSuite () {
|
||||||
var c;
|
var c;
|
||||||
|
|
||||||
|
@ -3871,6 +3999,7 @@ function optimizerIndexesSortTestSuite () {
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
jsunity.run(optimizerIndexesTestSuite);
|
jsunity.run(optimizerIndexesTestSuite);
|
||||||
|
jsunity.run(optimizerEdgeIndexTestSuite);
|
||||||
jsunity.run(optimizerIndexesInOrTestSuite);
|
jsunity.run(optimizerIndexesInOrTestSuite);
|
||||||
jsunity.run(optimizerIndexesRangesTestSuite);
|
jsunity.run(optimizerIndexesRangesTestSuite);
|
||||||
jsunity.run(optimizerIndexesSortTestSuite);
|
jsunity.run(optimizerIndexesSortTestSuite);
|
||||||
|
|
|
@ -563,6 +563,72 @@ TRI_vector_pointer_t TRI_LookupByKeyMultiPointer (TRI_memory_zone_t* zone,
|
||||||
return result;
|
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
|
/// @brief lookups an element given an element
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -34,10 +34,10 @@
|
||||||
#define ARANGODB_BASICS_C_ASSOCIATIVE__MULTI_H 1
|
#define ARANGODB_BASICS_C_ASSOCIATIVE__MULTI_H 1
|
||||||
|
|
||||||
#include "Basics/Common.h"
|
#include "Basics/Common.h"
|
||||||
|
|
||||||
#include "Basics/locks.h"
|
|
||||||
#include "Basics/vector.h"
|
#include "Basics/vector.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// --SECTION-- MULTI ASSOCIATIVE POINTERS
|
// --SECTION-- MULTI ASSOCIATIVE POINTERS
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
@ -191,6 +191,16 @@ TRI_vector_pointer_t TRI_LookupByKeyMultiPointer (TRI_memory_zone_t*,
|
||||||
TRI_multi_pointer_t*,
|
TRI_multi_pointer_t*,
|
||||||
void const* key);
|
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
|
/// @brief lookups an element given an element
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
Loading…
Reference in New Issue