mirror of https://gitee.com/bigwinds/arangodb
Added the implementation and more tests for EDGES in CXX only
This commit is contained in:
parent
69d9cce680
commit
cc35fdb7b0
|
@ -192,7 +192,7 @@ std::unordered_map<std::string, Function const> const Executor::FunctionNames{
|
|||
{ "VALUES", Function("VALUES", "AQL_VALUES", "a|b", true, true, false, true, true, &Functions::Values) },
|
||||
{ "MERGE", Function("MERGE", "AQL_MERGE", "la|+", true, true, false, true, true, &Functions::Merge) },
|
||||
{ "MERGE_RECURSIVE", Function("MERGE_RECURSIVE", "AQL_MERGE_RECURSIVE", "a,a|+", true, true, false, true, true) },
|
||||
{ "DOCUMENT", Function("DOCUMENT", "AQL_DOCUMENT", "h.|.", false, false, true, false, true, &Functions::Document) },
|
||||
{ "DOCUMENT", Function("DOCUMENT", "AQL_DOCUMENT", "h.|.", false, false, true, false, true, &Functions::Document, NotInCluster) },
|
||||
{ "MATCHES", Function("MATCHES", "AQL_MATCHES", ".,l|b", true, true, false, true, true) },
|
||||
{ "UNSET", Function("UNSET", "AQL_UNSET", "a,sl|+", true, true, false, true, true, &Functions::Unset) },
|
||||
{ "KEEP", Function("KEEP", "AQL_KEEP", "a,sl|+", true, true, false, true, true, &Functions::Keep) },
|
||||
|
@ -218,7 +218,7 @@ std::unordered_map<std::string, Function const> const Executor::FunctionNames{
|
|||
{ "GRAPH_TRAVERSAL", Function("GRAPH_TRAVERSAL", "AQL_GRAPH_TRAVERSAL", "s,als,s|a", false, false, true, false, false) },
|
||||
{ "TRAVERSAL_TREE", Function("TRAVERSAL_TREE", "AQL_TRAVERSAL_TREE", "h,h,s,s,s|a", false, false, true, false, false) },
|
||||
{ "GRAPH_TRAVERSAL_TREE", Function("GRAPH_TRAVERSAL_TREE", "AQL_GRAPH_TRAVERSAL_TREE", "s,als,s,s|a", false, false, true, false, false) },
|
||||
{ "EDGES", Function("EDGES", "AQL_EDGES", "h,s,s|l,o", true, false, true, false, false) },
|
||||
{ "EDGES", Function("EDGES", "AQL_EDGES", "h,s,s|l,o", true, false, true, false, false, &Functions::Edges, NotInCluster) },
|
||||
{ "GRAPH_EDGES", Function("GRAPH_EDGES", "AQL_GRAPH_EDGES", "s,als|a", false, false, true, false, false) },
|
||||
{ "GRAPH_VERTICES", Function("GRAPH_VERTICES", "AQL_GRAPH_VERTICES", "s,als|a", false, false, true, false, false) },
|
||||
{ "NEIGHBORS", Function("NEIGHBORS", "AQL_NEIGHBORS", "h,h,s,s|l,a", true, false, true, false, false, &Functions::Neighbors, NotInCluster) },
|
||||
|
|
|
@ -1799,6 +1799,51 @@ static inline Json ExpandShapedJson (VocShaper* shaper,
|
|||
return json;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief Reads a document by cid and key
|
||||
/// Also lazy locks the collection.
|
||||
/// Returns null if the document does not exist
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static Json readDocument (triagens::arango::AqlTransaction* trx,
|
||||
CollectionNameResolver const* resolver,
|
||||
TRI_voc_cid_t cid,
|
||||
char const* key) {
|
||||
auto collection = trx->trxCollection(cid);
|
||||
|
||||
if (collection == nullptr) {
|
||||
int res = TRI_AddCollectionTransaction(trx->getInternals(),
|
||||
cid,
|
||||
TRI_TRANSACTION_READ,
|
||||
trx->nestingLevel(),
|
||||
true,
|
||||
true);
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
THROW_ARANGO_EXCEPTION(res);
|
||||
}
|
||||
TRI_EnsureCollectionsTransaction(trx->getInternals());
|
||||
collection = trx->trxCollection(cid);
|
||||
|
||||
if (collection == nullptr) {
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "collection is a nullptr");
|
||||
}
|
||||
}
|
||||
|
||||
TRI_doc_mptr_copy_t mptr;
|
||||
int res = trx->readSingle(collection, &mptr, key);
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
return Json(Json::Null);
|
||||
}
|
||||
|
||||
return ExpandShapedJson(
|
||||
collection->_collection->_collection->getShaper(),
|
||||
resolver,
|
||||
cid,
|
||||
&mptr
|
||||
);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief Transforms VertexId to Json
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -2780,6 +2825,208 @@ AqlValue Functions::Document (triagens::aql::Query* query,
|
|||
// Id has invalid format
|
||||
return AqlValue(new Json(Json::Null));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief function Edges
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AqlValue Functions::Edges (triagens::aql::Query* query,
|
||||
triagens::arango::AqlTransaction* trx,
|
||||
FunctionParameters const& parameters) {
|
||||
size_t const n = parameters.size();
|
||||
|
||||
if (n < 3 || 5 < n) {
|
||||
THROW_ARANGO_EXCEPTION_PARAMS(TRI_ERROR_QUERY_FUNCTION_ARGUMENT_NUMBER_MISMATCH, "EDGES", (int) 3, (int) 5);
|
||||
}
|
||||
|
||||
Json collectionJson = ExtractFunctionParameter(trx, parameters, 0, false);
|
||||
if (! collectionJson.isString()) {
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL);
|
||||
}
|
||||
std::string collectionName = triagens::basics::JsonHelper::getStringValue(collectionJson.json(), "");
|
||||
|
||||
TRI_transaction_collection_t* collection = nullptr;
|
||||
TRI_voc_cid_t cid;
|
||||
RegisterCollectionInTransaction(trx, collectionName, cid, collection);
|
||||
if (collection->_collection->_type != TRI_COL_TYPE_EDGE) {
|
||||
RegisterWarning(query, "EDGES", TRI_ERROR_ARANGO_COLLECTION_TYPE_INVALID);
|
||||
return AqlValue(new Json(Json::Null));
|
||||
}
|
||||
|
||||
Json vertexJson = ExtractFunctionParameter(trx, parameters, 1, false);
|
||||
if (! vertexJson.isString()) {
|
||||
// Invalid Start vertex
|
||||
return AqlValue(new Json(Json::Null));
|
||||
}
|
||||
|
||||
std::string vertexId = basics::JsonHelper::getStringValue(vertexJson.json(), "");
|
||||
std::vector<std::string> parts = triagens::basics::StringUtils::split(vertexId, "/");
|
||||
if (parts.size() != 2) {
|
||||
// Invalid Start vertex
|
||||
return AqlValue(new Json(Json::Null));
|
||||
}
|
||||
|
||||
|
||||
Json directionJson = ExtractFunctionParameter(trx, parameters, 2, false);
|
||||
if (! directionJson.isString()) {
|
||||
RegisterWarning(query, "EDGES", TRI_ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH);
|
||||
return AqlValue(new Json(Json::Null));
|
||||
}
|
||||
std::string dirString = basics::JsonHelper::getStringValue(directionJson.json(), "");
|
||||
// transform String to lower case
|
||||
std::transform(dirString.begin(), dirString.end(), dirString.begin(), ::tolower);
|
||||
|
||||
TRI_edge_direction_e direction;
|
||||
|
||||
if (dirString == "inbound") {
|
||||
direction = TRI_EDGE_IN;
|
||||
}
|
||||
else if (dirString == "outbound") {
|
||||
direction = TRI_EDGE_OUT;
|
||||
}
|
||||
else if (dirString == "any") {
|
||||
direction = TRI_EDGE_ANY;
|
||||
}
|
||||
else {
|
||||
RegisterWarning(query, "EDGES", TRI_ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH);
|
||||
return AqlValue(new Json(Json::Null));
|
||||
}
|
||||
auto resolver = trx->resolver();
|
||||
|
||||
TRI_voc_cid_t startCid = resolver->getCollectionId(parts[0]);
|
||||
if (startCid == 0) {
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
char* key = const_cast<char*>(parts[1].c_str());
|
||||
std::vector<TRI_doc_mptr_copy_t> edges = TRI_LookupEdgesDocumentCollection(
|
||||
collection->_collection->_collection,
|
||||
direction,
|
||||
startCid,
|
||||
key
|
||||
);
|
||||
|
||||
size_t resultCount = edges.size();
|
||||
|
||||
auto shaper = collection->_collection->_collection->getShaper();
|
||||
if (n > 3) {
|
||||
// We might have examples
|
||||
Json exampleJson = ExtractFunctionParameter(trx, parameters, 3, false);
|
||||
if (! exampleJson.isNull()) {
|
||||
bool buildMatcher = false;
|
||||
if (exampleJson.isArray()) {
|
||||
size_t exampleCount = exampleJson.size();
|
||||
// We only support objects here so validate
|
||||
for (size_t k = 0; k < exampleCount; ++k) {
|
||||
if (! exampleJson.at(k).isObject()) {
|
||||
RegisterWarning(query, "EDGES", TRI_ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH);
|
||||
if (TRI_DeleteArrayJson(TRI_UNKNOWN_MEM_ZONE, exampleJson.json(), k)) {
|
||||
--k;
|
||||
--exampleCount;
|
||||
}
|
||||
else {
|
||||
// Should never occur.
|
||||
// If it occurs in production it will simply fall through
|
||||
// it can only retern more results than expected and not do any harm.
|
||||
TRI_ASSERT(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (exampleCount > 0) {
|
||||
buildMatcher = true;
|
||||
}
|
||||
// else we do not filter at all
|
||||
}
|
||||
else if (exampleJson.isObject()) {
|
||||
buildMatcher = true;
|
||||
}
|
||||
else {
|
||||
RegisterWarning(query, "EDGES", TRI_ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH);
|
||||
}
|
||||
if (buildMatcher) {
|
||||
try {
|
||||
triagens::arango::ExampleMatcher matcher(exampleJson.json(), shaper, resolver);
|
||||
for (size_t i = 0; i < resultCount; ++i) {
|
||||
if (! matcher.matches(cid, &edges[i])) {
|
||||
edges.erase(edges.begin() + i);
|
||||
--i;
|
||||
--resultCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (triagens::basics::Exception& e) {
|
||||
if (e.code() != TRI_RESULT_ELEMENT_NOT_FOUND) {
|
||||
throw e;
|
||||
}
|
||||
// Illegal match, we cannot filter anything
|
||||
edges.clear();
|
||||
resultCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool includeVertices = false;
|
||||
if (n == 5) {
|
||||
// We have options
|
||||
Json options = ExtractFunctionParameter(trx, parameters, 4, false);
|
||||
if (options.isObject() && options.has("includeVertices")) {
|
||||
includeVertices = triagens::basics::JsonHelper::getBooleanValue(options.json(), "includeVertices", false);
|
||||
}
|
||||
}
|
||||
|
||||
if (includeVertices) {
|
||||
Json result(Json::Array, resultCount);
|
||||
|
||||
for (size_t i = 0; i < resultCount; ++i) {
|
||||
Json resultPair(Json::Object, 2);
|
||||
resultPair.set("edge", ExpandShapedJson(
|
||||
shaper,
|
||||
resolver,
|
||||
cid,
|
||||
&(edges[i])
|
||||
));
|
||||
char const* targetKey;
|
||||
TRI_voc_cid_t targetCid;
|
||||
|
||||
switch (direction) {
|
||||
case TRI_EDGE_OUT:
|
||||
targetKey = TRI_EXTRACT_MARKER_TO_KEY(&edges[i]);
|
||||
targetCid = TRI_EXTRACT_MARKER_TO_CID(&edges[i]);
|
||||
break;
|
||||
case TRI_EDGE_IN:
|
||||
targetKey = TRI_EXTRACT_MARKER_FROM_KEY(&edges[i]);
|
||||
targetCid = TRI_EXTRACT_MARKER_FROM_CID(&edges[i]);
|
||||
break;
|
||||
case TRI_EDGE_ANY:
|
||||
targetKey = TRI_EXTRACT_MARKER_TO_KEY(&edges[i]);
|
||||
targetCid = TRI_EXTRACT_MARKER_TO_CID(&edges[i]);
|
||||
if (targetCid == startCid && strcmp(targetKey, key) == 0) {
|
||||
targetKey = TRI_EXTRACT_MARKER_FROM_KEY(&edges[i]);
|
||||
targetCid = TRI_EXTRACT_MARKER_FROM_CID(&edges[i]);
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
resultPair.set("vertex", readDocument(trx, resolver, targetCid, targetKey));
|
||||
result.add(resultPair);
|
||||
}
|
||||
return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, result.steal()));
|
||||
}
|
||||
Json result(Json::Array, resultCount);
|
||||
|
||||
for (size_t i = 0; i < resultCount; ++i) {
|
||||
result.add(ExpandShapedJson(
|
||||
shaper,
|
||||
resolver,
|
||||
cid,
|
||||
&(edges[i])
|
||||
));
|
||||
}
|
||||
|
||||
return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, result.steal()));
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- END-OF-FILE
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -118,6 +118,7 @@ namespace triagens {
|
|||
static AqlValue ParseIdentifier (triagens::aql::Query*, triagens::arango::AqlTransaction*, FunctionParameters const&);
|
||||
static AqlValue Minus (triagens::aql::Query*, triagens::arango::AqlTransaction*, FunctionParameters const&);
|
||||
static AqlValue Document (triagens::aql::Query*, triagens::arango::AqlTransaction*, FunctionParameters const&);
|
||||
static AqlValue Edges (triagens::aql::Query*, triagens::arango::AqlTransaction*, FunctionParameters const&);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -203,7 +203,6 @@ void ExampleMatcher::fillExampleDefinition (TRI_json_t const* example,
|
|||
}
|
||||
else {
|
||||
// no attribute path found. this means the result will be empty
|
||||
ExampleMatcher::cleanup();
|
||||
THROW_ARANGO_EXCEPTION(TRI_RESULT_ELEMENT_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
@ -213,7 +212,6 @@ void ExampleMatcher::fillExampleDefinition (TRI_json_t const* example,
|
|||
auto value = TRI_ShapedJsonJson(_shaper, jsonValue, false);
|
||||
|
||||
if (value == nullptr) {
|
||||
ExampleMatcher::cleanup();
|
||||
THROW_ARANGO_EXCEPTION(TRI_RESULT_ELEMENT_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
@ -313,12 +311,21 @@ ExampleMatcher::ExampleMatcher (TRI_json_t const* example,
|
|||
ExampleDefinition def;
|
||||
try {
|
||||
ExampleMatcher::fillExampleDefinition(TRI_LookupArrayJson(example, i), resolver, def);
|
||||
definitions.emplace_back(move(def));
|
||||
}
|
||||
catch (...) {
|
||||
catch (triagens::basics::Exception& e) {
|
||||
if (e.code() != TRI_RESULT_ELEMENT_NOT_FOUND) {
|
||||
ExampleMatcher::cleanup();
|
||||
throw;
|
||||
}
|
||||
definitions.emplace_back(move(def));
|
||||
// Result not found might happen. Ignore here because all other elemens
|
||||
// might be matched.
|
||||
}
|
||||
}
|
||||
if (definitions.size() == 0) {
|
||||
// None of the given examples could ever match.
|
||||
// Throw result not found so client can short circuit.
|
||||
THROW_ARANGO_EXCEPTION(TRI_RESULT_ELEMENT_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ function ahuacatlQueryEdgesTestSuite () {
|
|||
vertex.save({ _key: "v7", name: "v7" });
|
||||
|
||||
function makeEdge (from, to) {
|
||||
edge.save("UnitTestsAhuacatlVertex/" + from, "UnitTestsAhuacatlVertex/" + to, { what: from + "->" + to });
|
||||
edge.save("UnitTestsAhuacatlVertex/" + from, "UnitTestsAhuacatlVertex/" + to, { _key: from + "" + to, what: from + "->" + to });
|
||||
}
|
||||
|
||||
makeEdge("v1", "v2");
|
||||
|
@ -323,7 +323,44 @@ function ahuacatlQueryEdgesTestSuite () {
|
|||
});
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief checks EDGES()
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testEdgesFilterExample : function () {
|
||||
var queries = [
|
||||
"FOR e IN EDGES(UnitTestsAhuacatlEdge, @start, 'any', @examples) SORT e.what RETURN e.what",
|
||||
"FOR e IN NOOPT(EDGES(UnitTestsAhuacatlEdge, @start, 'any', @examples)) SORT e.what RETURN e.what",
|
||||
"FOR e IN NOOPT(V8(EDGES(UnitTestsAhuacatlEdge, @start, 'any', @examples))) SORT e.what RETURN e.what"
|
||||
];
|
||||
|
||||
queries.forEach(function (q) {
|
||||
var actual;
|
||||
actual = getQueryResults(q, {start: "UnitTestsAhuacatlVertex/v3", examples: {what: "v1->v3"} });
|
||||
assertEqual(actual, [ "v1->v3" ]);
|
||||
|
||||
actual = getQueryResults(q, {start: "UnitTestsAhuacatlVertex/v3", examples: [{what: "v1->v3"}, {what: "v3->v6"}] });
|
||||
assertEqual(actual, [ "v1->v3", "v3->v6"]);
|
||||
|
||||
actual = getQueryResults(q, {start: "UnitTestsAhuacatlVertex/v3", examples: [] });
|
||||
assertEqual(actual, [ "v1->v3", "v2->v3", "v3->v4", "v3->v6", "v3->v7", "v6->v3", "v7->v3" ]);
|
||||
|
||||
actual = getQueryResults(q, {start: "UnitTestsAhuacatlVertex/v3", examples: null });
|
||||
assertEqual(actual, [ "v1->v3", "v2->v3", "v3->v4", "v3->v6", "v3->v7", "v6->v3", "v7->v3" ]);
|
||||
|
||||
actual = getQueryResults(q, {start: "UnitTestsAhuacatlVertex/v3", examples: {non: "matchable"} });
|
||||
assertEqual(actual, [ ]);
|
||||
|
||||
actual = getQueryResults(q, {start: "UnitTestsAhuacatlVertex/v3", examples: [{what: "v1->v3"}, {non: "matchable"}] });
|
||||
assertEqual(actual, [ "v1->v3" ]);
|
||||
|
||||
actual = getQueryResults(q, {start: "UnitTestsAhuacatlVertex/v3", examples: [{what: "v1->v3"}, {non: "matchable"}] });
|
||||
assertEqual(actual, [ "v1->v3" ]);
|
||||
|
||||
actual = getQueryResults(q, {start: "UnitTestsAhuacatlVertex/v3", examples: ["UnitTestsAhuacatlEdge/v1v3", {what: "v3->v6" } ] });
|
||||
assertEqual(actual, [ "v3->v6" ]);
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue