I want to make function that will let me filter tree but it dont return all elements matching predicate.
type Tree =
| Empty
| Element of float * Tree * Tree
let rec filter predicate =
function
| Empty -> Empty
| Element(a,b,c) when predicate(a)
-> Element(a, filter predicate b, filter predicate c)
| Element(_,b,c) -> filter predicate b
filter predicate c
Theres three cases you have to consider when deleting a node from a binary tree.
when the deleted node has no children: This one is easy, we just skip it when constructing the result.
when deleted node has exactly one child: This one involves placing the child in the place of the deleted node.
when deleted node has two or more children: Here we need to consider if your tree has any special ordering such as the case of a binary search tree. If theres no considerations needed, just bring up any one of the children to the place of the deleted node.
Heres a similar answer for Haskell
delete node from binary search tree, haskell
Related
I run a Cypher query and update labels of the nodes matching a certain criteria. I also want to update nodes that do not pass that criteria in the same query, before I update the matched ones. Is there a construct in Cypher that can help me achieve this?
Here is a concrete formulation. I have a pool of labels from which I choose and assign to nodes. When I run a certain query, I assign one of those labels, l, to the nodes returned under the conditions specified by WHERE clause in the query. However, l could have been assigned to other nodes previously, and I want to rid all those nodes of l which are not the result of this query.
The conditions in WHERE clause could be arbitrary; hence simple negation would probably not work. An example code is as follows:
MATCH (v)
WHERE <some set of conditions>
// here I want to remove 'l' from the nodes
// not satisfied by the above condition
SET v:l
I have solved this problem by using a temporary label through this process:
Assign x to v.
Remove l from all nodes.
Assign l to all nodes containing x.
Removing x from all nodes.
Is there a better way to achieve this in Cypher?
This seems like one reasonable solution:
MATCH (v)
WITH REDUCE(s = {a:[], d:[]}, x IN COLLECT(v) |
CASE
WHEN <some set of conditions> AND NOT('l' IN LABELS(x)) THEN {a: s.a+x, d: s.d}
WHEN 'l' IN LABELS(x) THEN {a: s.a, d: s.d+x}
END) AS actions
FOREACH (a IN actions.a | SET a:l)
FOREACH (d IN actions.d | REMOVE d:l)
The above query tests every node, and remembers in the actions.a list the nodes that need the l label but do not yet have it, and in the actions.d list the nodes that have the label but should not. Then it performs the appropriate action for each list, without updating any nodes that are already OK.
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.
For directed graphs G = (V,E). That representation maintains an array A[...] indexed by V , in which A[v] is a linked list. The linked list holds the names of all the nodes u to which v points, i.e., nodes u for which (v, u) ∈ E. (Technically, A[v] contains a pointer to the first item in the linked list).This is the default adjacency list format and can be thought of as an out- adjacency list representation
An in-adjacency list representation would be one in which A[v] is the list of nodes that point to v.
Can anyone help to give me a pseudocode for an O(|V | + |E|) algorithm that transforms the out-adjacency list representation into an in-adjacency list repre- sentation. And please explain why your algorithm is correct and why it runs in O(|V | + |E|) time.
You could do that in O(V + E) but for that you have to modify the insert operation in linked list to be done in constant time O(1).
That could be easily done, by keeping a separate pointer last, where last points to the last element inserted in a linked list. With help of last, insert operation in a linked list can be done in O(1),as opposed to usual O(N).
Now coming to the problem, lets say our new adjacency list is adj_new. We start by traversing our original adjacency list starting from the first linked list.
For each element x of linked list A[0], we do insert operation:
insert 0 in linked list adj_new[x]
We do the above for each of the linked lists. Since traversing the entire adjacency list takes time
O(V + E), and every insert operation takes O(1) time, the total time taken is O(V + E)
Following is the pseudocode :
For each linked list A[i]
{
for each element x of A[i]
{
append i to linked list adj_new[x]
}
}
adj_new[] is the in-adjacency list. If you look carefully its nothing but reversing the direction of each edge of your directed graph.
Given the following schema / data set:
(a:A1)-[ONE]->(b:B1)-[TWO]->(c:C1)
(a:A1)-[ONE]->(b:B1)-[TWO]->(c:C2)
(a:A1)-[ONE]->(b:B2)-[TWO]->(c:C3)
(a:A2)-[ONE]->(b:B3)
(a:A2)-[ONE]->(b:B4)-[TWO]->(c:C4)
(a:A2)
I'm trying to assemble a query for some a properties, a list of b properties (list of strings), and finally a list of c property lists (list of list of strings). I'm pretty close using collect() but running into an issue keeping track of which c's belong to which b's.
The query I seek would produce a single row per a (2 rows for given data set), notice that the data can be sparse, thus an empty array in the results indicating hierarchy:
"A1", ["B1", "B2"], [["C1","C2"],["C3"]]
"A2", ["B3", "B4"], [[],["C4"]]
When you aggregate using COLLECT [or any other aggregation], the other, uncollected [unaggregated] values in the row serve as the aggregation key, so only rows that share all the other values will match. For your query, you basically need to stack COLLECTs in two separate steps like so, to first get lists of c keyed by a and b, and then collect all of the bs and all of the lists of cs keyed by a, like so:
MATCH (a) - [:ONE] -> (b)
OPTIONAL MATCH (b) - [:TWO] -> (c)
WITH a, b, COLLECT(c.property) AS cs
WITH a, COLLECT(b.property) AS bs, COLLECT(cs) AS cs_per_b
RETURN a.property, bs, cs_per_b
You can replace property with whatever property you want to get from the node, and if it's not a node property, but label or other value, just replace the whole expression inside COLLECT( ). You'll also get empty lists inside cs_per_b this way if there are no cs, as desired.
Although your question states you want to list node "properties", your sample results list node labels instead.
To display the node labels, the following query should work:
MATCH (a)-[:ONE]->(b)
OPTIONAL MATCH (b)-[:TWO]->(c)
WITH a, b, COLLECT(DISTINCT LABELS(c)[0]) AS lcs
RETURN LABELS(a)[0] AS la, COLLECT(LABELS(b)[0]) AS lb, COLLECT(lcs) AS lc;
The query assumes that it is sufficient to use the ONE and TWO relationship types to distinguish between the Ax, Bx and Cx node labels, and that those nodes only have a single label. It uses an OPTIONAL MATCH for the TWO relationship since your sample results imply that it is optional.
When I initially created my data set I thought a star topology would be nice, but now that I've gotten some sample data, I'd rather be using a linked list topology. Now I want to do an in-place conversion using Cypher only, and in one call. Is this possible?
To use a simplified example: a Blog node can have several children Post nodes, so I attached them directly like this:
CREATE (a:Blog), (a)-[:HAS]->(b:Post), (a)-[:HAS]->(c:Post)
RETURN a,b,c
Now as my design moves along, I think I'd rather have this as a linked list, something like this (assuming the :FIRST post is already created):
MATCH (a:Blog)-[r:FIRST]->(b:Post)
CREATE (a)-[:FIRST]->(c:Post)-[:PREV]->(b)
DELETE r
RETURN a,b,c
So I can code the linked list fine. What I need to do is to convert the old star topologies to the new linked list format. I tried a bunch of things but nothing worked, so here is my best guess, but is completely non-functional code:
MATCH (b:Blog)-[:HAS]-(p:Post)
WITH b, collect(p) as posts
CREATE b-[:FIRST]->posts[0]
FOREACH( i IN range(1,len(posts)-1) |
CREATE posts[i]-[:PREV]->posts[i-1]
)
RETURN b,p
Indexing collections doesn't seem to work in a CREATE statement (neither inside or outside the FOREACH).
Any way to achieve this translation?
EDIT
I thought an additional way might be to tag the posts with ordinal values and do some iteration over sequential ordinals, but I get a syntax error trying to add the ordinals:
MATCH (b:Blog)-[:HAS]-(p:Post)
WITH b, collect(p) as posts
FOREACH( i in range(0,length(posts)) |
SET posts[i].ordinal = i
}
The syntax error is:
Invalid input '[': expected an identifier character, node labels, a property map, a relationship pattern, '(', '.' or '=' (line 4, column 14)
" SET posts[i].ordinal = i"
^
EDIT/2
Another idea I had was to put the linked-list code inside the FOREACH, but this fails because MATCH cannot be used inside FOREACH. First I created and attached a dummy (b:Blog)-[:FIRST]->(p:Post) relationship, then wrote this query (which doesn't work):
MATCH (f)<-[:FIRST]-(b:Blog)-[:HAS]->(p:Post)
WITH b, collect(p) as posts
FOREACH( x IN posts |
MATCH (b)-[r:FIRST]->(f)
DELETE r
CREATE (b)-[r2:FIRST]->(x), (x)-[:PREV]->(f)
)
RETURN b-[*0..1]-()
As a crutch until you find a better workarund you could try nesting FOREACH (a IN [posts[i]] | FOREACH (b IN [posts[i-1]] | CREATE a-[:PREV]->b)) inside your original foreach loop, as a way to unpack the index slices. For edit 1 try SET (posts[i]).ordinal = i. I think I have a nicer workaround somewhere but can't remember at the moment, if I find it I'll put it up.
Try this Cypher Query
After Edit
MATCH (a:Blog)-[r:HAS]->(p:Post)
WITH a,r,collect(p) as posts,range(1,length(posts)) as rel
MERGE(firstPost:Post {created: head(posts).created , otherprop: head(posts).otherprop})
CREATE a-[:FIRST]->(firstPost)
FOREACH(i IN rel |
MERGE(thisPost:Post {created: (posts[i]).created , otherprop: (posts[i]).otherprop})
MERGE(prevPost:Post {created: (posts[i-1]).created , otherprop: (posts[i-1]).otherprop})
CREATE (prevPost)-[:NEXT]->(thisPost))
WITH a
MATCH a-[r:HAS]->() delete r
This will create something like (Blog)-[FIRST]->(Post)-[NEXT]->(Post)...
By default Cypher sorts the element in COLLECT(p) in natural ascending order. Hence if you have only created property it will sort in the most recent one at last, hence the last node in the next chain starting from BLOG will be your latest POST associated to that BLOG