【问题标题】:ES NEST - How to Create an index and Bulk index dynamic objects with geometry (geo_point or geo_shape)?ES NEST - 如何使用几何(geo_point 或 geo_shape)创建索引和批量索引动态对象?
【发布时间】:2021-06-10 17:40:34
【问题描述】:

我有一个数据集,我事先不知道或不知道其类型,也不知道属性的数量或其类型。

在执行时,我为该数据集获取一个 DatasetSchema,其中包含属性的名称、类型和一些标志。

对于几何属性,我将它们的 GeoJson 表示存储为字符串,并且我有一些标志(isGeoShape、isGeoPoint)来告诉 ES 属性类型。

如果需要将这些 GeoJson 解析为实际的 Geometry 对象,我也会使用 NetTopologySuite,但我宁愿不进行这种额外的解析,而是使用 GeoJson 字符串。

class DatasetSchema {
    List<DatasetField> Fields;
}

class DatasetField { 
    string Name;
    Type DataType;
    bool isGeoShape;
    bool isGeoPoint;
}

问题:

  1. 如何使用具有这些几何属性的 NEST 高级客户端创建具有未知/动态映射架构的 ES 索引?

  2. 如何使用具有这些几何属性的 NEST 高级客户端或 BulkAll API 批量索引这些文档?

我看到 herehere 可以使用 BulkDescriptor 完成批量索引:

dynamic obj = new System.Dynamic.ExpandoObject();
    // ….
var descriptor = new BulkDescriptor();
foreach (var doc in values)
{
    descriptor.Index<object>(i => i
        .Index("abc")
        .Id((Id)doc.Id)
        .Document((object)doc));
}
client.Bulk(descriptor);

不过,我很好奇应该如何处理几何类型?

非常感谢! 欢迎任何想法或建议!

【问题讨论】:

    标签: c# elasticsearch geometry nest elasticsearch-geo-shape


    【解决方案1】:

    Dynamic templates 将非常适合您的用例,此功能为您提供了一种控制弹性搜索如何映射您的动态数据架构的好方法。

    您可以根据字段名称利用match 参数和控制字段类型。如果DatasetField 的实例将IsGeoPoint 设置为true,我们可以为elasticsearch 字段名称添加GeoPoint 前缀,并配置动态模板为名称前缀为GeoPoint 的名称创建goe_point 字段

    {
        "mappings": {
            "dynamic_templates": [{
                    "geo_shape": {
                        "match": "GeoShape*",
                        "mapping": {
                            "type": "geo_shape"
                        }
                    }
                }, {
                    "geo_point": {
                        "match": "GeoPoint*",
                        "mapping": {
                            "type": "geo_point"
                        }
                    }
                }
            ]
        }
    }
    

    这是一个示例 C# 应用程序,展示了它的实际效果

    class Program
    {
        static async Task Main(string[] args)
        {
            string indexName = "my_index";
            var connectionSettings = new ConnectionSettings(new Uri("http://localhost:9200"));
            connectionSettings.DefaultIndex(indexName);
            var elasticClient = new ElasticClient(connectionSettings);
    
            await elasticClient.Indices.DeleteAsync(indexName);
            //create index mapping with two dynamic templates,
            //based on field suffix elasticsearch will map field to specific type
            var indexResponse = await elasticClient.Indices.CreateAsync(indexName, d => d
                .Map(map => map
                    .DynamicTemplates(dt => dt
                        .DynamicTemplate("geo_shape", gs => gs.Match("GeoShape*").Mapping(m => m.GeoShape(s => s)))
                        .DynamicTemplate("geo_point", gs => gs.Match("GeoPoint*").Mapping(m => m.GeoPoint(p => p)))
                    )));
    
            //some same data matching your schema
            var data = new List<DatasetField>
            {
                new () { Name = "Field1", IsGeoPoint = true },
                new () { Name = "Field2", IsGeoShape = true },
            };
    
            var document = new EsDocument();
            foreach (var datasetField in data)
            {
                //if the field is of type geo shape, prefix field name with GeoShape,
                //geo_shape dynamic template will match field name and will create geo_point type for it
                if (datasetField.IsGeoShape)
                {
                    document.Add($"GeoShape{datasetField.Name}", new PointGeoShape(new GeoCoordinate(0, 0)));
                }
                //if the field is of type geo point, prefix field name with GeoPoint,
                //geo_point dynamic template will match field name and will create geo_shape type for it
                if (datasetField.IsGeoPoint)
                {
                    document.Add($"GeoPoint{datasetField.Name}", new GeoLocation(0, 0));
                }
            }
    
            var response = await elasticClient.IndexDocumentAsync(document);
        }
    
        //this class is just an alias to dictionary
        class EsDocument : Dictionary<string,object>{}
    
        class DatasetField
        {
            public string Name { get; set; }
            public bool IsGeoShape { get; set; }
            public bool IsGeoPoint { get; set; }
        }
    }
    

    这将产生以下弹性搜索映射

    {
        "my_index": {
            "mappings": {
                "dynamic_templates": [{
                        "geo_shape": {
                            "match": "GeoShape*",
                            "mapping": {
                                "type": "geo_shape"
                            }
                        }
                    }, {
                        "geo_point": {
                            "match": "GeoPoint*",
                            "mapping": {
                                "type": "geo_point"
                            }
                        }
                    }
                ],
                "properties": {
                    "GeoPointField1": {
                        "type": "geo_point"
                    },
                    "GeoShapeField2": {
                        "type": "geo_shape"
                    }
                }
            }
        }
    }
    

    当涉及到批量索引文档时,最简单的方法是使用IndexManyAsync扩展方法

    await elasticClient.IndexManyAsync(new List<EsDocument>());
    

    还请查看this blog post 详细描述索引多个文档。检查“多个文档”部分。

    更新:将新的动态模板添加到现有动态模板的映射中

    var map = (await elasticClient.Indices.GetMappingAsync<EsDocument>()).Indices["your_index_name"];
    var dynamicTemplates = map.Mappings.DynamicTemplates;
    //add new
    dynamicTemplates.Add(new KeyValuePair<string, IDynamicTemplate>());
    await elasticClient.Indices.PutMappingAsync(new PutMappingRequest("your_index_name") { DynamicTemplates = dynamicTemplates });
    

    【讨论】:

    • 谢谢@Rob!不过,您是否知道如何根据我的输入数据模式中的一些约定有条件地/以编程方式添加/定义dynamicTemplate
    • 是的,您可以使用PutMapping 方法如await elasticClient.Indices.PutMapping&lt;EsDocument&gt;(m =&gt; m.DynamicTemplates()); 根据您的条件更新和添加新的动态模板到您现有的映射中。
    • 太好了,听起来像我需要的!不过,有没有办法按名称为特定索引调用PutMapping?我看到PutMappingAsync(IPutMappingRequest request, CancellationToken ct = default);PutMappingAsync&lt;TDocument&gt;(Func&lt;PutMappingDescriptor&lt;TDocument&gt;, IPutMappingRequest&gt; selector, CancellationToken ct = default);
    • 当然,您可以使用Index() 部分描述符指定索引,例如await elasticClient.Indices.PutMapping&lt;EsDocument&gt;(m =&gt; m.Index(your_index_name_goes_here).DynamicTemplates())
    • 看你的意思,检查我的更新。认为唯一的方法是检索索引的映射,添加新的动态模板,然后发回 old + new。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-11
    • 1970-01-01
    • 2017-04-04
    • 2017-04-21
    • 1970-01-01
    • 1970-01-01
    • 2020-03-17
    相关资源
    最近更新 更多