弹性搜索嵌套 Insert/Update
ElasticSearch Nest Insert/Update
我使用以下查询在 elastic 中创建了一个索引:
PUT public_site
{
"mappings": {
"page": {
"properties": {
"url": {
"type": "string"
},
"title":{
"type": "string"
},
"body":{
"type": "string"
},
"meta_description":{
"type": "string"
},
"keywords":{
"type": "string"
},
"category":{
"type": "string"
},
"last_updated_date":{
"type": "date"
},
"source_id":{
"type":"string"
}
}
}
}
}
我想使用 .net NEST 库将文档插入到该索引中。我的问题是 .net 更新方法的签名对我来说没有任何意义。
client.Update<TDocument>(IUpdateRequest<TDocument,TPartialDocument>)
Java 库对我来说更有意义:
UpdateRequest updateRequest = new UpdateRequest();
updateRequest.index("index");
updateRequest.type("type");
updateRequest.id("1");
updateRequest.doc(jsonBuilder()
.startObject()
.field("gender", "male")
.endObject());
client.update(updateRequest).get();
在 NEST 中,TDocument
和 TPartialDocument
类 来自哪里?
我制作的这些 C# 类 是否代表我的索引?
TDocument
和 TPartialDocument
是
的 POCO 类型的通用类型参数
- 表示 Elasticsearch 中的文档 (
TDocument
) 和
- 执行部分更新时,Elasticsearch (
TPartialDocument
) 中部分文档的表示。
在完全更新的情况下,TDocument
和 TPartialDocument
可能指的是相同的具体 POCO 类型。让我们看一些例子来证明。
让我们用您在上面定义的映射创建一个索引。首先,我们可以使用 POCO 类型
表示文档
public class Page
{
public string Url { get; set; }
public string Title { get; set; }
public string Body { get; set; }
[String(Name="meta_description")]
public string MetaDescription { get; set; }
public IList<string> Keywords { get; set; }
public string Category { get; set; }
[Date(Name="last_updated_date")]
public DateTimeOffset LastUpdatedDate { get; set; }
[String(Name="source_id")]
public string SourceId { get; set; }
}
默认情况下,当 NEST 序列化 POCO 属性时,它使用驼峰命名约定。因为您的索引具有某些属性的蛇形套管,例如"last_updated_date"
,我们可以覆盖 NEST 将这些序列化为使用属性的名称。
接下来,让我们创建要使用的客户端
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var pagesIndex = "pages";
var connectionSettings = new ConnectionSettings(pool)
.DefaultIndex(pagesIndex)
.PrettyJson()
.DisableDirectStreaming()
.OnRequestCompleted(response =>
{
// log out the request
if (response.RequestBodyInBytes != null)
{
Console.WriteLine(
$"{response.HttpMethod} {response.Uri} \n" +
$"{Encoding.UTF8.GetString(response.RequestBodyInBytes)}");
}
else
{
Console.WriteLine($"{response.HttpMethod} {response.Uri}");
}
Console.WriteLine();
// log out the response
if (response.ResponseBodyInBytes != null)
{
Console.WriteLine($"Status: {response.HttpStatusCode}\n" +
$"{Encoding.UTF8.GetString(response.ResponseBodyInBytes)}\n" +
$"{new string('-', 30)}\n");
}
else
{
Console.WriteLine($"Status: {response.HttpStatusCode}\n" +
$"{new string('-', 30)}\n");
}
});
var client = new ElasticClient(connectionSettings);
连接设置的配置方式有助于开发;
DefaultIndex()
- 默认索引已配置为 "pages"
。如果没有在请求中传递明确的索引名称并且无法为 POCO 推断出索引名称,则将使用默认索引。
PrettyJson()
- 美化(即缩进)json 请求和响应。这将有助于查看 Elasticsearch 发送和接收的内容。
DisableDirectStreaming()
- NEST 默认将 POCO 序列化为请求流,并从响应流中反序列化响应类型。禁用这种直接流式传输将在内存流中缓冲请求和响应字节,允许我们在 OnRequestCompleted()
中注销它们
OnRequestCompleted()
- 在收到响应后调用。这允许我们在开发时注销请求和响应。
2、3 和 4 在开发期间很有用,但会带来一些性能开销,因此您可能决定不在生产中使用它们。
现在,让我们使用页面映射创建索引
// delete the index if it exists. Useful for demo purposes so that
// we can re-run this example.
if (client.IndexExists(pagesIndex).Exists)
client.DeleteIndex(pagesIndex);
// create the index, adding the mapping for the Page type to the index
// at the same time. Automap() will infer the mapping from the POCO
var createIndexResponse = client.CreateIndex(pagesIndex, c => c
.Mappings(m => m
.Map<Page>(p => p
.AutoMap()
)
)
);
索引一个新的页面类型就像
一样简单
// create a sample Page
var page = new Page
{
Title = "Sample Page",
Body = "Sample Body",
Category = "sample",
Keywords = new List<string>
{
"sample",
"example",
"demo"
},
LastUpdatedDate = DateTime.UtcNow,
MetaDescription = "Sample meta description",
SourceId = "1",
Url = "/pages/sample-page"
};
// index the sample Page into Elasticsearch.
// NEST will infer the document type (_type) from the POCO type,
// by default it will camel case the POCO type name
var indexResponse = client.Index(page);
如果文档不存在,索引文档将创建该文档,如果存在,则覆盖现有文档。 Elasticsearch has optimistic concurrency control 可用于控制其在不同条件下的行为方式。
我们可以使用 Update
方法更新文档,但首先要了解一些背景知识。
我们可以通过指定索引、类型和id从Elasticsearch获取文档。 NEST 使这稍微容易一些,因为我们可以从 POCO 中推断出所有这些。当我们创建映射时,我们没有在 POCO 上指定 Id
属性;如果 NEST
看到一个名为 Id
的 属性,它会将其用作文档的 ID,但因为我们没有,所以这不是问题,因为 Elasticsearch 将为文档并将其放入文档元数据中。然而,由于文档元数据与源文档是分开的,这会使将文档建模为 POCO 类型有点棘手(但并非不可能);对于给定的响应,我们将可以通过元数据访问文档的 ID,并通过 _source
字段访问源。我们可以在应用程序中将 id 与我们的来源结合起来。
解决这个问题的更简单方法是在 POCO 上有一个 ID。我们可以在 POCO 上指定一个 Id
属性 ,这将用作文档的 id,但我们不必调用 属性 Id
如果我们不想,如果我们不想,我们需要告诉 NEST 哪个 属性 代表 id。这可以通过属性来完成。假设 SourceId
是 Page
实例的唯一 ID,请使用 ElasticsearchTypeAttribute
IdProperty
属性 来指定它。也许我们不应该也分析这个字符串而是逐字索引它,我们也可以通过 属性
属性的 Index
属性 来控制它
[ElasticsearchType(IdProperty = nameof(SourceId))]
public class Page
{
public string Url { get; set; }
public string Title { get; set; }
public string Body { get; set; }
[String(Name="meta_description")]
public string MetaDescription { get; set; }
public IList<string> Keywords { get; set; }
public string Category { get; set; }
[Date(Name="last_updated_date")]
public DateTimeOffset LastUpdatedDate { get; set; }
[String(Name="source_id", Index=FieldIndexOption.NotAnalyzed)]
public string SourceId { get; set; }
}
有了这些,我们需要像以前一样重新创建索引,以便这些更改反映在映射中,NEST 可以在索引 Page
实例时使用此配置。
现在,回到更新:) 我们可以从 Elasticsearch 获取文档,在应用程序中更新它,然后重新索引它
var getResponse = client.Get<Page>("1");
var page = getResponse.Source;
// update the last updated date
page.LastUpdatedDate = DateTime.UtcNow;
var updateResponse = client.Update<Page>(page, u => u.Doc(page));
第一个参数是我们想要获取的文档的 ID,NEST 可以从 Page
实例中推断出它。由于我们将 整个 文档传回此处,我们可以只使用 .Index()
而不是 Update()
,因为我们正在更新所有字段
var indexResponse = client.Index(page);
但是,由于我们只想更新 LastUpdatedDate
,必须从 Elasticsearch 获取文档,在应用程序中更新它,然后将文档发送回 Elasticsearch 是一项繁重的工作。我们可以只将更新后的 LastUpdatedDate
发送到 Elasticsearch,而不是使用 partial 文档。 C# 匿名类型在这里真的很有用
// model our partial document with an anonymous type.
// Note that we need to use the snake casing name
// (NEST will still camel case the property names but this
// doesn't help us here)
var lastUpdatedDate = new
{
last_updated_date = DateTime.UtcNow
};
// do the partial update.
// Page is TDocument, object is TPartialDocument
var partialUpdateResponse = client.Update<Page, object>("1", u => u
.Doc(lastUpdatedDate)
);
这里如果需要使用RetryOnConflict(int)
可以使用乐观并发控制
var partialUpdateResponse = client.Update<Page, object>("1", u => u
.Doc(lastUpdatedDate)
.RetryOnConflict(1)
);
对于部分更新,Elasticsearch 将获取文档,应用部分更新,然后索引更新后的文档;如果文档在获取和更新之间发生变化,Elasticsearch 将根据 RetryOnConflict(1)
.
再次重试
希望对您有所帮助:)
我使用以下查询在 elastic 中创建了一个索引:
PUT public_site
{
"mappings": {
"page": {
"properties": {
"url": {
"type": "string"
},
"title":{
"type": "string"
},
"body":{
"type": "string"
},
"meta_description":{
"type": "string"
},
"keywords":{
"type": "string"
},
"category":{
"type": "string"
},
"last_updated_date":{
"type": "date"
},
"source_id":{
"type":"string"
}
}
}
}
}
我想使用 .net NEST 库将文档插入到该索引中。我的问题是 .net 更新方法的签名对我来说没有任何意义。
client.Update<TDocument>(IUpdateRequest<TDocument,TPartialDocument>)
Java 库对我来说更有意义:
UpdateRequest updateRequest = new UpdateRequest();
updateRequest.index("index");
updateRequest.type("type");
updateRequest.id("1");
updateRequest.doc(jsonBuilder()
.startObject()
.field("gender", "male")
.endObject());
client.update(updateRequest).get();
在 NEST 中,TDocument
和 TPartialDocument
类 来自哪里?
我制作的这些 C# 类 是否代表我的索引?
TDocument
和 TPartialDocument
是
- 表示 Elasticsearch 中的文档 (
TDocument
) 和 - 执行部分更新时,Elasticsearch (
TPartialDocument
) 中部分文档的表示。
在完全更新的情况下,TDocument
和 TPartialDocument
可能指的是相同的具体 POCO 类型。让我们看一些例子来证明。
让我们用您在上面定义的映射创建一个索引。首先,我们可以使用 POCO 类型
表示文档public class Page
{
public string Url { get; set; }
public string Title { get; set; }
public string Body { get; set; }
[String(Name="meta_description")]
public string MetaDescription { get; set; }
public IList<string> Keywords { get; set; }
public string Category { get; set; }
[Date(Name="last_updated_date")]
public DateTimeOffset LastUpdatedDate { get; set; }
[String(Name="source_id")]
public string SourceId { get; set; }
}
默认情况下,当 NEST 序列化 POCO 属性时,它使用驼峰命名约定。因为您的索引具有某些属性的蛇形套管,例如"last_updated_date"
,我们可以覆盖 NEST 将这些序列化为使用属性的名称。
接下来,让我们创建要使用的客户端
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var pagesIndex = "pages";
var connectionSettings = new ConnectionSettings(pool)
.DefaultIndex(pagesIndex)
.PrettyJson()
.DisableDirectStreaming()
.OnRequestCompleted(response =>
{
// log out the request
if (response.RequestBodyInBytes != null)
{
Console.WriteLine(
$"{response.HttpMethod} {response.Uri} \n" +
$"{Encoding.UTF8.GetString(response.RequestBodyInBytes)}");
}
else
{
Console.WriteLine($"{response.HttpMethod} {response.Uri}");
}
Console.WriteLine();
// log out the response
if (response.ResponseBodyInBytes != null)
{
Console.WriteLine($"Status: {response.HttpStatusCode}\n" +
$"{Encoding.UTF8.GetString(response.ResponseBodyInBytes)}\n" +
$"{new string('-', 30)}\n");
}
else
{
Console.WriteLine($"Status: {response.HttpStatusCode}\n" +
$"{new string('-', 30)}\n");
}
});
var client = new ElasticClient(connectionSettings);
连接设置的配置方式有助于开发;
DefaultIndex()
- 默认索引已配置为"pages"
。如果没有在请求中传递明确的索引名称并且无法为 POCO 推断出索引名称,则将使用默认索引。PrettyJson()
- 美化(即缩进)json 请求和响应。这将有助于查看 Elasticsearch 发送和接收的内容。DisableDirectStreaming()
- NEST 默认将 POCO 序列化为请求流,并从响应流中反序列化响应类型。禁用这种直接流式传输将在内存流中缓冲请求和响应字节,允许我们在OnRequestCompleted()
中注销它们
OnRequestCompleted()
- 在收到响应后调用。这允许我们在开发时注销请求和响应。
2、3 和 4 在开发期间很有用,但会带来一些性能开销,因此您可能决定不在生产中使用它们。
现在,让我们使用页面映射创建索引
// delete the index if it exists. Useful for demo purposes so that
// we can re-run this example.
if (client.IndexExists(pagesIndex).Exists)
client.DeleteIndex(pagesIndex);
// create the index, adding the mapping for the Page type to the index
// at the same time. Automap() will infer the mapping from the POCO
var createIndexResponse = client.CreateIndex(pagesIndex, c => c
.Mappings(m => m
.Map<Page>(p => p
.AutoMap()
)
)
);
索引一个新的页面类型就像
一样简单// create a sample Page
var page = new Page
{
Title = "Sample Page",
Body = "Sample Body",
Category = "sample",
Keywords = new List<string>
{
"sample",
"example",
"demo"
},
LastUpdatedDate = DateTime.UtcNow,
MetaDescription = "Sample meta description",
SourceId = "1",
Url = "/pages/sample-page"
};
// index the sample Page into Elasticsearch.
// NEST will infer the document type (_type) from the POCO type,
// by default it will camel case the POCO type name
var indexResponse = client.Index(page);
如果文档不存在,索引文档将创建该文档,如果存在,则覆盖现有文档。 Elasticsearch has optimistic concurrency control 可用于控制其在不同条件下的行为方式。
我们可以使用 Update
方法更新文档,但首先要了解一些背景知识。
我们可以通过指定索引、类型和id从Elasticsearch获取文档。 NEST 使这稍微容易一些,因为我们可以从 POCO 中推断出所有这些。当我们创建映射时,我们没有在 POCO 上指定 Id
属性;如果 NEST
看到一个名为 Id
的 属性,它会将其用作文档的 ID,但因为我们没有,所以这不是问题,因为 Elasticsearch 将为文档并将其放入文档元数据中。然而,由于文档元数据与源文档是分开的,这会使将文档建模为 POCO 类型有点棘手(但并非不可能);对于给定的响应,我们将可以通过元数据访问文档的 ID,并通过 _source
字段访问源。我们可以在应用程序中将 id 与我们的来源结合起来。
解决这个问题的更简单方法是在 POCO 上有一个 ID。我们可以在 POCO 上指定一个 Id
属性 ,这将用作文档的 id,但我们不必调用 属性 Id
如果我们不想,如果我们不想,我们需要告诉 NEST 哪个 属性 代表 id。这可以通过属性来完成。假设 SourceId
是 Page
实例的唯一 ID,请使用 ElasticsearchTypeAttribute
IdProperty
属性 来指定它。也许我们不应该也分析这个字符串而是逐字索引它,我们也可以通过 属性
Index
属性 来控制它
[ElasticsearchType(IdProperty = nameof(SourceId))]
public class Page
{
public string Url { get; set; }
public string Title { get; set; }
public string Body { get; set; }
[String(Name="meta_description")]
public string MetaDescription { get; set; }
public IList<string> Keywords { get; set; }
public string Category { get; set; }
[Date(Name="last_updated_date")]
public DateTimeOffset LastUpdatedDate { get; set; }
[String(Name="source_id", Index=FieldIndexOption.NotAnalyzed)]
public string SourceId { get; set; }
}
有了这些,我们需要像以前一样重新创建索引,以便这些更改反映在映射中,NEST 可以在索引 Page
实例时使用此配置。
现在,回到更新:) 我们可以从 Elasticsearch 获取文档,在应用程序中更新它,然后重新索引它
var getResponse = client.Get<Page>("1");
var page = getResponse.Source;
// update the last updated date
page.LastUpdatedDate = DateTime.UtcNow;
var updateResponse = client.Update<Page>(page, u => u.Doc(page));
第一个参数是我们想要获取的文档的 ID,NEST 可以从 Page
实例中推断出它。由于我们将 整个 文档传回此处,我们可以只使用 .Index()
而不是 Update()
,因为我们正在更新所有字段
var indexResponse = client.Index(page);
但是,由于我们只想更新 LastUpdatedDate
,必须从 Elasticsearch 获取文档,在应用程序中更新它,然后将文档发送回 Elasticsearch 是一项繁重的工作。我们可以只将更新后的 LastUpdatedDate
发送到 Elasticsearch,而不是使用 partial 文档。 C# 匿名类型在这里真的很有用
// model our partial document with an anonymous type.
// Note that we need to use the snake casing name
// (NEST will still camel case the property names but this
// doesn't help us here)
var lastUpdatedDate = new
{
last_updated_date = DateTime.UtcNow
};
// do the partial update.
// Page is TDocument, object is TPartialDocument
var partialUpdateResponse = client.Update<Page, object>("1", u => u
.Doc(lastUpdatedDate)
);
这里如果需要使用RetryOnConflict(int)
var partialUpdateResponse = client.Update<Page, object>("1", u => u
.Doc(lastUpdatedDate)
.RetryOnConflict(1)
);
对于部分更新,Elasticsearch 将获取文档,应用部分更新,然后索引更新后的文档;如果文档在获取和更新之间发生变化,Elasticsearch 将根据 RetryOnConflict(1)
.
希望对您有所帮助:)