I have a node which has a property named authentication\username but when I try to use the cypher like:
match (n) where n.`authentication\username` = 'user' return n
neo4j shows an error
Neo.ClientError.Statement.SyntaxError
Invalid input 's': expected four hexadecimal digits specifying a unicode character
what is wrong with that?
Thanks
I am unsure, how this property got created, but now that it's there, it's pretty evident from the error message that \u in the property name is considered a unicode character. Directly matching, will not work here, so we will have to figure out the workarounds. I will suggest two workarounds, using a predicate function, any:
match (n) where any(property IN keys(properties(n)) WHERE property = "authentication\\username" AND apoc.map.get(properties(n), property) = "user") return n.
match (n) where any(property IN keys(properties(n)) WHERE property STARTS WITH "authentication" AND property ENDS WITH "username" AND apoc.map.get(properties(n), property) = "user") return n
In both the queries, we loop over the keys of the properties of a node.
In the first one, we look for the exact match of the key, notice I have escaped the \ while matching. In the second one, we look for key starting with authentication and ending with username, since that suits our pattern. Finally, I read the matching key value, using APOC map function. So you need to have APOC library installed.
One of them should definitely work for you. Try them out.
Related
I have a graph that consists of a set of disjoint family trees.
I have a working query that has a few OPTIONAL MATCH statements, which allow me to get only the immediate parents and siblings of someone in the main_person's family tree, assuming that those relatives are of interest to us:
MATCH (p:Person {main_person: 'y'})
OPTIONAL MATCH (p)<-[]-(parent:Person)
WHERE parent.`person_of_interest` = 'y'
OPTIONAL MATCH (parent:Person)-[]->(sib:Person)
WHERE sib <> p
AND sib.`person_of_interest` = 'y'
RETURN
p, parent, sib;
But say I want to qualify this by making sure:
at least one member of a family has a test_me = 'y' property. This can be a far, distant member of the family. It definitely doesn't have to be family member that is a person_of_interest, or is a close family member.
If at least one of them has this property, then we can return the family members we are looking for. But if nobody has the property, then we don't want any results for that family.
I'm not sure how to construct this. I keep trying to start with the test_me = 'y' part, and carry it with a WITH:
MATCH (p:Person)-[]-(m)
WHERE ANY m.test_me = 'y'
WITH p, m
. . .
Maybe it should be more like:
MATCH (p:Person {main_person: 'y'})
OPTIONAL MATCH (p)<-[]-(parent:Person)
OPTIONAL MATCH (parent:Person)-[]->(sib:Person)
WHERE sib <> p
HAVING <condition here>
RETURN
p, parent, sib;
If this were SQL, I'd try to use a temp table to pipe things along.
None of it is really working.
Thanks for reading this.
[UPDATED to answer updated question]
This query may work for you (or it may run out of memory or appear to run forever):
MATCH (p:Person {main_person: 'y'})
WHERE EXISTS((p)-[*0..]-({test_me: 'y'}))
OPTIONAL MATCH (p)<--(parent:Person)
WHERE parent.person_of_interest = 'y'
OPTIONAL MATCH (parent:Person)-->(sib:Person)
WHERE sib <> p AND sib.person_of_interest = 'y'
RETURN p, COLLECT(parent) AS parents, COLLECT(sib) AS sibs;
The [*0..] syntax denotes a variable length relationship search where the matching paths can have 0 or more relationships. The reason the query uses a lower bound of 0 instead of 1 (which is the default) is this: we also want to also test whether p itself has the desired test_me property value.
However, variable length relationship searches are notorious for using a lot of memory or taking a long time to finish when no upper bound is specified, so normally a query would specify a reasonable upper bound (e.g., [*0..5]).
By the way, you should probably pass values such as 'y' as parameters instead of hard-coding them.
You're definitely on the right track, I think you already have your answer even if you don't realize it.
What you have in your description works as the start of your query, with just a few modifications:
MATCH pattern=(p:Person{main_person: 'y'})-[*]-()
WHERE ANY (person IN nodes(pattern) WHERE person.test_me = 'y')
WITH p
...
The variable relationship lets you consider every person in the tree (if there are non-family relationships in your graph, you'll want to use types on your relationship to ensure you're only considering a single family's tree), as well as the main_person. If nobody in p's family tree has your desired property, p will be null, and any subsequent matchings using p will yield no results. This should let you specify the rest of the query freely, and as long as all matches include p, you shouldn't get any results at the end for families without the desired property value.
EDIT fixed my query a bit, the ANY() clause wasn't written correctly.
Is something wrong with this cypher query
MATCH (owner:SidNode)<-[:OWNED_BY]-(acl:AclNode)-[:SECURES]->(class:ClassNode)
OPTIONAL MATCH (acl)<-[:COMPOSES]-(ace:AceNode)-[:AUTHORIZES]->(sid:SidNode)
WITH acl, ace, owner, sid, class
WHERE (acl.objectIdIdentity = {objectIdIdentity1} AND class.className = {className1})
RETURN
owner.principal AS aclPrincipal,
owner.sid AS aclSid,
acl.objectIdIdentity AS objectIdIdentity,
ace.aceOrder AS aceOrder,
ID(acl) AS aclId,
acl.parentObject AS parentObject,
acl.entriesInheriting AS entriesInheriting,
ID(ace) AS aceId, ace.mask AS mask,
ace.granting AS granting,
ace.auditSuccess AS auditSuccess,
ace.auditFailure AS auditFailure,
sid.principal AS acePrincipal,
sid.sid AS aceSid,
class.className AS className
ORDER BY acl.objectIdIdentity ASC, ace.aceOrder ASC
It is returning null values for ace nodes even though there are multiple nodes available in graph db.
But some times it is returning proper values like 4 rows if there are 4 ace nodes in db.
code i am writing is about spring security acl
reference link:
https://github.com/shazin/spring-security-acl-neo4j/blob/master/src/main/java/org/springframework/security/acls/neo4j/Neo4jLookupStrategy.java
Please suggest modifications.
Your problem comes from OPTIONAL MATCH, according to Neo4j's documentation, OPTIONAL MATCH returns NULLif the property or the element is not found.
You are getting NULL values because of this. If your acl node doesn't have any ace node related to him, the node will be replaced with NULL
You bind the ace nodes in an optional match. When that optional match fails to match something, ace will be null.
If you think the optional match ought to be successful in cases when it is not, maybe you could provide an example. A good way to do so is to create a small sample graph at http://console.neo4j.org
The conditions in your WHERE clause look like they belong with your MATCH clause. You might want to move the WHERE clause up and you can then remove the WITH altogether. It won't affect nulls, but it will make your query more efficient and more readable. (Also, you don't need parentheses in your WHERE clause.)
MATCH (owner:SidNode)<-[:OWNED_BY]-(acl:AclNode)-[:SECURES]->(class:ClassNode)
WHERE acl.objectIdIdentity = {objectIdIdentity1} AND class.className = {className1}
OPTIONAL MATCH (acl)<-[:COMPOSES]-(ace:AceNode)-[:AUTHORIZES]->(sid:SidNode)
RETURN ...
I have a domain class which has a property by name "alias" which is an arraylist of strings like below:
private List<String> alias;
alias contains the following values: {"John Doe","Alex Smith","Greg Walsh"}
I'd like to be able to make a query like: "I saw Smith today" using my repository query shown below and get the array value Output "Alex Smith":
#Query("MATCH (p:Person) WHERE {0} IN p.alias RETURN p")
Iterable<Person> findByAlias(String query);
I tried a bunch of different queries like the one shown above but this would only match if the input query matches the array value exactly.
I want do a input query sub-string match with the array values.
Eg:
Input Query: "I saw Smith today"
Output: "Alex Smith"
Summary
It is sort of possible to do what you want, but the query will be horribly ugly and slow. You'd be better off using nodes and relationships instead of collection properties: it will make your queries more sensible and allows you to use a full-text index. You should also figure out what part of the 'input string' you are looking for before you send your query to the database. As it stands, you are confusing the regex pattern with the data it is supposed to match and even if it were possible to express your intention as a regex it would be much better to do that processing your application, before sending the query.
1) WHERE ... IN ... doesn't do regex
WHERE x IN y will not treat x as a regular expression, it will take x's value for what it is and look for an exact match. WHERE ... IN ... is analogous to WHERE ... = ... in this sense, and you would need the analogue of =~ for collections, something like IN~, for this. There is no such construct in Cypher.
2) You can do regex over collections with predicates, but it is inefficient
You can use a string as a regular expression to test for matches over a collection by using a predicate like ANY or FILTER.
CREATE (p:Person {collectionProperty:["Paulo","Jean-Paul"]})
and
WITH "(?i).*Paul" as param
MATCH (p:Person)
WHERE ANY(item IN p.collectionProperty WHERE item =~ param)
RETURN p
will return the node because it makes a successful regular expression match on "Jean-Paul".
This, however, will have terrible performance since you will run your regular expression on every item in every collectionProperty for every :Person in your database. The solution is to use a full-text index, but his query can't use indices for two reasons:
the values you are querying are in an array
you are using regular expressions to filter results instead of doing an index query
3) You can't do regex over collections at all with your kind of input
The biggest problem with your query is that you are trying to turn "I saw Smith today" into "Smith" by adding some regular expression sugar. How do you intend to do that? If you use the string as a regular expression, each of those characters is a literal character expected to be in the data that you match it on. You are confused about .*, which when used in 'Smith.*' would match 'Smith' plus zero or more additional characters in the data. But you try to use it to say that zero or more characters may follow something in the pattern.
Take the query in your comment:
MATCH (p:Person)
WHERE '(?i).*I saw Smith today.*' IN p.alias
RETURN p
The regular expression '(?i).*I saw Smith today.*' will match
ignoring the case of the literal string–'i SAW smith TOday', etc.
with zero or more characters before and after the literal string–'Yes, I saw Smith today and he looked happy.'
But adding .* won't somehow magically make the pattern mean '.*Smith.*'. What's more, it's almost impossible to express 'I saw Smith today' as a subset of 'Alex Smith' by any amount of added regular expression sugar. Instead, you should process that string and figure out what parts of it you want to use in a regular expression before you send your query. How is the database to know that 'Smith' is the part of the input string that you want to use? However it is that you know it, you should know it before you send the query, and only include that relevant part.
Aside: examples of added regular expression sugar that won't work and why
You could insert a ? after each character in the pattern to make each character optional
RETURN "Smith" =~ "I? ?s?a?w? ?S?m?i?t?h? ?t?o?d?a?y?"
But now your pattern is way too loosie goosie, and it will match strings like 'I sat today' and 'sam toy'. Moreover, it won't match 'Alex Smith' unless you prepend .*, but then it is even more loosie goosie and will match any string whatever.
You could divide characters that belong together into groups and make the groups and the spaces between them optional.
RETURN "Smith" =~ "(I)? ?(saw)? ?(Smith)? ?(today)?"
But this also is too broad, fails to match 'Alex Smith' and will match any string whatever if you prepend .*.
4) Bad solution
The only 'solution' I can think of is a hideous query that splits your string on whitespace, concatenates some regex sugar into each word and compares it as a regular expression in a predicate clause. It really is hideous, and it assumes that you already know that you want match the words in the string and not the whole string, in which case you should be doing that processing before you send your query and not in Cypher. Look upon this hideousness and weep
WITH "I saw Paul today" AS paramString
MATCH (p:Person)
WHERE ANY (param IN split(paramString, ' ')
WHERE ANY (item IN p.collectionProperty
WHERE item =~('(?i).*' + param)))
RETURN p
5) Conclusion
The conclusion is as follows:
1) Change your model.
Keep a node for each alias like so
CREATE (a:Alias)
SET a.aliasId = "Alex Smith"
Create a full-text index for these nodes. See blog and docs for the generic case and docs for SDN.
Connect the nodes that now have the alias in a collection property to the new alias node with a relationship.
Look up the alias node that you want and follow the relationship to the node that 'has' the alias. A node can still have many aliases, but you no longer store them in a collection property–your query logic will be more straightforward and you will benefit from the full-text lucene index. Query with START n=node:indexName("query") when using cypher and use findAllByQuery() in SDN. This is necessary for the query to use the full-text index.
Your query might then finally look something like
START n=node:myIndex("aliasId:*smith")
MATCH n<-[:HAS_ALIAS]-smith
RETURN smith
2) Don't do all your work in the database.
If your program is supposed to receive a string like 'I saw Smith today' and give back a node based on a pattern match on 'Smith', then don't send 'I saw' and 'today' to the database. You're better off identifying 'Smith' as the relevant part of the string in your application–when you send the query you should already know what it is you want.
You should use something like this:
MATCH (n:Test)
WHERE single(x IN n.prop WHERE x = "elem1")
RETURN n
It checks that collection has exactly one "elem1".
More info.
Let's suppose we have this simple pattern:
p=(a)-[r]-(b)
where nodes a and b have their own properties already set in the WHERE clause (e.g. a:Movie AND a.title = "The Matrix" AND b:Movie).
I'd like to add another condition in the WHERE clause like
LEGHT(p) =2 OR LENGTH(p)>6
(not the correct syntax, I know)
As far as I know, it is possibile to specify the length of a path in the MATCH clause with the syntax [r*min..max] but that doesn't cover the case I'm looking for.
Any help would be appreciated =)
Yes, that does work in neo4j, exactly as you've specified.
Sample data:
create (a:Foo)-[:stuff]->(b:Foo)-[:stuff]->(c:Foo);
Then this query:
MATCH p=(x:Foo)-[:stuff*]->(y:Foo)
WHERE length(p)>1 RETURN p;
Returns only the 2-step path from a->b->c and not either one-step path (a->b or b->c)
Does this work for you?
MATCH p=(a)-[r*2..]-(b)
WHERE LENGTH(r) = 2 OR LENGTH(r) > 6
RETURN p
Note that with a large DB this query can take a very long time, or not finish, since the MATCH clause does not set an upper bound on the path length.
I have a relation type named str-dist
The hyphen is making cypher very unhappy. What is the correct way to handle the hyphen? (We ultimately resolved this by renaming the relationsip type, but I remain curious for learnings' sake)
// Intended Query:
MATCH n:Cars-[:str-dist]-m:Cars
RETURN n, m
// Tried all of the following, with no luck
MATCH n:Cars-[:str`-dist]-m:Cars
MATCH n:Cars-[:str\-dist]-m:Cars
MATCH n:Cars-[:"str-dist"]-m:Cars
MATCH n:Cars-[:'str-dist']-m:Cars
MATCH n:Cars-[:`str-dist`]-m:Cars
The Neo4j manual uses backticks to escape special characters in an identifier or relationship type:
MATCH n:Cars-[:`str-dist`]-m:Cars