Jasper Krom
Jasper Krom

Reputation: 1

Unwinding and merging the results with dynamic labels in Neo4J Client for C#

I am currently trying to unwind a list of objects that I want to merge to the database using the Neo4J Client. What I would like to do is unwind the list and create the nodes with a label generated based on a property from the items themselves instead of hardcoding a label name. From what I can find I have to use the APOC merge method to do so. However, I am unable to translate this to the Neo4J client. In the neo4J explanation they yield a node after the apoc.merge.node call and then return the node. However, I cannot simply return the node nor can I set the node (I got to the point of just messing about, and at one point I got the labels to work but it overwrote all properties with the last item in the list).

I seem to miss something fundamental but i'm not quite sure what. Does anyone here know how to do this with neo4J client (and if possible, give a bit of an explanation what is going on)? I am very new to the development world and I feel I am just missing a crucial piece of understanding when it comes to this..

The code that I tried that turned all properties into the last node's properties but at least created the labels as I expected:

        public async void CreateBatchItems(List<TToDataBase> itemList)
        {
            await Client.Cypher
            .Unwind(itemList, "row")
            .Merge("(n)")
            .With("row, n")
            .Call("apoc.merge.node([n.Name], n)").Yield("node")
            .Set("n += node")
            .ExecuteWithoutResultsAsync();
        }

Thank you in advance!

Edit:

Some clarification about the input: The objects are actually very basic, as (at least for now), they merely contain a name and an objectID (and these object ID's are later used to create relations). So its a very basic class with two properties:

 public class Neo4JBaseClass
    {
        public Neo4JBaseClass() { }

        public Neo4JBaseClass(string name, string objectId)
        {
            Name = name;
            ObjectId = objectId;
        }

        [JsonProperty(PropertyName = "ObjectId")]
        public string ObjectId { get; set; }

        [JsonProperty(PropertyName = "Name")]
        public string Name { get; set; }
    }

I have also tried a slight variation where this class also has the added property

        [JsonProperty(PropertyName = "PropertyMap")]
        public IProperty PropertyMap { get; set; }  

where PropertyMap is another basic object holding the name and objectId. This seemed like a good idea for future proofing anyway, so the propertylist can be easily expanded without having to change the base object.

Upvotes: 0

Views: 1006

Answers (1)

cybersam
cybersam

Reputation: 67019

[EDITED]

The main issue is that Merge("(n)") matches any arbitrary node that already exists.

You have not shown the data structure for each element of itemList, so this answer will assume it looks like this:

{Name: 'SomeLabel', id: 123, Props: {foo: 'xyz', bar: true}}

With above data structure, this should work:

public async void CreateBatchItems(List<TToDataBase> itemList)
{
    await Client.Cypher
      .Unwind(itemList, "row")
      .Call("apoc.merge.node([row.ObjectId], row.id)").Yield("node")
      .Set("node += row.Props")
      .ExecuteWithoutResultsAsync();
}

[UPDATE]

The data structure you added to your question is very different than what I had imagined. Since neither of the properties in a row is a map, .Set("node += row.Props") would generate an error.

Using your data structure for each row, this might work:

public async void CreateBatchItems(List<TToDataBase> itemList)
{
    await Client.Cypher
      .Unwind(itemList, "row")
      .Merge("(n:Foo {id: row.ObjectId})")
      .Set("n += row.Name")
      .ExecuteWithoutResultsAsync();
}

This code assigns the node label Foo to all the generated nodes. A node should always have a label, which improves clarity and also tends to improve efficiency -- especially if you also create indexes. For example, an index on :Foo(id) would make the above query more efficient.

This code also assumes that the id property is supposed to contain a unique Foo node identifier.

Upvotes: 0

Related Questions