Can we make cypher field query Case-Insensitive - neo4j

It may seem duplicate of this but it is not. Can we make cypher query case insensitive based on fields. I know we can use regex for values but we need it based on fields.
e.g.
MATCH (c:customer) WHERE c.CUSTOMERNUMBER = '1088' RETURN c
Above query returns a result, but following does not
MATCH (c:Customer) WHERE c.CustomerNumber = '1088' RETURN c
Here lable Customer and property CustomerNumber are having different cases.

You can use PROPERTIES to get a map representation of a node, and then use KEYS so that you can iterate over them. Because "Name", "NAME", and "Prop1" are all equally unique property names, and they can all or none exist, as far as the DB is concerned. You will have to iterate every property of the node to find a field that matches your criteria.
MATCH (n)
WHERE ANY(key in KEYS(n) WHERE lower(key)="name" AND n[key]="Neo")
RETURN n
This is more flexible than simple case insensitivity, but it is also expensive.

Related

What is difference between ANY and IN clause in Neo4j?

Neo4j has two different clauses to find value in an array, ANY and IN. Please explain, how they are different as both are used to filter the data by checking if, the specified value is present in the array or not.
Query#1 : MATCH (n) WHERE any(color IN n.liked_colors WHERE color = 'yellow') RETURN n
Result#1: Node with name Eskil
Query#2 : Match (n) where 'yellow' in n.liked_colors return n
Result#2: Node with name Eskil
If both query return same results then where is the diffrence.
In your simple example case the results are the same.
However any() allows more complex means of filtering, especially when we aren't using this to simply detect membership of a known value in a list:
MATCH (n:Person)
WHERE any(color in n.liked_colors WHERE (n)<-[:KNOWS|MARRIED*]-(other:Person {eyes:color}))
Find a person where for any of their liked colors, we can traverse the given pattern to find another person with eyes of that color.

Match by preferred relationship attribute in Cypher

In a large graph I would like to select the recursive paths, where the relationship matches either value B or value C, like here below
match path=(a:item)-[rel:refers*]->(aa:item) where rel.name= 'B' or rel.name='C' return path
The above statement returns all the items when the relationship matches both values, I would like to choose the rel.name = 'B' relationship and only if rel.name='B' does not exist go for rel.name='C'
Is there a way to define this in Cypher, rather than returning everything and then filtering out the unwanted items ?
Thanks

Finding unique values of a node property

I have a neo4j database with several nodes each having many properties. I am trying to find a list of unique values available for each property.
Currently, I can search for nodes that have a certain value 'xxx' with a query in this manner below however, I want to find all the unique values 'xxx','yyy', etc. that may exist in all the nodes in my database.
match (n:Db_Nodes) where n.stringProperty = "xxx" return n
How should I go about structuring the query desired?
You can use the DISTINCT Clause to return all the unique values for this property.
There are two ways to get all the values:
Get all the values in a list. Here result will be one single record with all the unique values in the form of a list.
MATCH (n:Db_Nodes) RETURN COLLECT(DISTINCT n.stringProperty) as propertyList
Get one value per record. Here multiple records will be returned(One per unique property value).
MATCH (n:Db_Nodes) RETURN DISTINCT n.stringProperty

Collection in WITH clause gets expanded to one element per row

I want to create a map projection with node properties and some additional information.
Also I want to collect some ids in a collection and use this later in the query to filter out nodes (where ID(n) in ids...).
The map projection is created in an apoc call which includes several union matches.
call apoc.cypher.run('MATCH (n)-[:IS_A]->({name: "User"}) MATCH (add)-[:IS_A]->({name: "AdditionalInformationForUser"}) RETURN n{.*, info: collect(add.name), id: ID(n)} as nodeWithInfo UNION MATCH (n)-[:IS_A]->({Department}) MATCH (add)-[:IS_A]->({"AdditionalInformationForDepartment"}) RETURN n{.*, info: collect(add.name), id: ID(n)} as nodeWithInfo', NULL) YIELD value
WITH (value.nodeWithInfo) AS nodeWithInfo
WITH collect(nodeWithInfo.id) as nodesWithAdditionalInfosIds, nodeWithInfo
MATCH (n)-[:has]->({"Vacation"})
MATCH (u)-[:is]->({"Out of Order"})
WHERE ID(n) in nodesWithAdditionalInfosIds and ID(u) in nodesWithAdditionalInfosIds
return n, u, nodeWithInfo
This does not return anything because, when the where part is evaluated it doesn´t check "nodesWithAdditionalInfosIds" as a flat list but instead only gets one id per row.
The problem only exists because I am passing the ids (nodesWithAdditionalInfosIds) AND the nodeProjection (nodeWithInfo) on in the WITH clause.
If I instead only use the id collection and don´t use the nodeWithInfo projection the following adjustement works and returns my only the nodes which ids are in the id collection:
...
WITH collect(nodeWithInfo.id) as nodesWithAdditionalInfosIds
MATCH (n)-[:has]->({"Urlaub"})
MATCH (u)-[:is]->({"Out of Order"})
WHERE ID(n) in nodesWithAdditionalInfosIds and ID(u) in nodesWithAdditionalInfosIds
return n, u
If I just return the collection "nodesWithAdditionalInfosIds" directly after the WITH clause in both examples this gets obvious. Since the first one generates a flat list in one result row and the second one gives me one id per row.
I have the feeling that I am missing a crucial knowledge about neo4js With clause.
Is there a way I can pass on my listOfIds and use it as a flat list without the need to have an exclusive WITH clause for the collection?
edit:
Right now I am using the following workaround:
After I do the check on the ID of "n" and "u" I don´t return but instead keep the filtered "n" and "u" nodes and start a second apoc call that returns "nodeWithInfo" like before.
WITH n, u
call apoc.cypher.run('MATCH (n)-[:IS_A]->({name: "User"}) MATCH (add)-[:IS_A]->({name: "AdditionalInformationForUser"}) RETURN n{.*, info: collect(add.name), id: ID(n)} as nodeWithInfo UNION MATCH (n)-[:IS_A]->({Department}) MATCH (add)-[:IS_A]->({"AdditionalInformationForDepartment"}) RETURN n{.*, info: collect(add.name), id: ID(n)} as nodeWithInfo', NULL) YIELD value
WITH (value.nodeWithInfo) AS nodeWithInfo, n, u
WHERE nodeWithInfo.id = ID(n) OR nodeWithInfo.id = ID(u)
RETURN nodeWithInfo, n, u
This way I can return the nodes n, u and the additional information (to one of the nodes) per row. But I am sure there must be a better way.
I know ids in neo4j have to be used with care, if at all. In this case I only need them to be valid inside this query, so it doesn´t matter if the next time the same node has another id.
The problem is stripped down to the core problem (in my opinion), the original query is a little bigger with several UNION MATCH inside apoc and the actual match on nodes which ids are contained in my collection is checking for some more restrictions instead of asking for any node.
Aggregating functions, like COLLECT(), aggregate over a set of "grouping keys".
In the following clause:
WITH collect(nodeWithInfo.id) as nodesWithAdditionalInfosIds, nodeWithInfo
the grouping key is nodeWithInfo. Therefore, each nodesWithAdditionalInfosIds would always be a list containing one value.
And in the following clause:
WITH collect(nodeWithInfo.id) as nodesWithAdditionalInfosIds
there is no grouping key. Therefore, in this situation, nodesWithAdditionalInfosIds will contain all the nodeWithInfo.id values.

Neo4J Arrays in MATCH query

The intention of my Query is to mark similar words.
CREATE CONSTRAINT ON (n:Word) ASSERT n.title IS UNIQUE
MATCH (n) WHERE ID(n)={id}
MERGE (o:Word{title:{title}})
WITH n,o MERGE n-[r:SIMILAR{location:'{location}'}]->o
RETURN ID(o)
n is a existing Word. I want to create the relationsship & the other Word (o) if they don't exist yet.
The Problem with this query is, that it works for one title, but if I use a Array with titles the title of the Word o is the whole Array.
Can you suggest me another Query that does the same and/or a way to pass multiple values to title.
I'm using the Neography Gem on Rails e.g. the REST API
To use individual values in a parameter array you can use FOREACH, something like
MATCH (n)
WHERE ID (n) = {id}
FOREACH (t IN {title} |
MERGE (o:Word {title:t})
MERGE n-[:SIMILAR]->o
)
If you want to pass location also as a parameter (it is actually a string literal in your current query), such that merge operations for n should happen for each title, location pair in a parameter array, you can try
FOREACH (map IN {maps} |
MERGE (o:Word {title:map.title})
MERGE n-[:SIMILAR {location:map.location}]->o
)
with a parameter that looks something like
{
"maps": [
{
"title":"neography",
"location":"1.."
},{
"title":"coreography",
"location":"3.."
}
]
}
Other suggestions:
It's usually not great to look up nodes by internal id from parameter. In some cases when chaining queries it may be fine, but in most cases label index lookup would be better: MATCH (n:Word {title:"geography"})
If you are not using the transactional cypher endpoint, give it a shot. You can then make one or more calls with one or more queries in each call within one transaction. Performance improves and you may find you don't need to send the more complex parameter object, but can send many simple queries.

Resources