将连接类型与嵌套类型相结合,并在 Elasticsearch 6.x 中进行查询



全部,我正在使用带有NEST的ElasticSearch 6.x,为简单起见,我根据此处提供的NEST 6.x文档创建了具有以下POCO设置的映射 https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/parent-child-relationships.html。我的数据模型很简单

  • 客户

  • 次序

  • 订单项

    下面是 C# POCO 设置

    [ElasticsearchType(Name = "customer")]
    public class Customer 
    {
    [PropertyName("customerId")]
    public int CustomerId { get; set; }
    [PropertyName("firstName")]
    [Text]
    public string FirstName { get; set; }
    [PropertyName("lastName")]
    [Text]
    public string LastName { get; set; }
    [PropertyName("email")]
    [Keyword]
    public string Email { get; set; }
    [PropertyName("customer_join_field")]
    public JoinField CustomerJoinField { get; set; }
    }
    [ElasticsearchType(Name = "order")]
    public class Order : Customer
    {
    [PropertyName("orderId")]
    public int OrderId { get; set; }        
    [PropertyName("orderAmount")]
    public decimal Amount { get; set; }
    [Nested]
    [PropertyName("packages")]
    public List<Package> Packages { get; set; }
    [Nested]
    [PropertyName("orderItems")]
    public List<OrderItem> OrderItems { get; set; }
    }
    public class Package
    {
    public int PackageId { get; set; }
    public int Qty { get; set; }
    public int OrderId { get; set; }
    public string Weight { get; set; }
    }
    public class OrderItem
    {
    public int OrderItemId { get; set; }
    public int Quantity { get; set; }
    public decimal UnitPrice { get; set; }
    }
    

构建 ES 客户端

public static ElasticClient ESClient
{
get
{
var connectionPool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var settings = new ConnectionSettings(connectionPool)
.DefaultMappingFor<Customer>(i => i
.IndexName("fa")
.TypeName("customer"))
.DefaultMappingFor<Order>(i => i
.IndexName("fa")
.TypeName("customer"))                                              
.EnableDebugMode()
.PrettyJson()
.RequestTimeout(TimeSpan.FromMinutes(2));
return new ElasticClient(settings);
}
}

配置索引 (fa(

public static void ConfigureIndex()
{
try
{
var createIndexResponse = ESClient.CreateIndex("fa", c => c
.Index<Customer>()
.Mappings(ms => ms
.Map<Customer>(m => m
.RoutingField(r => r.Required())
.AutoMap<Customer>()
.AutoMap<Order>()
.Properties(props => props
.Join(j => j
.Name(p => p.CustomerJoinField)
.Relations(r => r
.Join<Customer, Order>()
)
)
)
)
)
);
}

添加客户和订单类型(在相同的客户类型下,因为索引在 ES 6.x 中只能有一个类型(

public static void AddCustomerDocument(Customer cust)
{
try
{
var result = ESClient.Index<Customer>(cust,
c => c
.Id(cust.CustomerId)//to avaoid random Ids
.Routing(cust.CustomerId)
);
//var response = ESClient.Index(cust, i => i.Routing(Routing.From(cust)));
}
catch (Exception ex)
{
throw;
}
}   

public static void AddOrderDocument(Order order)
{
try
{

var result = ESClient.Index<Customer>(order,
c => c
.Id(order.OrderId)//to avaoid random Ids
.Routing(order.CustomerId)
);
//var response = ESClient.IndexDocument<Order>(order);
}
catch (Exception ex)
{
throw;
}
}

在 ES 中生成的映射是

{
"fa": {
"mappings": {
"customer": {
"_routing": {
"required": true
},
"properties": {
"customerId": {
"type": "integer"
},
"customer_join_field": {
"type": "join",
"eager_global_ordinals": true,
"relations": {
"customer": "order"
}
},
"email": {
"type": "keyword"
},
"firstName": {
"type": "text"
},
"lastName": {
"type": "text"
},
"orderAmount": {
"type": "double"
},
"orderId": {
"type": "integer"
},
"orderItems": {
"type": "nested",
"properties": {
"orderItemId": {
"type": "integer"
},
"quantity": {
"type": "integer"
},
"unitPrice": {
"type": "double"
}
}
},
"packages": {
"type": "nested",
"properties": {
"orderId": {
"type": "integer"
},
"packageId": {
"type": "integer"
},
"qty": {
"type": "integer"
},
"weight": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}
}
}
}
}

我正在查询此索引以获取单价为 12.23 的特定客户 (CustomerId= 1( 下的特定订单项目。

这是我的查询DSL

{
"query":{
"parent_id":{
"type":"order",
"id":"1"
},
"nested":{
"path":"orderItems",
"score_mode":"avg",
"query":{
"bool":{
"must":[
{"match":{"orderItems.unitPrice" : "12.23"}}
]
}
}
}
}
}

当我这样做时,我收到以下错误消息

{
"error": {
"root_cause": [
{
"type": "parsing_exception",
"reason": "[parent_id] malformed query, expected [END_OBJECT] but found [FIELD_NAME]",
"line": 9,
"col": 4
}
],
"type": "parsing_exception",
"reason": "[parent_id] malformed query, expected [END_OBJECT] but found [FIELD_NAME]",
"line": 9,
"col": 4
},
"status": 400
}

我的问题是

  1. 是否有可能(或建议(同时进行父子关系和 ES 6.x 中的嵌套类型?

  2. 如果没有,我的选择是否仅限于非规范化模型( 有 包,订单项是客户的子项,而不是订购和添加 "包裹"和"订单项"中的字段?( 还是对 ES 进行多次查询,并将我想要的扁平化数据格式的逻辑移动到应用程序端?

这是我创建的示例数据(本地主机:9200/fa/customer/_search(

{
"took": 1,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 3,
"max_score": 1,
"hits": [
{
"_index": "fa",
"_type": "customer",
"_id": "1",
"_score": 1,
"_routing": "1",
"_source": {
"customerId": 1,
"firstName": "Rennish",
"lastName": "Joseph",
"email": "rennish@yahoo.com",
"customer_join_field": "customer"
}
},
{
"_index": "fa",
"_type": "customer",
"_id": "100",
"_score": 1,
"_routing": "1",
"_source": {
"orderId": 100,
"orderAmount": 23.45,
"packages": [
{
"packageId": 1,
"qty": 2,
"orderId": 1,
"weight": "2.3"
},
{
"packageId": 2,
"qty": 1,
"orderId": 1,
"weight": "2.5"
}
],
"orderItems": [
{
"orderItemId": 1,
"quantity": 2,
"unitPrice": 12.23
},
{
"orderItemId": 2,
"quantity": 1,
"unitPrice": 10.23
}
],
"customerId": 1,
"customer_join_field": {
"name": "order",
"parent": "1"
}
}
},
{
"_index": "fa",
"_type": "customer",
"_id": "101",
"_score": 1,
"_routing": "1",
"_source": {
"orderId": 101,
"orderAmount": 23.45,
"packages": [
{
"packageId": 1,
"qty": 2,
"orderId": 1,
"weight": "2.3"
},
{
"packageId": 2,
"qty": 1,
"orderId": 1,
"weight": "2.5"
}
],
"orderItems": [
{
"orderItemId": 1,
"quantity": 2,
"unitPrice": 12.23
},
{
"orderItemId": 2,
"quantity": 1,
"unitPrice": 10.23
}
],
"customerId": 1,
"customer_join_field": {
"name": "order",
"parent": "1"
}
}
}
]
}
}

我们最终为每个实体(客户、订单、包裹等(创建了单独的索引,并针对应用程序中的所有这些索引进行查询,并将结果组合到应用程序中。这是来自我们从 Elasticsearch 合作的顾问的建议。我们没有在任何索引映射中执行父子关系。这可能对将来的某人有所帮助。

最新更新