Elasticsearch 在使用 IndexMany 时推断动态类型的 _id
Elasticsearch infer _id on dynamic type when using IndexMany
我正在努力解决一个问题。我正在构建一个应用程序,我们在 Elastic 中为 assets 编制索引。 资产 的本质是非常动态的,因为它们包含客户端元数据,这因客户端而异。
因此,索引是从 C# 中的动态列表构建的。这实际上就像一个魅力。问题是,在使用 C# 接口时,我无法控制 Elastic 中的 _id 属性。这意味着当我更新文档时,不是更新正确的文档,而是创建一个新的副本。
我的代码如下所示:
List<dynamic> assets = new List<dynamic>();
var settings1 = new ConnectionSettings(
new Uri("http://localhost:9200")
).DefaultIndex("assets");
var client = new ElasticClient(settings1);
//assets is build here
var indexResponse = client.Indices.Create("assets");
var BulkResponse = client.IndexMany(assets);
这确实有效,而且索引的构建几乎符合我的预期。即使我在动态上有一个名为 Id 的 属性,它也没有被正确推断,这意味着文档被赋予了一个由 Elastic 决定的 _Id。因此,下次我 运行 此代码使用相同的 Id 时,将创建一个新文档而不是更新它。
我一直在上下搜索,但似乎找不到好的解决方案。我尝试过的一件事如下:
var bulkResponse = client.Bulk(bd => bd.IndexMany(assets, (descriptor, s) => descriptor.Id(s.Id)));
但这会引发一个我无法在 .net 内核中捕获的错误。这实际上适用于 Elastic 的较低版本,但似乎已被 C# 接口的 7.2 和 7.0.1 破坏。
非常感谢任何帮助。
您可以创建 dictionary-based 自定义类型,而不是使用 dynamic
类型,例如:
public class DynamicDocument : Dictionary<string, object>
{
public string Id => this["id"]?.ToString();
}
并按如下方式使用它:
class Program
{
public class DynamicDocument : Dictionary<string, object>
{
public string Id => this["id"]?.ToString();
}
static async Task Main(string[] args)
{
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var connectionSettings = new ConnectionSettings(pool);
connectionSettings.DefaultIndex("documents");
var client = new ElasticClient(connectionSettings);
await client.Indices.DeleteAsync("documents");
await client.Indices.CreateAsync("documents");
var response = await client.IndexAsync(
new DynamicDocument
{
{"id", "1"},
{"field1", "value"},
{"field2", 1}
}, descriptor => descriptor);
//will update document with id 1 as it's already exists
await client.IndexManyAsync(new[]
{
new DynamicDocument
{
{"id", "1"},
{"field1", "value2"},
{"field2", 2}
}
});
await client.Indices.RefreshAsync();
var found = await client.GetAsync<DynamicDocument>("1");
Console.WriteLine($"Id: {found.Source.Id}");
Console.WriteLine($"field1: {found.Source["field1"]}");
Console.WriteLine($"field2: {found.Source["field2"]}");
}
}
输出:
Id: 1
field1: value2
field2: 2
使用 elasticsearch 7.2.0 和 NEST 7.0.1 进行测试。
希望对您有所帮助。
允许以下工作
var bulkResponse = client.Bulk(bd => bd.IndexMany(assets, (descriptor, s) => descriptor.Id(s.Id)));
您只需将 Id
类型转换为它的类型。例如,如果它是 string
var client = new ElasticClient();
var assets = new dynamic[]
{
new { Id = "1", Name = "foo" },
new { Id = "2", Name = "bar" },
new { Id = "3", Name = "baz" },
};
var bulkResponse = client.Bulk(bd => bd.IndexMany(assets, (descriptor, s) => descriptor.Id((string)s.Id)));
这是一个运行时限制。
我正在努力解决一个问题。我正在构建一个应用程序,我们在 Elastic 中为 assets 编制索引。 资产 的本质是非常动态的,因为它们包含客户端元数据,这因客户端而异。
因此,索引是从 C# 中的动态列表构建的。这实际上就像一个魅力。问题是,在使用 C# 接口时,我无法控制 Elastic 中的 _id 属性。这意味着当我更新文档时,不是更新正确的文档,而是创建一个新的副本。
我的代码如下所示:
List<dynamic> assets = new List<dynamic>();
var settings1 = new ConnectionSettings(
new Uri("http://localhost:9200")
).DefaultIndex("assets");
var client = new ElasticClient(settings1);
//assets is build here
var indexResponse = client.Indices.Create("assets");
var BulkResponse = client.IndexMany(assets);
这确实有效,而且索引的构建几乎符合我的预期。即使我在动态上有一个名为 Id 的 属性,它也没有被正确推断,这意味着文档被赋予了一个由 Elastic 决定的 _Id。因此,下次我 运行 此代码使用相同的 Id 时,将创建一个新文档而不是更新它。
我一直在上下搜索,但似乎找不到好的解决方案。我尝试过的一件事如下:
var bulkResponse = client.Bulk(bd => bd.IndexMany(assets, (descriptor, s) => descriptor.Id(s.Id)));
但这会引发一个我无法在 .net 内核中捕获的错误。这实际上适用于 Elastic 的较低版本,但似乎已被 C# 接口的 7.2 和 7.0.1 破坏。
非常感谢任何帮助。
您可以创建 dictionary-based 自定义类型,而不是使用 dynamic
类型,例如:
public class DynamicDocument : Dictionary<string, object>
{
public string Id => this["id"]?.ToString();
}
并按如下方式使用它:
class Program
{
public class DynamicDocument : Dictionary<string, object>
{
public string Id => this["id"]?.ToString();
}
static async Task Main(string[] args)
{
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var connectionSettings = new ConnectionSettings(pool);
connectionSettings.DefaultIndex("documents");
var client = new ElasticClient(connectionSettings);
await client.Indices.DeleteAsync("documents");
await client.Indices.CreateAsync("documents");
var response = await client.IndexAsync(
new DynamicDocument
{
{"id", "1"},
{"field1", "value"},
{"field2", 1}
}, descriptor => descriptor);
//will update document with id 1 as it's already exists
await client.IndexManyAsync(new[]
{
new DynamicDocument
{
{"id", "1"},
{"field1", "value2"},
{"field2", 2}
}
});
await client.Indices.RefreshAsync();
var found = await client.GetAsync<DynamicDocument>("1");
Console.WriteLine($"Id: {found.Source.Id}");
Console.WriteLine($"field1: {found.Source["field1"]}");
Console.WriteLine($"field2: {found.Source["field2"]}");
}
}
输出:
Id: 1
field1: value2
field2: 2
使用 elasticsearch 7.2.0 和 NEST 7.0.1 进行测试。
希望对您有所帮助。
允许以下工作
var bulkResponse = client.Bulk(bd => bd.IndexMany(assets, (descriptor, s) => descriptor.Id(s.Id)));
您只需将 Id
类型转换为它的类型。例如,如果它是 string
var client = new ElasticClient();
var assets = new dynamic[]
{
new { Id = "1", Name = "foo" },
new { Id = "2", Name = "bar" },
new { Id = "3", Name = "baz" },
};
var bulkResponse = client.Bulk(bd => bd.IndexMany(assets, (descriptor, s) => descriptor.Id((string)s.Id)));
这是一个运行时限制。