DotNet4Neo4j / Neo4jClient

.NET client binding for Neo4j
https://www.nuget.org/packages/Neo4jClient
Microsoft Public License
431 stars 146 forks source link

Set("n += $Param") does not remove properties if the properties are null in $Param #457

Closed pandabytes closed 1 year ago

pandabytes commented 1 year ago

Describe the bug When using WithParam and a dictionary with null values, calling Set("n += $Param") will not remove the properties that are null in the dictionary from the node. For the following code below, the property adopted should be removed after the 2nd query is executed

class Person
{
  [property: JsonProperty(PropertyName = "identifier")]
  public string Id { get; init; } = string.Empty;

  [property: JsonProperty(PropertyName = "name")]
  public string Name { get; init; } = string.Empty;

  [property: JsonProperty(PropertyName = "adopted")]
  public bool? Adopted { get; init; } = null;
}

// Create a person
var createQuery = _cypherGraphClient.Cypher.Create("(p:Person {identifier: \"12345\", name: \"John\", adopted: false})");
Console.WriteLine($"[createQuery]{Environment.NewLine}{createQuery.Query.DebugQueryText}");
await createQuery.ExecuteWithoutResultsAsync();

// Updated adopted to null (i.e. remove it)
var updateQuery = _cypherGraphClient.Cypher
  .Match("(p:Person {identifier: \"12345\"})")
  .WithParam("PersonParam", new Dictionary<string, object?>() { { "adopted", null } })
  .Set($"p += $PersonParam");

Console.WriteLine($"[updateQuery]{Environment.NewLine}{updateQuery.Query.DebugQueryText}");
await updateQuery.ExecuteWithoutResultsAsync();

When I run the code above, this is the debug query text I get

[createQuery]
CREATE (p:Person {identifier: "12345", name: "John", adopted: false})
[updateQuery]
MATCH (p:Person {identifier: "12345"})
SET p += {
  adopted: null
}

The debug update query is correct because if I copy the update query text and run it directly in Neo4J, then the property adopted is removed. Also, specifying a literal dictionary for Set works. For example

// This works correctly (i.e adopted property is removed)
var updateQuery = _cypherGraphClient.Cypher
  .Match("(p:Person {identifier: \"12345\"})")
  .Set("p += { adopted: null }");

Versions:

To Reproduce Steps to reproduce the behavior: See code snippet above.

Expected behaviour I expect all null properties that are in the dictionary will be removed from the Neo4j node (assuming those properties already exist on a node) when using WithParam. Here is what Neo4J doc states:

Any properties that are in both the map and the node or relationship will be replaced in the node or relationship. However, if any property in the map is null, it will be removed from the node or relationship.

cskardon commented 1 year ago

You can force nulls to be serialized by setting the SerializeNullValues setting of the ExecutionConfiguration, like this:

var client = new BoltGraphClient("neo4j://localhost:7687", "user", "pass");
client.ExecutionConfiguration.SerializeNullValues = true;
await client.ConnectAsync();