neo4j order by on relationship not looking at index - neo4j

I have been looking into this problem for while now. I have neo4j setup with the following schema :
User has friends and User likes movie. One of my primary usecases is to get all the movies liked by all my friends. This is how I am doing it:
start user=node:userIndex(uid="1234")
match user-[friend_rela:FRIENDS]-(friend)-[movie_rela:LIKE]->movie
return distinct movie,movie_rela,friend
order by movie_rela.timeStamp desc
skip 0
limit 5;
It works allright, but the time taken by the query is in the order of ~10seconds. If I remove the 'order by movie_rela.timeStamp desc' it comes back in around 2 seconds. I have indexed the timestamp property in the relationship. I then read that Neo4J does not honor indexes on an Order by clause. Is there some way I can fix this ? The above query is one of my primary usecases.
Thanks.

You can redesign your graph. Like eg. suppose a friend can like multiple movies and different timestamps. So your graph can be
(u:User)-[:FRIENDS]->(friend:User)-[r1:Like]->(m1:Movie)-[r2:Like]->(m2:Movie)-...
Where the relationship Like is created from User in an order from latest to oldest. And r1,r2 will contain the timestamp as well as userid to uniquely form the path from a particular user to a movie in the entire chain. The Movie connected to User directly is the latest and as the distance (the number of Like relationship) increases between the User Node and the mOvie node, that particular movie is watched oldest.(you can reverse the order as per your convenience)
The benefit of having such design will be that there would be no requirement of ordering based on timestamp.
For any user if you wish to get all the movies he liked then just run
Match (u:User)-[:Like*1..]->(m:Movie) return m
here m will be returned in order of the kind of design you chose ie either ascending order or descending order of the movie watched timestamp.

Related

How to design neo4j db nodes that have ordered relationship with other nodes

I'm trying to design a Neo4j graph database, and I will illustrate my specific requirement. I'm designing an app that allows users to collaborate on the books and magazines they have read. Requirements:
The same Book can be read by multiple people
The order in which a specific person reads the books is important. For example, I want to be able to represent that Person A read Books B1 and B2 in that order, while Person B may have read Books B3, B2 and B1 in that order.
I'm thinking of having nodes representing a Book, Magazine, Person, etc.
What is the best way to ensure the order/sequence information? A couple of options I thought about:
Store a order ID or timestamp in the relationship between a Person and a Book node and use that to query all Books read by the person in the right order.
Store a Next/Previous relationship between Book nodes, but this approach will not work because the order can vary depending on which person read the books.
To me this is the way to go, easy and effective.
Store a order ID or timestamp in the relationship between a Person and
a Book node and use that to query all Books read by the person in the
right order.
Storing a Next/Previous relationship will not work, because you would have to save UserID in the NEXT relationship between books and that is just nonsense.

How do I represent a tags relationship in neo4j where user needs to be identified

So here's the problem I have with my data model
I have artists, users and tags
Tags are unique data objects that I am storing in nodes.
Users can tag artists with certain tags
I started with the following relationship
(user)-[:tags]->(tag)-[:on]->(artist)
OFcourse this fails to identify the user who tagged the artist.
Then I thought of trying the following approach
(user)-[:tags]->(artist)->[:with]->(tag)
Here, I can identify the artist, but cannot identify what the tag for the artist was.
I am a little lost here. I know I could simply go
(user)-[:tags {tagname}]->artist
But is there any way of representing tag as an independent entity while still maintaining data associated on both ends
You want an hypergraph with edges connecting more than 2 vertices (the user, the tag, the artist).
However, Neo4j is not an hypergraph implementation, so you'll need to introduce a node representing the "user tag" and connected to the 3 nodes with regular relationships:
MATCH (user:User {uuid: {userId}}),
(tag:Tag {uuid: {tagId}}),
(artist:Artist {uuid: {artistId}})
CREATE (user)-[:USER_TAGS]->(userTag:UserTag)-[:USES_TAG]->(tag),
(userTag)-[:TAGS_ARTIST]->(artist)
Neo4j is a property graph model. Generally:
Because hyperedges are multidimensional, hypergraph models are more generalized than property graphs. Yet, the two are isomorphic, so you can always represent a hypergraph as a property graph (albeit with more relationships and nodes) – whereas you can’t do the reverse.
https://neo4j.com/blog/other-graph-database-technologies/
One option is to break up this up into two discrete pieces: the tags that are applied to the user (assuming a unique constraint on the tag name), and the tagging relationship from the tagger to the taggee. Unsure whether your system only allows use of predefined tags, or if users are allowed to dynamically create them.
Let's assume for the moment that tags are predefined: You have nodes with the :Tag label, and you might use queries on that label to generate lists of tags users can use, or to use for autocompletion of tags as the user types.
So say a user wants to tag an artist with a tag. This will trigger an operation that first tags the artist with the tag, and then creates the :tags relationship between the user and the artist.
MATCH (t:Tag {name:tagname}), (a:Artist {id:artistID}), (u:User {id:userID})
MERGE (t)<-[:taggedAs]-(a)
MERGE (user)-[:tags {tagname}]->(artist)
The advantage of this approach is that you preserve both pieces of information (that a user has tagged an artist with specific tags, and that the artist is tagged as certain tags) in such a way that it's easy to query both pieces of information: given a user and an artist, we can quickly figure out the :tags relationships between them and get the tags with those names. We can also easily query what tags apply to the artist from all users without the expense of having to iterate through every single :tags relationship from all users.
The downside is that tag removal by a user is a more complex operation, possibly with a race condition: the :tags relationship between the user and artist has to be deleted, then all other :tags relationships from other users to that artist need to be checked to see if that tag still applies to the user, or if we need to remove it. You may need locks on this operation to prevent a race condition. If tag removal by a user is not allowed or is rare, then this could be an acceptable solution.
you can tag artist with the help of tagId to identify
(user)-[:tags {tagId}]->(artist)-[:with]->(tag {tagId})

Preload joined associations matching a condition with Rails

I want to display only the users who have a given skill and the following query works properly:
#users.joins(:personal_skills).where(personal_skills: search_conditions).distinct
Now in the search results, near a user, I want to display his personal_skill, that matching the wherecondition.
I can simply use user.personal_skills.where(search_conditions) for each user but that would cause the N+1 query problem.
How can I avoid that?
I mean, the Rails-way, otherwise just iterating over the returned rows would accomplish the task. Indeed each row contains both user data and the joined skill data: the problem is related to the object relational mapping.
Simply substituting joins with includes is not a solution because that would preload user.personal_skills and not the filtered set user.personal_skills.where(search_conditions) which is what I want to achieve.
You can get the users from the personal_skills
PersonalSkill.joins(:user).where(search_conditions, where(:user_id => #users.map(&:id))
with the result you can group the skills by the user

Neo4j - Searching from an array of nodes

In my situation I've a bunch of nodes that represent the users
and they have relation to books they read.
This user have a property that says where they are from, and I added them to an index, based on their country.
So I would like to search in the index for users from one country, and list the books that people from there read more, some sorting by grouping.
could any one give me a help how to do this?
I'm having some trouble getting users from the index, and doing the query
Couple of assumptions based on your descriptions:
users have a country property, it contains e.g. France as value
you have a index called users and you store the user node's country property there
relationship type to connect users and books is READ
book nodes have a title property
Based on these assumptions the cypher query would look like:
start user=node:users(country='France')
match user-[:READ]->book
return book.title, count(*) as rank
order by rank desc
limit 20
side note: best approach to ask this kind of questions is to create a sample graph on http://console.neo4j.org and share your setup on SO.

rails 3.1 How to retrieve in a query those elements with the maximum number of relationships in a many to many?

I have tried different combinations but it seems that I cannot work this out.
I want to retrieve, from an Event model, those events which have the biggest number of users.
For example, I retrieve users of an event like this
#users = Event.find(x).users
They can be counted using this
Event.find(x).users.count
So, How should be done to order the list by the number of users each event has. And then retrieve the 8 first?
The same issue was resolved in: How to get highest count of associated model (Rails)?
Event.order("events.users_count DESC")

Resources