How to replace entries in a string array with cypher - neo4j

I have a string array property like so:
["name1", "name2", "name3", "name2", "name4"]
I would like to replace in this array e.g. "name2" with "name5":
["name1", "name5", "name3", "name5", "name4"]
So far i came up with a query like this:
MATCH (parent)-[rel]->(child)
WHERE 'name2' IN rel.names
SET rel.names = [x IN (rel.names+['name5']) WHERE x<>"name2"]
Which results in nearly what i want:
["name1", "name3", "name4", "name5"]
The problem of this query is obvious - it just add's only one times "name5" statically without checking how often "name2" is in the array. For example if I have "name2" n-times the query only add's one "name5" instead of n-times.
Without the "where clause" the query add's a "name5" to arrays that doesn't even have a "name2" included. The right approach should be that the query should instead find 0 times "name2" and add 0 times "name5". So the where part shouldn't be required. How would you solve the problem and is my solution approach the right way to go?

This should work:
MATCH (parent)-[rel]->(child)
WHERE 'name2' IN rel.names
SET rel.names = [x IN rel.names | CASE WHEN "name2" = x THEN "name5" ELSE x END]

Related

Searchkick `all` for multiple arrays with OR

I need to build a boolean query with searchkick which will check multiple arrays and condition must be true if all elements of an array exist.
I wants records which contains ["2019-11-05", "2019-11-06", "2019-11-07"] all dates from one array OR from second array ["2019-11-08", "2019-11-09", "2019-11-10"]
it works perfectly for one array like this.
available_on: { all: ["2019-11-05", "2019-11-06", "2019-11-07"] }
I need something like this
available_on: { or: { all: ["2019-11-05", "2019-11-06", "2019-11-07"] , all: ["2019-11-08", "2019-11-09", "2019-11-10"]} }
how can we create a query available_on = A OR B
A, B are arrays and we need to match all elements of arrays
available_on is target term (also an array in index)
You should use where with or filter like this:
where {or: [[{available_on: {all: ["2019-11-05", "2019-11-06", "2019-11-07"]}},
{available_on: {all: ["2019-11-08", "2019-11-09", "2019-11-10"]}}]]}
or use _or filter:
where {_or: [{available_on: {all: ["2019-11-05", "2019-11-06", "2019-11-07"]}},
available_on: {all: ["2019-11-08", "2019-11-09", "2019-11-10"]}]}
There's no difference of results between _or and or, just a little different in syntax(or use one more pair square brackets)
Have you tried using this inside 'where' clause ?
_or: [{available_on: {all: ["2019-11-05", "2019-11-06", "2019-11-07"]}}, {available_on: {all: ["2019-11-05", "2019-11-06", "2019-11-07"]}} ]}

Neo4j cypher: Find all paths till node of certain type

In Neo4j I have stored data with nodes of type A and B. Between 2 nodes A there could be many nodes of type B. I would like to fetch first node of type A for each path outgoing from a given node A. I post example structure below.
/->A2->A3
A1-->A4->A5
\->B1->A6
For input: A1, I would like my query to return only A2, A4 and A6.
Query that I'm using right now is below:
MATCH p=(source:Node ) - [:relation*] -> (target:Node) WHERE
source.name = "A1" AND target.type = "A" RETURN target
However it returns nodes A3 and A5 which I want to get rid of.
I have used this sample data set to reproduce your scenario:
create (root:Node {name : "A1", type: "A"})-[:LINKED_TO]->(:Node{name : "A2", type: "A"})-[:LINKED_TO]->(:Node{name : "A3", type: "A"}),
(root)-[:LINKED_TO]->(:Node{name : "A4", type: "A"})-[:LINKED_TO]->(:Node{name : "A5", type: "A"}),
(root)-[:LINKED_TO]->(:Node{name : "B1", type: "B"})-[:LINKED_TO]->(:Node{name : "A6", type: "A"})
Then, this query, using filter() function:
// MATCH all paths between source and target, starting from 2 hops.
// target node should be the last node of the path.
MATCH p=(source:Node)-[:LINKED_TO*]->(target:Node)
WHERE source.name = "A1" AND target.type = "A" AND NOT (target)-->()
// grouping by path, I have filtered the nodes of each path, getting only ones that have type = "A"
// then I get the first one by index (index [0])
WITH p, filter(node IN nodes(p)[1..] WHERE node.type = "A")[0] as firstA
// return the firstA node
RETURN firstA
The output:
╒════════════════════════╕
│"firstA" │
╞════════════════════════╡
│{"name":"A6","type":"A"}│
├────────────────────────┤
│{"name":"A4","type":"A"}│
├────────────────────────┤
│{"name":"A2","type":"A"}│
└────────────────────────┘
Tip: instead of a property named type you can add another label for each node denoting your type, like :A and :B. Remember that labels are ideal for grouping nodes into sets. Also, a node can have more than one label.

Neo4j Query - Relations that one property is NOT EQUAL to a value in none of relations grouped by another property

Sorry for the title. I couldn't find a better one. My question is hard for me to explain in words so I'll try to give an example.
Say I have a relation named "FOO" and "FOO" has two property named "id" and "bar".
And there is a graph like this:
(n1)-[:FOO {id: "1", bar:"8"}]->(n2)-[:FOO {id: "1", bar:"27"}]->(n3)-[:FOO {id: "1", bar:"50"}]->(n4)
(m1)-[:FOO {id: "4", bar:"2"}]->(m2)-[:FOO {id: "4", bar:"12"}]->(n2)-[:FOO {id: "4", bar:"25"}]->(m3)
(n1)-[:FOO {id: "7", bar:"11"}]->(m2)-[:FOO {id: "7", bar:"50"}]->(o3)-[:FOO {id: "7", bar:"14"}]->(o5)
And so on...
What I want to do is getting all ids (or nodes and relations related to those ids) for the relation series doesn't have any "bar" property equal to 50.
In above graph, the result I want is second one, id: "4":
(m1)-[:FOO {id: "4", bar:"2"}]->(m2)-[:FOO {id: "4", bar:"12"}]->(m3)-[:FOO {id: "4", bar:"25"}]->(m3)
Plus to this query how can I get the all ids (or nodes and relations related to those ids) for the relation series whose last relations bar property is not equal to 50. In this one, what I would like to get is first and third one, id: "4" and id: "7" :
(m1)-[:FOO {id: "4", bar:"2"}]->(m2)-[:FOO {id: "4", bar:"12"}]->(n2)-[:FOO {id: "4", bar:"25"}]->(m3)
(n1)-[:FOO {id: "7", bar:"11"}]->(m2)-[:FOO {id: "7", bar:"50"}]->(o3)-[:FOO {id: "7", bar:"14"}]->(o5)
UNWIND [1, 4, 7] AS rel_id
MATCH path = () - [:FOO*3 {id:rel_id}] -> ()
WHERE NOT ANY(x IN RELATIONSHIPS(path) WHERE x.bar = '50')
RETURN path
And for number 2:
UNWIND [1, 4, 7] AS rel_id
MATCH path = () - [:FOO*3 {id: rel_id}] -> ()
WHERE NOT LAST(RELATIONSHIPS(path))['bar'] = '50'
RETURN RELATIONSHIPS(path)[0]['id']
This is basically the minimal framework to answer your problem. If you need to narrow down the possible paths, you can prefix the queries with something else to alias the nodes or relationships that you want to build the paths on.
EDIT: To get all possible ID's instead of passing the list in manually, replace the first line with:
MATCH () - [r:FOO] - ()
WITH r.id AS r_id
WITH COLLECT(DISTINCT r_id) AS rel_ids
UNWIND rel_ids AS rel_id
I don't know this is the correct way to do it but I managed to get first results I wanted like this:
MATCH () - [r:FOO] - ()
WHERE r.bar = "50"
WITH COLLECT(DISTINCT r.id) as bar50_ids
MATCH () - [r:FOO] - ()
WITH COLLECT(DISTINCT r.id) as all_ids, bar50_ids
RETURN [x IN all_ids WHERE NOT(x IN bar50_ids)] AS barnot50_ids
(Getting the DISTINCT id's whose in all its relation at least one .bar = "50", and getting their difference from all DISTINC id's will give the all id's that has not any relation contains bar = "50")
I don't know how to get the second one I wanted.

parsing a text file into groups using Scala

I have a CSV file that is really a set of many CSV files in one. Something like this:
"First Part"
"Some", "data", "in", "here"
"More", "stuff", "over", "here"
"Another Part"
"This", "section", "is", "not", "the", "same", "as", "the", "first"
"blah", "blah", "blah", "blah", "blah", "blah", "blah", "blah", "blah"
"Yet another section"
"And", "this", "is", "yet", "another"
"blah", "blah", "blah", "blah", "blah"
I'd like to break it into separate components. Given I know the header for each section, it'd be nice if I could do some kind of groupBy or something where I pass in a set of regexp's representing header patterns and return a Seq[Seq[String]] or something similar.
You could do the following:
val groups = List("\"First Part\"", "\"Another Part\"", "\"Yet another section\"")
val accumulator = List[List[String]]()
val result = input.split("\n").foldLeft(accumulator)((acc,e) => {
if (groups.contains(e)) {
// Make new group when we encounter a string matching one of the groups
Nil :: acc
} else {
// Grab current group and modify it
val newHead = e :: acc.head
newHead :: acc.tail
}
})
Each list in result now represent a group. If you want to use regex to find your matches then just replace the groups.contains(e) with a match test. There are some subtleties here that might deserve a mention:
The algorithm will fail if the input does not start with a heading
If a heading is present several times each time it is present will generate a new group
Groups will contain the lines in the input in reverse.
Empty lines will also be included in the result.
EDIT this is similar to the other solution that was posted at the same time. A similar thing for the sections headings could be done instead of my quick hack of size==1. This solution has the added benefit of including the secion name so ordering doesn't matter.
val file: List[String] = """
heading
1,2,3
4,5
heading2
5,6
""".split("\n").toList
val splitFile = file
.map(_.split(",").toList)
.filterNot(_ == List(""))
.foldLeft(List[(String, List[List[String]])]()){
case (h::t,l) => {if(l.size==1) (l(0),List()):: h :: t else (h._1, l :: h._2) :: t};
case (Nil, l)=> if(l.size==1) List((l(0),List())) else List() }
.reverse
produces
splitFile: List[(String, List[List[String]])] = List((heading,List(List(4, 5), List(1, 2, 3))), (heading2,List(List(5, 6))))

ruby - Putting elements of an array of arrays in a variable by index

I have this array of arrays :
array = [["z1", "y1", "x1"], ["z2", "y2", "x2"], ["z3", "y3", "x3"], ["z4", "y4" , "x4"]]
I want to be able to retrieve elements of the array within the bigger array by an index and putting them in a variable.
For example for the index 1 the output should be :
output = [["y1"], ["y2"], ["y3"], ["y4"]]
I want also after that to be able to push these results to form a new array. In other words, I want to re-order the elements of the array (if you could find a better solution than retrieving and pushing).
Example :
output_x = [["x1"], ["x2"], ["x3"], ["x4"]]
output_y = [["y1"], ["y2"], ["y3"], ["y4"]]
output_z = [["z1"], ["z2"], ["z3"], ["z4"]]
So the final result should look like :
result = [["x1", "y1", "z1"], ["x2", "y2", "z2"], ["x3", "y3", "z3"], ["x4", "y4" , "z4"]]
I really want to find a solution for this.
Thank you
PS : x,y and z are coordinates. Forget about sorting them
You just want Array#transpose, which is the inverse of Array#zip:
[["z1", "y1", "x1"],
["z2", "y2", "x2"],
["z3", "y3", "x3"],
["z4", "y4" , "x4"]].transpose
=> [["z1", "z2", "z3", "z4"],
["y1", "y2", "y3", "y4"],
["x1", "x2", "x3", "x4"]]
From there, it's easy enough to split those into individual arrays. If you just want to translate the arrays into XYZ (rather than ZYX), then you may perhaps want to just map the reversed arrays:
[["z1", "y1", "x1"],
["z2", "y2", "x2"],
["z3", "y3", "x3"],
["z4", "y4" , "x4"]].map(&:reverse)
=> [["x1", "y1", "z1"],
["x2", "y2", "z2"],
["x3", "y3", "z3"],
["x4", "y4", "z4"]]
You could also just map an arbitrary order. For example, to map from your ZYX to XZY, you would use:
coordinates.map {|c| [c[2], c[0], c[1]] }
You can do a sort on each element in array like this
array.each do |e|
e.sort!
end
if you run the code above, your array would look like what you want for
result = [["x1", "y1", "z1"], ["x2", "y2", "z2"], ["x3", "y3", "z3"], ["x4", "y4" , "z4"]]
For more information about sort, check here
Update
According to your comment below, you have coordinates in each sub array. Since the order right now is z, y, x, and you want them in x, y, z. Reversing the elements in the array would do the trick.
array.each do |e|
e.reverse!
end
For more information about reverse, check here
If you're just looking for the final outcome:
array = [["z1", "y1", "x1"], ["z2", "y2", "x2"], ["z3", "y3", "x3"], ["z4", "y4" , "x4"]]
option = []
array.each do |item|
option << item.reverse
end
puts option.inspect

Resources