Boolean value return from Neo4j cypher query - neo4j

I have this query for returning the hashTag name, hashTag count and if it has the Featured label return true. HashTag node just has a tag property`. It's working :
MATCH (:RateableEntity)<-[:TAG]-(hashtag:HashTag:Featured)
WITH hashtag,
(CASE WHEN 'Featured' IN LABELS(hashtag) THEN true ELSE false END) AS HASHTAG_FEATURED_LABEL
RETURN hashtag.tag As HASHTAG_NAME,
COUNT(hashtag) as HASHTAG_FREQUENTLY,
HASHTAG_FEATURED_LABEL
ORDER BY HASHTAG_NAME ASC
SKIP 0
LIMIT 20
But I'm searching for a better way without Case.
Does anyone have any idea?
Thanks

You can just return the expression, no need for the CASE here :
MATCH (:RateableEntity)<-[:TAG]-(hashtag:HashTag:Featured)
WITH hashtag,
'Featured' IN LABELS(hashtag) AS HASHTAG_FEATURED_LABEL
RETURN hashtag.tag As HASHTAG_NAME,
COUNT(hashtag) as HASHTAG_FREQUENTLY,
HASHTAG_FEATURED_LABEL
ORDER BY HASHTAG_NAME ASC
SKIP 0
LIMIT 20

Related

Neo4j - UNION of 3 different queries

I have a problem with one composed query, which has three parts.
Get direct friends
Get friends of friends
Get others - just fill up space to limit
So it should always return limited users, ordered by direct friends, friends of friends and others. First two parts are very fast, no problem here, but last part is slow and it's getting slower while db is growing on size. There are indexes on Person.number and Person.createdAt.
Does anyone have an idea how to improve or rewrite this query, to be more performant?
MATCH (me:Person { number: $number })-[r:KNOWS]-(contact:Person { registered: "true" }) WHERE contact.number <> $number AND (r.state = "contact" OR r.state = "declined")
MATCH (contact)-[:HAS_AVATAR]-(avatar:Avatar { primary: true })
WITH contact, avatar
RETURN contact AS friend, avatar, contact.createdAt AS rank
ORDER BY contact.createdAt DESC
UNION
MATCH (me:Person { number: $number })-[:KNOWS]-(friend)-[:KNOWS { state: "accepted" }]-(friend_of_friend:Person { registered: "true" }) WHERE NOT friend.username = 'default' AND NOT (me)-[:KNOWS]-(friend_of_friend)
MATCH (friend_of_friend)-[:HAS_AVATAR]-(avatar:Avatar { primary: true })
OPTIONAL MATCH (friend_of_friend)-[rel:KNOWS]-(friend)
RETURN friend_of_friend AS friend, avatar, COUNT(rel) AS rank
ORDER BY rank DESC
UNION
MATCH (me:Person { number: $number })
MATCH (others:Person { registered: "true" }) WHERE others.number <> $number AND NOT (me)-[:KNOWS]-(others) AND NOT (me)-[:KNOWS]-()-[:KNOWS { state: "accepted" }]-(others:Person { registered: "true" })
MATCH (others)-[:HAS_AVATAR]->(avatar:Avatar { primary: true })
OPTIONAL MATCH (others)-[rel:KNOWS { state: "accepted" }]-()
WITH others, rel, avatar
RETURN others AS friend, avatar, COUNT(rel) AS rank
ORDER BY others.createdAt DESC
SKIP $skip
LIMIT $limit
Here are some profiles:
https://i.stack.imgur.com/LfNww.png
https://i.stack.imgur.com/0EO0r.png
Final solution is to break down the whole query into three and call them separately, in our case it won't reach 3rd query in 99% and first two are super fast. And it seems that even if it reach 3rd stage, it is still fast, so maybe UNION was slowing the whole thing down the most.
const contacts = await this.neo4j.readQuery(`...
if (contacts.records.length < limit){
const friendOfFriend = await this.neo4j.readQuery(`...
if (contacts.records.length + friendOfFriend.records.length < limit){
const others = await this.neo4j.readQuery(`...
merge all results
You're doing a lot of work in that third query before the limit. You may want to move the ordering and LIMIT up sooner.
It's also going to be more efficient to pre-match to the friends (and friends of friends) in a single MATCH pattern, we can use *0..1 as an optional relationship to a potential next node.
And just a bit of style advice, I find it a good idea to reserve plurals for lists/collections and otherwise use singular, as you will only have a single one of those nodes per row.
Try this out for the third part:
MATCH (me:Person { number: $number })
OPTIONAL MATCH (me)-[:KNOWS]-()-[:KNOWS*0..1 { state: "accepted" }]-(other:Person {registered:"true"})
WITH collect(DISTINCT other) as excluded
MATCH (other:Person { registered: "true" }) WHERE other.createdAt < dateTime() AND other.number <> $number AND NOT other IN excluded
WITH other
ORDER BY other.createdAt DESC
SKIP $skip
LIMIT $limit
MATCH (other)-[:HAS_AVATAR]->(avatar:Avatar { primary: true })
WITH other, avatar, size((other)-[:KNOWS { state: "accepted" }]-()) AS rank
RETURN other AS friend, avatar, rank
If we know the type of createdAt then we can add a modification that may trigger index-backed ordering which could improve this.

apoc.periodic.commit doesn't result in updates

The following query results in 10 updated nodes:
MATCH (a:ns3__Organization)-[r:ns4__isDomiciledIn]->(b:Resource)
WITH a,b LIMIT 10
SET a.isDomiciledIn = b.Country
I'm trying to apply it to my whole graph with apoc.periodic.commit through the following query:
CALL apoc.periodic.commit("
MATCH (a:ns3__Organization)-[r:ns4__isDomiciledIn]->(b:Resource)
WITH a,b LIMIT $limit
SET a.isDomiciledIn = b.Country
", { limit : 50000});
Somehow it results in 0 update. What am I doing wrong?
Thanks for your help.
You should try this one :
CALL apoc.periodic.commit("
MATCH (a:ns3__Organization)-[r:ns4__isDomiciledIn]->(b:Resource)
WHERE NOT a.isDomiciledIn = b.Country
WITH a,b LIMIT $limit
SET a.isDomiciledIn = b.Country
RETURN count(*)
", { limit : 50000});
You errors :
no count(*) at the end of your query. SO your query never ends
no WHERE clause to filter the result to only nodes that are not yet updated

neo4j cypher left padding String in Where Clause

I have a String property in my nodes where the length of the String isn't fix.
Now i must search the right node by this property but i get a fixed length value from another System. For Example my Node has the Value '0123' but I get the Information '000123' for searching.
I need a function like left padding with Zeros and this in the Where Clause like
MATCH (a:LABEL) where leftPad(a.property, 6, '0') = '000123' return a
LIMIT 1
Is something like this possible with a good Performance?
You could do this:
MATCH (a:LABEL)
WHERE SUBSTRING('00000', 0, SIZE(a.property)) + a.property = '000123'
RETURN a
LIMIT 1;
Or, if all the characters are numeric, then you could do this:
MATCH (a:LABEL)
WHERE TOINT(a.property) = TOINT('000123')
RETURN a
LIMIT 1;
However, it would be even better if you could just store the property value as an integer in the first place, and also compare it to an integer, which would be the fastest. This might be very easy to do, depending on your situation.
MATCH (a:LABEL)
WHERE a.property = 000123
RETURN a
LIMIT 1;
Try it with reduce:
MATCH (a:LABEL)
WHERE REDUCE(lp='', n in RANGE(0,5-size(a.name)) | lp+'0')+a. a.property = '000123'
RETURN a
or try it with regular expression:
MATCH (a:LABEL)
WHERE a.property =~ '(0){0,3}123'
RETURN a

Can UNION and WITH play together?

Is there any way to use the results of a UNION in another sub-query using a WITH clause?
I'm looking for something like
(
MATCH (me:Person)-[:RATES]->(r:Rating)-[:RATED_BY]->(them:Person)
WHERE me.ident = {id} AND r.date = {date}
RETURN them
UNION ALL
MATCH (me:Person)<-[:RATED_BY]-(r:Rating)<-[:RATES]-(them:Person)
WHERE me.edent = {id} AND r.date = {date}
RETURN them
)
WITH them
RETURN them.name, COUNT( them.name) as ratingCount
ORDER BY ratingCount DESC
LIMIT 10
only nothing like this is supported by cypher.
And yes, I know that in this case I should be using
MATCH (me:Person)-[:RATES|RATED_BY]-(r:Rating)-[:RATES|RATED_BY]-(them:Person)
WHERE me.ident = {id} AND r.date = {date}
RETURN them.name, COUNT( them.name) as ratingCount
ORDER BY ratingCount DESC
LIMIT 10
which is fine and dandy, but I think that I'm going to get some more complex requests down the road where this won't work.

Cypher Query, make a where by several nodes properties if they have the same type of relationship

If we have a node related with other nodes by the same type of relationship
classmetadata<-INSTANCE_OF-instance(TheNodeINeed)-RELATED_TO->...................
- ->listype(The owner(name=d,etc))
- ->listype(The state(name=x,etc))
- ->listype(The propertie(name=y,etc))
- ->listype(The location(name=z,etc))
The instance node to find, must be looking by a node classmetadata within a index by its name(this is easy) and instance name (this is easy too) and also by the listype.name=.. and listype.name=.. and listype.name=.. and here is the problem:
If I try looking just for the instance with name MyInstance who is RELATED_TO a owner with name d, here I only quering about one listype node there's no problem, this query works
START classmetadata = node:classes(name = "MyClassMetadata")
MATCH classmetadata<-[:INSTANCE_OF]-instance-[:RELATED_TO]->listype
WHERE instance.name="MyInstance" and listype.name = "d"
RETURN instance, listype
ORDER BY instance.name ASC skip 0 limit 10
but if I need to look for the instance with name MyInstance who is RELATED_TO a owner with name d and is also RELATED to state with name x and is also RELATED to propertie with name y there is a problem the query result is always empty, is there any way to filter about two o three or more nodes.properties(listype.name) at same time if they are related by the same type of relatioship?
something like this is not working
START classmetadata = node:classes(name = "MyClassMetadata")
MATCH classmetadata<-[:INSTANCE_OF]-instance-[:RELATED_TO]->listype
WHERE instance.name = "MyInstance" AND listype.name = "x"
AND listype.name = "y" AND listype.name="d" RETURN instance, listype
ORDER BY instance.name ASC skip 0 limit 10
I added the name property to each relationship in order to be sure that I am making the filtering in the right node.
START classmetadata = node:classes(name = "MyClassMetadata")
MATCH classmetadata<-[:INSTANCE_OF]-instance-[r1:RELATED_TO]->listype1,
instance-[r2:RELATED_TO]->listype2,
instance-[r3:RELATED_TO]->listype3
WHERE instance.name = "MyInstance" AND
r1.name="state" AND listype1.name = "x" AND
r2.name="property" AND listype2.name = "y" AND
r3.name="owner" AND listype3.name="d"
RETURN instance, listype
ORDER BY instance.name ASC skip 0 limit 10
So you want to find all listypes where the name property of listtype can be d or x or y (in your first code snippet)?
If so, then
START classmetadata = node:classes(name = "NodeType ")
MATCH classmetadata<-[:INSTANCE_OF]-instance-[:RELATED_TO]->listype
WHERE instance.state="good" and (not(listype.name in ["d","x","y"]))
RETURN instance, listype
ORDER BY instance.name ASC skip 0 limit 10
Your query above too would work...just refer to listtype uniformly- no need for listtype1, listtype2 etc.
START classmetadata = node:classes(name = "NodeType ")
MATCH classmetadata<-[:INSTANCE_OF]-instance-[:RELATED_TO]->listype
WHERE instance.state="good" and AND listype.name! =~ ".Po."
AND listype.name! =~ ".Me."
RETURN instance, listype
ORDER BY instance.name ASC skip 0 limit 10
Is that what you're looking for?

Resources