Question

I have the following entities in my Neo4J database (using version 2.0).

Target
Library
Function

These are the relationships among each.

function[:used_in]->target
function[:included_in]->library
library[:part_of]->target

My ultimate goal is to construct a tree in C# that allows me to express the mapping between the Function(s) that are used in a Target, but with a way to relate them back to their originating Library. In the tree, the Library would be expressed as a parent node of the Function.

In other words, let's say a Library exposes 10 different functions, but only 5 of them are consumed by(used_in) a given Target. I'm only interested in those 5 and not the rest. On a per-function basis, I want to be able to quickly (in my tree) get to the Library that "includes" the given function. I have the collection of Targets and understand that I will most likely have to issue a separate query for each Target. My goal is to avoid issuing multiple queries for each Target/Library/Function.

Given the relationships that I have described, is it possible to construct a Cypher query that will return me the nodes of interest?

Here is the Cypher query that will return all of the functions for a given target.

MATCH (function:Function)-[:used_in]->(target:Target)
WHERE (target.Name = "target.exe")
RETURN function

Here is the Cypher query that will return the Library for a given function.

MATCH (function:Function)-[:included_in]->(library:Library)
WHERE (function.Name = "foo")
RETURN library

I'm interested to see if this can be combined into a single query. In my current scenario, I would need to run the second query for every Function. I am hopeful that my relationships are optimal enough to allow me to reduce the number of queries that I need to make.

I am using Neo4JClient - if someone helps to provide the Cypher query, I am hopeful that I can translate it to Neo4JClient. If someone goes even further and gives me the C# code, all the better, but please help me understand the Cypher behind it.

Here is the Cypher to construct the database:

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

For those interested, here is the Neo4Client code that I used based on the Cypher query that Wes provided.

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()
});
Was it helpful?

Solution

Seems like it's too easy, but is it as simple as something like this? Please provide more info if not.

MATCH (function:Function)-[:used_in]->(target:Target),
      (function:Function)-[:included_in]->(library:Library)
WHERE (target.Name = "target.exe")
RETURN function, library
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top