#neo4j #erlang #cypher
#neo4j #erlang #шифр
Вопрос:
Я работаю над проектом (если быть точным, refactorererl), где мы храним синтаксические деревья и семантические графы программ erlang для выполнения рефакторинга и других видов анализа, преобразований и запросов к ним. Ранее они хранились и сравнивались с различными хранилищами значений ключей, реляционными базами данных и базами данных на основе документов, но я решил попробовать и реализовать серверную часть neo4j, поскольку она казалась подходящей для запросов типа path, таких как «перечислите все переменные функций, которые находятся в вызываемом модуле xyz
» и аналогичные действия.
Я написал библиотеку-оболочку neo4j (neo4j_driver), поскольку более старые, которые я нашел, использовали API, который сейчас устарел, и дошел до того, что он почти работает с соответствующими фильтрами и всем прочим, но одна вещь, которую я просто не могу решить, — это отсутствие возможности сопоставить один и тот же узел поднесколько имен в запросе. Это кажется необходимым, поскольку бывают случаи, когда поиск в обратном направлении по связям неизбежен. Например, когда программа (например, потому, что это необходимо для рефакторинга) пытается выполнить поиск всех переменных, которые находятся в той же функции, что и, скажем, заданный X
(идентифицируемый путем указания параметра fromId
), он преобразует это в Cypher и выдает что-то вроде этого:
MATCH (from:var)<-[edge1:vardef]-(node1)-[edge2:vardef]->(node2) WHERE id(from) = $fromId RETURN labels(node2), id(node2) ORDER BY edge1.orderId, edge2.orderId"
(Вот orderId
лексический порядок элементов в файле.)
Проблема в том, что это дает только переменные внутри функции, которые содержат X
другие, чем X
она сама, поскольку они должны совпадать с именами «from» и «node2» одновременно. Есть ли способ создать такой запрос, который позволяет избежать этой проблемы? Я знаю, что я мог бы попытаться избежать последовательного присвоения имен узлам и фильтрации с помощью фигурных скобок внутри узла, но таким образом я мог бы выполнять только точные совпадения, а не, например, диапазоны.
ps.: Текущий код, который я написал для перевода запросов пути, выглядит следующим образом, пожалуйста, скажите мне, ужасно ли это, поскольку я чувствую, что могут быть лучшие способы сделать это:
filter_to_where_clause_element(NodeName, EdgeName, empty_filter) -> "";
filter_to_where_clause_element(NodeName, EdgeName, Filter) ->
case Filter of
{'not', InnerFilter} ->
"NOT (" filter_to_where_clause_element(NodeName, EdgeName, InnerFilter) ")";
{LeftFilter, 'and', RightFilter} ->
"((" filter_to_where_clause_element(NodeName, EdgeName, LeftFilter)
") AND (" filter_to_where_clause_element(NodeName, EdgeName, RightFilter) "))";
{LeftFilter, 'or', RightFilter} ->
"((" filter_to_where_clause_element(NodeName, EdgeName, LeftFilter)
") OR (" filter_to_where_clause_element(NodeName, EdgeName, RightFilter) "))";
{Property, '==', Value} ->
NodeName "." to_str(Property) " = '" to_str(Value) "'";
{Property, '/=', Value} ->
NodeName "." to_str(Property) " <> '" to_str(Value) "'";
{Property, '<', Value} ->
NodeName "." to_str(Property) " < '" to_str(Value) "'";
{Property, '>', Value} ->
NodeName "." to_str(Property) " > '" to_str(Value) "'";
{Property, '<=', Value} ->
NodeName "." to_str(Property) " <= '" to_str(Value) "'";
{Property, '>=', Value} ->
NodeName "." to_str(Property) " >= '" to_str(Value) "'";
% last ->
% EdgeName ".orderId = " to_str(get_last_id());
Id when is_integer(Id) ->
EdgeName ".orderId = " to_str(Id);
Other -> own_debug("Unknown filter", [Filter]), ""
end.
path_element_to_query_part({Id, {Tag, Direction, Filter}}) ->
NodeName = "node" to_str(Id),
EdgeName = "edge" to_str(Id),
Edge = "[" EdgeName ":" to_str(Tag) "]",
MatchClauseElement =
case Direction of
back -> "<-" Edge "-";
fwd -> "-" Edge "->"
end "(" NodeName ")",
WhereClauseElement = filter_to_where_clause_element(NodeName, EdgeName, Filter),
OrderByClauseElement = EdgeName ".orderId",
{ MatchClauseElement, WhereClauseElement, OrderByClauseElement }.
path(Node, Path) ->
{?NODETAG, Class, Id} = Node,
NormalizedIndexedPath = lists:zip(
lists:seq(1, length(Path)),
lists:map(fun(PE) -> normalize_path_element(PE) end, Path)
),
QueryPathElements = lists:map(
fun(IndexedPathElement) ->
path_element_to_query_part(IndexedPathElement)
end,
NormalizedIndexedPath
),
{ MatchClauseElements, WhereClauseElements, OrderByClauseElements } = {
lists:map(
fun(QueryPathElement) -> element(1, QueryPathElement) end,
QueryPathElements
),
lists:map(
fun(QueryPathElement) -> element(2, QueryPathElement) end,
QueryPathElements
),
lists:map(
fun(QueryPathElement) -> element(3, QueryPathElement) end,
QueryPathElements
)
},
JoinedMatchClause = string:join(MatchClauseElements, ""),
LastNodeName = "node" to_str(length(Path)),
JoinedWhereClause = string:join(
lists:filter(
fun(WhereClauseElement) -> WhereClauseElement /= "" end,
["id(from) = $fromId" | WhereClauseElements]
),
" AND "
),
JoinedOrderByClause = string:join(OrderByClauseElements, ", "),
Statements = [
[
{ statement,
"MATCH (from:" to_str(Class) ")"
JoinedMatchClause
" WHERE " JoinedWhereClause
" RETURN labels(" LastNodeName "), id(" LastNodeName ")"
" ORDER BY " JoinedOrderByClause
},
{ parameters, [
{ "fromId", Id }
] }
]
],
Result = run_cypher_query(Statements),
MatchedNodes = lists:map(fun (Row) ->
[[TargetClass], TargetId] = Row,
{?NODETAG, parse(TargetClass), TargetId}
end, get_rows(Result)),
{ ok, MatchedNodes }.
Комментарии:
1. Возможно, вы хотите разделить свое отдельное
MATCH
предложение на предложения, разделенные запятыми, илиMATCH
на несколько предложений?2. Спасибо за ответ! Насколько я могу судить по чтению в Интернете, предложения, разделенные запятыми, не решат эту проблему, потому что они по-прежнему используют одно и то же пространство имен, просто разрешают ветвление.
MATCH
Может работать несколько предложений, но тогда мне нужно будет соединить их вручную, чтобы связать цепочку вместе. Разве это не разрушит их производительность?3.
MATCH
Я полагаю, что несколько предложений создают перекрестный продукт, но мне обычно легче оптимизировать запрос (и / или дизайн базы данных) после получения правильных результатов.