在Elasticsearch 6.x中结合Join类型和Nested类型并进行查询。

问题描述 投票:2回答:1

所有的人,我正在用NEST玩ElasticSearch 6.x,为了简单起见,我根据这里提供的NEST 6.x文档,用下面的POCO设置创建了映射。 https:/www.elastic.coguideenelasticsearchclientnet-apicurrentparent-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
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

我正在查询这个索引,以获得特定客户(CustomerId= 1)下的特定订单项目,单价为12.23。

这是我的查询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. 如果不可以,我的选择是否仅限于对模型进行去规范化处理(将Package、OrderItems作为Customer的子代,而不是Order,并在Package和OrderItems中添加字段?)还是对ES进行多次查询,并将我想要的数据格式扁平化的逻辑移到应用端?

这是我创建的示例数据( localhost:9200facustomer_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": "[email protected]",
                    "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"
                    }
                }
            }
        ]
    }
}
c# elasticsearch nest
1个回答
0
投票

我们最终为每个实体(Customer、Order、Package等)创建了单独的索引,并从我们的应用程序中针对所有这些索引进行查询,然后将结果合并到应用程序中。这是我们与Elasticsearch的顾问的建议。我们没有在任何一个索引映射中做父子关系。这也许对将来的人有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.