ElasticSearch C# Nest 使用 5.1 获取热门词
ElasticSearch C# Nest Getting top words with 5.1
我有一个包含这些字段的 ElasticSearch object:
[Keyword]
public List<string> Tags { get; set; }
[Text]
public string Title { get; set; }
并且,在我使用此代码获取所有文档中的顶级标签之前:
var Match = Driver.Search<Metadata>(_ => _
.Query(Q => Q
.Term(P => P.Category, (int)Category)
&& Q.Term(P => P.Type, (int)Type))
.FielddataFields(F => F.Fields(F1 => F1.Tags, F2 => F2.Title))
.Aggregations(A => A.Terms("Tags", T => T.Field(F => F.Tags)
.Size(Limit))));
但是对于 Elastic 5.1,我收到错误 400 提示:
Fielddata is disabled on text fields by default. Set fielddata=true on
[Tags] in order to load fielddata in memory by uninverting the
inverted index.
然后ES documentation about parameter mapping告诉你"It usually doesn’t make sense to do so"和"have a text field for full text searches, and an unanalyzed keyword field with doc_values enabled for aggregations"。
但是唯一的文档是针对 5.0 的,而针对 5.1 的相同页面似乎不存在。
现在,5.1 有一个关于 Term Aggregation 的页面,似乎涵盖了我需要的内容,但在 C# / Nest 中绝对找不到我可以使用的东西。
所以,我想弄清楚如何从标签中获取所有文档中的热门词(其中每个标签都是自己的词;例如 "New York" 不是 "New" 和 "York")和标题(每个词都是自己的东西)在 C# 中。
我需要编辑这个 post 因为似乎有更深层次的问题。我写了一些测试代码来说明这个问题:
让我们创建一个简单的 object:
public class MyObject
{
[Keyword]
public string Id { get; set; }
[Text]
public string Category { get; set; }
[Text(Fielddata = true)]
public string Keywords { get; set; }
}
创建索引:
var Uri = new Uri(Constants.ELASTIC_CONNECTIONSTRING);
var Settings = new ConnectionSettings(Uri)
.DefaultIndex("test")
.DefaultFieldNameInferrer(_ => _)
.InferMappingFor<MyObject>(_ => _.IdProperty(P => P.Id));
var D = new ElasticClient(Settings);
用随机的东西填充索引:
for (var i = 0; i < 10; i++)
{
var O = new MyObject
{
Id = i.ToString(),
Category = (i % 2) == 0 ? "a" : "b",
Keywords = (i % 3).ToString()
};
D.Index(O);
}
并进行查询:
var m = D.Search<MyObject>(s => s
.Query(q => q.Term(P => P.Category, "a"))
.Source(f => f.Includes(si => si.Fields(ff => ff.Keywords)))
.Aggregations(a => a
.Terms("Keywords", t => t
.Field(f => f.Keywords)
.Size(Limit)
)
)
);
它和以前一样失败,有一个 400 和:
Fielddata is disabled on text fields by default. Set fielddata=true on
[Keywords] in order to load fielddata in memory by uninverting the inverted
index.
但是 [Keywords] 上的 Fielddata 设置为 true,但它一直抱怨它。
所以,让我们疯狂地修改 class 这样:
public class MyObject
{
[Text(Fielddata = true)]
public string Id { get; set; }
[Text(Fielddata = true)]
public string Category { get; set; }
[Text(Fielddata = true)]
public string Keywords { get; set; }
}
这样,所有内容都是文本,所有内容都具有 Fielddata = true.. 嗯,结果相同。
所以,要么我真的不理解简单的东西,要么它坏了或没有记录:)
你想要 Fielddata; for your particular search here where you want to return just the tags and the title fields from the search query, take a look at using Source Filtering 的情况并不常见
var Match = client.Search<Metadata>(s => s
.Query(q => q
.Term(P => P.Category, (int)Category) && q
.Term(P => P.Type, (int)Type)
)
.Source(f => f
.Includes(si => si
.Fields(
ff => ff.Tags,
ff => ff.Title
)
)
)
.Aggregations(a => a
.Terms("Tags", t => t
.Field(f => f.Tags)
.Size(Limit)
)
)
);
Fielddata 需要 uninvert 将倒排索引放入内存结构中以进行聚合和排序。虽然访问此数据的速度非常快,但对于大型数据集,它也会消耗大量内存。
编辑:
在您的编辑中,我没有看到您在任何地方创建 索引并明确映射您的 MyObject
POCO;无需显式创建索引和映射 POCO,Elasticsearch 将自动创建索引并根据收到的第一个 json 文档推断 MyObject
的映射,这意味着 Keywords
将被映射为具有 keyword
multi_field 和 Fielddata 的 text
字段将不会在 text
字段映射上启用。
这是一个演示它全部工作的例子
void Main()
{
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var defaultIndex = "test";
var connectionSettings = new ConnectionSettings(pool)
.DefaultIndex(defaultIndex)
.DefaultFieldNameInferrer(s => s)
.InferMappingFor<MyObject>(m => m
.IdProperty(p => p.Id)
);
var client = new ElasticClient(connectionSettings);
if (client.IndexExists(defaultIndex).Exists)
client.DeleteIndex(defaultIndex);
client.CreateIndex(defaultIndex, c => c
.Mappings(m => m
.Map<MyObject>(mm => mm
.AutoMap()
)
)
);
var objs = Enumerable.Range(0, 10).Select(i =>
new MyObject
{
Id = i.ToString(),
Category = (i % 2) == 0 ? "a" : "b",
Keywords = (i % 3).ToString()
});
client.IndexMany(objs);
client.Refresh(defaultIndex);
var searchResponse = client.Search<MyObject>(s => s
.Query(q => q.Term(P => P.Category, "a"))
.Source(f => f.Includes(si => si.Fields(ff => ff.Keywords)))
.Aggregations(a => a
.Terms("Keywords", t => t
.Field(f => f.Keywords)
.Size(10)
)
)
);
}
public class MyObject
{
[Keyword]
public string Id { get; set; }
[Text]
public string Category { get; set; }
[Text(Fielddata = true)]
public string Keywords { get; set; }
}
这个returns
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"failed" : 0
},
"hits" : {
"total" : 5,
"max_score" : 0.9808292,
"hits" : [
{
"_index" : "test",
"_type" : "myobject",
"_id" : "8",
"_score" : 0.9808292,
"_source" : {
"Keywords" : "2"
}
},
{
"_index" : "test",
"_type" : "myobject",
"_id" : "0",
"_score" : 0.2876821,
"_source" : {
"Keywords" : "0"
}
},
{
"_index" : "test",
"_type" : "myobject",
"_id" : "2",
"_score" : 0.13353139,
"_source" : {
"Keywords" : "2"
}
},
{
"_index" : "test",
"_type" : "myobject",
"_id" : "4",
"_score" : 0.13353139,
"_source" : {
"Keywords" : "1"
}
},
{
"_index" : "test",
"_type" : "myobject",
"_id" : "6",
"_score" : 0.13353139,
"_source" : {
"Keywords" : "0"
}
}
]
},
"aggregations" : {
"Keywords" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "0",
"doc_count" : 2
},
{
"key" : "2",
"doc_count" : 2
},
{
"key" : "1",
"doc_count" : 1
}
]
}
}
}
您还可以考虑将 Keywords
映射为具有 keyword
multi_field 的 text
字段,使用 text
字段进行非结构化搜索,并且 keyword
用于排序、聚合和结构化搜索。这样,您就可以两全其美,并且不需要启用 Fielddata
client.CreateIndex(defaultIndex, c => c
.Mappings(m => m
.Map<MyObject>(mm => mm
.AutoMap()
.Properties(p => p
.Text(t => t
.Name(n => n.Keywords)
.Fields(f => f
.Keyword(k => k
.Name("keyword")
)
)
)
)
)
)
);
然后在搜索中使用
var searchResponse = client.Search<MyObject>(s => s
.Query(q => q.Term(P => P.Category, "a"))
.Source(f => f.Includes(si => si.Fields(ff => ff.Keywords)))
.Aggregations(a => a
.Terms("Keywords", t => t
.Field(f => f.Keywords.Suffix("keyword"))
.Size(10)
)
)
);
我有一个包含这些字段的 ElasticSearch object:
[Keyword]
public List<string> Tags { get; set; }
[Text]
public string Title { get; set; }
并且,在我使用此代码获取所有文档中的顶级标签之前:
var Match = Driver.Search<Metadata>(_ => _
.Query(Q => Q
.Term(P => P.Category, (int)Category)
&& Q.Term(P => P.Type, (int)Type))
.FielddataFields(F => F.Fields(F1 => F1.Tags, F2 => F2.Title))
.Aggregations(A => A.Terms("Tags", T => T.Field(F => F.Tags)
.Size(Limit))));
但是对于 Elastic 5.1,我收到错误 400 提示:
Fielddata is disabled on text fields by default. Set fielddata=true on [Tags] in order to load fielddata in memory by uninverting the inverted index.
然后ES documentation about parameter mapping告诉你"It usually doesn’t make sense to do so"和"have a text field for full text searches, and an unanalyzed keyword field with doc_values enabled for aggregations"。
但是唯一的文档是针对 5.0 的,而针对 5.1 的相同页面似乎不存在。
现在,5.1 有一个关于 Term Aggregation 的页面,似乎涵盖了我需要的内容,但在 C# / Nest 中绝对找不到我可以使用的东西。
所以,我想弄清楚如何从标签中获取所有文档中的热门词(其中每个标签都是自己的词;例如 "New York" 不是 "New" 和 "York")和标题(每个词都是自己的东西)在 C# 中。
我需要编辑这个 post 因为似乎有更深层次的问题。我写了一些测试代码来说明这个问题:
让我们创建一个简单的 object:
public class MyObject
{
[Keyword]
public string Id { get; set; }
[Text]
public string Category { get; set; }
[Text(Fielddata = true)]
public string Keywords { get; set; }
}
创建索引:
var Uri = new Uri(Constants.ELASTIC_CONNECTIONSTRING);
var Settings = new ConnectionSettings(Uri)
.DefaultIndex("test")
.DefaultFieldNameInferrer(_ => _)
.InferMappingFor<MyObject>(_ => _.IdProperty(P => P.Id));
var D = new ElasticClient(Settings);
用随机的东西填充索引:
for (var i = 0; i < 10; i++)
{
var O = new MyObject
{
Id = i.ToString(),
Category = (i % 2) == 0 ? "a" : "b",
Keywords = (i % 3).ToString()
};
D.Index(O);
}
并进行查询:
var m = D.Search<MyObject>(s => s
.Query(q => q.Term(P => P.Category, "a"))
.Source(f => f.Includes(si => si.Fields(ff => ff.Keywords)))
.Aggregations(a => a
.Terms("Keywords", t => t
.Field(f => f.Keywords)
.Size(Limit)
)
)
);
它和以前一样失败,有一个 400 和:
Fielddata is disabled on text fields by default. Set fielddata=true on [Keywords] in order to load fielddata in memory by uninverting the inverted index.
但是 [Keywords] 上的 Fielddata 设置为 true,但它一直抱怨它。
所以,让我们疯狂地修改 class 这样:
public class MyObject
{
[Text(Fielddata = true)]
public string Id { get; set; }
[Text(Fielddata = true)]
public string Category { get; set; }
[Text(Fielddata = true)]
public string Keywords { get; set; }
}
这样,所有内容都是文本,所有内容都具有 Fielddata = true.. 嗯,结果相同。
所以,要么我真的不理解简单的东西,要么它坏了或没有记录:)
你想要 Fielddata; for your particular search here where you want to return just the tags and the title fields from the search query, take a look at using Source Filtering 的情况并不常见
var Match = client.Search<Metadata>(s => s
.Query(q => q
.Term(P => P.Category, (int)Category) && q
.Term(P => P.Type, (int)Type)
)
.Source(f => f
.Includes(si => si
.Fields(
ff => ff.Tags,
ff => ff.Title
)
)
)
.Aggregations(a => a
.Terms("Tags", t => t
.Field(f => f.Tags)
.Size(Limit)
)
)
);
Fielddata 需要 uninvert 将倒排索引放入内存结构中以进行聚合和排序。虽然访问此数据的速度非常快,但对于大型数据集,它也会消耗大量内存。
编辑:
在您的编辑中,我没有看到您在任何地方创建 索引并明确映射您的 MyObject
POCO;无需显式创建索引和映射 POCO,Elasticsearch 将自动创建索引并根据收到的第一个 json 文档推断 MyObject
的映射,这意味着 Keywords
将被映射为具有 keyword
multi_field 和 Fielddata 的 text
字段将不会在 text
字段映射上启用。
这是一个演示它全部工作的例子
void Main()
{
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var defaultIndex = "test";
var connectionSettings = new ConnectionSettings(pool)
.DefaultIndex(defaultIndex)
.DefaultFieldNameInferrer(s => s)
.InferMappingFor<MyObject>(m => m
.IdProperty(p => p.Id)
);
var client = new ElasticClient(connectionSettings);
if (client.IndexExists(defaultIndex).Exists)
client.DeleteIndex(defaultIndex);
client.CreateIndex(defaultIndex, c => c
.Mappings(m => m
.Map<MyObject>(mm => mm
.AutoMap()
)
)
);
var objs = Enumerable.Range(0, 10).Select(i =>
new MyObject
{
Id = i.ToString(),
Category = (i % 2) == 0 ? "a" : "b",
Keywords = (i % 3).ToString()
});
client.IndexMany(objs);
client.Refresh(defaultIndex);
var searchResponse = client.Search<MyObject>(s => s
.Query(q => q.Term(P => P.Category, "a"))
.Source(f => f.Includes(si => si.Fields(ff => ff.Keywords)))
.Aggregations(a => a
.Terms("Keywords", t => t
.Field(f => f.Keywords)
.Size(10)
)
)
);
}
public class MyObject
{
[Keyword]
public string Id { get; set; }
[Text]
public string Category { get; set; }
[Text(Fielddata = true)]
public string Keywords { get; set; }
}
这个returns
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"failed" : 0
},
"hits" : {
"total" : 5,
"max_score" : 0.9808292,
"hits" : [
{
"_index" : "test",
"_type" : "myobject",
"_id" : "8",
"_score" : 0.9808292,
"_source" : {
"Keywords" : "2"
}
},
{
"_index" : "test",
"_type" : "myobject",
"_id" : "0",
"_score" : 0.2876821,
"_source" : {
"Keywords" : "0"
}
},
{
"_index" : "test",
"_type" : "myobject",
"_id" : "2",
"_score" : 0.13353139,
"_source" : {
"Keywords" : "2"
}
},
{
"_index" : "test",
"_type" : "myobject",
"_id" : "4",
"_score" : 0.13353139,
"_source" : {
"Keywords" : "1"
}
},
{
"_index" : "test",
"_type" : "myobject",
"_id" : "6",
"_score" : 0.13353139,
"_source" : {
"Keywords" : "0"
}
}
]
},
"aggregations" : {
"Keywords" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "0",
"doc_count" : 2
},
{
"key" : "2",
"doc_count" : 2
},
{
"key" : "1",
"doc_count" : 1
}
]
}
}
}
您还可以考虑将 Keywords
映射为具有 keyword
multi_field 的 text
字段,使用 text
字段进行非结构化搜索,并且 keyword
用于排序、聚合和结构化搜索。这样,您就可以两全其美,并且不需要启用 Fielddata
client.CreateIndex(defaultIndex, c => c
.Mappings(m => m
.Map<MyObject>(mm => mm
.AutoMap()
.Properties(p => p
.Text(t => t
.Name(n => n.Keywords)
.Fields(f => f
.Keyword(k => k
.Name("keyword")
)
)
)
)
)
)
);
然后在搜索中使用
var searchResponse = client.Search<MyObject>(s => s
.Query(q => q.Term(P => P.Category, "a"))
.Source(f => f.Includes(si => si.Fields(ff => ff.Keywords)))
.Aggregations(a => a
.Terms("Keywords", t => t
.Field(f => f.Keywords.Suffix("keyword"))
.Size(10)
)
)
);