1
0
Fork 0

Merge branch 'spdvpk' of ssh://github.com/ArangoDB/ArangoDB into spdvpk

This commit is contained in:
Max Neunhoeffer 2016-03-09 13:28:43 +01:00
commit 5b807d6753
14 changed files with 460 additions and 497 deletions

View File

@ -33,6 +33,7 @@
#include "VocBase/collection.h" #include "VocBase/collection.h"
#include <velocypack/Iterator.h> #include <velocypack/Iterator.h>
#include <velocypack/Slice.h>
#include <velocypack/velocypack-aliases.h> #include <velocypack/velocypack-aliases.h>
using namespace arangodb::aql; using namespace arangodb::aql;
@ -1046,25 +1047,22 @@ AstNode* Ast::createNodeIntersectedArray(AstNode const* lhs,
size_t const nl = lhs->numMembers(); size_t const nl = lhs->numMembers();
size_t const nr = rhs->numMembers(); size_t const nr = rhs->numMembers();
std::unordered_map<TRI_json_t*, AstNode const*, arangodb::basics::JsonHash, std::unordered_map<VPackSlice, AstNode const*> cache(nl + nr);
arangodb::basics::JsonEqual>
cache(nl + nr, arangodb::basics::JsonHash(),
arangodb::basics::JsonEqual());
for (size_t i = 0; i < nl; ++i) { for (size_t i = 0; i < nl; ++i) {
auto member = lhs->getMemberUnchecked(i); auto member = lhs->getMemberUnchecked(i);
auto json = member->computeJson(); VPackSlice slice = member->computeValue();
cache.emplace(json, member); cache.emplace(slice, member);
} }
auto node = createNodeArray(); auto node = createNodeArray();
for (size_t i = 0; i < nr; ++i) { for (size_t i = 0; i < nr; ++i) {
auto member = rhs->getMemberUnchecked(i); auto member = rhs->getMemberUnchecked(i);
auto json = member->computeJson(); VPackSlice slice = member->computeValue();
auto it = cache.find(json); auto it = cache.find(slice);
if (it != cache.end()) { if (it != cache.end()) {
node->addMember((*it).second); node->addMember((*it).second);
@ -1085,10 +1083,7 @@ AstNode* Ast::createNodeUnionizedArray(AstNode const* lhs, AstNode const* rhs) {
size_t const nl = lhs->numMembers(); size_t const nl = lhs->numMembers();
size_t const nr = rhs->numMembers(); size_t const nr = rhs->numMembers();
std::unordered_map<TRI_json_t*, AstNode const*, arangodb::basics::JsonHash, std::unordered_map<VPackSlice, AstNode const*> cache(nl + nr);
arangodb::basics::JsonEqual>
cache(nl + nr, arangodb::basics::JsonHash(),
arangodb::basics::JsonEqual());
for (size_t i = 0; i < nl + nr; ++i) { for (size_t i = 0; i < nl + nr; ++i) {
AstNode* member; AstNode* member;
@ -1097,9 +1092,9 @@ AstNode* Ast::createNodeUnionizedArray(AstNode const* lhs, AstNode const* rhs) {
} else { } else {
member = rhs->getMemberUnchecked(i - nl); member = rhs->getMemberUnchecked(i - nl);
} }
auto json = member->computeJson(); VPackSlice slice = member->computeValue();
cache.emplace(json, member); cache.emplace(slice, member);
} }
auto node = createNodeArray(); auto node = createNodeArray();
@ -2039,16 +2034,14 @@ AstNode const* Ast::deduplicateArray(AstNode const* node) {
// TODO: sort values in place first and compare two adjacent members each // TODO: sort values in place first and compare two adjacent members each
std::unordered_map<TRI_json_t*, AstNode const*, arangodb::basics::JsonHash, std::unordered_map<VPackSlice, AstNode const*> cache(n);
arangodb::basics::JsonEqual>
cache(n, arangodb::basics::JsonHash(), arangodb::basics::JsonEqual());
for (size_t i = 0; i < n; ++i) { for (size_t i = 0; i < n; ++i) {
auto member = node->getMemberUnchecked(i); auto member = node->getMemberUnchecked(i);
auto json = member->computeJson(); VPackSlice slice = member->computeValue();
if (cache.find(json) == cache.end()) { if (cache.find(slice) == cache.end()) {
cache.emplace(json, member); cache.emplace(slice, member);
} }
} }

View File

@ -40,6 +40,7 @@
#endif #endif
#include <velocypack/Builder.h> #include <velocypack/Builder.h>
#include <velocypack/Slice.h>
#include <velocypack/velocypack-aliases.h> #include <velocypack/velocypack-aliases.h>
#include <array> #include <array>
@ -432,7 +433,7 @@ static bool IsEmptyString(char const* p, size_t length) {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
AstNode::AstNode(AstNodeType type) AstNode::AstNode(AstNodeType type)
: type(type), flags(0), computedJson(nullptr) {} : type(type), flags(0), computedValue(nullptr) {}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief create a node, with defining a value type /// @brief create a node, with defining a value type
@ -441,7 +442,7 @@ AstNode::AstNode(AstNodeType type)
AstNode::AstNode(AstNodeType type, AstNodeValueType valueType) : AstNode(type) { AstNode::AstNode(AstNodeType type, AstNodeValueType valueType) : AstNode(type) {
value.type = valueType; value.type = valueType;
TRI_ASSERT(flags == 0); TRI_ASSERT(flags == 0);
TRI_ASSERT(computedJson == nullptr); TRI_ASSERT(computedValue == nullptr);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -453,7 +454,7 @@ AstNode::AstNode(bool v, AstNodeValueType valueType)
TRI_ASSERT(valueType == VALUE_TYPE_BOOL); TRI_ASSERT(valueType == VALUE_TYPE_BOOL);
value.value._bool = v; value.value._bool = v;
TRI_ASSERT(flags == 0); TRI_ASSERT(flags == 0);
TRI_ASSERT(computedJson == nullptr); TRI_ASSERT(computedValue == nullptr);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -465,7 +466,7 @@ AstNode::AstNode(int64_t v, AstNodeValueType valueType)
TRI_ASSERT(valueType == VALUE_TYPE_INT); TRI_ASSERT(valueType == VALUE_TYPE_INT);
value.value._int = v; value.value._int = v;
TRI_ASSERT(flags == 0); TRI_ASSERT(flags == 0);
TRI_ASSERT(computedJson == nullptr); TRI_ASSERT(computedValue == nullptr);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -477,7 +478,7 @@ AstNode::AstNode(char const* v, size_t length, AstNodeValueType valueType)
TRI_ASSERT(valueType == VALUE_TYPE_STRING); TRI_ASSERT(valueType == VALUE_TYPE_STRING);
setStringValue(v, length); setStringValue(v, length);
TRI_ASSERT(flags == 0); TRI_ASSERT(flags == 0);
TRI_ASSERT(computedJson == nullptr); TRI_ASSERT(computedValue == nullptr);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -487,7 +488,7 @@ AstNode::AstNode(char const* v, size_t length, AstNodeValueType valueType)
AstNode::AstNode(Ast* ast, arangodb::basics::Json const& json) AstNode::AstNode(Ast* ast, arangodb::basics::Json const& json)
: AstNode(getNodeTypeFromJson(json)) { : AstNode(getNodeTypeFromJson(json)) {
TRI_ASSERT(flags == 0); TRI_ASSERT(flags == 0);
TRI_ASSERT(computedJson == nullptr); TRI_ASSERT(computedValue == nullptr);
auto query = ast->query(); auto query = ast->query();
@ -676,7 +677,7 @@ AstNode::AstNode(std::function<void(AstNode*)> registerNode,
arangodb::basics::Json const& json) arangodb::basics::Json const& json)
: AstNode(getNodeTypeFromJson(json)) { : AstNode(getNodeTypeFromJson(json)) {
TRI_ASSERT(flags == 0); TRI_ASSERT(flags == 0);
TRI_ASSERT(computedJson == nullptr); TRI_ASSERT(computedValue == nullptr);
switch (type) { switch (type) {
case NODE_TYPE_ATTRIBUTE_ACCESS: { case NODE_TYPE_ATTRIBUTE_ACCESS: {
@ -830,9 +831,8 @@ AstNode::AstNode(std::function<void(AstNode*)> registerNode,
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
AstNode::~AstNode() { AstNode::~AstNode() {
if (computedJson != nullptr) { if (computedValue != nullptr) {
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, computedJson); delete[] computedValue;
computedJson = nullptr;
} }
} }
@ -936,21 +936,23 @@ void AstNode::dump(int level) const {
#endif #endif
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief compute the JSON for a constant value node /// @brief compute the value for a constant value node
/// the JSON is owned by the node and must not be freed by the caller /// the value is owned by the node and must not be freed by the caller
/// note that the return value might be NULL in case of OOM /// note that the return value might be NULL in case of OOM
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
TRI_json_t* AstNode::computeJson() const { VPackSlice AstNode::computeValue() const {
TRI_ASSERT(isConstant()); TRI_ASSERT(isConstant());
if (computedJson == nullptr) { if (computedValue == nullptr) {
// note: the following may fail but we do not need to VPackBuilder builder;
// check that here toVelocyPackValue(builder);
computedJson = toJsonValue(TRI_UNKNOWN_MEM_ZONE);
computedValue = new uint8_t[builder.size()];
memcpy(computedValue, builder.data(), builder.size());
} }
return computedJson; return VPackSlice(computedValue);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -30,6 +30,8 @@
#include "Basics/json.h" #include "Basics/json.h"
#include "Basics/JsonHelper.h" #include "Basics/JsonHelper.h"
#include <velocypack/Slice.h>
#include <functional> #include <functional>
#include <iosfwd> #include <iosfwd>
@ -291,12 +293,12 @@ struct AstNode {
#endif #endif
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
/// @brief compute the JSON for a constant value node /// @brief compute the value for a constant value node
/// the JSON is owned by the node and must not be freed by the caller /// the value is owned by the node and must not be freed by the caller
/// note that the return value might be NULL in case of OOM /// note that the return value might be NULL in case of OOM
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
TRI_json_t* computeJson() const; arangodb::velocypack::Slice computeValue() const;
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
/// @brief sort the members of an (array) node /// @brief sort the members of an (array) node
@ -932,10 +934,10 @@ struct AstNode {
private: private:
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
/// @brief precomputed JSON value (used when executing expressions) /// @brief precomputed VPack value (used when executing expressions)
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
TRI_json_t mutable* computedJson; uint8_t mutable* computedValue;
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
/// @brief the node's sub nodes /// @brief the node's sub nodes

View File

@ -104,7 +104,7 @@ Expression::~Expression() {
switch (_type) { switch (_type) {
case JSON: case JSON:
TRI_ASSERT(_data != nullptr); TRI_ASSERT(_data != nullptr);
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, _data); delete[] _data;
break; break;
case ATTRIBUTE: { case ATTRIBUTE: {
@ -154,9 +154,7 @@ AqlValue$ Expression::execute(arangodb::AqlTransaction* trx,
case JSON: { case JSON: {
// TODO // TODO
TRI_ASSERT(_data != nullptr); TRI_ASSERT(_data != nullptr);
VPackBuilder builder; return AqlValue$(VPackSlice(_data), AqlValue$::AqlValueType::REFERENCE_STICKY);
JsonHelper::toVelocyPack(_data, builder);
return AqlValue$(builder);
} }
case SIMPLE: { case SIMPLE: {
@ -171,22 +169,8 @@ AqlValue$ Expression::execute(arangodb::AqlTransaction* trx,
case V8: { case V8: {
TRI_ASSERT(_func != nullptr); TRI_ASSERT(_func != nullptr);
try { ISOLATE;
ISOLATE; return _func->execute(isolate, _ast->query(), trx, argv, startPos, vars, regs);
return _func->execute(isolate, _ast->query(), trx, argv, startPos, vars,
regs);
} catch (arangodb::basics::Exception& ex) {
if (_ast->query()->verboseErrors()) {
ex.addToMessage(" while evaluating expression ");
auto json = _node->toJson(TRI_UNKNOWN_MEM_ZONE, false);
if (json != nullptr) {
ex.addToMessage(arangodb::basics::JsonHelper::toString(json));
TRI_Free(TRI_UNKNOWN_MEM_ZONE, json);
}
}
throw;
}
} }
case UNPROCESSED: { case UNPROCESSED: {
@ -408,12 +392,11 @@ void Expression::buildExpression() {
if (_type == JSON) { if (_type == JSON) {
TRI_ASSERT(_data == nullptr); TRI_ASSERT(_data == nullptr);
// generate a constant value // generate a constant value
_data = _node->toJsonValue(TRI_UNKNOWN_MEM_ZONE); VPackBuilder builder;
_node->toVelocyPackValue(builder);
if (_data == nullptr) { _data = new uint8_t[builder.size()];
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, memcpy(_data, builder.data(), builder.size());
"invalid json in simple expression");
}
} else if (_type == V8) { } else if (_type == V8) {
// generate a V8 expression // generate a V8 expression
_func = _executor->generateExpression(_node); _func = _executor->generateExpression(_node);
@ -651,16 +634,7 @@ AqlValue$ Expression::executeSimpleExpressionArray(
std::vector<Variable const*> const& vars, std::vector<Variable const*> const& vars,
std::vector<RegisterId> const& regs) { std::vector<RegisterId> const& regs) {
if (node->isConstant()) { if (node->isConstant()) {
auto json = node->computeJson(); return AqlValue$(node->computeValue(), AqlValue$::AqlValueType::REFERENCE_STICKY);
if (json == nullptr) {
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
}
VPackBuilder builder;
JsonHelper::toVelocyPack(json, builder);
return AqlValue$(builder);
} }
size_t const n = node->numMembers(); size_t const n = node->numMembers();
@ -689,16 +663,7 @@ AqlValue$ Expression::executeSimpleExpressionObject(
std::vector<Variable const*> const& vars, std::vector<Variable const*> const& vars,
std::vector<RegisterId> const& regs) { std::vector<RegisterId> const& regs) {
if (node->isConstant()) { if (node->isConstant()) {
auto json = node->computeJson(); return AqlValue$(node->computeValue(), AqlValue$::AqlValueType::REFERENCE_STICKY);
if (json == nullptr) {
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
}
VPackBuilder builder;
JsonHelper::toVelocyPack(json, builder);
return AqlValue$(builder);
} }
VPackBuilder builder; VPackBuilder builder;
@ -725,15 +690,7 @@ AqlValue$ Expression::executeSimpleExpressionObject(
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
AqlValue$ Expression::executeSimpleExpressionValue(AstNode const* node) { AqlValue$ Expression::executeSimpleExpressionValue(AstNode const* node) {
auto json = node->computeJson(); return AqlValue$(node->computeValue(), AqlValue$::AqlValueType::REFERENCE_STICKY);
if (json == nullptr) {
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
}
VPackBuilder builder;
JsonHelper::toVelocyPack(json, builder);
return AqlValue$(builder);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -1270,14 +1227,14 @@ AqlValue$ Expression::executeSimpleExpressionArithmetic(
trx, argv, startPos, vars, regs, true); trx, argv, startPos, vars, regs, true);
if (lhs.isObject()) { if (lhs.isObject()) {
return AqlValue$(VelocyPackHelper::NullValue(), AqlValue$::AqlValueType::REFERENCE_STICKY); return AqlValue$(VelocyPackHelper::NullValue());
} }
AqlValue$ rhs = executeSimpleExpression(node->getMember(1), AqlValue$ rhs = executeSimpleExpression(node->getMember(1),
trx, argv, startPos, vars, regs, true); trx, argv, startPos, vars, regs, true);
if (rhs.isObject()) { if (rhs.isObject()) {
return AqlValue$(VelocyPackHelper::NullValue(), AqlValue$::AqlValueType::REFERENCE_STICKY); return AqlValue$(VelocyPackHelper::NullValue());
} }
double const l = lhs.toDouble(); double const l = lhs.toDouble();
@ -1298,17 +1255,17 @@ AqlValue$ Expression::executeSimpleExpressionArithmetic(
case NODE_TYPE_OPERATOR_BINARY_DIV: case NODE_TYPE_OPERATOR_BINARY_DIV:
if (r == 0.0) { if (r == 0.0) {
RegisterWarning(_ast, "/", TRI_ERROR_QUERY_DIVISION_BY_ZERO); RegisterWarning(_ast, "/", TRI_ERROR_QUERY_DIVISION_BY_ZERO);
return AqlValue$(VelocyPackHelper::NullValue(), AqlValue$::AqlValueType::REFERENCE_STICKY); return AqlValue$(VelocyPackHelper::NullValue());
} }
return AqlValue$(builder); return AqlValue$(builder);
case NODE_TYPE_OPERATOR_BINARY_MOD: case NODE_TYPE_OPERATOR_BINARY_MOD:
if (r == 0.0) { if (r == 0.0) {
RegisterWarning(_ast, "/", TRI_ERROR_QUERY_DIVISION_BY_ZERO); RegisterWarning(_ast, "/", TRI_ERROR_QUERY_DIVISION_BY_ZERO);
return AqlValue$(VelocyPackHelper::NullValue(), AqlValue$::AqlValueType::REFERENCE_STICKY); return AqlValue$(VelocyPackHelper::NullValue());
} }
builder.add(VPackValue(fmod(l, r))); builder.add(VPackValue(fmod(l, r)));
return AqlValue$(builder); return AqlValue$(builder);
default: default:
return AqlValue$(VelocyPackHelper::NullValue(), AqlValue$::AqlValueType::REFERENCE_STICKY); return AqlValue$(VelocyPackHelper::NullValue());
} }
} }

View File

@ -490,7 +490,7 @@ class Expression {
union { union {
V8Expression* _func; V8Expression* _func;
struct TRI_json_t* _data; uint8_t* _data;
AttributeAccessor* _accessor; AttributeAccessor* _accessor;
}; };

View File

@ -36,9 +36,11 @@
#include "FulltextIndex/fulltext-result.h" #include "FulltextIndex/fulltext-result.h"
#include "FulltextIndex/fulltext-query.h" #include "FulltextIndex/fulltext-query.h"
#include "Indexes/Index.h" #include "Indexes/Index.h"
#include "Indexes/EdgeIndex.h"
#include "Indexes/FulltextIndex.h" #include "Indexes/FulltextIndex.h"
#include "Indexes/GeoIndex2.h" #include "Indexes/GeoIndex2.h"
#include "Rest/SslInterface.h" #include "Rest/SslInterface.h"
#include "Utils/OperationCursor.h"
#include "Utils/OperationOptions.h" #include "Utils/OperationOptions.h"
#include "Utils/OperationResult.h" #include "Utils/OperationResult.h"
#include "Utils/Transaction.h" #include "Utils/Transaction.h"
@ -64,6 +66,15 @@ using VertexId = arangodb::traverser::VertexId;
thread_local std::unordered_map<std::string, RegexMatcher*>* RegexCache = thread_local std::unordered_map<std::string, RegexMatcher*>* RegexCache =
nullptr; nullptr;
////////////////////////////////////////////////////////////////////////////////
/// @brief Insert a mptr into the result
////////////////////////////////////////////////////////////////////////////////
static void InsertMasterPointer(TRI_doc_mptr_t const* mptr, VPackBuilder& builder) {
builder.add(VPackValue(static_cast<void const*>(mptr->vpack()),
VPackValueType::External));
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief clear the regex cache in a thread /// @brief clear the regex cache in a thread
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -434,121 +445,10 @@ static bool SortNumberList(VPackSlice const& values,
return true; return true;
} }
static inline void ExpandShapedJson(
VocShaper* shaper, CollectionNameResolver const* resolver,
TRI_voc_cid_t const& cid, TRI_doc_mptr_t const* mptr, VPackBuilder& b,
bool keepTopLevelOpen,
std::unordered_set<std::string> const& forbidden) {
b.add(VPackValue(VPackValueType::Object));
TRI_df_marker_t const* marker =
static_cast<TRI_df_marker_t const*>(mptr->getDataPtr());
TRI_shaped_json_t shaped;
TRI_EXTRACT_SHAPED_JSON_MARKER(shaped, marker);
std::shared_ptr<VPackBuilder> tmp = TRI_VelocyPackShapedJson(shaper, &shaped);
// Copy the shaped into our local builder
for (auto const& it : VPackObjectIterator(tmp->slice())) {
std::string key = it.key.copyString();
if (forbidden.count(key) == 0) {
b.add(it.key.copyString(), it.value);
}
}
char const* key = TRI_EXTRACT_MARKER_KEY(marker);
std::string id(resolver->getCollectionName(cid));
id.push_back('/');
id.append(key);
if (forbidden.count(TRI_VOC_ATTRIBUTE_ID) == 0) {
b.add(TRI_VOC_ATTRIBUTE_ID, VPackValue(id));
}
if (forbidden.count(TRI_VOC_ATTRIBUTE_REV) == 0) {
b.add(TRI_VOC_ATTRIBUTE_REV,
VPackValue(std::to_string(TRI_EXTRACT_MARKER_RID(marker))));
}
if (forbidden.count(TRI_VOC_ATTRIBUTE_KEY) == 0) {
b.add(TRI_VOC_ATTRIBUTE_KEY, VPackValue(key));
}
#if 0
// TODO
if (TRI_IS_EDGE_MARKER(marker)) {
if (forbidden.count(TRI_VOC_ATTRIBUTE_FROM) == 0) {
std::string from(resolver->getCollectionNameCluster(
TRI_EXTRACT_MARKER_FROM_CID(marker)));
from.push_back('/');
from.append(TRI_EXTRACT_MARKER_FROM_KEY(marker));
b.add(TRI_VOC_ATTRIBUTE_FROM, VPackValue(from));
}
if (forbidden.count(TRI_VOC_ATTRIBUTE_TO) == 0) {
std::string to(
resolver->getCollectionNameCluster(TRI_EXTRACT_MARKER_TO_CID(marker)));
to.push_back('/');
to.append(TRI_EXTRACT_MARKER_TO_KEY(marker));
b.add(TRI_VOC_ATTRIBUTE_TO, VPackValue(to));
}
}
#endif
if (!keepTopLevelOpen) {
b.close();
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Reads a document by cid and key
/// Also lazy locks the collection.
/// Returns null if the document does not exist
////////////////////////////////////////////////////////////////////////////////
static void ReadDocument(arangodb::AqlTransaction* trx,
CollectionNameResolver const* resolver,
TRI_voc_cid_t cid, char const* key,
VPackBuilder& result) {
trx->addCollectionAtRuntime(cid);
OperationOptions options;
VPackSlice slice;
#warning fill slice from key
OperationResult opRes = trx->document(trx->collectionName(cid), slice, options);
#warning fill mptr
if (opRes.code != TRI_ERROR_NO_ERROR) {
result.add(VPackValue(VPackValueType::Null));
} else {
#warning convert opRes result to vpack external
result.add(VPackValue(static_cast<void const*>(nullptr), VPackValueType::External));
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief function to filter the given list of mptr
////////////////////////////////////////////////////////////////////////////////
static void FilterDocuments(arangodb::ExampleMatcher const* matcher,
TRI_voc_cid_t cid,
std::vector<TRI_doc_mptr_t>& toFilter) {
if (matcher == nullptr) {
return;
}
size_t resultCount = toFilter.size();
for (size_t i = 0; i < resultCount; /* nothing */) {
if (!matcher->matches(cid, &toFilter[i])) {
toFilter.erase(toFilter.begin() + i);
--resultCount;
} else {
++i;
}
}
}
static void RequestEdges(VPackSlice const& vertexSlice, static void RequestEdges(VPackSlice const& vertexSlice,
arangodb::AqlTransaction* trx, arangodb::AqlTransaction* trx,
CollectionNameResolver const* resolver, std::string const& collectionName,
VocShaper* shaper, TRI_voc_cid_t cid, std::string const& indexId,
TRI_document_collection_t* collection,
TRI_edge_direction_e direction, TRI_edge_direction_e direction,
arangodb::ExampleMatcher const* matcher, arangodb::ExampleMatcher const* matcher,
bool includeVertices, VPackBuilder& result) { bool includeVertices, VPackBuilder& result) {
@ -571,59 +471,109 @@ static void RequestEdges(VPackSlice const& vertexSlice,
vertexId); vertexId);
} }
TRI_voc_cid_t startCid = resolver->getCollectionIdLocal(parts[0]); if (trx->getCollectionType(parts[0]) == TRI_COL_TYPE_UNKNOWN) {
if (startCid == 0) {
THROW_ARANGO_EXCEPTION_FORMAT(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND, "'%s'", THROW_ARANGO_EXCEPTION_FORMAT(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND, "'%s'",
parts[0].c_str()); parts[0].c_str());
} }
char* key = const_cast<char*>(parts[1].c_str()); VPackBuilder searchValueBuilder;
std::vector<TRI_doc_mptr_t> edges = TRI_LookupEdgesDocumentCollection( searchValueBuilder.openArray();
trx, collection, direction, startCid, key); switch (direction) {
FilterDocuments(matcher, cid, edges); case TRI_EDGE_OUT:
size_t resultCount = edges.size(); searchValueBuilder.openArray();
searchValueBuilder.openObject();
searchValueBuilder.add(TRI_SLICE_KEY_EQUAL, VPackValue(vertexId));
searchValueBuilder.close();
searchValueBuilder.close();
searchValueBuilder.add(VPackValue(VPackValueType::None));
break;
case TRI_EDGE_IN:
searchValueBuilder.add(VPackValue(VPackValueType::None));
searchValueBuilder.openArray();
searchValueBuilder.openObject();
searchValueBuilder.add(TRI_SLICE_KEY_EQUAL, VPackValue(vertexId));
searchValueBuilder.close();
searchValueBuilder.close();
break;
case TRI_EDGE_ANY:
searchValueBuilder.openArray();
searchValueBuilder.openObject();
searchValueBuilder.add(TRI_SLICE_KEY_EQUAL, VPackValue(vertexId));
searchValueBuilder.close();
searchValueBuilder.close();
searchValueBuilder.openArray();
searchValueBuilder.openObject();
searchValueBuilder.add(TRI_SLICE_KEY_EQUAL, VPackValue(vertexId));
searchValueBuilder.close();
searchValueBuilder.close();
}
searchValueBuilder.close();
VPackSlice search = searchValueBuilder.slice();
OperationCursor cursor = trx->indexScan(
collectionName, arangodb::Transaction::CursorType::INDEX, indexId,
search, 0, UINT64_MAX, 1000, false);
if (cursor.failed()) {
THROW_ARANGO_EXCEPTION(cursor.code);
}
if (includeVertices) { while (cursor.hasMore()) {
for (size_t i = 0; i < resultCount; ++i) { cursor.getMore();
VPackObjectBuilder guard(&result); VPackSlice edges = cursor.slice();
result.add(VPackValue("edge")); TRI_ASSERT(edges.isArray());
std::unordered_set<std::string> unused; if (includeVertices) {
#warning convert to vpack for (auto const& edge : VPackArrayIterator(edges)) {
ExpandShapedJson(shaper, resolver, cid, &(edges[i]), result, false, unused); VPackObjectBuilder guard(&result);
char const* targetKey = nullptr; if (matcher->matches(edge)) {
TRI_voc_cid_t targetCid = 0; result.add("edge", edge);
switch (direction) { std::string target;
case TRI_EDGE_OUT: TRI_ASSERT(edge.hasKey(TRI_VOC_ATTRIBUTE_FROM));
targetKey = TRI_EXTRACT_MARKER_TO_KEY(&edges[i]); TRI_ASSERT(edge.hasKey(TRI_VOC_ATTRIBUTE_TO));
targetCid = TRI_EXTRACT_MARKER_TO_CID(&edges[i]); switch (direction) {
break; case TRI_EDGE_OUT:
case TRI_EDGE_IN: target = edge.get(TRI_VOC_ATTRIBUTE_TO).copyString();
targetKey = TRI_EXTRACT_MARKER_FROM_KEY(&edges[i]); break;
targetCid = TRI_EXTRACT_MARKER_FROM_CID(&edges[i]); case TRI_EDGE_IN:
break; target = edge.get(TRI_VOC_ATTRIBUTE_FROM).copyString();
case TRI_EDGE_ANY: break;
targetKey = TRI_EXTRACT_MARKER_TO_KEY(&edges[i]); case TRI_EDGE_ANY:
targetCid = TRI_EXTRACT_MARKER_TO_CID(&edges[i]); target = edge.get(TRI_VOC_ATTRIBUTE_TO).copyString();
if (targetCid == startCid && strcmp(targetKey, key) == 0) { if (target == vertexId) {
targetKey = TRI_EXTRACT_MARKER_FROM_KEY(&edges[i]); target = edge.get(TRI_VOC_ATTRIBUTE_FROM).copyString();
targetCid = TRI_EXTRACT_MARKER_FROM_CID(&edges[i]); }
break;
} }
break;
}
if (targetKey == nullptr || targetCid == 0) { if (target.empty()) {
// somehow invalid // somehow invalid
continue; continue;
}
std::vector<std::string> split = arangodb::basics::StringUtils::split(target, "/");
TRI_ASSERT(split.size() == 2);
VPackBuilder vertexSearch;
vertexSearch.openObject();
vertexSearch.add(TRI_VOC_ATTRIBUTE_KEY, VPackValue(split[1]));
vertexSearch.close();
OperationOptions opts;
OperationResult vertexResult = trx->document(split[0], vertexSearch.slice(), opts);
if (vertexResult.failed()) {
if (vertexResult.code == TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND) {
// This is okay
result.add("vertex", VPackValue(VPackValueType::Null));
} else {
THROW_ARANGO_EXCEPTION(vertexResult.code);
}
} else {
result.add("vertex", vertexResult.slice());
}
}
}
} else {
for (auto const& edge : VPackArrayIterator(edges)) {
if (matcher->matches(edge)) {
result.add(edge);
}
} }
result.add(VPackValue("vertex"));
ReadDocument(trx, resolver, targetCid, targetKey, result);
}
} else {
for (size_t i = 0; i < resultCount; ++i) {
#warning convert to vpack
// ExpandShapedJson(shaper, resolver, cid, &(edges[i]), result, false, unused);
} }
} }
} }
@ -655,6 +605,108 @@ static void UnsetOrKeep(VPackSlice const& value,
} }
} }
static void RegisterCollectionInTransaction(
arangodb::AqlTransaction* trx, std::string const& collectionName,
TRI_voc_cid_t& cid) {
cid = trx->resolver()->getCollectionIdLocal(collectionName);
if (cid == 0) {
THROW_ARANGO_EXCEPTION_FORMAT(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND, "'%s'",
collectionName.c_str());
}
trx->addCollectionAtRuntime(cid);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Helper function to get a document by it's identifier
/// Lazy Locks the collection if necessary.
////////////////////////////////////////////////////////////////////////////////
static void GetDocumentByIdentifier(arangodb::AqlTransaction* trx,
std::string const& collectionName,
std::string const& identifier,
bool ignoreError,
VPackBuilder& result) {
OperationOptions options;
OperationResult opRes;
VPackBuilder searchBuilder;
searchBuilder.openObject();
searchBuilder.add(VPackValue(TRI_VOC_ATTRIBUTE_KEY));
std::vector<std::string> parts =
arangodb::basics::StringUtils::split(identifier, "/");
if (parts.size() == 1) {
searchBuilder.add(VPackValue(identifier));
searchBuilder.close();
try {
TRI_voc_cid_t cid;
RegisterCollectionInTransaction(trx, collectionName, cid);
} catch (arangodb::basics::Exception const& ex) {
if (ignoreError) {
return;
}
throw;
}
opRes = trx->document(collectionName, searchBuilder.slice(), options);
} else if (parts.size() == 2) {
if (collectionName.empty()) {
searchBuilder.add(VPackValue(parts[1]));
searchBuilder.close();
try {
TRI_voc_cid_t cid;
RegisterCollectionInTransaction(trx, parts[0], cid);
} catch (arangodb::basics::Exception const& ex) {
if (ignoreError) {
return;
}
throw;
}
opRes = trx->document(parts[0], searchBuilder.slice(), options);
} else if (parts[0] != collectionName) {
// Reqesting an _id that cannot be stored in this collection
if (ignoreError) {
return;
}
THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_CROSS_COLLECTION_REQUEST);
} else {
searchBuilder.add(VPackValue(parts[1]));
searchBuilder.close();
try {
TRI_voc_cid_t cid;
RegisterCollectionInTransaction(trx, collectionName, cid);
} catch (arangodb::basics::Exception const& ex) {
if (ignoreError) {
return;
}
throw;
}
opRes = trx->document(collectionName, searchBuilder.slice(), options);
}
} else {
if (ignoreError) {
return;
}
THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_HANDLE_BAD);
}
if (opRes.failed()) {
if (ignoreError) {
return;
}
THROW_ARANGO_EXCEPTION(opRes.code);
}
result.add(opRes.slice());
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief Helper function to merge given parameters /// @brief Helper function to merge given parameters
/// Works for an array of objects as first parameter or arbitrary many /// Works for an array of objects as first parameter or arbitrary many
@ -732,63 +784,25 @@ static AqlValue$ MergeParameters(arangodb::aql::Query* query,
return AqlValue$(b); return AqlValue$(b);
} }
////////////////////////////////////////////////////////////////////////////////
/// @brief Transforms VertexId to VelocyPack
////////////////////////////////////////////////////////////////////////////////
static void VertexIdToVPack(arangodb::AqlTransaction* trx,
CollectionNameResolver const* resolver,
VertexId const& id,
VPackBuilder& b) {
trx->addCollectionAtRuntime(id.cid);
OperationOptions options;
VPackSlice slice;
#warning fill slice from id.key
OperationResult opRes = trx->document(trx->collectionName(id.cid), slice, options);
#warning fill mptr
int res = opRes.code;
if (res != TRI_ERROR_NO_ERROR) {
if (res == TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND) {
b.add(VPackValue(VPackValueType::Null));
return;
}
THROW_ARANGO_EXCEPTION(res);
}
#warning convert to vpack
b.add(VPackValue(static_cast<void const*>(nullptr), VPackValueType::External));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Transforms VertexId to std::string
////////////////////////////////////////////////////////////////////////////////
static std::string VertexIdToString(CollectionNameResolver const* resolver,
VertexId const& id) {
return resolver->getCollectionName(id.cid) + "/" + std::string(id.key);
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief Transforms an unordered_map<VertexId> to AQL VelocyPack values /// @brief Transforms an unordered_map<VertexId> to AQL VelocyPack values
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
static AqlValue$ VertexIdsToAqlValueVPack( static AqlValue$ VertexIdsToAqlValueVPack(arangodb::aql::Query* query,
arangodb::aql::Query* query, arangodb::AqlTransaction* trx, arangodb::AqlTransaction* trx,
CollectionNameResolver const* resolver, std::unordered_set<VertexId>& ids, std::unordered_set<std::string>& ids,
bool includeData = false) { bool includeData = false) {
std::shared_ptr<VPackBuilder> result = query->getSharedBuilder(); std::shared_ptr<VPackBuilder> result = query->getSharedBuilder();
{ {
VPackArrayBuilder b(result.get()); VPackArrayBuilder b(result.get());
if (includeData) { if (includeData) {
for (auto& it : ids) { for (auto& it : ids) {
VertexIdToVPack(trx, resolver, it, *result); // THROWS ERRORS if the Document was not found
GetDocumentByIdentifier(trx, "", it, false, *result);
} }
} else { } else {
for (auto& it : ids) { for (auto& it : ids) {
result->add(VPackValue(VertexIdToString(resolver, it))); result->add(VPackValue(it));
} }
} }
} }
@ -832,12 +846,9 @@ static arangodb::Index* getGeoIndex(arangodb::AqlTransaction* trx,
} }
static AqlValue$ buildGeoResult(arangodb::aql::Query* query, static AqlValue$ buildGeoResult(arangodb::aql::Query* query,
GeoCoordinates* cors, VocShaper* shaper, GeoCoordinates* cors,
CollectionNameResolver const* resolver,
TRI_voc_cid_t const& cid, TRI_voc_cid_t const& cid,
std::string const& attributeName) { std::string const& attributeName) {
// TODO FIXME
// note: shaper will always be nullptr here...
if (cors == nullptr) { if (cors == nullptr) {
std::shared_ptr<VPackBuilder> b = query->getSharedBuilder(); std::shared_ptr<VPackBuilder> b = query->getSharedBuilder();
{ {
@ -892,19 +903,22 @@ static AqlValue$ buildGeoResult(arangodb::aql::Query* query,
try { try {
VPackArrayBuilder guard(b.get()); VPackArrayBuilder guard(b.get());
std::unordered_set<std::string> forbidden; std::unordered_set<std::string> forbidden;
bool saveAttr = !attributeName.empty(); if (!attributeName.empty()) {
if (saveAttr) { // We have to copy the entire document
forbidden.emplace(attributeName); for (auto& it : distances) {
} VPackObjectBuilder docGuard(b.get());
for (auto& it : distances) {
#warning convert to vpack
ExpandShapedJson(shaper, resolver, cid, it._mptr, *b,
saveAttr, forbidden);
if (saveAttr) {
// The Object is Open and attributeName is not set
b->add(attributeName, VPackValue(it._distance)); b->add(attributeName, VPackValue(it._distance));
b->close(); VPackSlice doc(it._mptr->vpack());
for (auto const& entry : VPackObjectIterator(doc)) {
std::string key = entry.key.copyString();
if (key != attributeName) {
b->add(key, entry.value);
}
}
}
} else {
for (auto& it : distances) {
InsertMasterPointer(it._mptr, *b);
} }
} }
} catch (...) { } catch (...) {
@ -2332,7 +2346,7 @@ AqlValue$ Functions::Neighbors(arangodb::aql::Query* query,
if (n > 4) { if (n > 4) {
auto edgeExamples = ExtractFunctionParameter(trx, parameters, 4); auto edgeExamples = ExtractFunctionParameter(trx, parameters, 4);
if (!(edgeExamples.isArray() && edgeExamples.length() == 0)) { if (!(edgeExamples.isArray() && edgeExamples.length() == 0)) {
opts.addEdgeFilter(edgeExamples, eci->getShaper(), eCid, resolver); opts.addEdgeFilter(edgeExamples, eCid);
} }
} }
@ -2349,10 +2363,10 @@ AqlValue$ Functions::Neighbors(arangodb::aql::Query* query,
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG); THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
} }
std::unordered_set<VertexId> neighbors; std::unordered_set<std::string> neighbors;
TRI_RunNeighborsSearch(edgeCollectionInfos, opts, neighbors); #warning TRI_RunNeighborsSearch(edgeCollectionInfos, opts, neighbors);
return VertexIdsToAqlValueVPack(query, trx, resolver, neighbors, includeData); return VertexIdsToAqlValueVPack(query, trx, neighbors, includeData);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -2426,7 +2440,7 @@ AqlValue$ Functions::Near(arangodb::aql::Query* query,
trx, latitude.getNumericValue<double>(), trx, latitude.getNumericValue<double>(),
longitude.getNumericValue<double>(), limitValue); longitude.getNumericValue<double>(), limitValue);
return buildGeoResult(query, cors, nullptr, resolver, cid, attributeName); return buildGeoResult(query, cors, cid, attributeName);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -2488,7 +2502,7 @@ AqlValue$ Functions::Within(arangodb::aql::Query* query,
trx, latitude.getNumericValue<double>(), trx, latitude.getNumericValue<double>(),
longitude.getNumericValue<double>(), radius.getNumericValue<double>()); longitude.getNumericValue<double>(), radius.getNumericValue<double>());
return buildGeoResult(query, cors, nullptr, resolver, cid, attributeName); return buildGeoResult(query, cors, cid, attributeName);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -2693,104 +2707,6 @@ AqlValue$ Functions::Minus(arangodb::aql::Query* query,
return AqlValue$(b.get()); return AqlValue$(b.get());
} }
static void RegisterCollectionInTransaction(
arangodb::AqlTransaction* trx, std::string const& collectionName,
TRI_voc_cid_t& cid) {
cid = trx->resolver()->getCollectionIdLocal(collectionName);
if (cid == 0) {
THROW_ARANGO_EXCEPTION_FORMAT(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND, "'%s'",
collectionName.c_str());
}
trx->addCollectionAtRuntime(cid);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Helper function to get a document by it's identifier
/// The collection has to be locked by the transaction before
////////////////////////////////////////////////////////////////////////////////
static void GetDocumentByIdentifier(arangodb::AqlTransaction* trx,
std::string const& collectionName,
std::string const& identifier,
VPackBuilder& result) {
OperationOptions options;
std::vector<std::string> parts =
arangodb::basics::StringUtils::split(identifier, "/");
if (parts.size() == 1) {
VPackSlice slice;
#warning fill slice from parts[0]
OperationResult opRes = trx->document(collectionName, slice, options);
#warning fill mptr
if (!opRes.successful()) {
return;
}
} else if (parts.size() == 2) {
if (parts[0] != collectionName) {
// Reqesting an _id that cannot be stored in this collection
return;
}
VPackSlice slice;
#warning fill slice from parts[1]
OperationResult opRes = trx->document(collectionName, slice, options);
#warning fill mptr
if (!opRes.successful()) {
return;
}
} else {
return;
}
#warning convert to vpack
result.add(VPackValue(static_cast<void const*>(nullptr), VPackValueType::External));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Helper function to get a document by its _id
/// This function will lazy read-lock the collection.
/// this function will not throw if the document or the collection cannot be
/// found
////////////////////////////////////////////////////////////////////////////////
static void GetDocumentByIdentifier(arangodb::AqlTransaction* trx,
std::string const& identifier,
VPackBuilder& result) {
std::vector<std::string> parts =
arangodb::basics::StringUtils::split(identifier, "/");
if (parts.size() != 2) {
return;
}
std::string collectionName = parts[0];
TRI_voc_cid_t cid = 0;
try {
RegisterCollectionInTransaction(trx, collectionName, cid);
} catch (arangodb::basics::Exception const& ex) {
// don't throw if collection is not found
if (ex.code() == TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND) {
return;
}
throw;
}
OperationOptions options;
VPackSlice slice;
#warning fill slice from parts[1]
OperationResult opRes = trx->document(collectionName, slice, options);
#warning fill mptr
if (opRes.code != TRI_ERROR_NO_ERROR) {
return;
}
#warning convert opRes result to vpack external
result.add(VPackValue(static_cast<void const*>(nullptr), VPackValueType::External));
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief function Document /// @brief function Document
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -2811,7 +2727,7 @@ AqlValue$ Functions::Document(arangodb::aql::Query* query,
std::shared_ptr<VPackBuilder> b = query->getSharedBuilder(); std::shared_ptr<VPackBuilder> b = query->getSharedBuilder();
if (id.isString()) { if (id.isString()) {
std::string identifier = id.copyString(); std::string identifier = id.copyString();
GetDocumentByIdentifier(trx, identifier, *b); GetDocumentByIdentifier(trx, "", identifier, true, *b);
if (b->isEmpty()) { if (b->isEmpty()) {
// not found // not found
b->add(VPackValue(VPackValueType::Null)); b->add(VPackValue(VPackValueType::Null));
@ -2819,13 +2735,9 @@ AqlValue$ Functions::Document(arangodb::aql::Query* query,
} else if (id.isArray()) { } else if (id.isArray()) {
VPackArrayBuilder guard(b.get()); VPackArrayBuilder guard(b.get());
for (auto const& next : VPackArrayIterator(id)) { for (auto const& next : VPackArrayIterator(id)) {
try { if (next.isString()) {
if (next.isString()) { std::string identifier = next.copyString();
std::string identifier = next.copyString(); GetDocumentByIdentifier(trx, "", identifier, true, *b);
GetDocumentByIdentifier(trx, identifier, *b);
}
} catch (arangodb::basics::Exception const&) {
// Ignore all ArangoDB exceptions here
} }
} }
} else { } else {
@ -2840,19 +2752,8 @@ AqlValue$ Functions::Document(arangodb::aql::Query* query,
} }
std::string collectionName = collectionSlice.copyString(); std::string collectionName = collectionSlice.copyString();
TRI_voc_cid_t cid;
bool notFound = false; bool notFound = false;
try {
RegisterCollectionInTransaction(trx, collectionName, cid);
} catch (arangodb::basics::Exception const& ex) {
// don't throw if collection is not found
if (ex.code() != TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND) {
throw;
}
notFound = true;
}
VPackSlice id = ExtractFunctionParameter(trx, parameters, 1); VPackSlice id = ExtractFunctionParameter(trx, parameters, 1);
if (id.isString()) { if (id.isString()) {
if (notFound) { if (notFound) {
@ -2862,7 +2763,7 @@ AqlValue$ Functions::Document(arangodb::aql::Query* query,
} }
std::shared_ptr<VPackBuilder> b = query->getSharedBuilder(); std::shared_ptr<VPackBuilder> b = query->getSharedBuilder();
std::string identifier = id.copyString(); std::string identifier = id.copyString();
GetDocumentByIdentifier(trx, collectionName, identifier, *b); GetDocumentByIdentifier(trx, collectionName, identifier, true, *b);
if (b->isEmpty()) { if (b->isEmpty()) {
b->add(VPackValue(VPackValueType::Null)); b->add(VPackValue(VPackValueType::Null));
} }
@ -2873,13 +2774,9 @@ AqlValue$ Functions::Document(arangodb::aql::Query* query,
VPackArrayBuilder guard(b.get()); VPackArrayBuilder guard(b.get());
if (!notFound) { if (!notFound) {
for (auto const& next : VPackArrayIterator(id)) { for (auto const& next : VPackArrayIterator(id)) {
try { if (next.isString()) {
if (next.isString()) { std::string identifier = next.copyString();
std::string identifier = next.copyString(); GetDocumentByIdentifier(trx, collectionName, identifier, true, *b);
GetDocumentByIdentifier(trx, collectionName, identifier, *b);
}
} catch (arangodb::basics::Exception const&) {
// Ignore all ArangoDB exceptions here
} }
} }
} }
@ -2916,7 +2813,6 @@ AqlValue$ Functions::Edges(arangodb::aql::Query* query,
TRI_voc_cid_t cid; TRI_voc_cid_t cid;
RegisterCollectionInTransaction(trx, collectionName, cid); RegisterCollectionInTransaction(trx, collectionName, cid);
TRI_document_collection_t* documentCollection = trx->documentCollection(cid);
if (!trx->isEdgeCollection(collectionName)) { if (!trx->isEdgeCollection(collectionName)) {
RegisterWarning(query, "EDGES", TRI_ERROR_ARANGO_COLLECTION_TYPE_INVALID); RegisterWarning(query, "EDGES", TRI_ERROR_ARANGO_COLLECTION_TYPE_INVALID);
@ -2964,18 +2860,20 @@ AqlValue$ Functions::Edges(arangodb::aql::Query* query,
return AqlValue$(b.get()); return AqlValue$(b.get());
} }
auto resolver = trx->resolver();
auto shaper = documentCollection->getShaper();
std::unique_ptr<arangodb::ExampleMatcher> matcher; std::unique_ptr<arangodb::ExampleMatcher> matcher;
TRI_document_collection_t* documentCollection = trx->documentCollection(cid);
arangodb::EdgeIndex* edgeIndex = documentCollection->edgeIndex();
TRI_ASSERT(edgeIndex != nullptr); // Checked because collection is edge Collection.
std::string indexId = arangodb::basics::StringUtils::itoa(edgeIndex->id());
if (n > 3) { if (n > 3) {
// We might have examples // We might have examples
VPackSlice exampleSlice = ExtractFunctionParameter(trx, parameters, 3); VPackSlice exampleSlice = ExtractFunctionParameter(trx, parameters, 3);
if ((exampleSlice.isArray() && exampleSlice.length() != 0)|| exampleSlice.isObject()) { if ((exampleSlice.isArray() && exampleSlice.length() != 0)|| exampleSlice.isObject()) {
try { try {
matcher.reset( matcher.reset(
new arangodb::ExampleMatcher(exampleSlice, resolver, false)); new arangodb::ExampleMatcher(exampleSlice, false));
} catch (arangodb::basics::Exception const& e) { } catch (arangodb::basics::Exception const& e) {
if (e.code() != TRI_RESULT_ELEMENT_NOT_FOUND) { if (e.code() != TRI_RESULT_ELEMENT_NOT_FOUND) {
throw; throw;
@ -3008,16 +2906,14 @@ AqlValue$ Functions::Edges(arangodb::aql::Query* query,
if (vertexSlice.isArray()) { if (vertexSlice.isArray()) {
for (auto const& v : VPackArrayIterator(vertexSlice)) { for (auto const& v : VPackArrayIterator(vertexSlice)) {
try { try {
RequestEdges(v, trx, resolver, shaper, cid, RequestEdges(v, trx, collectionName, indexId, direction,
documentCollection, direction,
matcher.get(), includeVertices, *b); matcher.get(), includeVertices, *b);
} catch (...) { } catch (...) {
// Errors in Array are simply ignored // Errors in Array are simply ignored
} }
} }
} else { } else {
RequestEdges(vertexSlice, trx, resolver, shaper, cid, RequestEdges(vertexSlice, trx, collectionName, indexId, direction,
documentCollection, direction,
matcher.get(), includeVertices, *b); matcher.get(), includeVertices, *b);
} }
} }
@ -4309,7 +4205,6 @@ AqlValue$ Functions::Fulltext(arangodb::aql::Query* query,
THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL); THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL);
} }
auto shaper = document->getShaper();
size_t const numResults = queryResult->_numDocuments; size_t const numResults = queryResult->_numDocuments;
std::shared_ptr<VPackBuilder> b = query->getSharedBuilder(); std::shared_ptr<VPackBuilder> b = query->getSharedBuilder();
@ -4317,11 +4212,8 @@ AqlValue$ Functions::Fulltext(arangodb::aql::Query* query,
VPackArrayBuilder guard(b.get()); VPackArrayBuilder guard(b.get());
for (size_t i = 0; i < numResults; ++i) { for (size_t i = 0; i < numResults; ++i) {
std::unordered_set<std::string> unused; InsertMasterPointer((TRI_doc_mptr_t const*)queryResult->_documents[i],
#warning convert to vpack *b);
ExpandShapedJson(shaper, resolver, cid,
(TRI_doc_mptr_t const*)queryResult->_documents[i], *b,
false, unused);
} }
} catch (...) { } catch (...) {
TRI_FreeResultFulltextIndex(queryResult); TRI_FreeResultFulltextIndex(queryResult);

View File

@ -668,8 +668,7 @@ class PropagateConstantAttributesHelper {
return; return;
} }
if (TRI_CompareValuesJson(value->computeJson(), previous->computeJson(), if (!value->computeValue().equals(previous->computeValue())) {
true) != 0) {
// different value found for an already tracked attribute. better not // different value found for an already tracked attribute. better not
// use this attribute // use this attribute
(*it2).second = nullptr; (*it2).second = nullptr;

View File

@ -241,6 +241,31 @@ void EdgeIndexIterator::reset() {
_buffer = nullptr; _buffer = nullptr;
} }
TRI_doc_mptr_t* AnyDirectionEdgeIndexIterator::next() {
TRI_doc_mptr_t* res = nullptr;
if (_useInbound) {
do {
res = _inbound->next();
} while (res != nullptr && _seen.find(res) != _seen.end());
return res;
}
res = _outbound->next();
if (res == nullptr) {
_useInbound = true;
return next();
}
_seen.emplace(res);
return res;
}
void AnyDirectionEdgeIndexIterator::reset() {
_useInbound = false;
_seen.clear();
_outbound->reset();
_inbound->reset();
}
EdgeIndex::EdgeIndex(TRI_idx_iid_t iid, TRI_document_collection_t* collection) EdgeIndex::EdgeIndex(TRI_idx_iid_t iid, TRI_document_collection_t* collection)
: Index(iid, collection, : Index(iid, collection,
std::vector<std::vector<arangodb::basics::AttributeName>>( std::vector<std::vector<arangodb::basics::AttributeName>>(
@ -529,6 +554,52 @@ arangodb::aql::AstNode* EdgeIndex::specializeCondition(
return matcher.specializeOne(this, node, reference); return matcher.specializeOne(this, node, reference);
} }
////////////////////////////////////////////////////////////////////////////////
/// @brief creates an IndexIterator for the given VelocyPackSlices.
/// The searchValue is a an Array with exactly two Entries.
/// If the first is set it means we are searching for _from (OUTBOUND),
/// if the second is set we are searching for _to (INBOUND).
/// if both are set we are search for ANY direction. Result is made
/// DISTINCT.
/// Each defined slice that is set has to be list of keys to search for.
/// Each key needs to have the following formats:
///
/// 1) {"eq": <compareValue>} // The value in index is exactly this
///
/// Reverse is not supported, hence ignored
/// NOTE: The iterator is only valid as long as the slice points to
/// a valid memory region.
////////////////////////////////////////////////////////////////////////////////
IndexIterator* EdgeIndex::iteratorForSlice(
arangodb::Transaction* trx, IndexIteratorContext*,
arangodb::velocypack::Slice const searchValues, bool) const {
if (!searchValues.isArray() || searchValues.length() != 2) {
// Invalid searchValue
return nullptr;
}
VPackSlice const from = searchValues.at(0);
VPackSlice const to = searchValues.at(1);
if (!from.isNone()) {
TRI_ASSERT(from.isArray());
if (!to.isNone()) {
// ANY search
TRI_ASSERT(to.isArray());
auto left = std::make_unique<EdgeIndexIterator>(trx, _edgesFrom, from);
auto right = std::make_unique<EdgeIndexIterator>(trx, _edgesTo, to);
return new AnyDirectionEdgeIndexIterator(left.release(), right.release());
}
// OUTBOUND search
TRI_ASSERT(to.isNone());
return new EdgeIndexIterator(trx, _edgesFrom, from);
} else {
// INBOUND search
TRI_ASSERT(to.isArray());
return new EdgeIndexIterator(trx, _edgesTo, to);
}
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief create the iterator /// @brief create the iterator
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -58,6 +58,20 @@ class EdgeIndexIterator final : public IndexIterator {
_batchSize(50) { // This might be adjusted _batchSize(50) { // This might be adjusted
} }
EdgeIndexIterator(arangodb::Transaction* trx,
TRI_EdgeIndexHash_t const* index,
arangodb::velocypack::Slice searchValues)
: _trx(trx),
_index(index),
_searchValues(nullptr),
_keys(searchValues),
_position(0),
_last(nullptr),
_buffer(nullptr),
_posInBuffer(0),
_batchSize(50) { // This might be adjusted
}
~EdgeIndexIterator() { ~EdgeIndexIterator() {
// Free the vector space, not the content // Free the vector space, not the content
delete _buffer; delete _buffer;
@ -75,6 +89,32 @@ class EdgeIndexIterator final : public IndexIterator {
size_t _batchSize; size_t _batchSize;
}; };
class AnyDirectionEdgeIndexIterator final : public IndexIterator {
public:
TRI_doc_mptr_t* next() override;
void reset() override;
AnyDirectionEdgeIndexIterator(EdgeIndexIterator* outboundIterator,
EdgeIndexIterator* inboundIterator)
: _outbound(outboundIterator),
_inbound(inboundIterator),
_useInbound(false),
_done(false) {}
~AnyDirectionEdgeIndexIterator() {
delete _outbound;
delete _inbound;
}
private:
EdgeIndexIterator* _outbound;
EdgeIndexIterator* _inbound;
std::unordered_set<TRI_doc_mptr_t*> _seen;
bool _useInbound;
bool _done;
};
class EdgeIndex final : public Index { class EdgeIndex final : public Index {
public: public:
EdgeIndex() = delete; EdgeIndex() = delete;
@ -152,6 +192,26 @@ class EdgeIndex final : public Index {
arangodb::aql::AstNode* specializeCondition( arangodb::aql::AstNode* specializeCondition(
arangodb::aql::AstNode*, arangodb::aql::Variable const*) const override; arangodb::aql::AstNode*, arangodb::aql::Variable const*) const override;
////////////////////////////////////////////////////////////////////////////////
/// @brief creates an IndexIterator for the given VelocyPackSlices.
/// The searchValue is a an Array with exactly two Entries, one of them
/// has to be NONE.
/// If the first is set it means we are searching for _from (OUTBOUND),
/// if the second is set we are searching for _to (INBOUND).
/// The slice that is set has to be list of keys to search for.
/// Each key needs to have the following formats:
///
/// 1) {"eq": <compareValue>} // The value in index is exactly this
///
/// Reverse is not supported, hence ignored
/// NOTE: The iterator is only valid as long as the slice points to
/// a valid memory region.
////////////////////////////////////////////////////////////////////////////////
IndexIterator* iteratorForSlice(arangodb::Transaction*, IndexIteratorContext*,
arangodb::velocypack::Slice const,
bool) const override;
private: private:
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
/// @brief create the iterator /// @brief create the iterator

View File

@ -594,7 +594,7 @@ OperationResult Transaction::documentLocal(std::string const& collectionName,
} }
if (!options.silent) { if (!options.silent) {
resultBuilder.add(VPackSlice(mptr.vpack())); resultBuilder.add(VPackValue(static_cast<void const*>(mptr.vpack()), VPackValueType::External));
} }
return TRI_ERROR_NO_ERROR; return TRI_ERROR_NO_ERROR;
@ -1584,7 +1584,7 @@ OperationCursor Transaction::indexScan(
std::string const& indexId, VPackSlice const search, std::string const& indexId, VPackSlice const search,
uint64_t skip, uint64_t limit, uint64_t batchSize, bool reverse) { uint64_t skip, uint64_t limit, uint64_t batchSize, bool reverse) {
// TODO Who checks if indexId is valid and is used for this collection? #warning TODO Who checks if indexId is valid and is used for this collection?
// For now we assume indexId is the iid part of the index. // For now we assume indexId is the iid part of the index.
if (ServerState::instance()->isCoordinator()) { if (ServerState::instance()->isCoordinator()) {

View File

@ -248,13 +248,12 @@ bool BasicOptions::matchesVertex(VertexId const& v) const {
// OperationResult opRes = trx->document(it->second.col, slice, options); // OperationResult opRes = trx->document(it->second.col, slice, options);
OperationResult opRes(TRI_ERROR_INTERNAL); OperationResult opRes(TRI_ERROR_INTERNAL);
#warning fill vertex #warning fill vertex
TRI_doc_mptr_t vertex;
if (!opRes.successful()) { if (!opRes.successful()) {
return false; return false;
} }
return it->second.matcher->matches(v.cid, &vertex); return it->second.matcher->matches(opRes.slice());
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -302,13 +301,12 @@ void BasicOptions::addEdgeFilter(Json const& example, VocShaper* shaper,
/// @brief Insert a new edge matcher object /// @brief Insert a new edge matcher object
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void BasicOptions::addEdgeFilter(VPackSlice const& example, VocShaper* shaper, void BasicOptions::addEdgeFilter(VPackSlice const& example,
TRI_voc_cid_t const& cid, TRI_voc_cid_t const& cid) {
CollectionNameResolver const* resolver) {
useEdgeFilter = true; useEdgeFilter = true;
auto it = _edgeFilter.find(cid); auto it = _edgeFilter.find(cid);
if (it == _edgeFilter.end()) { if (it == _edgeFilter.end()) {
_edgeFilter.emplace(cid, new ExampleMatcher(example, resolver, true)); _edgeFilter.emplace(cid, new ExampleMatcher(example, true));
} }
} }
@ -332,7 +330,7 @@ bool BasicOptions::matchesEdge(EdgeId& e, TRI_doc_mptr_t* edge) const {
return false; return false;
} }
return it->second->matches(e.cid, edge); return it->second->matches(VPackSlice(edge->vpack()));
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -121,9 +121,8 @@ struct BasicOptions {
TRI_voc_cid_t const& cid, TRI_voc_cid_t const& cid,
arangodb::CollectionNameResolver const* resolver); arangodb::CollectionNameResolver const* resolver);
void addEdgeFilter(arangodb::velocypack::Slice const& example, VocShaper* shaper, void addEdgeFilter(arangodb::velocypack::Slice const& example,
TRI_voc_cid_t const& cid, TRI_voc_cid_t const& cid);
arangodb::CollectionNameResolver const* resolver);
void addVertexFilter(v8::Isolate* isolate, void addVertexFilter(v8::Isolate* isolate,
v8::Handle<v8::Value> const& example, v8::Handle<v8::Value> const& example,

View File

@ -75,7 +75,7 @@ void ExampleMatcher::fillExampleDefinition(
} }
void ExampleMatcher::fillExampleDefinition( void ExampleMatcher::fillExampleDefinition(
VPackSlice const& example, CollectionNameResolver const* resolver, VPackSlice const& example,
ExampleDefinition& def) { ExampleDefinition& def) {
TRI_ASSERT(def._values.isEmpty()); TRI_ASSERT(def._values.isEmpty());
VPackArrayBuilder guard(&def._values); VPackArrayBuilder guard(&def._values);
@ -166,11 +166,10 @@ ExampleMatcher::ExampleMatcher(TRI_json_t const* example,
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
ExampleMatcher::ExampleMatcher(VPackSlice const& example, ExampleMatcher::ExampleMatcher(VPackSlice const& example,
CollectionNameResolver const* resolver,
bool allowStrings) { bool allowStrings) {
if (example.isObject() || example.isString()) { if (example.isObject() || example.isString()) {
ExampleDefinition def; ExampleDefinition def;
ExampleMatcher::fillExampleDefinition(example, resolver, def); ExampleMatcher::fillExampleDefinition(example, def);
definitions.emplace_back(std::move(def)); definitions.emplace_back(std::move(def));
} else if (example.isArray()) { } else if (example.isArray()) {
for (auto const& e : VPackArrayIterator(example)) { for (auto const& e : VPackArrayIterator(example)) {
@ -179,7 +178,7 @@ ExampleMatcher::ExampleMatcher(VPackSlice const& example,
// We do not match strings in Array // We do not match strings in Array
continue; continue;
} }
ExampleMatcher::fillExampleDefinition(e, resolver, def); ExampleMatcher::fillExampleDefinition(e, def);
definitions.emplace_back(std::move(def)); definitions.emplace_back(std::move(def));
} }
if (definitions.empty()) { if (definitions.empty()) {
@ -190,16 +189,11 @@ ExampleMatcher::ExampleMatcher(VPackSlice const& example,
} }
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief Checks if the given mptr matches the examples in this class /// @brief Checks if the given velocyPack matches the examples in this class
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
bool ExampleMatcher::matches(TRI_voc_cid_t, TRI_doc_mptr_t const* mptr) const { bool ExampleMatcher::matches(VPackSlice const toMatch) const {
if (mptr == nullptr) {
return false;
}
VPackSlice toMatch(mptr->vpack());
for (auto const& def : definitions) { for (auto const& def : definitions) {
VPackSlice const compareValue = def.slice(); VPackSlice const compareValue = def.slice();
size_t i = 0; size_t i = 0;

View File

@ -29,8 +29,6 @@
#include <v8.h> #include <v8.h>
struct TRI_doc_mptr_t;
namespace arangodb { namespace arangodb {
namespace velocypack { namespace velocypack {
@ -50,7 +48,6 @@ class ExampleMatcher {
std::vector<ExampleDefinition> definitions; std::vector<ExampleDefinition> definitions;
void fillExampleDefinition(arangodb::velocypack::Slice const& example, void fillExampleDefinition(arangodb::velocypack::Slice const& example,
arangodb::CollectionNameResolver const* resolver,
ExampleDefinition& def); ExampleDefinition& def);
void fillExampleDefinition(v8::Isolate* isolate, void fillExampleDefinition(v8::Isolate* isolate,
@ -69,12 +66,11 @@ class ExampleMatcher {
arangodb::CollectionNameResolver const* resolver); arangodb::CollectionNameResolver const* resolver);
ExampleMatcher(arangodb::velocypack::Slice const& example, ExampleMatcher(arangodb::velocypack::Slice const& example,
arangodb::CollectionNameResolver const* resolver,
bool allowStrings); bool allowStrings);
~ExampleMatcher() { } ~ExampleMatcher() { }
bool matches(TRI_voc_cid_t, TRI_doc_mptr_t const* mptr) const; bool matches(arangodb::velocypack::Slice const) const;
}; };
} }