ES NEST - 如何使用几何(geo_point 或 geo_shape)创建索引和批量索引动态对象?

ES NEST - How to Create an index and Bulk index dynamic objects with geometry (geo_point or geo_shape)?

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

执行时,我为该数据集获取了一个 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. 如何使用具有这些几何属性的批量或 BulkAll API 使用 NEST 高级客户端批量索引这些文档?

我看到 and here 可以使用 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);

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

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

Dynamic templates 将非常适合您的用例,此功能为您提供了一种很好的方式来控制 elasticsearch 如何映射您的动态数据模式。

您可以利用 match 参数并根据字段名称控制字段类型。如果 DatasetField 的实例将 IsGeoPoint 设置为 true,我们可以在 elasticsearch 字段名称前加上 GeoPoint 并配置动态模板为以 GeoPoint[=23] 为前缀的名称创建 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; }
    }
}

这将产生以下 elasticsearch 映射

{
    "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 });