2017-01-27 1 views
0

У меня есть объект ElasticSearch с этими полями:ElasticSearch C# Nest Получение лучших слов с 5.1

[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 отключен текстовых полей по умолчанию. Установите fielddata = true на [Теги], чтобы загрузить полевые данные в памяти путем деинвертирования инвертированного индекса .

Тогда ES documentation about parameter mapping говорит вам «Это, как правило, не имеет смысла делать это» и «иметь текстовое поле для полнотекстового поиска, и ключевое слово непроанализированных поле с doc_values ​​поддержкой для укрупненных».

Но единственный документ с этим значением для 5.0, и та же самая страница для 5.1, похоже, не существует.

Теперь у 5.1 есть страница о Term Aggregation, которая, кажется, покрывает то, что мне нужно, но в C#/Nest нет ничего, что я могу использовать.

Итак, я пытаюсь понять, как я могу просто получить верхние слова, во всех документах, с тегами (где каждый тег является его собственное слово, например «Нью-Йорк» не " «Новые» и «Йорк») и название (где каждое слово - это его собственная вещь) на 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 отключен текстовых полей по умолчанию. Установите fielddata = true на [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 = истинный .. ну, тот же результат.

так, как я на самом деле не понимая, что-то простое, или он сломан или не документированы :)

ответ

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 перевернутого индекса в памяти структура для агрегации и сортировка. Хотя доступ к этим данным может быть очень быстрым, он также может потреблять много памяти для большого набора данных.

EDIT:

В правки, я не вижу где-нибудь, где вы создать индекс и явно сопоставить MyObject ПОКО; без явного создания индекса и отображения POCO, Elasticsearch автоматически создаст индекс и выведет отображение для MyObject на основе первого документа json, который он получит, то есть Keywords будет отображаться как поле text с 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 multi_field, используя 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) 
     ) 
    ) 
); 
+0

мы говорим 10million документы, примерно 500 тегов; размер, пропорциональный количеству документов или разнообразию тегов? - Было бы более разумным, чтобы я управлял этим другим путем, имея специальную таблицу для тегов и их счет? – Thomas

+0

Как установить fielddata = true, поскольку теги [keyword], а не [text]? – Thomas

+0

Теперь я добавил дополнительное поле только для тегов, это поле [text] с полем FieldData, установленным в true; и я все еще получаю ошибку 400, говорящую мне, чтобы установить FieldData в true в том же самом поле. Кто-нибудь использовал драйвер C# с этой функциональностью, чтобы исключить какую-либо проблему? это может быть связано: https://github.com/10up/ElasticPress/issues/643 – Thomas

Смежные вопросы