When i use "neo4j browser", i see the below response...
When i use Neo4jClient with .Net, i send the desired class but it serializes only the "properties" section and not all attributes. In this example i have a relationship and i want to extract also the attributes:
"startNodeElementId": "836369",
"endNodeElementId": "566040"
So, when you call via an application you only get back what you asked for. The browser does extra work behind the scenes to show you that data.
If you want it - you need to ask for it explicitly, there's a couple of ways - depending on how you want to, in both cases I'll be using this class as an output:
public class ActedIn
{
[JsonProperty("role")]
public string Role {get;set;}
public long Id {get;set;}
public long StartId {get;set;}
public long EndId { get;set;}
public string Type {get; set;}
}
Option 1 - Do the work in Return:
var query = client.Cypher
.Match("(:Movie {title:'Jumanji'})<-[r:ACTED_IN]-(p:Person)")
.Return(r => new ActedIn
{
Id = r.Id(),
StartId = Return.As<long>("id(startNode(r))"),
EndId = Return.As<long>("id(endNode(r))"),
Type = Return.As<string>("type(r)"),
Role = r.As<ActedIn>().Role
});
(await query.ResultsAsync).Dump("Using Return");
Option 2 - Use a WITH to structure it how you want first:
Hi, Thanks for your reply but...
My query is a little more complicated.
I want a person with a collection of related nodes, so my query is: (working but without the "internal" attributes)
var query = graphClient.Cypher
.Match("(p:Person)-[r]->(a:Auto)")
.With("p, collect(a) as auto, collect(r) as rel")
.OrderByDescending("size(auto)")
.Return((p, auto, rel) => new
{
person = p.As(),
autos = auto.As<IEnumerable>(),
rel = rel.As<IEnumerable>()
})
.Limit(limit);
Okey dokes, the principal is exactly the same as above, in your .With you still do the mapping:
var query = client.Cypher
.Match("(p:Person)-[r]->(movie:Movie)")
.With("p, collect({Movie: movie, Id: id(movie)}) AS movies, collect({EndId: id(endNode(r))}) AS rel")
.OrderByDescending("size(movies)")
.Return((p, movies, rel) => new
{
person = p.As<string>(),
movies = movies.As<IEnumerable<MovieOut>>(),
rel = rel.As<IEnumerable<string>>()
})
.Limit(limit);
(await query.ResultsAsync).Dump("Actual");
In this case, I've also added these two C# POCOs for output, obvs - you'd use your own:
public class MovieOut
{
public Movie Movie {get; set;}
public long Id {get; set;}
}
public class Movie
{
[JsonProperty("title")]
public string Title {get; set;}
}
You have a variable length pattern. I believe it is a deprecated feature now, but the variable ‘r’ may be a list of relations to represent the relationships along the path. The collect is across rows of results. You are trying to collect the endNode of ‘r’. The endNode() method expects a single relationship. In your case ‘r’ is a list.
What information do you need? Do you want the endNode of each relationship of this type along the path, or do you want just the endNode of the last relationship along the path?
i need the 'current node' (n) and it's information and a list of all ids (of the nodes connecting). So the equivalent of endNode for each entry on that list.
I have done that if I have 1 node as a reference, but this one is traversing.
It worries me that my payload is unnecessarily large (I only need endNodeElementId)
For reference - my user can come at any assigned node on a graph.
He can only see the ancestors (which could be a single node list .. or a graph) and the graph for all the descendants.
I split the query into 2 (ancestors/descendants) and do a union (good stuff, no problem).
Since I have all the information on each node from the query, I only need to know the list of elementId's to which that node is connected. Looking at the payload of information I need, it would turn that the relationships JSON is as big (or bigger) than the actual node's information (inefficient for thousands of nodes), plus involves extra unnecessary processing of the relationships' envelope (waste of CPU).
The are multiple ways to accomplish this, but let’s pick the one closer to what you were doing. Let’s say your match pattern is as follows, where ‘n’ is your current node and you want the elementIds of the nodes along the path.
(n)<-[r:partOf*0..]-(m)
Let’s say ‘n’ is attached to a chain of 5 nodes. Your match will return 6 rows back, one for ‘n’ (since your lower bound is zero), and a row for each segment, up to the max length of 5 nodes. In each case, ‘m’ will always represent the last node of the chain for each segment. This means the collection of nodes ‘m’ will be equivalent to all the nodes attached to ‘n’ along the path of max length. This sounds like what you want.
The following should work:
match (n{id:100})
match (n)<-[:partOf*0..]-(m)
return n, collect(elementId(m))
If there is a graph off of ‘n’ and there could common nodes along some oth these paths (like in the bottom part of your diagram), you can eliminate collecting duplicate elementIds by including ‘distinct’.
match (n{id:100})
match (n)<-[:partOf*0..]-(m)
return n, collect(distinct elementId(m))
You may want to review the apoc path methods if you are querying for subgraphs from a root node.
Just a node, because of your min length of zero, I believe the collection will contain ‘n’ as well. Are you using zero because there may not be any connected nodes and you want to guarantee a result?
n-3, null
n-2, [parent list]
n-1, [parent list]
n [parent list] <--- this is my starting point
n+1, [parent list]
I already have a query that gives me what I want, except that the relationships list has a lot of attributes I don't need - I only need endNodeElementId
MATCH (p)<-[r:partOf*0..]-(a:NodeType {uuid:'ae66e077-f7ea-410d-9112-5160c396dfa0'})
WITH p, elementId(p) AS uuid, collect(r) AS rel
RETURN uuid,
rel
UNION
MATCH (a:NodeType {uuid:'ae66e077-f7ea-410d-9112-5160c396dfa0'})<-[r:partOf*0..]-(p:NodeType)
WITH p, elementId(p) AS uuid, collect(r) AS rel
RETURN uuid,
rel
Ok, if ‘r’ is a list, you can extract the elementIds (and an other info you want) using list comprehension.
MATCH (p)<-[r:partOf*0..]-(a:NodeType {uuid:'ae66e077-f7ea-410d-9112-5160c396dfa0'})
WITH p, elementId(p) AS uuid, [i in r | elementId(i)] AS rel
RETURN uuid,
rel
UNION
MATCH (a:NodeType {uuid:'ae66e077-f7ea-410d-9112-5160c396dfa0'})<-[r:partOf*0..]-(p:NodeType)
WITH p, elementId(p) AS uuid, [i in r | elementId(i)] AS rel
RETURN uuid,
rel
Binding a variable to a variable length path and using it as you are is a deprecated feature. Here is a solution that doesn’t rely on ‘r’.
MATCH path=(p)<-[:partOf*0..]-(a:NodeType {uuid:'ae66e077-f7ea-410d-9112-5160c396dfa0'})
WITH p, elementId(p) AS uuid, [i in relationships(path) where type(i)=“partOf”| elementId(i)] AS rel
RETURN uuid,
rel
UNION
MATCH path=(a:NodeType {uuid:'ae66e077-f7ea-410d-9112-5160c396dfa0'})<-[:partOf*0..]-(p:NodeType)
WITH p, elementId(p) AS uuid, [i in relationships(path) where type(i)=“partOf”| elementId(i)] AS rel
RETURN uuid,
rel
You don’t need the “where” condition checking for partOf in your case, since you only have one relationship type in your path pattern. I put it in to demonstrate, so you can leverage at a later date when needed. You can remove it and use this instead.
I read the list comprehension documentation, but the examples appeared that it would work only on the node side (I know relationships are nodes), I just couldn't articulate the statement
Just to clarify, nodes are nodes and relationships are relationships. They are not the same, but they are both entities. All entities have an elementId (which used to be id) and properties. Relationships also have a start node and end node.
You are not being annoying: we are here to help. Also, helping with these is like doing daily puzzles. I prefer this to sudoku.
If you want the node elementIds, you can get them from the path.
MATCH path=(p)<-[:partOf*0..]-(a:NodeType {uuid:'ae66e077-f7ea-410d-9112-5160c396dfa0'})
WITH p, elementId(p) AS uuid, [i in tail(nodes(path))| elementId(i)] AS nodeIds
RETURN uuid,
nodeIds
UNION
MATCH path=(a:NodeType {uuid:'ae66e077-f7ea-410d-9112-5160c396dfa0'})<-[:partOf*0..]-(p:NodeType)
WITH p, elementId(p) AS uuid, [i in tail(nodes(path))| elementId(i)] AS nodeIds
RETURN uuid,
nodeIds
Tail returns a new list with the first element removed.
Let me do some testing to see one of the node(path) has to be reversed to avoid including ‘p’ and keeping the order correct. If would just be a matter of replacing nodes(path) with reverse(nodes(path)).
match path=(p:Person)<-[:PartOf*0..]-(a:Person{name:"Alice"})
return 1 as query, p.name, [i in nodes(path) | i.name] as pathNames
union
match path=(a:Person{name:"Alice"})<-[:PartOf*0..]-(p:Person)
return 2 as query, p.name, [i in nodes(path) | i.name] as pathNames
I used names here instead of elementId so I could identify the nodes. Is this what you want? For each node along both paths you want that node and its nodes back to the specified node? Note, each row is a subset of the longest row. How about just getting the longest chain instead of all the intermediate ones?
In case you don't want the 'p' node in the list of nodes, you can use list indexing to filter them out:
match path=(p:Person)<-[:PartOf*0..]-(a:Person{name:"Alice"})
return 1 as query, p.name, [i in nodes(path)[1..] | i.name] as pathNames
union
match path=(a:Person{name:"Alice"})<-[:PartOf*0..]-(p:Person)
return 2 as query, p.name, [i in nodes(path)[..-1] | i.name] as pathNames