1

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

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

我在这里这里看到 批量索引可以使用 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);

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

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

4

1 回答 1

1

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

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

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

另请查看此博客文章,详细描述索引多个文档。检查“多个文档”部分。

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

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 });
于 2021-06-10T19:24:53.110 回答