<p dir="auto">E.g. if I want to de-duplicate tweets your friends are retweeting:</p>
<div class="highlight highlight-source-cypher notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="MATCH (friend) -[:retweeted]-&gt; (tweet)
RETURN DISTINCT tweet"><pre class="notranslate"><span class="pl-k">MATCH</span> (<span class="pl-smi">friend</span>) <span class="pl-c1">-</span><span class="pl-k">[</span><span class="pl-en"><span class="pl-k">:</span><span class="pl-en">retweeted</span></span><span class="pl-k">]</span><span class="pl-c1">-&gt;</span> (<span class="pl-smi">tweet</span>)
<span class="pl-k">RETURN</span> <span class="pl-k">DISTINCT</span> <span class="pl-smi">tweet</span></pre></div>
<p dir="auto">But still return who the first friend was to retweet that tweet (just like Twitter does).</p>
<p dir="auto">This won't work:</p>
<div class="highlight highlight-source-cypher notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="MATCH (friend) -[retweet:retweeted]-&gt; (tweet)
RETURN DISTINCT tweet, friend, retweet.time AS time
ORDER BY time"><pre class="notranslate"><span class="pl-k">MATCH</span> (<span class="pl-smi">friend</span>) <span class="pl-c1">-</span><span class="pl-k">[</span><span class="pl-smi">retweet</span><span class="pl-en"><span class="pl-k">:</span><span class="pl-en">retweeted</span></span><span class="pl-k">]</span><span class="pl-c1">-&gt;</span> (<span class="pl-smi">tweet</span>)
<span class="pl-k">RETURN</span> <span class="pl-k">DISTINCT</span> <span class="pl-smi">tweet</span>, <span class="pl-smi">friend</span>, <span class="pl-smi">retweet</span>.<span class="pl-smi">time</span> <span class="pl-k">AS</span> <span class="pl-smi">time</span>
<span class="pl-k">ORDER BY</span> <span class="pl-smi">time</span></pre></div>
<p dir="auto">Because <code class="notranslate">DISTINCT</code> looks at the entire row, and every row will have a different <code class="notranslate">tweet</code> + <code class="notranslate">friend</code> combination. The result: you get duplicated tweets.</p>
<p dir="auto">I can achieve today with this workaround involving aggregating and slicing:</p>
<div class="highlight highlight-source-cypher notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="MATCH (friend) -[retweet:retweeted]-&gt; (tweet)
WITH tweet, friend, retweet.time AS time
ORDER BY time
RETURN tweet, HEAD(COLLECT(friend)) AS friend"><pre class="notranslate"><span class="pl-k">MATCH</span> (<span class="pl-smi">friend</span>) <span class="pl-c1">-</span><span class="pl-k">[</span><span class="pl-smi">retweet</span><span class="pl-en"><span class="pl-k">:</span><span class="pl-en">retweeted</span></span><span class="pl-k">]</span><span class="pl-c1">-&gt;</span> (<span class="pl-smi">tweet</span>)
<span class="pl-k">WITH</span> <span class="pl-smi">tweet</span>, <span class="pl-smi">friend</span>, <span class="pl-smi">retweet</span>.<span class="pl-smi">time</span> <span class="pl-k">AS</span> <span class="pl-smi">time</span>
<span class="pl-k">ORDER BY</span> <span class="pl-smi">time</span>
<span class="pl-k">RETURN</span> <span class="pl-smi">tweet</span>, <span class="pl-c1">HEAD</span>(<span class="pl-c1">COLLECT</span>(<span class="pl-smi">friend</span>)) <span class="pl-k">AS</span> <span class="pl-smi">friend</span></pre></div>
<p dir="auto">That's pretty tedious and non-obvious. It'd be nice if I could simply specify which variables (columns) should be looked at for the <code class="notranslate">DISTINCT</code> instead of going through those hoops.</p>
<p dir="auto">Strawman proposal: simply specify <code class="notranslate">DISTINCT</code> <em>after</em> variable names, to mean that only those variables should be de-duplicated.</p>
<div class="highlight highlight-source-cypher notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="MATCH (friend) -[retweet:retweeted]-&gt; (tweet)
RETURN tweet DISTINCT, friend
ORDER BY retweet.time"><pre class="notranslate"><span class="pl-k">MATCH</span> (<span class="pl-smi">friend</span>) <span class="pl-c1">-</span><span class="pl-k">[</span><span class="pl-smi">retweet</span><span class="pl-en"><span class="pl-k">:</span><span class="pl-en">retweeted</span></span><span class="pl-k">]</span><span class="pl-c1">-&gt;</span> (<span class="pl-smi">tweet</span>)
<span class="pl-k">RETURN</span> <span class="pl-smi">tweet</span> <span class="pl-k">DISTINCT</span>, <span class="pl-smi">friend</span>
<span class="pl-k">ORDER BY</span> <span class="pl-smi">retweet</span>.<span class="pl-smi">time</span></pre></div>
<p dir="auto">Related: issue <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="21141272" data-permission-text="Title is private" data-url="https://github.com/neo4j/neo4j/issues/1322" data-hovercard-type="issue" data-hovercard-url="/neo4j/neo4j/issues/1322/hovercard" href="https://github.com/neo4j/neo4j/issues/1322">#1322</a>. This is a great example: the suggested answer in that SO post actually won't work. Adding the <code class="notranslate">time</code> to the <code class="notranslate">RETURN DISTINCT</code> (so that you can <code class="notranslate">ORDER BY</code> it) will mean de-duplication no longer happens. Hopefully a fix here will fix that case too.</p>
<p dir="auto">In Cypher, there is currently no way to set an existing Node's labels to an explicit set of labels. The SET statement can be used to add labels to the set of labels for a Node, but there is no way to update all of the labels for an exiting Node.  This is in contrast to Node properties where you can do SET n = {map_of_props} to set or update all properties of the Node. This makes it difficult to set the entire state of an existing Node (I consider both the properties and labels of the Node to be part of the state) in a single Cypher query.</p>
<p dir="auto">In our application, all Nodes have a unique id property (a GUID) and we use MERGE queries with ON CREATE/ON MATCH to implement "in place" create-or-update logic on Nodes using a query similar to:</p>
<p dir="auto"><code class="notranslate">MERGE (n:Node {guid:theguid}) ON CREATE SET n:label1:label2:label3, n = {props_for_create} ON MATCH SET n:label4:label5, n = {props_for_update} RETURN n</code></p>
<p dir="auto">This allows us to implement save logic without having to know whether the Node with the specified GUID exists (i.e., whether the operation is a create or update).  The intent is that an existing Node would have its properties set to the properties in the props_for_upate map and the labels set to "label4", label5. However, this doesn't quite work because <code class="notranslate">ON MATCH SET n:label4:label5</code> adds "label4" and "label5" to the Node rather than replacing the existing labels. For us to be able to implement the "update" part of the create-or-update logic efficiently, we need to be able to replace the entire state of the node including properties and labels.</p>