Cypher questions - return updates count, update 2 things in a single query

97 views
Skip to first unread message

Wujek Srujek

unread,
Oct 15, 2012, 4:56:30 AM10/15/12
to ne...@googlegroups.com
Hi. I have the following structure:
NodeA has relationships to multiple NodeB; the relationships have a property called 'preferred', which takes boolean values. I need to be able to atomically change the preferred NodeB. I am doing this with Cypher on version 1.8.
What I have now is two queries which look like this:

// reset the currently preferred, if any
start me = node({me}) match me-[r:LIKES]->t set r.preferred = false where r.preferred = true // this might result in no relationships changed, that's Ok - no preference yet
// set the new preferred
start me = node({me}), t = node({t}) match me-[r:LIKES]->t set r.preferred = true

(Parameters 'me' and 't' are Nodes that I already have and don't have to look up.)

This will be done in a single transaction so that it is atomic. However, I have a couple of questions:

1. Is it possible to do such a thing in a single query? Is this practical? Is it better to issue 2 simpler queries than 1 complex?
2. I would like to learn if the second query actually changed any relationship. This is for error checking - node 't' in the second query might not be related to 'me' at all, in which case the query would change 0 relationships. I think the easiest way for me would be to check the count of changed relationships if I could return that, and if it is 0, node 't' was unrelated to 'me'. How can I express this in Cypher?

Regards,
wujek

Abdul Azeez Shaik

unread,
Oct 15, 2012, 6:56:21 AM10/15/12
to ne...@googlegroups.com
Hi Wujek,

You can use WITH clause to have two sub-queries in one query.
Your query would be,
start me = node({me}), like=node{{like}} match me-[r:LIKES]->t set r.preferred = false where r.preferred = true
WITH me, like
match me-[r:LIKES]->t set r.preferred = true

Hope this helps.

Thanks,
Abdul

--
 
 

Andres Taylor

unread,
Oct 15, 2012, 7:03:30 AM10/15/12
to ne...@googlegroups.com
On Mon, Oct 15, 2012 at 10:56 AM, Wujek Srujek <wujek....@gmail.com> wrote:

2. I would like to learn if the second query actually changed any relationship. This is for error checking - node 't' in the second query might not be related to 'me' at all, in which case the query would change 0 relationships. I think the easiest way for me would be to check the count of changed relationships if I could return that, and if it is 0, node 't' was unrelated to 'me'. How can I express this in Cypher?

We actually already keep this information, and it is exposed in the Scala API, but not in the Java API. We should definitely move that over. 


Andrés

wujek

unread,
Oct 17, 2012, 4:27:43 AM10/17/12
to ne...@googlegroups.com
Hi.
Thanks for your answers.
So how do I do the latter (getting the count of changed nodes) now, in Java / Groovy? Is the only way issuing another query?

As a side question - why this duality in APIs? Why does Java need a special package with facade-like classes that delegate to the Scala API? Is Java-Scala integration so difficult on the Java side that there is need for such solutions? It is an honest question, as I never used Scala classes from Java, but I used Groovy without any problems whatsoever, and this comes rather surprising.

wujek

Peter Neubauer

unread,
Oct 17, 2012, 5:32:06 AM10/17/12
to ne...@googlegroups.com
Wujek,
mostly, the Scala Iterators and Collections used internally in Cypher
do not have any easy resolution in Java-land. It is unfortunate that
the class names overlap, we will pull them apart at some point. You
are right, there should only be ONE public API, and most probably that
is going to be the Java API since it is most applicable over different
langauges.

Cheers,

/peter neubauer

G: neubauer.peter
S: peter.neubauer
P: +46 704 106975
L: http://www.linkedin.com/in/neubauer
T: @peterneubauer

Neo4j 1.8 GA - http://www.dzone.com/links/neo4j_18_release_fluent_graph_literacy.html
> --
>
>

wujek

unread,
Oct 17, 2012, 3:01:45 PM10/17/12
to ne...@googlegroups.com
Hi. This:

start me = node({me}), like=node{{like}} match me-[r:LIKES]->t set r.preferred = false where r.preferred = true
with me, like
match me-[r:LIKES]->t set r.preferred = true

doesn't work for me in the following scenario: there is no preferred yet (all relations have preferred = false), so the first part doesn't update anything, and then the second doesn't update anything as well.
When I first set a preferred to some node, then the query works fine. Which means I cannot be used as is in my use case, as there is no preferred initially, and this seems to prevent setting one.

Am I doing something wrong?

wujek

Abdul Azeez Shaik

unread,
Oct 17, 2012, 4:03:02 PM10/17/12
to ne...@googlegroups.com
AFIK, WHERE predicate can be removed, as anyways r.preferred is false.
But, i don't know what would be the performance effect on that. May be Peter or Michael can answer this better.

BTW, what is your data size?

Thanks,
Abdul

--
 
 

Abdul Azeez Shaik

unread,
Oct 17, 2012, 4:06:53 PM10/17/12
to ne...@googlegroups.com
Also, you can do other way round,

start me = node({me}), like=node{{like}} match me-[r:LIKES]->like set r.preferred = true
WITH me
match me-[r:LIKES]->t set r.preferred = false where r.preferred = true

wujek

unread,
Oct 18, 2012, 12:31:19 PM10/18/12
to ne...@googlegroups.com
Hi. I still can't get it to work.
Your comment about getting rid of the r.preferred predicate as it is always false is wrong - it is false only the first time, when no preference is defined; after the initial preferred is set, setting the next one must unset the previous one, which will then mean the predicate is will find something. And this predicate proves to be the problematic one, as the 'with' subquery doesn't work if the where is false. I am not sure why that is, and the documentation on 'with' is very vague on this.

The second option of inverting the query parts also doesn't work. I also don't follow it logically - whereas the first part sets the new preferred to true for a reference to the node I want, the second part will just reset it to false, as it does that for every relationship of type LIKE, right?

Maybe I wasn't specific enough about what I want to achieve. So: A person has many things that she likes, and there can be either one or no preferred thing. If there is none, setting a new preferred will just set it; if there is one already, setting another thing as preferred must unset the previously set one.

I am able to do it with 2 queries - first unset the previous preferred thingy, and then set the new one. It seems it is not possible to do it in just one query - it's fine, I was just curious if this is possible.

Thanks for your help, though.

wujek

Wes Freeman

unread,
Oct 18, 2012, 1:41:41 PM10/18/12
to ne...@googlegroups.com
It is better to do it with one query, if possible, because it creates a sort of transaction (and is obviously more efficient over REST especially, since it's one request instead of two).

I didn't have time to look at this before, but here goes a pretty hacky attempt:

start me=node(3), like=node(2) 
match me-[l:likes]->() 
// this where is optional, but this way there's less modified
where not(me-[l]->like) and l.preferred! = true 
set l.preferred = false 
// hack to always return a row (we don't even use the count)
with count(*) 
// we have to re-start the query, since the with wiped out our previous named vars
start me=node(1), like=node(3) 
match me-[l:likes]->like 
set l.preferred=true 
return l,me, like; 

Wes

--
 
 

Wujek Srujek

unread,
Oct 18, 2012, 2:12:50 PM10/18/12
to ne...@googlegroups.com
I start the transaction myself before the first query, and finish it after the second. As far as I know, cyhper will start own transactions for write queries if there is no active transaction - so in this case the transaction penalty should not exist, I think?
Will try your approach as soon as I can.
wujek

--
 
 

Reply all
Reply to author
Forward
0 new messages