У меня есть следующие объекты в моей базе данных Neo4J (с использованием версии 2.0).
Target
Library
Function
Это отношения между каждым.
function[:used_in]->target
function[:included_in]->library
library[:part_of]->target
Моя конечная цель — построить дерево на C#, которое позволит мне выразить сопоставление между функциями, которые используются в Target, но со способом связать их обратно с их исходной библиотекой. В дереве библиотека будет представлена как родительский узел функции.
Другими словами, предположим, что библиотека предоставляет 10 различных функций, но только 5 из них используются (используются_в) данной целью. Меня интересуют только эти 5, а не остальные. Для каждой функции я хочу иметь возможность быстро (в моем дереве) добраться до библиотеки, которая «включает» данную функцию. У меня есть коллекция таргетов и я понимаю, что мне, скорее всего, придется выдавать отдельный запрос для каждого таргета. Моя цель - избежать выдачи нескольких запросов для каждой цели/библиотеки/функции.
Учитывая отношения, которые я описал, возможно ли построить запрос Cypher, который вернет мне интересующие узлы?
Вот запрос Cypher, который вернет все функции для данной цели.
MATCH (function:Function)-[:used_in]->(target:Target)
WHERE (target.Name = "target.exe")
RETURN function
Вот запрос Cypher, который вернет библиотеку для данной функции.
MATCH (function:Function)-[:included_in]->(library:Library)
WHERE (function.Name = "foo")
RETURN library
Мне интересно посмотреть, можно ли это объединить в один запрос. В моем текущем сценарии мне нужно будет выполнить второй запрос для каждой функции. Я надеюсь, что мои отношения достаточно оптимальны, чтобы позволить мне сократить количество запросов, которые мне нужно сделать.
Я использую Neo4JClient — если кто-то поможет предоставить запрос Cypher, я надеюсь, что смогу перевести его в Neo4JClient. Если кто-то пойдет еще дальше и даст мне код C#, тем лучше, но, пожалуйста, помогите мне понять стоящий за ним шифр.
Вот Cypher для создания базы данных:
CREATE (t:Target { Name: 'Target.exe' })
CREATE (t:Target { Name: 'Target2.exe' })
CREATE (t:Target { Name: 'Target3.exe' })
CREATE (l:Library { Name: 'Library.lib', Path: 'i386' })
CREATE (l:Library { Name: 'Library.lib', Path: 'amd64' })
CREATE (l:Library { Name: 'Library2.lib', Path: 'amd64' })
CREATE (f:Function { Name: 'FunctionA' })
CREATE (f:Function { Name: 'FunctionB' })
CREATE (f:Function { Name: 'FunctionC' })
CREATE (f:Function { Name: 'FunctionZ' })
MATCH (f:Function),(l:Library) WHERE (f.Name = "FunctionA") AND (l.Name = "Library.lib") AND (l.Path = "amd64") CREATE f-[:included_in]->l
MATCH (f:Function),(l:Library) WHERE (f.Name = "FunctionB") AND (l.Name = "Library.lib") AND (l.Path = "amd64") CREATE f-[:included_in]->l
MATCH (f:Function),(l:Library) WHERE (f.Name = "FunctionC") AND (l.Name = "Library2.lib") AND (l.Path = "amd64") CREATE f-[:included_in]->l
MATCH (f:Function),(l:Library) WHERE (f.Name = "FunctionZ") AND (l.Name = "Library.lib") AND (l.Path = "i386") CREATE f-[:included_in]->l
MATCH (f:Function),(l:Library) WHERE (f.Name = "FunctionZ") AND (l.Name = "Library.lib") AND (l.Path = "amd64") CREATE f-[:included_in]->l
MATCH (f:Function),(t:Target) WHERE (f.Name = "FunctionA") AND (t.Name = "Target.exe") CREATE f-[:used_in]->t
MATCH (f:Function),(t:Target) WHERE (f.Name = "FunctionB") AND (t.Name = "Target.exe") CREATE f-[:used_in]->t
MATCH (f:Function),(t:Target) WHERE (f.Name = "FunctionC") AND (t.Name = "Target2.exe") CREATE f-[:used_in]->t
MATCH (f:Function),(t:Target) WHERE (f.Name = "FunctionZ") AND (t.Name = "Target3.exe") CREATE f-[:used_in]->t
MATCH (l:Library),(t:Target) WHERE (l.Name = "Library.lib") AND (l.Path ="amd64") AND (t.Name = "Target.exe") CREATE l-[:part_of]->t
MATCH (l:Library),(t:Target) WHERE (l.Name = "Library2.lib") AND (l.Path ="amd64") AND (t.Name = "Target2.exe") CREATE l-[:part_of]->t
MATCH (l:Library),(t:Target) WHERE (l.Name = "Library.lib") AND (l.Path ="i386") AND (t.Name = "Target3.exe") CREATE l-[:part_of]->t
Для тех, кто заинтересован, вот код Neo4Client, который я использовал на основе запроса Cypher, предоставленного Уэсом.
var query = graphClient.Cypher
.Match("(function:Function)-[:used_in]->(target:Target),(function:Function)-[:included_in]->(library:Library)")
.Where((Target target) => target.Name == sourceTarget.Name)
.AndWhere((Target target) => target.Path == sourceTarget.Path)
.AndWhere((Target target) => target.PEType == sourceTarget.PEType)
.AndWhere((Target target) => target.FileArch == sourceTarget.FileArch)
.AndWhere((Library library) => library.Name == sourceLibrary.Name)
.AndWhere((Library library) => library.Path == sourceLibrary.Path)
.AndWhere((Library library) => library.PEType == sourceLibrary.PEType)
.AndWhere((Library library) => library.FileArch == sourceLibrary.FileArch)
.Return((function) => new
{
Functions = function.CollectAs<Function>()
})
.Results
.Select(result => new FunctionCollection()
{
Collection = result.Functions.Select(a => a.Data).ToList()
});