【问题标题】:Elasticsearch NEST partial update of anonymous object with an empty nested object具有空嵌套对象的匿名对象的 Elasticsearch NEST 部分更新
【发布时间】:2015-12-24 18:47:47
【问题描述】:

尝试“重置”文档中的嵌套对象,但不会将其设置回空。

我有一个 POCO:

public class StreetAddress 
{
    public int HouseNumber { get; set; }
    public string Street { get; set; }
}

public class FullAddress
{
    public string City{ get; set; }

    public string State { get; set; }

    public StreetAddress StreetAddress { get; set; }

    public int Zip { get; set; }

    public List<string> Codes { get; set; }
}

所以目前,如果我为 FullAddress 创建了这个新文档,并且 StreetAddress 已经设置,则查询时文档看起来像这样:

"_source": {          
    "city": "Los Angeles",          
    "state": "California",          
    "zip": 90019,            
    "streetAddress": {            
        "houseNumber": 1,
        "street": "Apple Street"          
    },
    "codes" : [
        { "la-601" }
    ]     
}

现在我想调用 NEST 客户端更新来重置 StreetAddress 嵌套对象:

StreetAddress localStreetAddress = new StreetAddress();

var partialAddress = new 
{ 
    City = "NewCity",
    Zip = 11111,
    StreetAddress = localStreetAddress
};

this._client.Update<ElasticsearchProject, object>(update => update
    .Id(1) 
    .Doc(partialAddress)
);

我在查询时希望的最终结果是在上述更新调用之后:

"_source": {          
    "city": "NewCity",          
    "state": "California",          
    "zip": 11111,            
    "streetAddress": {}     
}

但是,部分更新会做两件不希望的事情:

  1. 它只更新 City 和 Zip 字段并保留 StreetAddress 和以前一样,并没有将其清除为空或空。
  2. 由于部分更新不包括该列表,因此它会将代码列表清除为空。

我知道我可以将 StreetAddress 设置为 null 并添加 JSON 属性以包含 null,如下所示:

[JsonProperty(NullValueHandling = NullValueHandling.Include)]
public StreetAddress StreetAddress { get; set; }

但结果是文档更新会将其设置为 null 而不是空的,我不确定这是否是文档所需的结果:

"_source": {          
        "city": "NewCity",          
        "state": "California",          
        "zip": 11111,            
        "streetAddress": null     
    }

不确定是否有办法在不通过脚本路径将嵌套对象设置回空的情况下进行部分更新。

【问题讨论】:

    标签: elasticsearch nest


    【解决方案1】:

    Nested objects 实际上在 Elasticsearch 中被映射为单独的 hidden 文档,正如您所发现的,NEST 默认配置为不通过空值发送。最终结果是嵌套类型不受您对顶级属性的部分更新的影响。

    处理此问题的一种方法是在没有 StreetAddress 的情况下再次简单地重新索引文档。无论如何,更新本质上是在幕后删除和插入。

    举个例子

    void Main()
    {
        var settings = new ConnectionSettings(new Uri("http://localhost:9200"), "address");
    
        var client = new ElasticClient(settings);
    
        // create the index
        client.CreateIndex("address", c => c
            .AddMapping<FullAddress>(m => m
                .MapFromAttributes()
            )
        );
    
        // index our document
        client.Index(new FullAddress
        {
            City = "Los Angeles",
            State = "California",
            Zip = 90019,
            Codes = new List<string>
            {
                "la-601"
            },
            StreetAddress = new StreetAddress
            {
                HouseNumber = 1,
                Street = "Apple Street"
            }
        }, c => c.Id(1));
    
        // Now at some later point, we need to update it,
        // so get the current document
        var response = client.Get<FullAddress>(1);
    
        // make the changes to the current document
        var address = response.Source;
        address.StreetAddress = null;
    
        // and re-index
        client.Index<FullAddress>(address, c => c.Id(1));
    }
    
    public class StreetAddress
    {
        public int HouseNumber { get; set; }
        public string Street { get; set; }
    }
    
    public class FullAddress
    {
        public string City { get; set; }
    
        public string State { get; set; }
    
        public StreetAddress StreetAddress { get; set; }
    
        public int Zip { get; set; }
    
        public List<string> Codes { get; set; }
    }
    

    最终结果如预期

    {
      "_index": "address",
      "_type": "fulladdress",
      "_id": "1",
      "_version": 2,
      "found": true,
      "_source": {
        "city": "Los Angeles",
        "state": "California",
        "zip": 90019,
        "codes": [
          "la-601"
        ]
      }
    }
    

    您可能还想在执行此操作时使用optimistic concurrency control,以处理getindex 之间的任何更改。

    【讨论】:

    • 对于假期的延误,我深表歉意,但我明白 Elasticsearch 会删除并重新索引更新的新文档。您指出的唯一担心是具有乐观并发控制的版本控制。我们的系统现在通过不同的跨国类别处理事情,我们通过 groovy 进行部分更新和脚本更新。您的回答似乎是一个有效的观点,我们可以使用相同的方法重新索引,但这基本上不是 Updates 应该在幕后做的事情,所以看起来我们在这里尝试做同样的事情。
    • 这是更新在幕后所做的,是的。您遇到的问题是 NEST 中的设计选择,这意味着在 大多数 的情况下不会通过线路发送不必要的数据,边缘情况就是其中之一。您可以像您所做的那样使用该属性将null 发送到ES。 null 不会在 ES 中被索引,因此不会影响搜索。
    • 不幸的是,在更新对象的那一刻,我们并不总是能够访问完整的对象。因此,我们需要按照您的建议对文档进行部分更新而不是重新索引。感谢您的意见,让我对事情有了更深入的了解。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多