Returning multiple nodes from the pattern in Neo4JClient? - neo4jclient

I'm liking the NEO4J Client so far, but I can't see to formulate this Cypher query in the NEO4JClient, and I can't see any examples that are the same:
match (e:CRMEntity)-[:LINKED_BY_USER]->(e2)-[:IS_SAME_AS*]-(e3)
RETURN e2, e3
This works in Cypher, returning e2 as related from a CRMEntity through LINKED_BY_USER, as well as all e3's that I can reach from e2.
I can't quite seem to formulate that in the NEO4JClient api though. This is what I have:
var results = client.Cypher
.Match("(e:CRMEntity)-[:LINKED_BY_USER]->(e2)")
.OptionalMatch("(e2)-[:IS_SAME_AS*]-(e3)")
.Where((EntityGraphObject e) => e.Id == entity.Id)
.ReturnDistinct(e3 => new
{
GraphObject = e3.As<EntityGraphObject>(),
Type = e3.Labels()
})
.Results;
I can see that perhaps you could create a new custom object and put both e2 and e3 in that, but that seems kind of silly, given that there are multiple results for e3 for every e2.
Is that the only way or is there some syntax I'm not getting? Rather new to Cypher and NEO4JClient, so I might be missing something simple. Thanks.
UPDATE:
I think I figured it out. What I needed was a *0.. on the second relationship like so:
var inferredLinks = client.Cypher
.Match("(e:CRMEntity {Id: {EntityId}})-[:LINKED_BY_USER]->()-[:IS_SAME_AS*0..]-(e3)")
.WithParam("EntityId", entity.Id)
.ReturnDistinct(e3 => new
{
GraphObject = e3.As<EntityGraphObject>(),
Type = e3.Labels()
})
.Results;

UPDATE: I think I figured it out. What I needed was a *0.. on the second relationship like so:
var inferredLinks = client.Cypher
.Match("(e:CRMEntity {Id: {EntityId}})-[:LINKED_BY_USER]->()-[:IS_SAME_AS*0..]-(e3)")
.WithParam("EntityId", entity.Id)
.ReturnDistinct(e3 => new
{
GraphObject = e3.As<EntityGraphObject>(),
Type = e3.Labels()
})
.Results;

Related

Logical grouping of operator with Neo4jClient

I cant seem to figure out how to write this query using the Neo4jClient
MATCH (n)
WHERE (n.Id="1ef17d65-492e-4c0d-aa79-13065edd37f2" AND n.CaseType="NaturalPerson") OR (n.Id="a5c143d4-0306-4057-a96f-e39c5c50eb22" AND n.CaseType="NaturalPerson")
RETURN n
If i write the query like this
this.client.Cypher.Match("(n)")
.Where("f.Id=\"1ef17d65-492e-4c0d-aa79-13065edd37f2\"")
.AndWhere("n.CaseType=\"NaturalPerson\"")
.OrWhere("f.Id=\"1ef17d65-492e-4c0d-aa79-13065edd37f2\"")
.AndWhere("n.CaseType=\"NaturalPerson\"")
.Return((n, r) => n.As<T>());
The following query is generated, but it does not have the parentheses '(' ')' which logicly group the two and clauses
MATCH (n)
WHERE f.Id="1ef17d65-492e-4c0d-aa79-13065edd37f2"
AND n.CaseType="NaturalPerson"
OR f.Id="1ef17d65-492e-4c0d-aa79-13065edd37f2"
AND n.CaseType="NaturalPerson"
RETURN n AS Node, r AS Metadata
You either do:
.Where("n.Id='1ef17d65-492e-4c0d-aa79-13065edd37f2' AND n.CaseType='NaturalPerson')
.OrWhere("n.Id='a5c143d4-0306-4057-a96f-e39c5c50eb22' AND n.CaseType='NaturalPerson'")
or, you could put it all in one .Where
But, if it was me, I would do:
.Where("n.CaseType = 'NaturalPerson')
.AndWhere("n.Id IN ['1ef17d65-492e-4c0d-aa79-13065edd37f2','a5c143d4-0306-4057-a96f-e39c5c50eb22']
And I would definitely be using parameters like so:
var ids = new [] {'a5c143d4-0306-4057-a96f-e39c5c50eb22', '1ef17d65-492e-4c0d-aa79-13065edd37f2'};
var caseType = "NaturalPerson";
this.client.Cypher.Match("(n)")
.Where("n.CaseType = $caseTypeParam")
.AndWhere("n.Id IN $idsParam")
.WithParam("caseTypeParam", caseType)
.WithParam("idsParam", ids")
/*.. etc..*/

neo4j sum up values

I'm trying to get Total(Sum) of a property of "df" object.I have attached screen shot of sample database I'm using
I tried to get the graph using following query
MATCH P= (n:Org)-[:O_CH*0..]->()-[:LEAF*0..]->()-[:CH*0..]->()-[:FOR*0..]->() RETURN P
To create objects
create(n:Org{name:'Root',id:1})
create(n:Org{name:'L1.1',id:2,parent:1})
create(n:Org{name:'L1.2',id:3,parent:1})
create(n:Org{name:'L1.3',id:4,parent:1})
create(n:Org{name:'L2.1',id:5,parent:3})
create(n:Org{name:'L2.2',id:6,parent:4})
create(n:Op{name:'CH1',id:7,orgId:5})
create(n:Op{name:'CH2',id:8,orgId:5 ,parent:'CH1'})
create(n:Proj{name:'P1',id:9,opp:'CH2'})
create(n:Proj{name:'P2',id:10,opp:'CH1'})
create(n:R{name:'R1',id:200,orgId:2 })
create (n:df{id:100,map:8,forecast:toFloat(10)})
create (n:df{id:101,map:7,forecast:toFloat(10)})
create (n:df{id:102,map:9,forecast:toFloat(10)})
create (n:df{id:103,map:10,forecast:toFloat(10)})
create (n:df{id:104,map:200,forecast:toFloat(10)})
To Crate relationships
MATCH (c:Org),(p:Org) WHERE c.parent = p.id create (p)-[:O_CH]->(c)
MATCH (c:Op),(p:Op) WHERE c.parent = p.name create (p)-[:CH]->(c)
MATCH (c:Op),(p:Org) WHERE c.orgId = p.id create (p)-[:LEAF]->(c)
MATCH (c:Proj),(p:Op) WHERE c.opp = p.name create (p)-[:CH]->(c)
MATCH (c:R),(p:Org) WHERE c.orgId = p.id create (p)-[:LEAF]->(c)
MATCH (c:df),(p:) WHERE c.map = p.id create (p)-[:FOR]->(c)
I'm expecting 60 as total where I get 260 as total. Please let me know where I'm wrong . Need your help to figure out.
I'm trying to get Total(Sum) of a property of "df" object.
I believe you needs a simple query that match all nodes with label :df and return the sum of node.forecast. Try it:
// Match all nodes with :df label
MATCH(n:df)
// Return the sum of the property 'forecast' of each matched node
RETURN sum(n.forecast)
From comments:
Thank you Bruno.But the thing i need to get the aggregated value as a
example if i select L2.1 , i need to get the sum of df objects which
are under that node
This should work:
MATCH({name:'L2.1'})-[*]->(n)
RETURN SUM(n.forecast)

Search a collection of objects

I'm trying to search for a collection of potential nodes but unable to do it...
I have a product that has a relationship with many instances. I would like to query the DB and get all the instances that are in a list that i get from the user.
Cypher:
var query = _context
.Cypher
.Start(new
{
instance = startBitsList,
product = productNode.Reference,
})
.Match("(product)-[:HasInstanceRel]->(instance)")
.Return(instance => instance.Node<ProductInstance>());
The problem is startBitsList... I use StringBuilder to generate a query that contains all the instances I'm looking for:
private static string CreateStartBits(IEnumerable<string> instanceNames)
{
var sb = new StringBuilder();
sb.AppendFormat("node:'entity_Name_Index'(");
foreach (var id in productIds)
{
sb.AppendFormat("Name={0} OR ", id);
}
sb.Remove(sb.Length - 4, 4);
sb.Append(")");
var startBitsList = sb.ToString();
return startBitsList;
}
I get exceptions when trying to run this cypher...
Is there a better way to search for multiple items that are stored in the collection I get from the user?
OK, I think there are a couple of issues at play here, first I'm presuming you are using Neo4j 1.9 and not 2.0 - hence using the .Start.
Have you tried taking your query and running it in Neo4j? This should be your first port of call, typically it's easy to add a breakpoint on the .Results call and add a 'watch' for query.Query.DebugText.
However, I don't think you need to use the StartBits the way you are, I think you'd be better off filtering with a .Where as you already have the start point:
private static ICypherFluentQuery CreateWhereClause(ICypherFluentQuery query, ICollection<string> instanceNames)
{
query = query.Where((Instance instance) => instance.Name == instanceNames.First());
query = instanceNames.Skip(1).Aggregate(query, (current, localInstanceName) => current.OrWhere((Instance instance) => instance.Name == localInstanceName));
return query;
}
and your query becomes something like:
var prodReference = new NodeReference<Product>(2);
var query =
Client.Cypher
.ParserVersion(1, 9)
.Start(new {product = prodReference})
.Match("(product)-[:HasInstanceRel]->(instance)");
query = CreateWhereClause(query, new[] {"Inst2", "Inst1"});
var resultsQuery = query.Return(instance => instance.As<Node<Instance>>());
2 things of note
We're not using the indexes - there is no benefit to using them as you have the start point and traversing to the 'instances' is a simple process for Neo4j.
The 'CreateWhereClause' method will probably go wrong if you pass in an empty list :)
The nice thing about not using the indexes is that - because they are legacy - you are set up better for Neo4j 2.0

neo4jclient ill formed cypher request sent to server

Trying to execute the following cypher query (which executes ok from neoclipse)
START a=node(*) MATCH a-[:Knows]->p WHERE (p.Firstname! = "Steve" ) RETURN p
from neo4jclient with the following statement
protected void Populate()
{
var client = new GraphClient(new Uri("http://altdev:7474/db/data"));
client.Connect();
var query = client.Cypher
.Start(new RawCypherStartBit("all", "node(*)"))
.Match("all-[:Knows]->p")
.Where((Person p) => p.Firstname == "Steve")
.Return<Node<Person>>("Person");
var people = query.Results;
}
the client throws an exception, as follows
The query was: START all=node(*)
MATCH all-[:Knows]->p
WHERE (p.Firstname! = {p0})
RETURN Person
The response status was: 400 Bad Request
The response from Neo4j (which might include useful detail!) was: {
"message" : "Unknown identifier `Person`.",
"exception" : "SyntaxException",
"fullname" : "org.neo4j.cypher.SyntaxException",
"stacktrace" : [ "org.neo4j.cypher.internal.symbols.SymbolTable.evaluateType(SymbolTable.scala:59)", "org.neo4j.cypher.internal.commands.expressions.Identifier.evaluateType(Identifier.scala:47)", "org.neo4j.cypher.internal.commands.expressions.Expression.throwIfSymbolsMissing(Expression.scala:52)", "org.neo4j.cypher.internal.pipes.ColumnFilterPipe$$anonfun$throwIfSymbolsMissing$1.apply(ColumnFilterPipe.scala:61)", "org.neo4j.cypher.internal.pipes.ColumnFilterPipe$$anonfun$throwIfSymbolsMissing$1.apply(ColumnFilterPipe.scala:61)", "scala.collection.immutable.List.foreach(List.scala:309)", "org.neo4j.cypher.internal.pipes.ColumnFilterPipe.throwIfSymbolsMissing(ColumnFilterPipe.scala:61)", "org.neo4j.cypher.internal.pipes.PipeWithSource.<init>(Pipe.scala:63)", "org.neo4j.cypher.internal.pipes.ColumnFilterPipe.<init>(ColumnFilterPipe.scala:30)", "org.neo4j.cypher.internal.executionplan.builders.ColumnFilterBuilder.handleReturnClause(ColumnFilterBuilder.scala:60)", "org.neo4j.cypher.internal.executionplan.builders.ColumnFilterBuilder.apply(ColumnFilterBuilder.scala:38)", "org.neo4j.cypher.internal.executionplan.ExecutionPlanImpl.prepareExecutionPlan(ExecutionPlanImpl.scala:54)", "org.neo4j.cypher.internal.executionplan.ExecutionPlanImpl.<init>(ExecutionPlanImpl.scala:36)", "org.neo4j.cypher.ExecutionEngine$$anonfun$prepare$1.apply(ExecutionEngine.scala:80)", "org.neo4j.cypher.ExecutionEngine$$anonfun$prepare$1.apply(ExecutionEngine.scala:80)", "org.neo4j.cypher.internal.LRUCache.getOrElseUpdate(LRUCache.scala:37)", "org.neo4j.cypher.ExecutionEngine.prepare(ExecutionEngine.scala:80)", "org.neo4j.cypher.ExecutionEngine.execute(ExecutionEngine.scala:72)", "org.neo4j.cypher.ExecutionEngine.execute(ExecutionEngine.scala:76)", "org.neo4j.cypher.javacompat.ExecutionEngine.execute(ExecutionEngine.java:79)", "org.neo4j.server.rest.web.CypherService.cypher(CypherService.java:94)", "java.lang.reflect.Method.invoke(Method.java:616)", "org.neo4j.server.rest.security.SecurityFilter.doFilter(SecurityFilter.java:112)" ]
}
As you can see, essentially the queries are the same. And the nodes I'm trying to get are of _type "Person"
TIA
Marcelo
In your MATCH clause you use the identity p:
.Match("all-[:Knows]->p") // becomes MATCH all-[:Knows]->p
In your RETURN clause you change to using the identity Person:
.Return<Node<Person>>("Person"); // becomes RETURN person
You need to pick one and be consistent.
Integrating some other clean up too, your full query should be:
var query = client.Cypher
.Start(new { all = All.Nodes })
.Match("all-[:Knows]->p")
.Where((Person p) => p.Firstname == "Steve")
.Return<Person>("p");
It's down to the way you are creating the Cypher code, specifically what you are returning:
var query = client.Cypher
.Start(new RawCypherStartBit("all", "node(*)"))
.Match("all-[:Knows]->p")
.Where((Person p) => p.Firstname == "Steve")
.Return<Node<Person>>("p"); // <-- THIS LINE SHOULD SAY 'P' NOT 'Person'
The return statement requires the name to be the same as the node you have defined. So you use: all-[:knows]->p where you define p. Now you need to return it.
(Which is what Peter is saying in his answer :))
You are trying to return a Person node that is not assigned to anything in the query.

Returning multiple columns in Neo4jClient Cypher Query

I am using Azure and finding the performance to be slow. In order to reduce the round trip time, I am clubbing the following queries into one query.
var queryItem = _graphClient
.Cypher
.Start(new
{
n = Node.ByIndexLookup("item_idx", "SKU", sSKU1),
})
.Return<Node<Item>>("n");
somewhere else in the code I have following statements
var queryItem = _graphClient
.Cypher
.Start(new
{
m = Node.ByIndexLookup("item_idx", "SKU", sSKU2),
})
.Return<Node<Item>>("m");
I tried to combine above two queries into a single query like this
var queryItem = _graphClient
.Cypher
.Start(new
{
n = Node.ByIndexLookup("item_idx", "SKU", sSKU1),
m = Node.ByIndexLookup("item_idx", "SKU", sSKU2),
})
.Return<Node<Item>>("n");
I know above is only for single column so I tried using following return statement
.Return((n, m) => new
{
N = n.CollectAs<Node<Item>>(),
M = m.CollectAs<Node<Item>>()
});
but then I have problems with the following statement
Node<Item> item1 = itemQueryResult.First();
It says Error Cannot implicitly convert type 'AnonymousType#1' to 'Neo4jClient.Node.
Can you please suggest a simple syntax or returning multiple columns and a way to extract the first node? TIA.
I think what you're missing here is that the Return statement returns one object per Cypher row.
Your query is returning a table like this:
|-----------------|
| n | m |
|-----------------|
| Node | Node |
|------------------
That's one table, with one row, with two columns.
In this statement, you are returning an anonymous type per Cypher row:
.Return((n, m) => new
{
N = n.CollectAs<Node<Item>>(),
M = m.CollectAs<Node<Item>>()
});
The return type of that method is IEnumerable<AnonymousType>.
You're then trying to get the first row (an anonymous type) and implicitly cast that to Node<Item>, which is not valid.
You should get the row, then get the properties within it.
Some other things to note:
You don't want to use CollectAs in this scenario: that will turn each cell of your table into an array with a single value, which just adds more indirection.
.As<Node<T>>() can be written as .Node<T>()
With that in mind, here's the query you want:
var result = _graphClient
.Cypher
.Start(new
{
n = Node.ByIndexLookup("item_idx", "SKU", sSKU1),
m = Node.ByIndexLookup("item_idx", "SKU", sSKU2),
})
.Return((n, m) => new
{
N = n.Node<Item>(),
M = m.Node<Item>()
})
.Results
.Single();
var n = result.N;
var m = result.M;
Make sense?
PS: I hope you aren't actually clubbing anything. Baby seals don't like that. You are combining queries.

Resources