【问题标题】:ElasticSearch C# Nest Getting top words with 5.1ElasticSearch C# Nest 使用 5.1 获取热门词
【发布时间】:2017-06-13 16:18:22
【问题描述】:

我有一个包含这些字段的 ElasticSearch 对象:

[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=true on [标签] 为了通过反相将字段数据加载到内存中 倒排索引。

然后ES documentation about parameter mapping 会告诉您“这样做通常没有意义”和“有一个用于全文搜索的文本字段,以及一个为聚合启用 doc_values 的未分析关键字字段”。

但唯一的文档是针对 5.0 的,而针对 5.1 的同一页面似乎不存在。

现在,5.1 有一个关于 Term Aggregation 的页面,似乎涵盖了我需要的内容,但在 C#/Nest 中绝对找不到我可以使用的内容。

所以,我试图弄清楚如何从标签中获取所有文档中最热门的单词(其中每个标签都是自己的单词;例如 "New York"不是 "New""York") 和 C# 中的标题(每个单词都是它自己的东西)。


我需要编辑这篇文章,因为似乎存在更深层次的问题。我写了一些测试代码来说明这个问题:

让我们创建一个简单的对象:

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=true on [关键字] 为了通过反转反转来加载内存中的字段数据 索引。

但是 [Keywords] 上的 Fielddata 设置为 true,但它一直在抱怨它。

所以,让我们发疯,用这种方式修改类:

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.. 好吧,同样的结果。

所以,要么我真的不理解一些简单的东西,要么它坏了或没有记录:)

【问题讨论】:

    标签: c# elasticsearch nest


    【解决方案1】:

    您想要Fielddata 的情况不太常见;对于您在此处的特定搜索,您只想从搜索查询中返回标签和标题字段,请查看使用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 将被映射为带有 @ 的 text 字段987654331@multi_field 和 Fielddata 将不会在 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; }
    }
    

    返回

    {
      "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 映射为text 字段和keyword 多字段,使用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)
            )
        )
    );
    

    【讨论】:

    • 我们说的是 1000 万个文档,大约 500 个标签;大小是否与文档数量或标签种类成正比? - 我用另一种方式来管理标签及其计数是否更有意义?
    • 如何设置 fielddata=true,因为标签是 [keyword] 而不是 [text]?
    • 现在,我为标签添加了一个额外的字段,它是一个 FieldData 设置为 true 的 [text] 字段;我仍然收到一个错误 400,告诉我在同一个字段上将 FieldData 设置为 true。有没有人使用具有此功能的 C# 驱动程序来排除那里的任何问题?这可能是相关的:github.com/10up/ElasticPress/issues/643
    • 好的,这阐明了我需要知道的一切;在我们的任何代码中,我从来没有调用过 createindex 函数,因为我认为它是自动发生的。看起来我已经按顺序进行了一些修复!
    • 为了显式映射(使用 NEST 从 POCO 属性类型、属性或使用流畅映射进行推断),您必须在索引创建时调用 .CreateIndex() 调用 .Map&lt;T&gt; 或调用.Map&lt;T&gt;() 之前 映射任何文档。查看自动映射文档:elastic.co/guide/en/elasticsearch/client/net-api/5.x/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多