Neo4J returned many values - neo4j

I have query like this:
MATCH p = (ob:Obiect)<--(w:Word { value:'human' })-[*]-(x) RETURN {id: id(x), value: x.value}
I am returning "x" but i want to return also the “w”. The big question is: how to have “w” returned?
I tried like this:
MATCH p = (ob:Obiect)<--(w:Word { value:'human' })-[*]-(x) RETURN {id: id(x), value: x.value, rootid: id(w)}
but then the output looks like
x.id | x.value | w.id | w.value
x.id | x.value | w.id | w.value
x.id | x.value | w.id | w.value
but for NULL values of x there are no values of “w”, but I also need them.

try OPTIONAL MATCH, see: http://neo4j.com/docs/2.1.5/query-optional-match.html
MATCH (ob:Obiect)<--(w:Word { value:'human' })
OPTIONAL MATCH (w)-[*]-(x)
RETURN {id: id(x), value: x.value, rootid: id(w)}

Related

extract decorating nodes if it exists but still return path if decorating nodes does not exist

I have the following graph
(y1:Y)
^
|
(a1:A) -> (b1:B) -> (c1:C)
(e1:E)
^
|
(d1:D)
^
|
(a2:A) -> (b2:B) -> (c2:C)
(a3:A) -> (b3:B) -> (c3:C)
I would like to find path between node label A and C. I can use the query
match p=((:A)-[*]->(:C))
return p
But I also want to get node label Y and node label D, E if these decorating nodes exists. If I try:
match p=((:A)-[*]->(cc:C)), (cc)-->(yy:Y), (cc)-[*]->(dd:D)-[*]->(ee:E)
return p, yy, dd, ee
Then it is only going to return the path if the C node has Y, D, E connects to it.
The output that I need is:
a1->b1->c1, y1, null
a2->b2->c2, null, [[d1, e1]]
a3->b3->c3, null, null
I.e., if decorating node does not exist, then just return null. For the array, it can be null or empty array. Also D and E nodes will be group into an array of arrays since there could be many pairs of D and E.
What is the best way to achieve this?
This should do it, returning an empty array for the deDecoration if there aren't any D-E decorations
MATCH p=((:A)-[*]->(c:C))
WITH p,
HEAD([(c)--(y:Y) | y ]) AS yDecoration,
[(c)-[*]->(d:D)-[*]->(e:E) | [d,e]] AS deDecoration
RETURN p, yDecoration, deDecoration
with this graph (multiple D-E)
this query
MATCH p=((:A)-[*]->(c:C))
WITH REDUCE(s='' , node IN nodes(p) | s + CASE WHEN s='' THEN '' ELSE '->' END + node.name) AS p,
HEAD([(c)--(y:Y) | y.name ]) AS yDecoration,
[(c)-[*]->(d:D)-[*]->(e:E) | [d.name,e.name]] AS deDecoration
RETURN p, yDecoration, deDecoration
returns
╒════════════╤═════════════╤═════════════════════════╕
│"p" │"yDecoration"│"deDecoration" │
╞════════════╪═════════════╪═════════════════════════╡
│"A2->B2->C2"│null │[] │
├────────────┼─────────────┼─────────────────────────┤
│"A1->B1->C1"│null │[["D2","E2"],["D1","E1"]]│
├────────────┼─────────────┼─────────────────────────┤
│"A3->B3->C3"│"Y1" │[] │
└────────────┴─────────────┴─────────────────────────┘

neo4j: type counts by distance

I want to have some count statistics by type by distance from root. For example,
(A type:'private')-[value:20]->(B type:'private')-[value:40]->(C type:'private')
(A type:'private')-[value:0]->(D type:'public')-[value:20]->(E type:'private')
CREATE (:firm {name:'A', type:'private'}), (:firm {name:'B', type:'private'}), (:firm {name:'C', type:'private'}), (:firm {name:'D', type:'public'}), (:firm {name:'E', type:'private'});
MATCH (a:firm {name:'A'}), (b:firm {name:'B'}), (c:firm {name:'C'}), (d:firm {name:'D'}), (e:firm {name:'E'})
CREATE (a)-[:REL {value: 20}]->(b)->[:REL {value: 40}]->(c),
(a)-[:REL {value: 0}]->(d)->[:REL {value: 20}]->(e);
I want to get the count of each type of A's immediate neighbors and that of the 2nd layer neighbors, i.e.,
+-----------------------------+
| distance | type | count |
+-----------------------------+
| 0 | private | 1 |
| 0 | public | 0 |
| 1 | private | 1 |
| 1 | public | 1 |
| 2 | private | 2 |
| 2 | public | 0 |
+-----------------------------+
Here is a related question about aggregate statistics by distance.
Thanks!
For this on, the apoc library comes in handy:
MATCH path=(:firm {name:'A'})-[:REL*]->(leaf:firm)
WHERE NOT (leaf)-[:REL]->(:firm)
WITH COLLECT(path) AS paths, max(length(path)) AS longest
UNWIND RANGE(0,longest) AS depth
WITH depth,
apoc.coll.frequencies([node IN apoc.coll.toSet(REDUCE(arr=[], path IN [p IN paths WHERE length(p) >= depth] |
arr
+ nodes(path)[depth]
)
) | node.type
]) as typesAtDepth
UNWIND typesAtDepth AS typeAtDepth
RETURN depth, typeAtDepth.item AS type, typeAtDepth.count AS count
for this dataset
CREATE (_174:`firm` { `name`: 'A', `type`: 'type2' }) CREATE (_200:`firm` { `name`: 'D', `type`: 'type2' }) CREATE (_202:`firm` { `name`: 'E', `type`: 'type2' }) CREATE (_203:`firm` { `name`: 'F', `type`: 'type1' }) CREATE (_191:`firm` { `name`: 'B', `type`: 'type1' }) CREATE (_193:`firm` { `name`: 'C', `type`: 'type2' }) CREATE (_174)-[:`REL` { `value`: '0' }]->(_200) CREATE (_200)-[:`REL` { `value`: '20' }]->(_202) CREATE (_202)-[:`REL` { `value`: '99' }]->(_203) CREATE (_174)-[:`REL` { `value`: '20' }]->(_191) CREATE (_191)-[:`REL` { `value`: '40' }]->(_193)
it returns this result:
╒═══════╤═══════╤═══════╕
│"depth"│"type" │"count"│
╞═══════╪═══════╪═══════╡
│0 │"type2"│1 │
├───────┼───────┼───────┤
│1 │"type2"│1 │
├───────┼───────┼───────┤
│1 │"type1"│1 │
├───────┼───────┼───────┤
│2 │"type2"│2 │
├───────┼───────┼───────┤
│3 │"type1"│1 │
└───────┴───────┴───────┘

Pattern matching with OR and WHEN clauses

I have problem with pattern matching not compiling when it's expected
Current simplified version looks like this
match smth with
| pattern1 when isSomething pattern1 ->
doWork()
| pattern2 when isSomething pattern2 ->
doWork()
| _ -> () // do nothing
Problem here is that there is duplication of body in different cases
I've tried to use OR pattern to transform it to this
match smth with
| pattern1 when isSomething pattern1
| pattern2 when isSomething pattern2 ->
doWork()
| _ -> () // do nothing
But it gives Unexpected symbol '|' in pattern matching. Expected '->' or other token. F# Compiler 10
Attempts to surround isSomething with parens doesn't help. How to avoid duplication in that case?
Original code
match state.SearchOptions with
| { Pattern = empty } when String.IsNullOrEmpty empty ->
ValueSome (i, p, state.SelectedConfiguration, state.Solution.Configurations)
| { Pattern = pattern; IsRegex = true; IsCaseSensitive = true } when Regex.isMatch p.Name pattern ->
ValueSome (i, p, state.SelectedConfiguration, state.Solution.Configurations)
| { Pattern = pattern; IsRegex = true; IsCaseSensitive = false } when Regex.isMatchCaseInsensitive p.Name pattern ->
ValueSome (i, p, state.SelectedConfiguration, state.Solution.Configurations)
| { Pattern = pattern; IsRegex = false; IsCaseSensitive = true } when p.Name.Contains(pattern, StringComparison.InvariantCulture) ->
ValueSome (i, p, state.SelectedConfiguration, state.Solution.Configurations)
| { Pattern = pattern; IsRegex = false; IsCaseSensitive = false } when p.Name.Contains(pattern, StringComparison.InvariantCultureIgnoreCase) ->
ValueSome (i, p, state.SelectedConfiguration, state.Solution.Configurations)
| _ -> ValueNone
As mentioned in the comment, this cannot be done - the when clause applies to the whole clause. I think there are three alternative approaches.
First of all, you could define the result value as a variable before the match expression and then just return this from all four cases (as suggested in the comment).
Second, you could actually write this as a plain if expression:
let so = state.SearchOptions
if String.IsNullOrEmpty so.Pattern then
ValueSome (i, p, state.SelectedConfiguration, state.Solution.Configurations)
elif ( so.IsRegex && so.IsCaseSensitive &&
Regex.isMatch p.Name so.Pattern )
|| ( so.IsRegex && not so.IsCaseSensitive &&
Regex.isMatchCaseInsensitive p.Name so.Pattern )
|| ( not so.IsRegex && so.IsCaseSensitive &&
p.Name.Contains(so.Pattern, StringComparison.InvariantCulture) )
|| ( not so.IsRegex && not so.IsCaseSensitive &&
p.Name.Contains(so.Pattern, StringComparison.InvariantCultureIgnoreCase) ) then
ValueSome (i, p, state.SelectedConfiguration, state.Solution.Configurations)
else ValueNone
Another option would be to use active patterns - that way, you can integrate the conditions into the pattern itself. Something like this should do the trick:
let (|RegexMatch|_|) input pattern =
if Regex.isMatch input pattern then Some() else None
let (|RegexMatchInsensitive|_|) input pattern =
if Regex.isMatchCaseInsensitive input pattern then Some() else None
let (|Contains|_|) (input:string) pattern
input.Contains(pattern, StringComparison.InvariantCulture)
let (|ContainsInsensitive|_|) (input:string) pattern
input.Contains(pattern, StringComparison.InvariantCultureIgnoreCase)
match state.SearchOptions with
| { Pattern = empty } when String.IsNullOrEmpty empty ->
ValueSome (i, p, state.SelectedConfiguration, state.Solution.Configurations)
| { Pattern = Match p.Name; IsRegex = true; IsCaseSensitive = true }
| { Pattern = RegexMatchInsensitive p.Name; IsRegex = true; IsCaseSensitive = false }
| { Pattern = Contains p.Name; IsRegex = false; IsCaseSensitive = true }
| { Pattern = ContainsInsensitive p.Name; IsRegex = false; IsCaseSensitive = false } ->
ValueSome (i, p, state.SelectedConfiguration, state.Solution.Configurations)
| _ -> ValueNone
If I was doing this in just one place, I would probably go with either option one or two (depending on what you find more readable). If you are doing more things like this, the third option is a nice alternative.

Aggregating over Distinct Nodes

The data model I have is: (Item)-[:HAS_AN]->(ItemType) and both node types have a field called ID. Items can be related to multiple ItemTypes and in some cases, these ItemTypes can have the same IDs. I'm trying to populate a structure {ID:..., SameType:...}, where SameType = 1 if a node has the same item type as some node (with ID = 1234), and 0 otherwise.
First, I get the candidate list of nodes qList and the source node's ItemType:
MATCH (p:Item{ID:1234})-[:HAS_AN]->(i)
WITH i as pItemType, qList
Then, I go through qList, comparing each node's ItemType to pItemType (which is the ItemType of the source node):
UNWIND qList as q
MATCH (q)-[:HAS_AN]->(i)
WITH q.ID as qID, pItemType, i,
CASE i
WHEN pItemType THEN 1
ELSE 0
END as SameType
RETURN DISTINCT i, qID, pItemType, SameType
The problem I have is when some nodes have two ItemTypes with the same ID. This gives results where some of the entries are duplicates:
{ | | { |
"ID": 18 | 35258417 | "ID": 71 | 0
} | | } |
{ | | { |
"ID": 18 | 35258417 | "ID": 71 | 0
} | | } |
while I'd like to only take one such row, if more than one exists.
Placing DISTINCT where I have in the last part of the query doesn't seem to work. What's the best way to filter out such duplicates?
Update:
Here is a sample data subset: http://console.neo4j.org/r/f74pdq
Here are the queries that I'm running
MATCH (q:Item) WHERE q.ID <> 1234 WITH COLLECT(DISTINCT(q)) as qList
MATCH (p:Item{ID:1234})-[:HAS_AN]->(i:ItemType) WITH i as pItemType, qList
UNWIND qList as q
MATCH (q)-[:HAS_AN]->(i:ItemType) WITH q.ID as qID, pItemType, i,
CASE i
WHEN pItemType THEN 1
ELSE 0
END as SameType
RETURN DISTINCT i, qID, pItemType, SameType
In this example, Item with ID = 2 has two HAS_AN relations with 2 ItemType nodes with the same ID. I would like only one of them to be returned.
I've tried to simplify your query. Take a look:
MATCH (:Item {ID : 1234})-[:HAS_AN]->(target:ItemType)
MATCH (item:Item)-[:HAS_AN]->(itemType:ItemType)
WHERE item.ID <> 1234
WITH
itemType.ID AS i,
item.ID AS qID,
collect({
pItemType : target,
SameType : CASE exists((item)-[:HAS_AN]-(target))
WHEN true THEN 1 ELSE 0 END
})[0] as Item
RETURN i, qID, Item.pItemType AS pItemType, Item.SameType AS SameType
The trick is in the two lines after WITH. At this point I'm grouping by itemType.ID and item.ID and not ( and not itemType and item). In your original query you are using pItemType to group. This does not work because the two ItemTypes with ID = 34 are different nodes although they have the same ID.
The output from your console:
+-------------------------------------+
| i | qID | pItemType | SameType |
+-------------------------------------+
| 31 | 4 | Node[2]{ID:5} | 0 |
| 5 | 3 | Node[2]{ID:5} | 1 |
| 31 | 5 | Node[2]{ID:5} | 0 |
| 45 | 5 | Node[2]{ID:5} | 0 |
| 5 | 1 | Node[2]{ID:5} | 1 |
| 34 | 2 | Node[2]{ID:5} | 0 |
+-------------------------------------+
6 rows
33 ms
Thanks to #Bruno's solution, I was able to get the right answers. However, the original solution did not work right off the bat for me for two reasons - I needed the qList since I was referring to it later, and it had approximately 4 times the DB hits as the query in my question. So, I tried a few optimizations that brought the number of DB hits down to half, and am sharing it here for posterity.
MATCH (q:Item) WHERE q.ID <> 1234 WITH COLLECT(DISTINCT(q)) as qList
MATCH (p:Item{ID:1234})-[:HAS_AN]->(i:ItemType) WITH i as pItemType, qList
UNWIND qList as item
MATCH (item)-[:HAS_AN]->(i)
WITH
i, pItemType,
item.ID AS qID,
collect({
pItemType : pItemType,
SameType : CASE i.ID
WHEN pItemType.ID THEN 1 ELSE 0 END
})[0] as Item
RETURN i, qID, Item.pItemType AS pItemType, Item.SameType AS SameType
Turns out running MATCH (item:Item)-[:HAS_AN]->(itemType:ItemType) was adding a Filter operation that took almost as many DB hits as it had matches.

Match and bind the fields of record type in one match pattern?

Is it possible to match and bind the fields of record type in the match pattern statement?
type R = { A:int; B:string }
type R' = R option
let record: R' = .....
match record with
| Some(r) ->
let a, b = r.A, r.B // Can this row be merged to the match pattern like the tuple example below?
....
| None -> ....
Expected something like the following for tuple
match record with
| Some(a, b) -> ....
match record with
| Some({A = a; B = b }) -> ...

Resources