2017-01-27 3 views
0

Erste ich habe ein Elasticsearch Objekt mit diesen Feldern:Elasticsearch C# Nest oben Wörter mit 5.1

[Keyword] 
public List<string> Tags { get; set; } 
[Text] 
public string Title { get; set; } 

Und bevor ich die Top Tags zu erhalten verwendet werden, um in allen Dokumenten, mit diesem Code:

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)))); 

Aber mit Elastic 5.1, bekomme ich einen Fehler 400 mit diesem Hinweis:

Fielddata auf Textfelder standardmäßig deaktiviert ist. Setzen Sie fielddata = true auf [Tags], um Felddaten in den Speicher zu laden, indem Sie den invertierten Index nicht invertieren.

Dann wird die ES documentation about parameter mapping sagen Sie, „Es ist in der Regel nicht sinnvoll zu tun, machen so“ und „für die Volltextsuche ein Textfeld zu haben, und ein unanalysiert Stichwort Feld mit doc_values ​​für Aggregationen freigegeben“.

Aber das einzige Dokument mit diesem ist für 5.0, und die gleiche Seite für 5.1 scheint nicht zu existieren.

Jetzt hat 5.1 eine Seite über Term Aggregation, die zu decken scheint, was ich brauche, aber es gibt absolut nichts in C#/Nest, das ich verwenden kann.

Also, ich versuche, herauszufinden, wie ich nur die besten Worte bekommen, über alle Dokumente, von den Etikett (wo jeder Tag sein eigenes Wort ist, zum Beispiel „New York“ ist nicht " New " und " York ") und der Titel (wo jedes Wort seine eigene Sache ist) in C#.


Ich muss diesen Beitrag bearbeiten, weil es ein tieferes Problem zu sein scheint. Ich schrieb einige Test-Code, der die Frage stellt:

Lassen Sie uns ein einfaches Objekt erstellen:

public class MyObject 
{ 
    [Keyword] 
    public string Id { get; set; } 
    [Text] 
    public string Category { get; set; } 
    [Text(Fielddata = true)] 
    public string Keywords { get; set; } 
} 

erstellen den Index:

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); 

den Index mit random stuff füllen:

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); 
} 

und die Abfrage durchführen:

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) 
     ) 
    ) 
); 

Es schlägt fehl, auf die gleiche Weise wie zuvor, mit einem 400 und:

Fielddata ist auf Textfelder standardmäßig deaktiviert. Setzen Sie fielddata = true auf [Keywords], um Felddaten in den Speicher zu laden, indem Sie den invertierten Index invertieren.

aber Fielddata wird auf [Keywords] auf True gesetzt, aber es beschwert sich darüber.

so, lassen Sie uns verrückt und die Klasse auf diese Weise ändern:

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; } 
} 

auf diese Weise alles ist ein Text und alles hat Fielddata = true .. gut, das gleiche Resultat.

so, entweder ich bin wirklich nicht etwas einfach zu verstehen, oder es funktioniert nicht oder nicht dokumentiert :)

Antwort

1

Es ist weniger üblich, dass Sie Fielddata wollen; Ihre hier bestimmte Suche, wo man nur die Tags und die Titelfelder aus der Suchanfrage zurückkehren möchten, um mit Source Filtering für dieses

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 muss uninvert dem invertierten Index in eine im Speicher einen Blick Struktur für Aggregationen und Sortierung. Während der Zugriff auf diese Daten sehr schnell sein kann, kann sie auch viel Speicher für einen großen Datensatz verbrauchen.

EDIT:

In Ihrem bearbeiten, ich weiß nicht überall sehen, wo Sie der Index erstellen und explizit Karte Ihre MyObject POCO; Ohne den Index explizit zu erstellen und das POCO zuzuordnen, erstellt Elasticsearch automatisch den Index und leitet das Mapping für MyObject basierend auf dem ersten empfangenen json-Dokument ab, was bedeutet, dass Keywords als text-Feld mit keywordmulti_field und Fielddata nicht zugeordnet wird aktiviert auf der text Feldzuordnung.

Hier ist ein Beispiel, es zu zeigen, alle

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; } 
} 

Das gibt

{ 
    "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 
     } 
     ] 
    } 
    } 
} 

Sie arbeiten könnten auch Mapping betrachten Keywords als text Feld mit einem keyword multi_field, für unstrukturierte Suche das text Feld mit und die keyword für Sortieren, Aggregationen und strukturierte Suche. Auf diese Weise erhalten Sie das Beste aus beiden Welten und müssen nicht 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") 
         ) 
        ) 
       ) 
      ) 
     ) 
    ) 
); 

dann auf der Suche Verwendung

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

wir sprechen 10 Millionen Dokumente, etwa 500 Tags ermöglichen; ist die Größe proportional zur Anzahl der Dokumente oder der Anzahl der Tags? - Würde es mehr Sinn machen, dass ich das anders mache, indem ich eine dedizierte Tabelle für Tags und ihre Anzahl habe? – Thomas

+0

Wie setze ich fielddata = true, da die Tags [keyword] und nicht [text] sind? – Thomas

+0

Nun habe ich ein extra Feld nur für die Tags hinzugefügt, es ist ein [Text] Feld mit FieldData auf True gesetzt; und ich bekomme immer noch einen Fehler 400, der mir sagt, FieldData auf dem gleichen Feld auf true zu setzen. Hat jemand den C# -Treiber mit dieser Funktionalität verwendet, um ein Problem dort auszuschließen? könnte dies verwandt sein: https://github.com/10up/ElasticPress/issues/643 – Thomas

Verwandte Themen