Search code examples
c#neo4jcypherneo4jclient

How to unwind two lists with neo4jclient?


I have a method which I pass the title of an existing node, and two lists. One containing titles for new nodes and one containing the count which should be added to the new relationships as a property. Both lists contain the same number of items.

I unwind the list of new articles and can successfully add the new nodes using this code:

public async Task AddArticlesWithRelationshipsAsync(List<string> newArticleTitles, string linkedFromArticle, List<int> count)
{
    await client
        .Cypher
        .Unwind(newArticleTitles, "newArticleTitle")
        .Match("(linkedFromArticle:Article)")
        .Where("linkedFromArticle.title = { linkedFromArticle}")
        .WithParam("linkedFromArticle", linkedFromArticle)
        .Merge("(newArticle: Article { title: newArticleTitle } )")
        .Merge("(newArticle)< -[:REFERENCES { count: 500 }]-(linkedFromArticle)")
        .ExecuteWithoutResultsAsync();
}

However, this has a hardcoded count of 500. I am struggling to make use of the count of <int>. My best attempt so far is to also unwind the count list:

public async Task AddArticlesWithRelationshipsAsync(List<string> newArticleTitles, string linkedFromArticle, List<int> count)
{
    await client
        .Cypher
        .Unwind(newArticleTitles, "newArticleTitle")
        .Unwind(count, "count")
        .Match("(linkedFromArticle:Article)")
        .Where("linkedFromArticle.title = { linkedFromArticle}")
        .WithParam("linkedFromArticle", linkedFromArticle)
        .Merge("(newArticle: Article { title: newArticleTitle } )")
        .Merge("(newArticle)< -[:REFERENCES { count: count }]-(linkedFromArticle)")
        .ExecuteWithoutResultsAsync();
}

Unfortunately this adds relationships between all of the new nodes with all of the relationship counts. How do I pair up the two lists with items that have the same index?


Solution

  • I wrote a Cypher query that solve the problem using the range() function and FOREACH.

    First, I created an :Article node with:

    CREATE (:Article {title : 'Article A'})
    

    After it, I configured the Neo4j Browser parameters with:

    :params { linkedFromArticle : 'Article A', newArticleTitles : ['Article B', 'Article C', 'Article D'], count : [10, 20, 30] }
    

    Then, the Cypher query:

    MATCH (linkedFromArticle:Article)
    WHERE linkedFromArticle.title = {linkedFromArticle}
    WITH linkedFromArticle, RANGE(0, SIZE({count}) - 1) AS indexes
    FOREACH(index IN indexes |
        MERGE (newArticle:Article { title: {newArticleTitles}[index] } )
        MERGE (newArticle)-[:REFERENCES {count : {count}[index]}]->(linkedFromArticle)
    )
    

    The resultant graph:

    Resultant graph

    EDIT:

    The equivalent C# function using Neo4jClient:

    public async Task AddArticlesWithRelationshipsAsync(List<string> newArticleTitles, string linkedFromArticle, List<int> count) {
        await client
        .Cypher
        .WithParam("newArticleTitles", newArticleTitles)
        .WithParam("linkedFromArticle", linkedFromArticle)
        .WithParam("count", count)
        .Match("(linkedFromArticle:Article)")
        .Where("linkedFromArticle.title = {linkedFromArticle}")
        .With("linkedFromArticle, RANGE(0, SIZE({count}) - 1) AS indexes")
        .ForEach("(index IN indexes | " +
                 "MERGE (newArticle:Article { title: {newArticleTitles}[index] } )" +
                 "MERGE (newArticle)-[:REFERENCES {count : {count}[index]}]->(linkedFromArticle)" +
         ")")
        .ExecuteWithoutResultsAsync();
    }