I have photo albums and their photos stored in Neo4j. I would like to be able to find one album and get a certain amount of photos. The goal is to lazily load photos as required (pagination).
Now I can do the following to achieve what I want:
match(p:Photo)-[bt:BELONGS_TO]->(a:Album) where a.name = "Summer 2019" return a, collect(p)[..4] as photos
However I would like to be able to sort the list of photos by different criteria such as their upload date or creation date. I'm not exactly sure whether this is the best approach to do this.
match(p:Photo)-[bt:BELONGS_TO]->(a:Album) where a.name = "Summer 2019" return a, collect(p)[4..] as photos order by p.file_name
Fails and tells me the following:
In a WITH/RETURN with DISTINCT or an aggregation, it is not possible to access variables declared before the WITH/RETURN: p
I would like to keep the exact same format of the result (one album, one page of photos) if possible so that I don't have to do complicated mapping inside my application code:
╒══════════════════════╤══════════════════════════════════════════════════════════════════════╕
│"a" │"photos" │
╞══════════════════════╪══════════════════════════════════════════════════════════════════════╡
│{"name":"Summer 2019"}│[{"file_name":"cat.jpeg"},{"file_name":"dog.jpeg"},{"file_name":"birdi│
│ │e.jpeg"},{"file_name":"bird.jpeg"}] │
└──────────────────────┴──────────────────────────────────────────────────────────────────────┘
Is there a clean way to get this format while being able to sort the photos?
You need to ORDER BY before collecting your p nodes
MATCH (p:Photo)-[bt:BELONGS_TO]->(a:Album)
WHERE a.name = "Summer 2019"
WITH
a,
p
ORDER BY p.file_name
RETURN a, collect(p)[4..] as photos
Related
I have to make a query that returns me a club or clubs, where play the most amount of players that are not representing the country, from where the club is.
My query works fine, but I want to filter, so my result is ONLY clubs that size is the most.
As for now the biggest size is 4, and I have 4 clubs that have 4 players which were supposed to be there.
The only thing comes to my mind to filter it out was by using LIMIT 1 in the end, but then, I cut out three clubs, that also fill the predicate.
MATCH (c: Club)<-[r: PLAYS_FOR]-(p: Player)-[r2: REPRESENTS]->(n: NationalTeam)
WHERE c.country<>n.country
WITH c,collect(p.name) as list_players,n.country as country,size(collect(p.name)) as size
RETURN c,list_players,country,size ORDER BY size DESC LIMIT 1
edit:
I managed to do something like this, don't know if it's optimal, but it is working:
MATCH (c: Club)<-[r: PLAYS_FOR]-(p: Player)-[r2: REPRESENTS]->(n: NationalTeam)
WHERE c.country<>n.country
WITH c,collect(p.name) as list_players,n.country as country,size(collect(p.name)) as size
WITH c,list_players,country,size ORDER BY size DESC LIMIT 1
WITH size
MATCH (c: Club)<-[r: PLAYS_FOR]-(p: Player)-[r2: REPRESENTS]->(n: NationalTeam)
WHERE c.country<>n.country
WITH size,c,collect(p.name) as list_players,n.country as country,size(collect(p.name)) as size2 WHERE size(collect(p.name)) = size
RETURN c,list_players,country,size
If you install APOC Procedures, there is an aggregation function you can use to get the items associated with a maximum value, and this works even when multiple items are tied for that value: apoc.agg.maxItems()
The trouble now is that all the club-specific data needs to be encapsulated into the item itself, so you'll need to add them to a map and use the map as the item, and the size of the person collection as the value.
Also your aggregation isn't quite correct. You're collecting player names, but you have the country of the player as a part of the grouping key (when you aggregate, all non-aggregation terms form the grouping key), and that isn't likely want you want. Maybe you wanted the country of the club instead?
Try working from this:
MATCH (c: Club)<-[r: PLAYS_FOR]-(p: Player)-[r2: REPRESENTS]->(n: NationalTeam)
WHERE c.country<>n.country
WITH c,collect(p) as list_players
WITH apoc.agg.maxItems({club:c, players:list_players}, size(list_players)) as maxResults
UNWIND maxResults.items as result
WITH result.club as c, [player IN result.players | player.name] as list_players, maxResults.value as size
RETURN c,list_players,size
I am still rather new to Neo4j, Cypher and programming in general.
Is there a way to access the posted output below, i.e. access the "count" values for every "item“ (which has to be the pair), and also access the "item" values? I need the amount of how often a pair, i.e. specific neighboring nodes occur not only as information, but as values with which I can further work with in order to adjust my graph.
My last lines of code (in the preceding lines I just ordered the nodes sequentially):
...
WITH apoc.coll.pairs(a) as pairsOfa
WITH apoc.coll.frequencies(pairsOfa) AS giveBackFrequencyOfPairsOfa
UNWIND giveBackFrequencyOfPairsOfa AS x
WITH DISTINCT x
RETURN x
Output from the Neo4j Browser that I need to work with:
"x"
│{"count":1,"item":[{"aName“:"Rob","time":1},{"aName":"Edwin“,"time“:2}]},{„count“:4,“item":[{"aName":"Edwin","time":2},{"aName“:"Celesta","time":3}]}
...
Based on your code, your result should contain multiple x records (not a single record, as implied by the "output" provided in your question). Here is an example of what I would expect:
╒══════════════════════════════════════════════════════════════════════╕
│"x" │
╞══════════════════════════════════════════════════════════════════════╡
│{"count":1,"item":[{"aName":"Rob","time":1},{"aName":"Edwin","time":2}│
│]} │
├──────────────────────────────────────────────────────────────────────┤
│{"count":1,"item":[{"aName":"Edwin","time":2},{"aName":"Celesta","time│
│":3}]} │
└──────────────────────────────────────────────────────────────────────┘
If that is true, then you can just access the count and item properties of each x directly via x.count and x.item. To get each value within an item, you could use x.item[0] and x.item[1].
Asides: you probably want to use apoc.coll.pairsMin instead of apoc.coll.pairs, to avoid the generation of a spurious "pair" (whose second element is null) when the number of values to be paired is odd. Also, you probably do not need the DISTINCT step.
I am currently investigating how to model a bitemporal graph in neo4j. Unfortunately noone seems to have publicly undertaken this before.
One particular thing I am looking at is whether I can store in a new node only those values that have changed and then express a query that would merge all those values ordered by a given timestamp:
This creates the data I am playing with:
CREATE (:P1 {id: '1'})<-[:EXPANDS {date:5200, recorded:5100}]-(:P1Data {name:'Joe', wage: 3000})
// New data, recorded 2014-10-1 for 2015-1-1
MATCH (p:P1 {id: '1'}) CREATE (:P1Data { wage:3100 })-[:EXPANDS { date:5479, recorded: 5387}]->(p)
Now, I can get a history for a given point in time so far, e.g. like
MATCH (:P1 { id: '1' })<-[x:EXPANDS]-(d:P1Data)
WHERE x.recorded < 6000
WITH {date: x.date, data:d} as data
RETURN data
ORDER BY data.date DESC
What I would like to achieve is to merge the name and wage values such that I get a whole view of the data at a given point in time. The answer may also be that this is not really possible.
(PS: I say only in query, because I found a refactor function in apoc which does merge nodes, but that procedure actually merges and persists the node, while I would just want to query it).
As with most things, you can do it using REDUCE like so:
MATCH (:P1 { id: '1' })<-[x:EXPANDS]-(d:P1Data)
WITH x.date AS date, d AS data
ORDER BY date
WITH COLLECT(data) AS datas
WITH REDUCE(s = {}, y IN datas|
{name: COALESCE(y.name, s.name),
wage: COALESCE(y.wage, s.wage)})
AS most_recent_fields
RETURN most_recent_fields.name AS name, most_recent_fields.wage AS wage
You can do it in descending order instead (swap s and y inside the COALESCE statements if so), but there isn't really a way to shortcut processing the entire set of results from your queried time back to the start.
UPDATE: This will, of course, generate a Map and not a Node, but if you only want the properties and don't want to create a permanent record, a Map is actually better suited to your needs.
EXTENDED: If you don't want to specify which keys to use, you can do it without REDUCE like this instead:
MATCH (:P1 { id: '1' })<-[x:EXPANDS]-(d:P1Data)
WITH x.date AS date, d AS data
ORDER BY date
WITH COLLECT(data) AS datas
CREATE (t:Temp)
FOREACH(data IN datas|
SET t += data)
DELETE t
RETURN t
This approach does create a node, but if you DELETE it right before you RETURN it, it won't persist at all. += ensures that pre-existing properties aren't removed, only overwritten if the data node has existing values.
I am trying to build a Cypher query which allows me to build in-feed ads:
An example is how on the Facebook Mobile App an ad appears inside the feed for every X numbers of posts (Lets say 1 ad for every 5 posts on same feed).
So far I have this: "MATCH (P:Post) (A:Ad) return P,A"
Post would be the User's Posts.
Ad would be ads to put inside a User's feed.
I'm able to get both collections, but am lost on how to combine this to create an effect similar to in-Feed apps.
What is your actual use-case?
Do you have a feed of Ads somewhere and want to merge it with user's posts?
How do you model ad-feeds and post-feeds?
You probably also have Ad-Publishers, Categories etc? Same for posts?
So something like this:
MATCH (u:User {login:"john"})-[:POSTED]->(p:Post)
WITH p
LIMIT 20
MATCH (:Publisher {id:"3829472"})-[:PUBLISHED]->(ad:Ad)<-[:AD_CATEGORY]-(c)-[:POST_CATEGORY]->(p)
RETURN p,case when random() < 0.2 then ad else null end
You should probably look into graph modeling.
For actual cypher questions check the manual and the refcard.
Does it possible to have an order by "property" with a where clause and now the "index/position" of the result?
I mean, when using order for sorting we need to be able to know the position of the result in the sort.
Imagine a scoreboard with 1 million user node, i do an order by on user node.score with a where "name = user_name" and i wan't to know the current rank of the user. I do not find how to do this using order by ...
start game=node(1)
match game-[:has_child_user]->user
with user
order by user.score
with user
where user.name = "my_user"
return user , "the position in the sort";
the expected result would be :
node_user | rank
(i don't want to fetch one million entries at client side to know the current rank/position of a node in the ORDER BY!)
This functionality does not exist today in Cypher. Do you have an example of what this would look like in SQL? Would the below be something that fits the bill? (just a sketch, not working!)
(your code)
start game=node(1)
match game-[:has_child_user]->user
with user
order by user.score
(+ this code)
with user, index() as rank
return user.name, rank;
If you have more thoughts or want to start hacking on this please open an issue at https://github.com/neo4j/neo4j/issues
For the time being there is a work around that you can do:
start n=node(0),rank_node=node(1)
match n-[r:rank]->rn
where rn.score <= rank_node.score
return rank_node,count(*) as pos;
For live example see: http://console.neo4j.org/?id=bela20