ElasticSearch - 使用 NEST 5.x 在嵌套列表 <T> 中添加元素

ElasticSearch - Add element inside nested List<T> using NEST 5.x

我的 ElasticSearch 索引中有一个列表。如何使用 NEST 2 将新元素添加到列表中?我已经搜索了我的问题的答案,但我找到的都是 ,但它对我不起作用。

型号:

public class TicketModel
{
    public Guid Id { get; set; }
    public Guid IdUser { get; set; }
    public Guid IdAuthor { get; set; }
    public Guid? IdAssignedUser { get; set; }
    public Guid? IdAsset { get; set; }
    public Guid? IdOwner { get; set; }
    public DateTime CreationDate { get; set; }
    public DateTime? ClosingDate { get; set; }
    public DateTime? DueDate { get; set; }
    public DateTime? LastDateUserView { get; set; }
    public DateTime? LastDateAssignedUserView { get; set; }
    public string TicketCode { get; set; }
    public string Title { get; set; }
    public int IdCategory { get; set; }
    public int? IdSubcategory { get; set; }
    public short? IdPriority { get; set; }
    public short IdState { get; set; }
    public bool IsNew { get; set; }

    public List<TicketMessageModel> TicketMessageList { get; set; }
}

public class TicketMessageModel
{
    public Guid Id { get; set; }
    public Guid IdTicket { get; set; }
    public Guid IdUserFrom { get; set; }
    public Guid? IdUserTo { get; set; }
    public DateTime? CreationDate { get; set; }
    public DateTime? DeleteDate { get; set; }
    public string Message { get; set; }
    public bool HideToFinalUser { get; set; }
    public byte? MessageType { get; set; }

    public List<TicketMessageFilesModel> MessageFileList { get; set; }
}

public class TicketMessageFilesModel
{
    public Guid Id { get; set; }
    public Guid? IdTicketMessage { get; set; }
    public string FileName { get; set; }
    public string FileTitle { get; set; }
    public string FileOriginalName { get; set; }
    public byte FileType { get; set; }
}

我尝试使用其他答案找到出路,但我被困在这里:

client.Update<ElasticSearchTickets.TicketMessageModel, object>(new DocumentPath<ElasticSearchTickets.TicketMessageModel>(elem.Id), 
                q => q.Script(x => x.Inline("ctx._source.MessageFileList += elem"))./*??*/);

提前致谢。

编辑:

这是我尝试在列表中插入新元素的代码:

ElasticSearchTickets.TicketMessageModel elem = new ElasticSearchTickets.TicketMessageModel()
            {
                CreationDate = message.CreationDate,
                DeleteDate = message.DeleteDate,
                HideToFinalUser = message.HideToFinalUser,
                Id = message.Id,
                IdTicket = message.IdTicket,
                IdUserFrom = message.IdUserFrom,
                IdUserTo = message.IdUserTo,
                Message = message.Message,
                MessageType = message.MessageType,
                MessageFileList = tempList
            };
            var response = client.Update<ElasticSearchTickets.TicketModel, object>(new DocumentPath<ElasticSearchTickets.TicketModel>(elem.IdTicket.ToString()), q => q
                .Script(s => s
                    .Inline("if (ctx._source.TicketMessageList == null) { ctx._source.TicketMessageList = element; } else { ctx._source.TicketMessageList += element; }")
                    .Params(d => d.Add("element", new [] { elem }))
                ));

但是现在我得到一个服务器错误,因为 属性 "element" 还没有声明。如何解决这个问题?

这是映射:

"mappings": {
  "ticket": {
    "properties": {
      "ClosingDate": {
        "type": "date"
      },
      "CreationDate": {
        "type": "date"
      },
      "DueDate": {
        "type": "date"
      },
      "Id": {
        "type": "keyword"
      },
      "IdAsset": {
        "type": "keyword"
      },
      "IdAssignedUser": {
        "type": "keyword"
      },
      "IdAuthor": {
        "type": "keyword"
      },
      "IdCategory": {
        "type": "integer"
      },
      "IdOwner": {
        "type": "keyword"
      },
      "IdPriority": {
        "type": "short"
      },
      "IdState": {
        "type": "short"
      },
      "IdSubcategory": {
        "type": "integer"
      },
      "IdUser": {
        "type": "keyword"
      },
      "IsNew": {
        "type": "boolean"
      },
      "LastDateAssignedUserView": {
        "type": "date"
      },
      "LastDateUserView": {
        "type": "date"
      },
      "TicketCode": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      },
      "TicketMessageList": {
        "type": "nested",
        "properties": {
          "CreationDate": {
            "type": "date"
          },
          "DeleteDate": {
            "type": "date"
          },
          "HideToFinalUser": {
            "type": "boolean"
          },
          "Id": {
            "type": "keyword"
          },
          "IdTicket": {
            "type": "keyword"
          },
          "IdUserFrom": {
            "type": "keyword"
          },
          "IdUserTo": {
            "type": "keyword"
          },
          "Message": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          },
          "MessageFileList": {
            "properties": {
              "FileName": {
                "type": "text",
                "fields": {
                  "keyword": {
                    "type": "keyword",
                    "ignore_above": 256
                  }
                }
              },
              "FileOriginalName": {
                "type": "text",
                "fields": {
                  "keyword": {
                    "type": "keyword",
                    "ignore_above": 256
                  }
                }
              },
              "FileTitle": {
                "type": "text",
                "fields": {
                  "keyword": {
                    "type": "keyword",
                    "ignore_above": 256
                  }
                }
              },
              "FileType": {
                "type": "short"
              },
              "Id": {
                "type": "keyword"
              },
              "IdTicketMessage": {
                "type": "keyword"
              }
            }
          },
          "MessageType": {
            "type": "short"
          }
        }
      },
      "Title": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      }
    }

这是一个精简的例子

void Main()
{
    var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
    var defaultIndex = "default-index";
    var connectionSettings = new ConnectionSettings(pool)
            .DefaultIndex(defaultIndex);

    var client = new ElasticClient(connectionSettings);

    if (client.IndexExists(defaultIndex).Exists)
        client.DeleteIndex(defaultIndex);

    client.CreateIndex(defaultIndex, c => c
        .Mappings(ms => ms
            .Map<TicketMessageModel>(m => m
                .AutoMap()
                .Properties(p => p
                    .Nested<TicketMessageFilesModel>(n => n
                        .Name(nn => nn.MessageFileList)
                        .AutoMap()
                    )
                )
            )
        )
    );

    var id = "ticketmessage";

    client.Index(new TicketMessageModel
    {
        Id = id,
        MessageFileList = new List<UserQuery.TicketMessageFilesModel>
        {
            new TicketMessageFilesModel { Id = "file1" } 
        }
    });

    client.Update<TicketMessageModel, object>(id, q => q
        .Script("if (ctx._source.messageFileList == null) { ctx._source.messageFileList = elem; } else { ctx._source.messageFileList += elem; }")
        .Params(d => d
            .Add("elem", new[] { new TicketMessageFilesModel { Id = "file2" } })
        )
    );

    var getResponse = client.Get<TicketMessageModel>(id);
}

public class TicketMessageModel
{
    public string Id { get; set; }
    public List<TicketMessageFilesModel> MessageFileList { get; set; }
}

public class TicketMessageFilesModel
{
    public string Id { get; set; }
}

getResponse JSON 响应是

{
  "_index" : "default-index",
  "_type" : "ticketmessagemodel",
  "_id" : "ticketmessage",
  "_version" : 2,
  "found" : true,
  "_source" : {
    "id" : "ticketmessage",
    "messageFileList" : [ {
      "id" : "file1"
    }, {
      "id" : "file2"
    } ]
  }
}

一些兴趣点:

  1. 您需要通过检查它是否为空来处理列表中可能没有任何项目的情况
  2. 为了追加到列表中,您需要将要追加的项目包装在一个数组中,这样列表和数组就可以追加在一起。
  3. 您需要使用 _source 中存在的序列化字段名称。默认情况下,在 Elasticsearch
  4. 中序列化为字段名称时,NEST camel 会使用 POCO 属性 名称

编辑:

看起来您正在使用 NEST 5.x 对抗 Elasticsearch 5.x; 5.x 中的默认脚本语言是 Painless 而不是 2.x 中的 Groovy,因此必须对脚本进行一些细微的差别。

这是一个适用于 5.x 的无痛版本

client.Update<TicketMessageModel, object>(id, q => q
    .Script(s => s
        .Inline("if (ctx._source.messageFileList == null) { ctx._source.messageFileList = new ArrayList(); } ctx._source.messageFileList.add(params.elem);")
        .Params(d => d
            .Add("elem", new TicketMessageFilesModel { Id = "file2" })
        )
    )
);

查看 guide on Painless 了解更多详情。您也可以将原始示例与 Groovy 一起使用,方法是在 .Script() 内指定 .Lang("groovy"),但您还需要允许内联 Groovy 脚本到 运行添加

script.engine.groovy.inline: true

到Elasticsearch.yml配置。 Groovy scripts are disabled by default for security reasons.