我使用以下查询在弹性中创建了一个索引:
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
TDocument并且TPartialDocument是为POCO类型的泛型类型参数
在完全更新的情况下,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将该名称序列化为using属性的名称。
"last_updated_date"
接下来,让我们创建要使用的客户端
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"
PrettyJson()
DisableDirectStreaming()
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() ) ) );
查看自动映射文档,以获取有关如何控制POCO类型映射的更多详细信息。
索引新的页面类型很简单
// 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具有乐观的并发控制,可用于控制它在不同条件下的行为。
我们可以使用这些Update方法来更新文档,但首先要介绍一些背景知识。
Update
我们可以通过指定索引,类型和ID从Elasticsearch获取文档。NEST使此操作稍微容易些,因为我们可以从POCO推断所有这些信息。创建映射时,我们没有Id在POCO上指定属性;如果NEST看到一个名为的属性Id,它将使用它作为文档的ID,但是由于我们没有该属性,因此这不是问题,因为Elasticsearch会为该文档生成一个ID并将其放入文档元数据中。但是,由于文档元数据与源文档是分开的,因此这可能会使作为POCO类型的文档建模更加棘手(但并非没有可能)。对于给定的响应,我们将可以通过元数据访问文档的ID,并可以通过_source领域。我们可以在应用程序中将id与我们的源结合起来。
Id
NEST
_source
解决这个问题的一种更简单的方法是在POCO上有一个ID。我们可以Id在POCO上指定一个属性,该属性将用作文档的ID,但是Id如果我们不想调用该属性,则不必调用该属性,如果需要,我们需要告诉NEST属性代表ID。这可以通过属性来完成。假设这SourceId是一个Page实例的唯一ID ,请使用ElasticsearchTypeAttribute IdProperty属性来指定它。也许我们不应该也分析此字符串而是逐字索引它,我们也可以通过Index属性上的属性来控制它
SourceId
Page
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实例中推断出该ID 。由于我们将 整个 文档传递回此处,因此我们可以使用.Index()代替Update(),因为我们正在更新所有字段
.Index()
Update()
var indexResponse = client.Index(page);
但是,由于我们只想更新LastUpdatedDate,而必须从Elasticsearch中获取文档,在应用程序中对其进行更新,然后将文档发送回Elasticsearch是很多工作。我们只可以LastUpdatedDate使用 部分 文档来发送更新到Elasticsearch 。C#匿名类型在这里真的很有用
LastUpdatedDate
// 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)
RetryOnConflict(int)
var partialUpdateResponse = client.Update<Page, object>("1", u => u .Doc(lastUpdatedDate) .RetryOnConflict(1) );
进行部分更新后,Elasticsearch将获取文档,应用部分更新,然后为更新后的文档建立索引;如果文档在获取和更新之间更改,Elasticsearch将基于再次重试RetryOnConflict(1)。
RetryOnConflict(1)
希望有帮助:)