How to strengthen relationship between nodes within same set

create

(n1:User{name:"User_A"}),

(n2:User{name:"User_B"}),

(n3:User{name:"User_C"}),

(b1:Book{title:"A"}),

(b2:Book{title:"B"}),

(b3:Book{title:"C"}),

(b4:Book{title:"D"}),

(b5:Book{title:"E"}),

(b6:Book{title:"F"}),

(b7:Book{title:"G"}),

(b8:Book{title:"H"}),

(n1)-[:HAS_READ]->(b1),

(n1)-[:HAS_READ]->(b3),

(n1)-[:HAS_READ]->(b7),

(n2)-[:HAS_READ]->(b1),

(n2)-[:HAS_READ]->(b2),

(n2)-[:HAS_READ]->(b3),

(n2)-[:HAS_READ]->(b4),

(n2)-[:HAS_READ]->(b5),

(n2)-[:HAS_READ]->(b6),

(n3)-[:HAS_READ]->(b4),

(n3)-[:HAS_READ]->(b5),

(n3)-[:HAS_READ]->(b6),

(n3)-[:HAS_READ]->(b8)

Since User C and B has read 3 books so how can I show C and B are related or similar and that similarity line between two nodes should get thicker (showing strength of relationship). This line should get thicker or show number of common documents they have read .

User B and A has read 2 common docs so their relationship strength should be 2

Since I cannot share actual data so sharing sample data shared by @glilienfield in some other question . Thanks @glilienfield for this .

You can't do this in Neo4j desktop, but you may be able to using Bloom. I am not a user, so I can't help answer that question.

@glilienfield can we show data in table or graph as user a and B as 2 nodes and relationship between them represents number of common documents they have read .

Visualization is not my goal to achieve. What I am trying to achieve here is as follows lets say user A and B has read 10 common documents . So we want to create recommendation tool which can suggest document to user A which has not read but B has read and vice versa .

More common documents they have read we assume more strength is their relation and hence recommendation tool will recommend more aggressively.

I hope my use case is clear .

Try this:

match(n1:User)-[:HAS_READ]->()<-[:HAS_READ]-(n2:User)
where elementId(n1) < elementId(n2)
return n1.name, n2.name, count(*) as noOfCommonBooks

Screen Shot 2024-01-02 at 5.57.15 PM

Note: the above assumes not two users have the same name, as the grouping is done by name. This can be corrected by grouping on the node first, then returning each node's name, or any other identifiable property.

can this be shown in graph also ?

@glilienfield can we show data in table or graph as user a and B as 2 nodes and relationship between them represents number of common documents they have read .

Visualization is not my goal to achieve. What I am trying to achieve here is as follows lets say user A and B has read 10 common documents . So we want to create recommendation tool which can suggest document to user A which has not read but B has read and vice versa .

More common documents they have read we assume more strength is their relation and hence recommendation tool will recommend more aggressively.

I hope my use case is clear .

try this:

match(n1:User)-[:HAS_READ]->()<-[:HAS_READ]-(n2:User)
where elementId(n1) < elementId(n2)
with n1, n2, count(*) as noOfCommonBooks
return n1, n2, apoc.create.vRelationship(n1,'HAS_COMMON_BOOKS',{number:noOfCommonBooks}, n2) as rel

Hello @glilienfield when I am running for my actual data

match(n1:ClientInfo)-[:ACCESSED]->()<-[:ACCESSED]-(n2:ClientInfo)
where n1.pcode= 3
with n1, n2, count(*) as noOfCommonDocs
return n1, n2, apoc.create.vRelationship(n1,'HAS_COMMON_DOCS',{number:noOfCommonDocs}, n2) as rel

It is not showing count on relationship and still showing similar in graph . Do I need to empty some cache .

You need change the relationship display setting to show the count. It will show the relationship type by default.

There are many ways to compute a similarity score. You can do it with cypher counting the number of common neighbours but you can also use one of the node similarity algorithms Node Similarity - Neo4j Graph Data Science

I have an example for how to use node similarity here: notebooks/ColabNotebook_Neo4j_GDS_Demo.ipynb at main · lqst/notebooks · GitHub

Note: My use of amount as the relationshipWeightProperty is not optimal, it was part of the exercise to improve that an use a better normalized weight for "relationship strength".

Once you have a User to User relationship with the score stored as a property on it. You can visualize it in neo4j bloom (rule based styling for the relationship type) or in neodash. I don't think you can have a dynamic line weight or colour in neo4j browser.

1 Like

Hello @glilienfield

I am trying to find that option >>>relationship display setting but not getting it .

Also I would like to understand meaning of match(n1:User)-[:HAS_READ]->()<-[:HAS_READ]-(n2:User)

I was trying to find in documents but not getting it

In cypher, you are asking to find patterns of nodes and relationships. Each found pattern is returned as one result. At first we where looking for users and the books they have read. This can be expressed with the following pattern:

(u:User)-[:HAS_READ]->(b:Book)

This says "fined all User nodes and their related Book nodes, related through a HAS_READ relationships. For each found result, assign the User node to the variable 'u' and the Book node to the variable 'b". The binding to variables is used to reference the entities in your query or return statement.

In your last request, you asked to find the number of books that each user has read in common. To do that, we need to specify a pattern that defines that relationship between two User nodes. The pattern I used was:

(n1:User)-[:HAS_READ]->()<-[:HAS_READ]-(n2:User)

This pattern is asking to find two User nodes that have a HAS_READ relationship to the same entity. In this example, I did not specify the type of node that they have in common (indicated by '()') because I knew from your data model that a User can only have a HAS_READ relationship to a Book node. If this was not the case and you could have a User be related to more than one type of node through a HAS_READ relationships, you can add the "Book" constraint as follows:

(n1:User)-[:HAS_READ]->(:Book)<-[:HAS_READ]-(n2:User)

When you match on the above pattern you will get rows of pairs of Users for each book they have read in common. To count the number of books in common, you can use the "count" aggregation clause. The key is to count the number of books in common for each pair of users. To do this, you use "n1" and "n2" as grouping keys, so the count is done for each distinct pair of nodes "n1" and "n2". This is what this query does. The variables outside the aggregation clause are used to group the rows to aggregate over.

match(n1:User)-[:HAS_READ]->()<-[:HAS_READ]-(n2:User)
return n1, n2, count(*) as noOfCommonBooks

Here is some reading on patterns:

Here is some reading on aggregation functions:

You can change what is shown in the Neo4j Desktop graph view by changing the Label and Relationship type display settings. You do that by clicking on a label or relationship and then selecting what to display in the 'caption'.

In the screenshot below, click on the "HAS_COMMON_BOOKS" relationship type on the right-hand side. The modal shown in the next screenshot will appear. In it, select what property you want shown as the caption.

Screen Shot 2024-01-03 at 9.49.26 AM

1 Like

Hello @glilienfield

Why am I seeing similar relationship type when I am running following

match(n1:ClientInfo)-[:ACCESSED]->()<-[:ACCESSED]-(n2:ClientInfo)
where n1.peopleCode = 304989205
with n1, n2, count(*) as noOfCommonDocs
return n1, n2, apoc.create.vRelationship(n1,'HAS_COMMON_DOCS',{number:noOfCommonDocs}, n2) as rel

image

@hakan.lofqvist1 makes a good point. The similarity score is a simple calculation that does not require the GDS library. By calculating it yourself, you eliminate the dependency on the GDS library. The biggest benefit is you don't need to project your graph and then run similarity scores on a static snapshot of your data.

I have used the Jaccard similarity score defined in the GDS manual to calculate the similarity in the following query. As you can see, the results are the same as what was calculated by the GDS library.

match(n1:User)-[:HAS_READ]->()<-[:HAS_READ]-(n2:User)
where elementId(n1) < elementId(n2)
with n1, n2, count(*) as commonBooks
with n1, n2, commonBooks, count{(n1)-[:HAS_READ]->()} as n1Books, count{(n2)-[:HAS_READ]->()} as n2Books
return n1.name, n2.name, commonBooks, n1Books, n2Books, toFloat(commonBooks) / toFloat(n1Books + n2Books - commonBooks) as similarity

Now you can combine everything to one query that derives the statistics and visualizes them through virtual relationships.

match(n1:User)-[:HAS_READ]->()<-[:HAS_READ]-(n2:User)
where elementId(n1) < elementId(n2)
with n1, n2, count(*) as commonBooks
with n1, n2, commonBooks, count{(n1)-[:HAS_READ]->()} as n1Books, count{(n2)-[:HAS_READ]->()} as n2Books
with n1, n2, commonBooks, round(toFloat(commonBooks) / toFloat(n1Books + n2Books - commonBooks), 2) as similarity
return n1, n2, apoc.create.vRelationship(n1,'SIMARITY',{similarity:similarity}, n2), apoc.create.vRelationship(n1,'HAS_COMMON_BOOKS',{commonBooks:commonBooks}, n2)

2 Likes

Hello @glilienfield

But In query we have not used similarity anywhere then why in graph I am seeing similar as relationship type . For now I dont want see those

match(n1:ClientInfo)-[:ACCESSED]->()<-[:ACCESSED]-(n2:ClientInfo)
where n1.peopleCode = 304989205
with n1, n2, count(*) as noOfCommonDocs
return n1, n2, apoc.create.vRelationship(n1,'HAS_COMMON_DOCS',{number:noOfCommonDocs}, n2) as rel

But above solution is great . Let me try to understand that and implement it

Use whatever you need. I was also trying to expose you to more ideas/concepts so you can learn.

@glilienfield @hakan.lofqvist1 Thank you its great help

1 Like

Sorry, I overlooked your last question. You are seeing other relationship types in your graph even though they were not included in the query because of the default behavior of the neo4j desktop application. It will show ALL relations between nodes displayed in the query results. You can turn this behavior off in the settings. Look for the gear icon in the lower left-hand corner. There is a checkbox near the bottom that references connecting nodes. Deselect it to disable this behavior.

Remember to rerun the query after deselecting the checkbox, as the change takes affect for subsequent queries.

Screen Shot 2024-01-02 at 1.00.05 PM

1 Like

Hello @glilienfield thank you so much . I have already figured it out and corrected it . Appreciate your efforts .

Hello @glilienfield as next part of POC now I need to create cluster of all users which have read common books . Can you give some idea which I use to implement? Some idea would be helpful .