diff --git a/src/cypher/procedure/procedure.cpp b/src/cypher/procedure/procedure.cpp index 42bd65dd78..3007ad86de 100644 --- a/src/cypher/procedure/procedure.cpp +++ b/src/cypher/procedure/procedure.cpp @@ -3484,32 +3484,41 @@ void AlgoFunc::ShortestPath(RTContext *ctx, const Record *record, const cypher:: if (args.size() == 3) { auto &map = args[2].Map(); // edgeFilter - auto iter = map.find("edgeFilter"); + auto iter = map.find("relationshipQuery"); if (iter != map.end()) { - if (iter->second.type != parser::Expression::LIST) CYPHER_TODO(); - for (auto& item : iter->second.List()) { - if (item.type != parser::Expression::MAP) CYPHER_TODO(); - auto& edge_filter = item.Map(); - auto label_iter = edge_filter.find("label"); - if (label_iter == edge_filter.end()) CYPHER_TODO(); + if (iter->second.type != parser::Expression::LIST && + iter->second.type != parser::Expression::STRING) CYPHER_TODO(); + if (iter->second.type == parser::Expression::STRING) { EdgeFilter e_filter; - e_filter.label = label_iter->second.String(); - auto property_filter_iter = edge_filter.find("property_filter"); - if (property_filter_iter != edge_filter.end()) { - if (property_filter_iter->second.type != parser::Expression::MAP) CYPHER_TODO(); - for (auto &kv : property_filter_iter->second.Map()) { - if (kv.second.type != parser::Expression::MAP) CYPHER_TODO(); - std::unordered_map filter_t; - for (auto& filter_pair : kv.second.Map()) { - lgraph::FieldData field; - if (!filter_pair.second.IsLiteral()) CYPHER_TODO(); - field = parser::MakeFieldData(filter_pair.second); - filter_t.emplace(filter_pair.first, field); + e_filter.label = iter->second.String(); + edge_filters.emplace_back(std::move(e_filter)); + } else { + for (auto &item : iter->second.List()) { + if (item.type != parser::Expression::MAP) CYPHER_TODO(); + auto &edge_filter = item.Map(); + auto label_iter = edge_filter.find("label"); + if (label_iter == edge_filter.end()) CYPHER_TODO(); + EdgeFilter e_filter; + e_filter.label = label_iter->second.String(); + auto property_filter_iter = + edge_filter.find("property_filter"); + if (property_filter_iter != edge_filter.end()) { + if (property_filter_iter->second.type != parser::Expression::MAP) + CYPHER_TODO(); + for (auto &kv : property_filter_iter->second.Map()) { + if (kv.second.type != parser::Expression::MAP) CYPHER_TODO(); + std::unordered_map filter_t; + for (auto &filter_pair : kv.second.Map()) { + lgraph::FieldData field; + if (!filter_pair.second.IsLiteral()) CYPHER_TODO(); + field = parser::MakeFieldData(filter_pair.second); + filter_t.emplace(filter_pair.first, field); + } + e_filter.property_filter.emplace(kv.first, filter_t); } - e_filter.property_filter.emplace(kv.first, filter_t); } + edge_filters.emplace_back(std::move(e_filter)); } - edge_filters.emplace_back(std::move(e_filter)); } } // maxHops @@ -3578,15 +3587,20 @@ void AlgoFunc::AllShortestPaths(RTContext *ctx, const Record *record, const cyph std::vector edge_labels; if (args.size() == 3) { auto &map = args[2].Map(); - auto iter = map.find("edgeFilter"); + auto iter = map.find("relationshipQuery"); if (iter != map.end()) { - if (iter->second.type != parser::Expression::LIST) CYPHER_TODO(); - for (auto& item : iter->second.List()) { - if (item.type != parser::Expression::MAP) CYPHER_TODO(); - auto& edge_filter = item.Map(); - auto label_iter = edge_filter.find("label"); - if (label_iter == edge_filter.end()) CYPHER_TODO(); - edge_labels.push_back(label_iter->second.String()); + if (iter->second.type != parser::Expression::LIST && + iter->second.type != parser::Expression::STRING) CYPHER_TODO(); + if (iter->second.type == parser::Expression::STRING) { + edge_labels.push_back(iter->second.String()); + } else { + for (auto &item : iter->second.List()) { + if (item.type != parser::Expression::MAP) CYPHER_TODO(); + auto &edge_filter = item.Map(); + auto label_iter = edge_filter.find("label"); + if (label_iter == edge_filter.end()) CYPHER_TODO(); + edge_labels.push_back(label_iter->second.String()); + } } } } diff --git a/test/resource/cases/finbench/cypher/complex_read.result b/test/resource/cases/finbench/cypher/complex_read.result index 23821905cb..0f3d9eadd3 100644 --- a/test/resource/cases/finbench/cypher/complex_read.result +++ b/test/resource/cases/finbench/cypher/complex_read.result @@ -49,7 +49,7 @@ CALL algo.shortestPath( dst, { direction: 'PointingRight', - edgeFilter: [ + relationshipQuery: [ { label:'transfer', property_filter: { diff --git a/test/resource/cases/finbench/cypher/complex_read.test b/test/resource/cases/finbench/cypher/complex_read.test index f44b2c6abd..c045b7469f 100644 --- a/test/resource/cases/finbench/cypher/complex_read.test +++ b/test/resource/cases/finbench/cypher/complex_read.test @@ -47,7 +47,7 @@ CALL algo.shortestPath( dst, { direction: 'PointingRight', - edgeFilter: [ + relationshipQuery: [ { label:'transfer', property_filter: { diff --git a/test/resource/unit_test/algo/cypher/algo.result b/test/resource/unit_test/algo/cypher/algo.result index 96feff715a..27b8d7ca5d 100644 --- a/test/resource/unit_test/algo/cypher/algo.result +++ b/test/resource/unit_test/algo/cypher/algo.result @@ -12,7 +12,9 @@ MATCH (n1:Loc {name:'A'}), (n2:Loc {name:'E'}) CALL algo.allShortestPaths(n1, n2 [{"cost":2.0,"nodeIds":[0,2,4],"relationshipIds":"[0_2_0_0_0,2_4_0_0_0]"},{"cost":2.0,"nodeIds":[0,3,4],"relationshipIds":"[0_3_0_0_0,3_4_0_0_0]"}] MATCH (n1:Loc {name:'A'}), (n2:Loc {name:'E'}) CALL algo.allShortestPaths(n1, n2) YIELD nodeIds,relationshipIds,cost WITH nodeIds,relationshipIds,cost UNWIND relationshipIds AS rid CALL algo.native.extract(rid, {isNode:false, field:'cost'}) YIELD value RETURN value; [{"value":50.0},{"value":80.0},{"value":100.0},{"value":30.0}] -MATCH (n1:Loc {name:'A'}), (n2:Loc {name:'E'}) CALL algo.allShortestPaths(n1, n2, {edgeFilter:[{label:'ROAD'}]}) YIELD nodeIds,relationshipIds WITH nodeIds,relationshipIds UNWIND relationshipIds AS rid CALL algo.native.extract(rid, {isNode:false, field:'cost'}) YIELD value RETURN nodeIds, sum(value) AS score; +MATCH (n1:Loc {name:'A'}), (n2:Loc {name:'E'}) CALL algo.allShortestPaths(n1, n2, {relationshipQuery:[{label:'ROAD'}]}) YIELD nodeIds,relationshipIds WITH nodeIds,relationshipIds UNWIND relationshipIds AS rid CALL algo.native.extract(rid, {isNode:false, field:'cost'}) YIELD value RETURN nodeIds, sum(value) AS score; [{"nodeIds":[0,3,4],"score":130.0},{"nodeIds":[0,2,4],"score":130.0}] -MATCH (n1:Loc {name:'A'}), (n2:Loc {name:'E'}) CALL algo.allShortestPaths(n1, n2, {edgeFilter:[{label:'ROAD'}]}) YIELD nodeIds,relationshipIds,cost WITH nodeIds,relationshipIds,cost UNWIND relationshipIds AS rid CALL algo.native.extract(rid, {isNode:false, field:'cost'}) YIELD value WITH nodeIds, sum(value) AS score CALL algo.native.extract(nodeIds, {isNode:true, field:'name'}) YIELD value RETURN value, score; +MATCH (n1:Loc {name:'A'}), (n2:Loc {name:'E'}) CALL algo.allShortestPaths(n1, n2, {relationshipQuery:[{label:'ROAD'}]}) YIELD nodeIds,relationshipIds,cost WITH nodeIds,relationshipIds,cost UNWIND relationshipIds AS rid CALL algo.native.extract(rid, {isNode:false, field:'cost'}) YIELD value WITH nodeIds, sum(value) AS score CALL algo.native.extract(nodeIds, {isNode:true, field:'name'}) YIELD value RETURN value, score; +[{"score":130.0,"value":"[A,D,E]"},{"score":130.0,"value":"[A,C,E]"}] +MATCH (n1:Loc {name:'A'}), (n2:Loc {name:'E'}) CALL algo.allShortestPaths(n1, n2, {relationshipQuery:'ROAD'}) YIELD nodeIds,relationshipIds,cost WITH nodeIds,relationshipIds,cost UNWIND relationshipIds AS rid CALL algo.native.extract(rid, {isNode:false, field:'cost'}) YIELD value WITH nodeIds, sum(value) AS score CALL algo.native.extract(nodeIds, {isNode:true, field:'name'}) YIELD value RETURN value, score; [{"score":130.0,"value":"[A,D,E]"},{"score":130.0,"value":"[A,C,E]"}] diff --git a/test/resource/unit_test/algo/cypher/algo.test b/test/resource/unit_test/algo/cypher/algo.test index 9e2617b623..5e962ab96a 100644 --- a/test/resource/unit_test/algo/cypher/algo.test +++ b/test/resource/unit_test/algo/cypher/algo.test @@ -5,5 +5,6 @@ MATCH (n1:Loc {name:'A'}), (n2:Loc {name:'F'}) CALL algo.shortestPath(n1, n2) YI MATCH (n1:Loc {name:'A'}), (n2:Loc {name:'G'}) CALL algo.shortestPath(n1, n2) YIELD nodeCount RETURN nodeCount; MATCH (n1:Loc {name:'A'}), (n2:Loc {name:'E'}) CALL algo.allShortestPaths(n1, n2) YIELD nodeIds,relationshipIds,cost RETURN nodeIds,relationshipIds,cost; MATCH (n1:Loc {name:'A'}), (n2:Loc {name:'E'}) CALL algo.allShortestPaths(n1, n2) YIELD nodeIds,relationshipIds,cost WITH nodeIds,relationshipIds,cost UNWIND relationshipIds AS rid CALL algo.native.extract(rid, {isNode:false, field:'cost'}) YIELD value RETURN value; -MATCH (n1:Loc {name:'A'}), (n2:Loc {name:'E'}) CALL algo.allShortestPaths(n1, n2, {edgeFilter:[{label:'ROAD'}]}) YIELD nodeIds,relationshipIds WITH nodeIds,relationshipIds UNWIND relationshipIds AS rid CALL algo.native.extract(rid, {isNode:false, field:'cost'}) YIELD value RETURN nodeIds, sum(value) AS score; -MATCH (n1:Loc {name:'A'}), (n2:Loc {name:'E'}) CALL algo.allShortestPaths(n1, n2, {edgeFilter:[{label:'ROAD'}]}) YIELD nodeIds,relationshipIds,cost WITH nodeIds,relationshipIds,cost UNWIND relationshipIds AS rid CALL algo.native.extract(rid, {isNode:false, field:'cost'}) YIELD value WITH nodeIds, sum(value) AS score CALL algo.native.extract(nodeIds, {isNode:true, field:'name'}) YIELD value RETURN value, score; \ No newline at end of file +MATCH (n1:Loc {name:'A'}), (n2:Loc {name:'E'}) CALL algo.allShortestPaths(n1, n2, {relationshipQuery:[{label:'ROAD'}]}) YIELD nodeIds,relationshipIds WITH nodeIds,relationshipIds UNWIND relationshipIds AS rid CALL algo.native.extract(rid, {isNode:false, field:'cost'}) YIELD value RETURN nodeIds, sum(value) AS score; +MATCH (n1:Loc {name:'A'}), (n2:Loc {name:'E'}) CALL algo.allShortestPaths(n1, n2, {relationshipQuery:[{label:'ROAD'}]}) YIELD nodeIds,relationshipIds,cost WITH nodeIds,relationshipIds,cost UNWIND relationshipIds AS rid CALL algo.native.extract(rid, {isNode:false, field:'cost'}) YIELD value WITH nodeIds, sum(value) AS score CALL algo.native.extract(nodeIds, {isNode:true, field:'name'}) YIELD value RETURN value, score; +MATCH (n1:Loc {name:'A'}), (n2:Loc {name:'E'}) CALL algo.allShortestPaths(n1, n2, {relationshipQuery:'ROAD') YIELD nodeIds,relationshipIds,cost WITH nodeIds,relationshipIds,cost UNWIND relationshipIds AS rid CALL algo.native.extract(rid, {isNode:false, field:'cost'}) YIELD value WITH nodeIds, sum(value) AS score CALL algo.native.extract(nodeIds, {isNode:true, field:'name'}) YIELD value RETURN value, score; \ No newline at end of file diff --git a/test/resource/unit_test/procedure/cypher/procedure.result b/test/resource/unit_test/procedure/cypher/procedure.result index 03b9125e56..dc983e40ca 100644 --- a/test/resource/unit_test/procedure/cypher/procedure.result +++ b/test/resource/unit_test/procedure/cypher/procedure.result @@ -168,7 +168,7 @@ MATCH (n1 {name:'Michael Redgrave'}),(n2:City) CALL algo.shortestPath(n1,n2) YIE [{"n2.name":"Houston","nodeCount":6,"totalCost":5.0},{"n2.name":"London","nodeCount":3,"totalCost":2.0},{"n2.name":"New York","nodeCount":4,"totalCost":3.0}] MATCH (n1 {name:'Michael Redgrave'}),(n2:City) CALL algo.shortestPath(n1,n2,{maxHops:3}) YIELD nodeCount RETURN n2.name,nodeCount /* 2 results */; [{"n2.name":"Houston","nodeCount":0},{"n2.name":"London","nodeCount":3},{"n2.name":"New York","nodeCount":4}] -MATCH (n1 {name:'Michael Redgrave'}),(n2 {name:'Rachel Kempson'}) CALL algo.shortestPath(n1,n2,{edgeFilter:[{label:'HAS_CHILD'}}]) YIELD nodeCount,totalCost RETURN nodeCount,totalCost /* 3,2.0 */; +MATCH (n1 {name:'Michael Redgrave'}),(n2 {name:'Rachel Kempson'}) CALL algo.shortestPath(n1,n2,{relationshipQuery:[{label:'HAS_CHILD'}}]) YIELD nodeCount,totalCost RETURN nodeCount,totalCost /* 3,2.0 */; [{"nodeCount":3,"totalCost":2.0}] MATCH (n1 {name:'Corin Redgrave'}),(n2 {name:'London'}) CALL algo.allShortestPaths(n1,n2) YIELD nodeIds,cost RETURN nodeIds,cost /* 2 */; [{"cost":3.0,"nodeIds":[3,0,2,14]},{"cost":3.0,"nodeIds":[3,1,2,14]}] diff --git a/test/resource/unit_test/procedure/cypher/procedure.test b/test/resource/unit_test/procedure/cypher/procedure.test index 799cbe095b..7e67f8a2fa 100644 --- a/test/resource/unit_test/procedure/cypher/procedure.test +++ b/test/resource/unit_test/procedure/cypher/procedure.test @@ -87,7 +87,7 @@ MATCH (n1 {name:'Michael Redgrave'}),(n2 {name:'Rachel Kempson'}) CALL algo.shor MATCH (n1 {name:'Michael Redgrave'}),(n2 {name:'Houston'}) CALL algo.shortestPath(n1,n2) YIELD nodeCount,totalCost RETURN nodeCount,totalCost /* 6,5.0 */; MATCH (n1 {name:'Michael Redgrave'}),(n2:City) CALL algo.shortestPath(n1,n2) YIELD nodeCount,totalCost RETURN n2.name,nodeCount,totalCost /* 3 results */; MATCH (n1 {name:'Michael Redgrave'}),(n2:City) CALL algo.shortestPath(n1,n2,{maxHops:3}) YIELD nodeCount RETURN n2.name,nodeCount /* 2 results */; -MATCH (n1 {name:'Michael Redgrave'}),(n2 {name:'Rachel Kempson'}) CALL algo.shortestPath(n1,n2,{edgeFilter:[{label:'HAS_CHILD'}]}) YIELD nodeCount,totalCost RETURN nodeCount,totalCost /* 3,2.0 */; +MATCH (n1 {name:'Michael Redgrave'}),(n2 {name:'Rachel Kempson'}) CALL algo.shortestPath(n1,n2,{relationshipQuery:[{label:'HAS_CHILD'}]}) YIELD nodeCount,totalCost RETURN nodeCount,totalCost /* 3,2.0 */; MATCH (n1 {name:'Corin Redgrave'}),(n2 {name:'London'}) CALL algo.allShortestPaths(n1,n2) YIELD nodeIds,cost RETURN nodeIds,cost /* 2 */; MATCH (n1 {name:'Corin Redgrave'}),(n2 {name:'Liam Neeson'}) CALL algo.allShortestPaths(n1,n2) YIELD nodeIds,cost RETURN nodeIds,cost /* 4 */; MATCH (n1 {name:'Corin Redgrave'}),(n2 {name:'Liam Neeson'}) CALL algo.allShortestPaths(n1,n2) YIELD nodeIds,relationshipIds,cost RETURN nodeIds,relationshipIds,cost /* 4 */; diff --git a/test/test_cypher.cpp b/test/test_cypher.cpp index 9045a56237..30e645a3e5 100644 --- a/test/test_cypher.cpp +++ b/test/test_cypher.cpp @@ -1374,7 +1374,11 @@ int test_procedure(cypher::RTContext *ctx) { "CALL algo.shortestPath(n1,n2,{maxHops:3}) YIELD nodeCount RETURN n2.name,nodeCount /* 2 " "results */", "MATCH (n1 {name:'Michael Redgrave'}),(n2 {name:'Rachel Kempson'})\n" - "CALL algo.shortestPath(n1,n2,{edgeFilter:[{label:'HAS_CHILD'}]}) YIELD nodeCount,totalCost" + "CALL algo.shortestPath(n1,n2,{relationshipQuery:[{label:'HAS_CHILD'}]}) " + "YIELD nodeCount,totalCost" + " RETURN nodeCount,totalCost /* 3,2.0 */", + "MATCH (n1 {name:'Michael Redgrave'}),(n2 {name:'Rachel Kempson'})\n" + "CALL algo.shortestPath(n1,n2,{relationshipQuery:'HAS_CHILD'}) YIELD nodeCount,totalCost" " RETURN nodeCount,totalCost /* 3,2.0 */", "MATCH (n1 {name:'Corin Redgrave'}),(n2 {name:'London'})\n" "CALL algo.allShortestPaths(n1,n2) YIELD nodeIds,cost RETURN nodeIds,cost /* 2 */", @@ -2047,13 +2051,19 @@ CREATE (e)-[:ROAD {cost:40}]->(f); "UNWIND relationshipIds AS rid\n" "CALL algo.native.extract(rid, {isNode:false, field:'cost'}) YIELD value RETURN value", "MATCH (n1:Loc {name:'A'}), (n2:Loc {name:'E'})\n" - "CALL algo.allShortestPaths(n1, n2, {edgeFilter:[{label:'ROAD'}]}) YIELD " + "CALL algo.allShortestPaths(n1, n2, {relationshipQuery:[{label:'ROAD'}]}) YIELD " + "nodeIds,relationshipIds WITH nodeIds,relationshipIds\n" + "UNWIND relationshipIds AS rid\n" + "CALL algo.native.extract(rid, {isNode:false, field:'cost'}) YIELD value RETURN nodeIds, " + "sum(value) AS score", + "MATCH (n1:Loc {name:'A'}), (n2:Loc {name:'E'})\n" + "CALL algo.allShortestPaths(n1, n2, {relationshipQuery:'ROAD'}) YIELD " "nodeIds,relationshipIds WITH nodeIds,relationshipIds\n" "UNWIND relationshipIds AS rid\n" "CALL algo.native.extract(rid, {isNode:false, field:'cost'}) YIELD value RETURN nodeIds, " "sum(value) AS score", "MATCH (n1:Loc {name:'A'}), (n2:Loc {name:'E'})\n" - "CALL algo.allShortestPaths(n1, n2, {edgeFilter:[{label:'ROAD'}]}) YIELD " + "CALL algo.allShortestPaths(n1, n2, {relationshipQuery:[{label:'ROAD'}]}) YIELD " "nodeIds,relationshipIds,cost WITH nodeIds,relationshipIds,cost\n" "UNWIND relationshipIds AS rid\n" "CALL algo.native.extract(rid, {isNode:false, field:'cost'}) YIELD value WITH nodeIds, "