Not able to manipulate paths collection in neo4j cypher - collections

Still struggling to get the query right. Following graph is mostly unidirectional and represents who likes whom. But people with "Friends" relationship are particular about with whom they are ultimate friends with.
create (jacob {short:"Jacob",level:"start"})
,(leena{short:"Leena"})
,(bob{short:"Bob",level:"end"})
,(brad{short:"Brad",level:"end"})
,(jacob)-[:LIKES]->(leena)
,(leena)-[:LIKES]->(bob)
,(leena)-[:LIKES]->(brad)
,(jacob)-[:FRIENDS]->(brad)
,(kyle{short:"Kyle",level:"start"})
,(rick{short:"Rick"})
,(kyle)-[:LIKES]->(leena)
,(kyle)-[:LIKES]->(rick)
,(rick)-[:LIKES]->(brad);
All source to end 'likes' are given by
match p=({level:'start'})-[:LIKES*1..6]->({level:'end'})
return distinct extract(m in nodes(p) | m.short);
[Jacob, Leena, Bob]
[Jacob, Leena, Brad]
[Kyle, Rick, Brad]
[Kyle, Leena, Bob]
[Kyle, Leena, Brad]
But for Brad the LIKES should come from his FRIENDS. In the graph, for Brad, only Jacob is his FRIEND. So ultimate likes through Kyle should be excluded from the output.
The logic I'm trying to implement is to get unnecessary paths and exclude them from above output.
unnecessary paths given by following query. There where clause is simple because the FRIENDship is always found between 2 and then its easy to collect first and last nodes in collection.
match q=()-[:FRIENDS*1]->()
with collect(nodes(q)[0]) as y,collect(nodes(q)[1]) as z
match p=({level:'start'})-[:LIKES*1..6]->({level:'end'})
where any(x in nodes(p) where x in z) and not any(x in nodes(p) where x in y)
return distinct extract(m in nodes(p) | m.short);
[Kyle, Rick, Brad]
[Kyle, Leena, Brad]
Then the exclusion
match q=()-[:FRIENDS*1]->()
with collect(nodes(q)[0]) as y,collect(nodes(q)[1]) as z
match p=({level:'start'})-[:LIKES*1..6]->({level:'end'})
where any(x in nodes(p) where x in z) and not any(x in nodes(p) where x in y)
match r=({level:'start'})-[:LIKES*1..6]->({level:'end'})
where not all(node2 in nodes(p) where node2 in nodes(r))
return distinct extract(m in nodes(r) | m.short);
Still I get all paths including unnecessary ones. In cypher can complete collection be compared? In above query not all(.. is not working.
To make the graph generic, the start and end nodes can exist inbetween ie there can be nodes beyond end nodes and before start.

If the first query returns the complete set of LIKES paths, and the second query returns the set of LIKES paths to be excluded, does this query give you the next result you are looking for?
It collects each set in a set to keep and an irrelevant set then returns only the sets in the keep set that are not in the irrelevant set.
match p=({level:'start'})-[:LIKES*1..6]->({level:'end'})
with collect(nodes(p)) as full_set
match q=()-[:FRIENDS*1]->()
with full_set, collect(nodes(q)[0]) as y,collect(nodes(q)[1]) as z
match p=({level:'start'})-[:LIKES*1..6]->({level:'end'})
where any(x in nodes(p) where x in z) and not any(x in nodes(p) where x in y)
with full_set, collect(nodes(p)) as irrelevent_set
with filter (keep in full_set where not keep in irrelevent_set) as keep_sets
unwind keep_sets as keep
return extract(person in keep | person.short)
Edit to return paths instead:
match p=({level:'start'})-[:LIKES*1..6]->({level:'end'})
with collect(p) as full_set
match q=()-[:FRIENDS*1]->()
with full_set, collect(nodes(q)[0]) as y,collect(nodes(q)[1]) as z
match p=({level:'start'})-[:LIKES*1..6]->({level:'end'})
where any(x in nodes(p) where x in z) and not any(x in nodes(p) where x in y)
with full_set, collect(p) as irrelevent_set
with filter (keep in full_set where not keep in irrelevent_set) as keep_sets
unwind keep_sets as keep
return keep

Related

How to get an element from a list of pairs in Erlang

I'm trying to make a function that recursively returns an element from a list of pairs. It takes two arguments, a list of pairs (an association list), and a Value, if the value matches the first element of a pair in a list, then it should return the second element of the pair. Else return an error if the value does not match. For example, searchpair([{K,V}], K). Should return V.
Here's what I've tried. Not sure how to add in the tuple and recurse on it.
searchpair([], _) -> error;
searchpair([[K, V] | Rest], Search) when V = Search -> K;
searchpair([_ | Rest], Search) -> seachPair(Rest, Search).
You don't need a when, you can bind the K directly:
searchpair([], _) ->
error;
searchpair([{K,V} | _Rest], K) ->
V;
searchpair([_T | Rest], Search) ->
searchpair(Rest, Search).
Also, a tuple is not a list. Moreover, function names are case sensitive.

How can I find groups nodes sharing common traits in a graph

Lets say I have a graph that relates food items to traits such as sour, sweet, spicy, tangy, ...
How can I query the graph to give me a set of food items matching each possible combination of traits.
i.e.
all foods that are sweet and spicy
all foods that are sweet and sour
all foods that are sweet, sour, and spicy
The graph tuples would look as follows:
F1 > Spicy
F1 > Sweet
F2 > Sour
F2 > Sweet
F3 > Sour
...
The query should output sets of food matching each possible combination of traits.
Spicy => F1, F2, F3, F4, F5
Spicy & Sweet => F1, F3, F5
Spicy & Sweet & Sour => F3
Spicy & Sweet & Sour # Tangy => F3
Spicy & Sour => ...
Spicy & Sour & Tangy => ...
Spicy & Tangy => ...
1) Assume the following inputs:
UNWIND [ {name: 'F1', traits: ['Spicy', 'Sweet' ]},
{name: 'F2', traits: ['Sour' , 'Sweet' ]},
{name: 'F3', traits: ['Tangy', 'Sour', 'Spicy' ]},
{name: 'F4', traits: ['Tangy', 'Sour', 'Spice', 'Tart']} ] AS food
MERGE (F:Food {name: food.name}) WITH F, food
UNWIND food.traits as trait
MERGE (T:Trait {name: trait})
MERGE (F)-[:hasTrait]->(T)
RETURN F, T
2) Now we need to get all combinations of traits. For this we need apoc library:
MATCH (T:Trait)
WITH collect(T) as traits
// Here we count the number of combinations of traits as a power of two
WITH traits, toInt(round(exp( log(2) * size(traits) )))-1 as combCount
// Go through all the combinations
UNWIND RANGE(1, combCount) as combIndex
UNWIND RANGE(0, size(traits)-1 ) as p
// Check whether the trait is present in the combination
CALL apoc.bitwise.op( toInt(round( exp(log(2) * p) )),'&',combIndex) YIELD value
WITH combIndex, collect(CASE WHEN value > 0 THEN traits[p] END) as comb
// Return all combinations of traits
RETURN comb ORDER BY size(comb)
3) Now, for each combination we need to find the intersection for food:
MATCH (T:Trait)
WITH collect(T) as traits
// Here we count the number of combinations of traits as a power of two
WITH traits, toInt(round(exp( log(2) * size(traits) )))-1 as combCount
// Go through all the combinations
UNWIND RANGE(1, combCount) as combIndex
UNWIND RANGE(0, size(traits)-1 ) as p
// Check whether the trait is present in the combination
CALL apoc.bitwise.op( toInt(round( exp(log(2) * p) )),'&',combIndex) YIELD value
WITH combIndex, collect(CASE WHEN value > 0 THEN traits[p] END) as comb
// Take foods for the first trait:
WITH comb, head(comb) as ft
OPTIONAL MATCH (ft)<-[:hasTrait]-(F:Food)
// We find the intersection of each food with other traits
WITH comb, collect(F) as testFoods
UNWIND testFoods as food
UNWIND comb as trait
OPTIONAL MATCH p = (food)-[:hasTrait]->(trait)
WITH comb, food, trait, size(collect(p)) as pairs
// Check that the number of crossings for food with traits
// for each combination of the same number of traits
WITH comb, food, collect(CASE WHEN pairs > 0 THEN trait END) as pairs
WITH comb, collect(CASE WHEN size(pairs)=size(comb) THEN food END) as pairs
// Return combinations where there is a common food
WITH comb, pairs WHERE size(pairs)>0
RETURN comb, pairs ORDER BY size(comb)
Keep in mind that the format of neo4j query output is designed for rows with columns, not your desired output format, so this makes things a little tricky.
I would highly recommend just outputting your food items on each row, with boolean columns for membership in each distinct simple trait, then in your application code, insert the food objects into sets for each trait. Then using application logic you can calculate all possible combinations of traits you need, and perform set intersection to generate them.
This would make the neo4j query very easy:
MATCH (f:Food)
WITH f
RETURN f.name, EXISTS((f)-[:IS]->(:Trait{name:'tangy'})) AS tangy,
EXISTS((f)-[:IS]->(:Trait{name:'sweet'})) AS sweet,
EXISTS((f)-[:IS]->(:Trait{name:'sour'})) AS sour,
EXISTS((f)-[:IS]->(:Trait{name:'spicy'})) AS spicy
That said, if you're determined to do the entire thing with a neo4j query, it's going to be messy, since you'll need to track and generate all the combinations you need yourself. For intersection operations, you'll want to install the APOC procedures library.
Seems to me that the best start is to create sets of food nodes according to each individual trait.
MATCH (f:Food)-[:IS]->(:Trait{name:'spicy'})
WITH COLLECT(f) AS spicyFood
MATCH (f:Food)-[:IS]->(:Trait{name:'sour'})
WITH COLLECT(f) AS sourFood, spicyFood
MATCH (f:Food)-[:IS]->(:Trait{name:'sweet'})
WITH COLLECT(f) AS sweetFood, sourFood, spicyFood
MATCH (f:Food)-[:IS]->(:Trait{name:'tangy'})
WITH COLLECT(f) AS tangyFood, sweetFood, sourFood, spicyFood
Now that you have these, you can do your intersections with every combination you're interested in.
CALL apoc.coll.intersection(tangyFood, sweetFood) YIELD value AS tangySweetFood
CALL apoc.coll.intersection(tangyFood, sourFood) YIELD value AS tangySourFood
CALL apoc.coll.intersection(tangyFood, spicyFood) YIELD value AS tangySpicyFood
CALL apoc.coll.intersection(tangySweetFood, sourFood) YIELD value AS tangySweetSourFood
CALL apoc.coll.intersection(tangySweetFood, spicyFood) YIELD value AS tangySweetSpicyFood
CALL apoc.coll.intersection(tangySourFood, spicyFood) YIELD value AS tangySourSpicyFood
CALL apoc.coll.intersection(tangySweetSourFood, spicyFood) YIELD value AS tangySweetSourSpicyFood
CALL apoc.coll.intersection(sweetFood, sourFood) YIELD value AS sweetSourFood
CALL apoc.coll.intersection(sweetFood, spicyFood) YIELD value AS sweetSpicyFood
CALL apoc.coll.intersection(sweetSourFood, spicyFood) YIELD value AS sweetSourSpicyFood
CALL apoc.coll.intersection(sourFood, spicyFood) YIELD value AS sourSpicyFood
RETURN tangyFood, sweetFood, sourFood, spicyFood,
tangySweetFood, tangySourFood, tangySpicyFood,
tangySweetSourFood, tangySweetSpicyFood, tangySourSpicyFood,
tangySweetSourSpicyFood,
sweetSourFood, sweetSpicyFood,
sweetSourSpicyFood,
sourSpicyFood

Map List onto shifted self

I have finally found an excellent entry point into functional programming with elm, and boy, do I like it, yet I still lack some probably fundamental elegance concerning a few concepts.
I often find myself writing code similar to the one below, which seems to be doing what it should, but if someone more experienced could suggest a more compact and direct approach, I am sure that could give some valuable insights into this so(u)rcery.
What I imagine this could boil down to, is something like the following
(<-> is a vector subtraction operator):
edgeDirections : List Vector -> List Vector
edgeDirections corners = List.map2 (\p v -> p <-> v) corners (shiftr 1 corners)
but I don't really have a satisfying approach to a method that would do a shiftr.
But the rules of stackoverflow demand it, here is what I tried. I wrote an ugly example of a possible usage for shiftr (I absolutely dislike the Debug.crash and I am not happy about the Maybe):
Given a list of vectors (the corner points of a polygon), calculate the directional vectors by calculating the difference of each corner-vector to its previous one, starting with the diff between the first and the last entry in the list.
[v1,v2,v3] -> [v1-v3,v2-v1,v3-v2]
Here goes:
edgeDir : Vector -> ( Maybe Vector, List Vector ) -> ( Maybe Vector, List Vector )
edgeDir p ( v, list ) =
case v of
Nothing ->
Debug.crash ("nono")
Just vector ->
( Just p, list ++ [ p <-> vector ] )
edgeDirections : List Vector -> List Vector
edgeDirections corners =
let
last =
List.head <| List.reverse corners
in
snd <| List.foldl edgeDir ( last, [] ) corners
main =
show <| edgeDirections [ Vector -1 0, Vector 0 1, Vector 1 0 ]
I appreciate any insight into how this result could be achieved in a more direct manner, maybe using existing language constructs I am not aware of yet, or any pointers on how to lessen the pain with Maybe. The latter may Just not be possible, but I am certain that the former will a) blow me away and b) make me scratch my head a couple times :)
Thank you, and many thanks for this felicitous language!
If Elm had built-in init and last functions, this could be cleaner.
You can get away from all those Maybes by doing some pattern matching. Here's my attempt using just pattern matching and an accumulator.
import List exposing (map2, append, reverse)
shiftr list =
let shiftr' acc rest =
case rest of
[] -> []
[x] -> x :: reverse acc
(x::xs) -> shiftr' (x::acc) xs
in shiftr' [] list
edgeDirections vectors =
map2 (<->) vectors <| shiftr vectors
Notice also the shortened writing of the mapping function of (<->), which is equivalent to (\p v -> p <-> v).
Suppose Elm did have an init and last function - let's just define those quickly here:
init list =
case list of
[] -> Nothing
[_] -> Just []
(x::xs) -> Maybe.map ((::) x) <| init xs
last list =
case list of
[] -> Nothing
[x] -> Just x
(_::xs) -> last xs
Then your shiftr function could be shortened to something like:
shiftr list =
case (init list, last list) of
(Just i, Just l) -> l :: i
_ -> list
Just after I "hung up", I came up with this, but I am sure this can still be greatly improved upon, if it's even correct (and it only works for n=1)
shiftr : List a -> List a
shiftr list =
let
rev =
List.reverse list
in
case List.head rev of
Nothing ->
list
Just t ->
[ t ] ++ (List.reverse <| List.drop 1 rev)
main =
show (shiftr [ 1, 2, 3, 4 ] |> shiftr)

Set Intersection with Tail Recursion

I am trying to produce the solution for an intersection of two sets using tail recursion and an empty list [] as an accu:
let rec setintersect list list =
let rec setintersect2 a b c =
match a with
| [] -> (match b with [] -> (setsimplify c) | h::t -> (setsimplify c))
| h1::t1 -> (match b with [] -> (setsimplify c) |h2::t2 -> (if (elementof h1 b) then (setintersect2 t1 b (c#[h1])) else (setintersect2 t1 b c))) in
setintersect2 list list [];;
Elementof takes takes "an int and a list" and is correctly working to give true if x is an element of the list, false otherwise..
Here is the problem:
# setintersect [5;2;1] [2;6;9];;
- : int list = [2; 6; 9]
and it should give [2].
What am I doing wrong?
I feel like there's something really simple that I am misunderstanding!
Edit:
Thanks for the responses so far.
setsimplify just removes the duplicates.
so [2,2,3,5,6,6] becomes [2,3,5,6]. Tested and made sure it is working properly.
I am not supposed to use anything from the List library either. Also, I must use "tail recursion" with the accumulator being a list that I build as I go.
Here is the thought:
Check the head element in list1, IF it exists in list2, THEN recurse with the "tail of list1, list2, and list c with that element added to it". ELSE, then recurse with "tail of list1, list2 and list c(as it is)".
end conditions are either list1 or list2 are empty or both together are empty, return list c (as it is).
let rec setintersect list list = is wrong: the two arguments should be named differently (you should of course update the call to setintersect2 accordingly), otherwise the second will shadow the first. I would have thought that OCaml would have at least warned you about this fact, but it appears that it is not the case.
Apart from that, the code seems to do the trick. There are a couple of things that could be improved though:
setintersect itself is not recursive (only setintersect2 is), you thus don't need the rec
you should find a different name for the argument of setintersect2. In particular, it is not obvious which is the accumulator (acc or accu will be understood by most OCaml programmers in these circumstances).
c#[h1] is inefficient: you will traverse c completely each time you append an element. It's better to do h1::c and reverse the result at the end
As a bonus point, if you append element at the beginning of c, and assume that a is ordered, you don't have to call setsimplify at the end of the call: just check whether c is empty, and if this is not the case, append h1 only if it is not equal to the head of c.
First, You didn't list out your setsimplify function.
To write an ocaml function, try to split it first, and then combine if possible.
To solve this task, you just go through all elements in l1, and for every element, you check whether it is in l2 or not, right?
So definitely you need a function to check whether an element is in a list or not, right?
let make one:
let rec mem x = function
| [] -> false
| hd::tl -> hd = x || mem x tl
Then you can do your intersection:
let rec inter l1 l2 =
match l1 with
| [] -> []
| hd::tl -> if mem hd l2 then hd::(inter tl l2) else inter tl l2
Note that the above function is not tail-recursive, I guess you can change it to tail-recursive as an excise.
If you use std library, then it is simple:
let intersection l1 l2 = List.filter (fun x -> List.mem x l2) l1

need help understanding this erlang code

I'm having trouble understanding this line.
[Pid2 ! {delete, V1a}
|| {Pid1a, V1a} <- PV1a, Pid2 <- P2, Pid1a /= Pid2
],
Here is what I understand:
anything before the double pipe "||" is done repeatedly, according what's after the double pipe. so messages with delete atom is repeated sent to Pid2.
I know '/=' mean inequality. I don't understand what '<-' means, and ultimately what the whole line means.
[something(X) || X <- L], is a list comprehension. L is a list of elements, and this expression creates a list of new elements, forming each element by invoking something() on it.
[something(X,Y) || X <-L, Y<-M] is similar, but an element is created for the Cartesian product of each element in X and Y.
[something(X) || X <-L, Expr] is a filter expression. Same as the first one, but it is executed only for elements of L, where Expr is true for the given X.
[something(X) || {X,..} <-L, Expr] is another kind of filter. In the list comprehension only those elements are taken that can be matched by the element.
One more thing to know is that this can not only be used for generating another list, but also for executing a command for each element. If the result of the list comprehension is not matched, the compiler will know not to generate a list at all. This behavior can be used to mimic foreach from other languages.
Some examples:
1> [ X*2 || X <- [1,2,3] ].
[2,4,6]
2> [ X*Y || X <- [1,2], Y <- [3,4,5] ].
[3,4,5,6,8,10]
3> [ X*3 || X <- [1,2,3,4], X rem 2 == 0 ].
[6,12]
4> [ X || {a,X} <- [{a,1},{a,2},{b,3},{c,4}] ].
[1,2]
So your code generates the Cartesian product of all {Pid1a, V1a} elements from PV1a and Pid2 elements from P2, except for those elements where Pid1a equals Pid2, and for each of these pairs sends the {delete, V1a} message to Pid2.
I don't know Erlang, but this looks just like list comprehensions from a bunch of languages I do know. Hopefully this guess will help you until somebody who knows Erlang can answer:
[Pid2 ! {delete, V1a} || {Pid1a, V1a} <- PV1a, Pid2 <- P2, Pid1a /= Pid2],
Translates to imperative-style pseudocode:
For each item in PV1a, unpacking item to {Pid1a, V1a}
For each Pid2 in P2
If Pid1a /= Pid2
Pid2 ! {delete, V1a}
In other words, for each Pid in PV1a and P2, send the message delete V1a to Pid2 as long as Pid1 and Pid2 are not the same Pid.
It is a list comprehension and the <- operator is used for generators.
Look at a more popular introduction example for LCs; to find triangles where the squares of the integer sides equals the square of the integer hypotenuse, but for a given range of integers Ns.
Ns = [1,2,3,4,5,6,7,8,9].
[{X,Y,C} || X <- Ns, Y <- Ns, C <- Ns, X*X + Y*Y == C*C].
This gives us the following list as output.
[{3,4,5},{4,3,5}]
Which seems correct:
3² + 4² = 5²
9 + 16 = 25
25 = 25
So the list comprehension can be read as give us every X,Y and C such that X is taken from Ns, Y is taken from Ns and C is taken from Ns, and X² + Y² equals C².

Resources