我在https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/custom-serialization.html#_serializing_type_information以获取弹性搜索中文档的$type信息。
然而,如页面上所述,这只返回外部文档的类型信息:
类型信息是为外部MyDocument实例序列化的,但不是为SubDocuments集合中的每个MySubDocument实例序列化。
所以我现在的问题是,是否有人知道如何获取子文档的类型信息?
我尝试过使用与Elasticsearch(使用LinqPad(分离的JsonSerializerSettings示例中相同的Json序列化设置,在那里我还获得了子文档的类型信息:
void Main()
{
var temp = new ListBlock
{
Id = 1,
Title = "Titel",
Blocks = new List<BlockContent> {
new InnerBlock {
Id = 11,
MyProperty ="Inner Property"
},
new InnerBlock2 {
Id = 12,
MyProperty2 = "Inner property 2"
}
}
};
var serializeOptions = new Newtonsoft.Json.JsonSerializerSettings
{
TypeNameHandling = Newtonsoft.Json.TypeNameHandling.All,
NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
TypeNameAssemblyFormatHandling = Newtonsoft.Json.TypeNameAssemblyFormatHandling.Simple,
Formatting = Newtonsoft.Json.Formatting.Indented
};
var serialized = Newtonsoft.Json.JsonConvert.SerializeObject(temp, serializeOptions);
serialized.Dump();
}
public class BlockContent
{
public int Id { get; set; }
}
public class ListBlock : BlockContent
{
public string Title { get; set; }
public List<BlockContent> Blocks { get; set; }
}
public class ListBlock2 : BlockContent
{
public string Title2 { get; set; }
public List<BlockContent> Blocks { get; set; }
}
public class InnerBlock : BlockContent
{
public string MyProperty { get; set; }
}
public class InnerBlock2 : BlockContent
{
public string MyProperty2 { get; set; }
}
此代码产生以下json:
{
"$type": "UserQuery+ListBlock, LINQPadQuery",
"Title": "Titel",
"Blocks": {
"$type": "System.Collections.Generic.List`1[[UserQuery+BlockContent, LINQPadQuery]], System.Private.CoreLib",
"$values": [
{
"$type": "UserQuery+InnerBlock, LINQPadQuery",
"MyProperty": "Inner Property",
"Id": 11
},
{
"$type": "UserQuery+InnerBlock2, LINQPadQuery",
"MyProperty2": "Inner property 2",
"Id": 12
}
]
},
"Id": 1
}
目前使用这些版本:
- 弹性搜索7.4.2
- 嵌套7.4.2
更新:
下面Russ Cam提供的解决方案对响应中包含的数据模型来说很有魅力,但我在下面举了一个例子,基于我们如何创建索引(使用automap(和对文档的初始列表进行批量索引。如果我们排除模型中的Guid列表(CategoryId(,这很好,但如果我们包括它,则会引发以下异常:
{
"took": 8,
"errors": true,
"items": [{
"index": {
"_index": "testindex",
"_type": "_doc",
"_id": "1",
"status": 400,
"error": {
"type": "mapper_parsing_exception",
"reason": "failed to parse field [categoryIds] of type [keyword] in document with id '1'. Preview of field's value: '{$values=[], $type=System.Collections.Generic.List`1[[System.Guid, System.Private.CoreLib]], System.Private.CoreLib}'",
"caused_by": {
"type": "illegal_state_exception",
"reason": "Can't get text on a START_OBJECT at 1:140"
}
}
}
}, {
"index": {
"_index": "testindex",
"_type": "_doc",
"_id": "2",
"status": 400,
"error": {
"type": "mapper_parsing_exception",
"reason": "failed to parse field [categoryIds] of type [keyword] in document with id '2'. Preview of field's value: '{$values=[], $type=System.Collections.Generic.List`1[[System.Guid, System.Private.CoreLib]], System.Private.CoreLib}'",
"caused_by": {
"type": "illegal_state_exception",
"reason": "Can't get text on a START_OBJECT at 1:141"
}
}
}
}
]
}
这里有一个简单的(.Net 5(控制台应用程序,希望其他人也能复制这种行为:
using System;
using System.Collections.Generic;
using System.Linq;
using Elasticsearch.Net;
using Nest;
using Nest.JsonNetSerializer;
using Newtonsoft.Json;
namespace ElasticsearchTypeSerializer
{
internal class Program
{
private const string IndexName = "testindex";
private static void Main(string[] args)
{
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var settings = new ConnectionSettings(pool,
(builtin, settings) => new MySecondCustomJsonNetSerializer(builtin, settings));
settings.DisableDirectStreaming();
var client = new ElasticClient(settings);
CreateIndex(client);
IndexDocuments(client);
var documents = GetDocuments(client);
}
private static void CreateIndex(IElasticClient client)
{
var createIndexResponse = client.Indices.Create(IndexName, x => x.Map<MyDocument>(m => m.AutoMap()));
}
private static void IndexDocuments(IElasticClient client)
{
var documents = new List<MyDocument>
{
new()
{
Id = 1,
Name = "My first document",
OwnerId = 2,
SubDocuments = new List<SubDocument>
{
new MySubDocument {Id = 11, Name = "my first sub document"},
new MySubDocument2 {Id = 12, Description = "my second sub document"}
}
},
new()
{
Id = 2,
Name = "My second document",
OwnerId = 3,
SubDocuments = new List<SubDocument>
{
new MySubDocument {Id = 21, Name = "My third sub document"}
}
}
};
var bulkIndexResponse = client.Bulk(b => b.Index(IndexName).IndexMany(documents).Refresh(Refresh.True));
}
private static IEnumerable<MyDocument> GetDocuments(IElasticClient client)
{
var searchResponse = client.Search<MyDocument>(s => s.Index(IndexName).Query(q => q.MatchAll()));
var documents = searchResponse.Documents.ToList();
return documents;
}
}
public class MyDocument
{
public int Id { get; set; }
public string Name { get; set; }
public string FilePath { get; set; }
public int OwnerId { get; set; }
public List<Guid> CategoryIds { get; set; } = new();
public List<SubDocument> SubDocuments { get; set; }
}
public class SubDocument
{
public int Id { get; set; }
}
public class MySubDocument : SubDocument
{
public string Name { get; set; }
}
public class MySubDocument2 : SubDocument
{
public string Description { get; set; }
}
public class MySecondCustomJsonNetSerializer : ConnectionSettingsAwareSerializerBase
{
public MySecondCustomJsonNetSerializer(IElasticsearchSerializer builtinSerializer,
IConnectionSettingsValues connectionSettings)
: base(builtinSerializer, connectionSettings)
{
}
protected override JsonSerializerSettings CreateJsonSerializerSettings()
{
return new()
{
TypeNameHandling = TypeNameHandling.All,
NullValueHandling = NullValueHandling.Ignore,
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple
};
}
}
}
非常感谢您对这个问题的任何帮助!
如果您希望为文档上的类型集合包括类型信息,则可以省略派生的合同解析程序,它支持集合项类型的类型处理
private static void Main()
{
var pool = new SingleNodeConnectionPool(new Uri($"http://localhost:9200"));
var settings = new ConnectionSettings(pool,
(builtin, settings) => new MySecondCustomJsonNetSerializer(builtin, settings));
var client = new ElasticClient(settings);
var document = new MyDocument
{
Id = 1,
Name = "My first document",
OwnerId = 2,
SubDocuments = new[]
{
new MySubDocument { Name = "my first sub document" },
new MySubDocument { Name = "my second sub document" },
}
};
var indexResponse = client.IndexDocument(document);
}
public class MyDocument
{
public int Id { get; set; }
public string Name { get; set; }
public string FilePath { get; set; }
public int OwnerId { get; set; }
public IEnumerable<MySubDocument> SubDocuments { get; set; }
}
public class MySubDocument
{
public string Name { get; set; }
}
public class MySecondCustomJsonNetSerializer : ConnectionSettingsAwareSerializerBase
{
public MySecondCustomJsonNetSerializer(IElasticsearchSerializer builtinSerializer, IConnectionSettingsValues connectionSettings)
: base(builtinSerializer, connectionSettings) { }
protected override JsonSerializerSettings CreateJsonSerializerSettings() =>
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
NullValueHandling = NullValueHandling.Ignore,
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple
};
}