I have currently visualised a graph between myself and a number of other people.
My Current query is:
MATCH (p)-[:emailed]->(m)
WITH p,count(m) as rels, collect(m) as Contact
WHERE rels > 2
RETURN p,Contact, rels
It creates a pretty complex graph as per image below:
Messy Graph
You can manually remove them by directly clicking on them as per below:
Manually hide node from visualisation
Which then results in a very different looking graph.
Q. How do I change my query to automatically show the graph visualisation without showing the nodes that I wish to remove? (i.e by editing the query, so I dont have to manually remove each one)
By doing either
A) Adding a list of the specific Node ID's in the query to ignore, OR
B) (Ideally) Exclude all nodes that meet a criteria against the node Property
In this case: Ignore [Slug: "myname" ] where includes 'myname'
MATCH (p)-[:emailed]->(m)
WITH p,count(m) as rels, collect(m) as Contact
WHERE rels > 2 AND NOT WHERE p.slug Contains 'Mahdi'
RETURN p,Contact, rels
Thanks for any help!
I would change it slightly. If you collect the actual :emailed relationships rather than just counting the node they are connect to you can use them in your result set. Then if you turn off autocomplete as JeromeB suggests above then you will actually see some relationship. If you turn off autocomplete in your current query there will only be nodes and no relationships which I don't think you are after (unless of course you are).
You could also check to make sure that the p.slug attribute exists when testing for CONTAINS otherwise if the attribute does not exist you will not generate any results for that row.
MATCH (p:User)-[r:emailed]->(m:User)
WITH p, COLLECT(r) as rels, COLLECT(m) as contact
WHERE (NOT p.slug CONTAINS 'Mahdi' OR NOT EXISTS(p.slug))
AND size(rels) > 2
RETURN p, contact, rels
I would also add a label to the nodes in the match and an index on the slug property.
The autocomplete is 'Connect result nodes' in the Gear tab.
Related
I have Profile node which may be linked to Location node via :LOCATED_IN relationship.
In turn, Location represents unlimited composite structure of nested Location nodes linked with :CONTAINS relationships between them.
I need to find all Profiles which linked to specified location IDs or their parent locations on unlimited levels.
I'm trying to create such Cypher query but it doesn't work as expected. This is what I have so far:
MATCH (d:Profile)-[:LOCATED_IN]-(l:Location)
OPTIONAL MATCH (pl:Location)-[:CONTAINS*]->(l)
WHERE any(x IN l.id WHERE x IN [100,34]) OR any(x IN pl.id WHERE x IN [100,34])
RETURN d;
What am I doing wrong and how to fix it?
I guess this could work
WITH [100,34] AS locationIDs
// get paths, starting at root, ending at the nodes I need
MATCH locationPath=(root:Location)-[:CONTAINS*0..]->(descendant:Location)
WHERE NOT EXISTS (()-[:CONTAINS]->(root))
AND id(descendant) IN (locationIDs+[id(root)])
// create a collection of unique nodes on the locationPaths
UNWIND nodes(locationPath) AS pathNode
WITH COLLECT(DISTINCT pathNode) AS pathNodes
// find profiles linked to at least one of the pathNodes
MATCH (d:Profile)-[:LOCATED_IN]->(pathNode)
WHERE pathNode IN pathNodes
RETURN d
If I understand you correctly, you should start from the Location node and work your way up to the Profile node.
MATCH (l:Location)-[:CONTAINS*0..]->()-[:LOCATED_IN]-(p:Profile)
WHERE l.id IN [100,34]
RETURN distinct p
So you identify your start Location by id.
Then use the CONTAINS*0.., which means that it should traverse the CONTAINS relationship from zero hops (meaning that it can skip it) to theoretically infinite hops and look if any of those nodes have a profile attached to it.
I'm not sure if parent location is a directed CONTAINS relationship from child to parent, but judging from your initial query it is. Other than that, I think it should work.
I have different nodes that share one same property field, i need to merge these nodes into one and in the same time copy all the rest of the other properties in the merge node.
example:
(n1,g,p1) (n2,g,p2) (n3,g,p3) =>(n,g,p1,p2,p3)
Important to Note that i don't need the apoc solutions since user defined functions dosen't work in CAPS that i m working at
update :
geohash is the field that have a repeated values, so i want to merge the nodes by this field .
The CAPS team gave me this cypher query to have distinct geohash nodes from the intial graph:
CATALOG CREATE GRAPH temp {
FROM GRAPH session.inputGraph
MATCH (n)
WITH DISTINCT n.geohash AS geohash
CONSTRUCT
CREATE (:HashNode {geohash: geohash})
RETURN GRAPH
}
, however it still missing is the collect of the rest of the properties on the merged nodes.
I haven't a problem for the relationship ,cause we can copy them later from the intial graph:
FROM GRAPH inputGraph
MATCH (from)-[via]->(to)
FROM GRAPH temp
MATCH (n), (m)
WHERE from.geohash = n. AND AND to.geohash = m.geohash
CONSTRUCT
CREATE (n)-[COPY OF via]->(m)
RETURN GRAPH
It's not 100% possible in pure cypher, that's why there is an APOC procedure for that.
To merge two nodes , you have to :
create the merge node with all the properties
to create all the relationship of the nodes on the merge one
For the first part it's possible in cypher. Example :
MATCH (n) WHERE id(n) IN [106, 68]
WITH collect(n) AS nodes
CREATE (new:MyNode)
with nodes, new
UNWIND nodes as node
SET new += properties(node)
RETURN new
But for the second part, you need to be able to create relationship with a dynamic type and dynamic direction, and this is not allowed in cypher ...
I am working with bill of materials (BOM) and part data in a Neo4J database.
There are 3 types of nodes in my graph:
(ItemUsageInstance) these are the elements of the bill of materials tree
(Item) one exists for each unique item on the BOM tree
(Material)
The relationships are:
(ItemUsageInstance)-[CHILD_OF]->(ItemUsageInstance)
(ItemUsageInstance)-[INSTANCE_OF]->(Item)
(Item)-[MADE_FROM]->(Material)
The schema is pictured below:
Here is a simplified picture of the data. (Diagram with nodes repositioned to enhance visibility):
What I would like to do is find subtrees of adjacent ItemUsageInstances whose Itemss are all made from the same Materials
The query I have so far is:
MATCH (m:Material)
WITH m AS m
MATCH (m)<-[:MADE_FROM]-(i1:Item)<-[]-(iui1:ItemUsageInstance)-[:CHILD_OF]->(iui2:ItemUsageInstance)-[]->(i2:Item)-[:MADE_FROM]->(m) RETURN iui1, i1, iui2, i2, m
However, this only returns one such subtree, the adjacent nodes in the middle of the graph that have a common Material of "M0002". Also, the rows of the results are separate entries, one for each parent-child pair in the subtree:
╒══════════════════════════╤══════════════════════╤══════════════════════════╤══════════════════════╤═══════════════════════╕
│"iui1" │"i1" │"iui2" │"i2" │"m" │
╞══════════════════════════╪══════════════════════╪══════════════════════════╪══════════════════════╪═══════════════════════╡
│{"instance_id":"inst5002"}│{"part_number":"p003"}│{"instance_id":"inst7003"}│{"part_number":"p004"}│{"material_id":"M0002"}│
├──────────────────────────┼──────────────────────┼──────────────────────────┼──────────────────────┼───────────────────────┤
│{"instance_id":"inst7002"}│{"part_number":"p003"}│{"instance_id":"inst7003"}│{"part_number":"p004"}│{"material_id":"M0002"}│
├──────────────────────────┼──────────────────────┼──────────────────────────┼──────────────────────┼───────────────────────┤
│{"instance_id":"inst7001"}│{"part_number":"p002"}│{"instance_id":"inst7002"}│{"part_number":"p003"}│{"material_id":"M0002"}│
└──────────────────────────┴──────────────────────┴──────────────────────────┴──────────────────────┴───────────────────────┘
I was expecting a second subtree, which happens to also be a linked list, to be included. This second subtree consists of ItemUsageInstances inst7006, inst7007, inst7008 at the far right of the graph. For what it's worth, not only are these adjacent instances made from the same Material, they are all instances of the same Item.
I confirmed that every ItemUsageInstance node has an [INSTANCE_OF] relationship to an Item node:
MATCH (iui:ItemUsageInstance) WHERE NOT (iui)-[:INSTANCE_OF]->(:Item) RETURN iui
(returns 0 records).
Also confirmed that every Item node has a [MADE_FROM] relationship to a Material node:
MATCH (i:Item) WHERE NOT (i)-[:MADE_FROM]->(:Material) RETURN i
(returns 0 records).
Confirmed that inst7008 is the only ItemUsageInstance without an outgoing [CHILD_OF] relationship.
MATCH (iui:ItemUsageInstance) WHERE NOT (iui)-[:CHILD_OF]->(:ItemUsageInstance) RETURN iui
(returns 1 record: {"instance_id":"inst7008"})
inst5000 and inst7001 are the only ItemUsageInstances without an incoming [CHILD_OF] relationship
MATCH (iui:ItemUsageInstance) WHERE NOT (iui)<-[:CHILD_OF]-(:ItemUsageInstance) RETURN iui
(returns 2 records: {"instance_id":"inst7001"} and {"instance_id":"inst5000"})
I'd like to collect/aggregate the results so that each row is a subtree. I saw this example of how to collect() and got the array method to work. But it still has duplicate ItemUsageInstances in it. (The "map of items" discussed there failed completely...)
Any insights as to why my query is only finding one subtree of adjacent item usage instances with the same material?
What is the best way to aggregate the results by subtree?
Finding the roots is easy. MATCH (root:ItemUsageInstance) WHERE NOT ()-[:CHILD_OF]->(root)
And for the children, you can include the root by specifying a min distance of 0 (default is 1).
MATCH p=(root)-[:CHILD_OF*0..25]->(ins), (m:Material)<-[:MADE_FROM]-(:Item)<-[:INSTANCE_OF]-(ins)
And then assuming only one item-material per instance, aggregate everything based on material (You can't aggregate in an aggregate, so use WITH to get the depth before collecting the depth with the node)
WITH ins, SIZE(NODES(p)) as depth, m RETURN COLLECT({node:ins, depth:depth}) as instances, m as material
So, all together
MATCH (root:ItemUsageInstance),
p=(root)<-[:CHILD_OF*0..25]-(ins),
(m:Material)<-[:MADE_FROM]-(:Item)<-[:INSTANCE_OF]-(ins)
WHERE NOT ()<-[:CHILD_OF]-(root)
AND NOT (m:Material)<-[:MADE_FROM]-(:Item)<-[:INSTANCE_OF]-()<-[:CHILD_OF]-(ins)
MATCH p2=(ins)<-[:CHILD_OF*1..25]-(cins)
WHERE ALL(n in NODES(p2) WHERE (m)<-[:MADE_FROM]-(:Item)<-[:INSTANCE_OF]-(n))
WITH ins, cins, SIZE(NODES(p2)) as depth, m ORDER BY depth ASC
RETURN ins as collection_head, ins+COLLECT(cins) as instances, m as material
In your pattern, you don't account for situations like the link between inst_5001 and inst_7001. Inst_5001 doesn't have any links to any part usages, but your match pattern requires that both usages have such a link. I think this is where you're going off track. The inst_5002 tree you're finding because it happens to have a link to an usage as your pattern requires.
In terms of "aggregating by subtree", I would return the ID of the root of the tree (e.g. id(iui1) and then count(*) the rest, to show how many subtrees a given root participates in.
Here is my heavily edited query:
MATCH path = (cinst:ItemUsageInstance)-[:CHILD_OF*1..]->(pinst:ItemUsageInstance), (m:Material)<-[:MADE_FROM]-(:Item)<-[:INSTANCE_OF]-(pinst)
WHERE ID(cinst) <> ID(pinst) AND ALL (x in nodes(path) WHERE ((x)-[:INSTANCE_OF]->(:Item)-[:MADE_FROM]->(m)))
WITH nodes(path) as insts, m
UNWIND insts AS instance
WITH DISTINCT instance, m
RETURN collect(instance), m
It returns what I was expecting:
╒═════════════════════════════════════════════════════════════════════════════════════════════════════════════╤═══════════════════════╕
│"collect(instance)" │"m" │
╞═════════════════════════════════════════════════════════════════════════════════════════════════════════════╪═══════════════════════╡
│[{"instance_id":"inst7002"},{"instance_id":"inst7003"},{"instance_id":"inst7001"},{"instance_id":"inst5002"}]│{"material_id":"M0002"}│
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────┼───────────────────────┤
│[{"instance_id":"inst7007"},{"instance_id":"inst7008"},{"instance_id":"inst7006"}] │{"material_id":"M0001"}│
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────┴───────────────────────┘
The one limitation is that it does not distinguish the root of the subtree from the children. Ideally the list of {"instance_id"} would be sorted by depth in the tree.
According to this post I tried to map all related entities in a list.
I used the same query into the post with a condition to return a list of User but it returns duplicate object
MATCH (user:User) WHERE <complex conditions>
WITH user, calculatedValue
MATCH p=(user)-[r*0..1]-() RETURN user, calculatedValue, nodes(p), rels(p)
Is it a bug? I'm using SDN 4.2.4.RELEASE with neo4j 3.2.1
Not a bug.
Keep in mind a MATCH in Neo4j will find all occurrences of a given pattern. Let's look at your last MATCH:
MATCH p=(user)-[r*0..1]-()
Because you have a variable match of *0..1, this will always return at least one row with just the user itself (with rels(p) empty and nodes(p) containing only the user), and then you'll get a row for every connected node (user will always be present on that row, and in the nodes(p) collection, along with the other connected node).
In the end, when you have a single user node and n directly connected nodes, you will get n + 1 rows. You can run the query in the Neo4j browser, looking at the table results, to confirm.
A better match might be something like:
...
OPTIONAL MATCH (user)-[r]-(b)
RETURN user, calculatedValue, collect(r) as rels, collect(b) as connectedNodes
Because we aggregate on all relationships and connected nodes (rather than just the relationships and nodes for each path), you'll get a single row result per user node.
I have the following query in Neo4J Cypher 2.0:
MATCH (u:User{uid:'1111111'}), (c1:Concept), (c2:Concept),
c1-[:BY]->u, c2-[:BY]->u, c1-[rel:TO]->c2
WITH c1,c2,rel
MATCH c1-[:AT]->ctx, c2-[:AT]-ctx
WHERE ctx.uid = rel.context
RETURN c1.uid AS source_id, c1.name AS source_name,
c2.uid AS target_id, c2.name AS target_name,
rel.uid AS edge_id,
rel.context AS context_id, ctx.name AS context_name;
What it does is that it looks for all the nodes of the Concept label (c1 and c2) connected to User node u, finds their (c1 to c2) connections to one another (rel), then it tries to find which different contexts (ctx) those concept nodes (c1 and c2) appear in, but only those, whose uid matches the uid of the .context property of the relationships rel (rel.context) and then returns them in a table, where we have the source id and name, the target id and name, the connection id, as well as the .context id property of that relation and the name of the context with that id.
So all works fine, but the question is: WHY?
I mean how does Cypher matches so neatly the right ctx.uid to the right rel.context to know that it should be inserted exactly at the right place of the results table?
Can somebody explain me the magic behind this?
Or am I completely wrong and just getting messy results?
Thank you!
It creates a pattern-graph that represents your combined match patterns. And then it uses indexes to find bound nodes that it starts to apply the pattern graph to and returns a result row for every match found.
While applying the pattern graph it uses your WHERE conditions to filter out paths that you don't want eagerly as early as possible.
If it can't find bound nodes it has to go over all nodes of a label (like :Concept) or over all nodes of the graph (if you haven't specify any label or lookup condition).