2017-12-15 6 views
1

Ich habe zwei Indizes mit der folgenden Abbildung (I ihre Zuordnungen Verknüpfung wird):Wie wird mit Nest ElasticSearch in mehreren Indizes gesucht?

1) Account Mapping:

elasticClient.CreateIndex("account", i => i 
       .Settings(s => s 
          .NumberOfShards(2) 
          .NumberOfReplicas(0) 
         ) 
          .Mappings(m => m 
            .Map<AccountType>(map => map 
               .AutoMap() 
               .Properties(p => p 
                .Text(c => c 
                  .Name(n => n.Name) 
                  .Analyzer("standard") 
                ) 
                .Text(c => c 
                  .Name(n => n.Description) 
                  .Analyzer("standard") 
                ) 
               ) 
            ) 
          ) 
         ); 

2) Product Mapping:

elasticClient.CreateIndex("proudct", i => i 
       .Settings(s => s 
          .NumberOfShards(2) 
          .NumberOfReplicas(0) 
         ) 
          .Mappings(m => m 
            .Map<ProductType>(map => map 
               .AutoMap() 
               .Properties(p => p 
                .Text(c => c 
                  .Name(n => n.Title) 
                  .Analyzer("standard") 
                ) 
                .Text(c => c 
                  .Name(n => n.Description) 
                  .Analyzer("standard") 
                ) 
               ) 
            ) 
          ) 
         ); 

Jetzt habe ich mehrere Dinge, die ich brauche, um sie klar zu bekommen:

1) Zuerst ist es eine gute Idee, einen Index zu haben, der in meinem Fall Konto ist und produc hat ts als verschachtelte Objekte, aber hier muss ich für jedes Mal, wenn ich ein neues Produkt aktualisieren/hinzufügen möchte, das gesamte Account-Dokument neu indizieren (updaten)?

2) Meine zweite Frage ist: Ich möchte Suchfunktionalität haben. Wenn der Benutzer also durch Eingabe eines Textfelds sucht, möchte ich die besten Übereinstimmungen sowohl für die Konten als auch für die Produkte erhalten (hier suche ich nach Titel und Beschreibung) Plus Kontonamen und die Beschreibung dann immer am besten Matches):

Also hier wie gegen mehrere Indizes zur Suche mit Nest ElasticSeach, oder wenn es nicht möglich ist, ist es eine gute Idee, die besten Übereinstimmungen aus jedem Index zu bekommen, dann am besten bekommen Übereinstimmungen von beiden Ergebnissen abhängig von der Punktzahl?

PS: Hier ein Beispiel ist innerhalb Produkt Index für die Suche:

 var result = elasticClient.Search<ProductType>(s => s 
              .Size(10) 
              .Query(q => q 
               .MultiMatch(m => m 
               .Fields(f => f.Field(p => p.Title, 1.5).Field(p => p.Description, 0.8)) 
               .Operator(Operator.Or) 
               .Query(query) 
              ) 
              ) 
             ); 

Antwort

1

1) Zuerst es eine gute Idee ist, einen Index zu haben, die in meinem Fall ist Konto und hat Produkte als verschachtelte Objekte , aber hier muss ich jedes Mal, wenn ich ein neues Produkt aktualisieren/hinzufügen möchte, das gesamte Konto neu indizieren (aktualisieren)?

Es wird generell empfohlen, einen Typ pro Index und in Elasticsearch 6.0+, you can only have one type per index zu verwenden. Wenn Produkte als verschachtelte Objekte in einem Konto dargestellt werden, müssen Sie das neue Dokument einem Konto hinzufügen (entweder in Ihrem Anwendungscode oder in Elasticsearch).

2) Meine zweite Frage ist: Ich möchte Suchfunktion haben, so dass, wenn der Benutzer Suche in einem Textfeld, indem Sie würde ich die besten Übereinstimmungen für beide Konten bekommen und Produkte (hier werde ich gegen Titel Produkt suchen und Beschreibung Plus-Konto des Namens und der Beschreibung dann immer am besten Matches):

Sie über mehrere Indizes suchen, überprüfen the documentation of covariant search results; Es zeigt ein Beispiel für die Rückgabe mehrerer verschiedener Typen aus einem Index (dieses Beispiel wird für 6.0 aktualisiert!), aber es ist möglich, dies über mehrere Indizes hinweg durchzuführen. Hier ein Beispiel:

private static void Main() 
{ 
    var settings = new ConnectionSettings(new Uri("http://localhost:9200")) 
     .InferMappingFor<AccountType>(i => i 
      .IndexName("account") 
     ) 
     .InferMappingFor<ProductType>(i => i 
      .IndexName("product") 
     ) 
     // useful for development, to make the request/response bytes 
     // available on the response 
     .DisableDirectStreaming() 
     // indented JSON in requests/responses 
     .PrettyJson() 
     // log out all requests/responses 
     .OnRequestCompleted(callDetails => 
     { 
      if (callDetails.RequestBodyInBytes != null) 
      { 
       Console.WriteLine(
        $"{callDetails.HttpMethod} {callDetails.Uri} \n" + 
        $"{Encoding.UTF8.GetString(callDetails.RequestBodyInBytes)}"); 
      } 
      else 
      { 
       Console.WriteLine($"{callDetails.HttpMethod} {callDetails.Uri}"); 
      } 

      Console.WriteLine(); 

      if (callDetails.ResponseBodyInBytes != null) 
      { 
       Console.WriteLine($"Status: {callDetails.HttpStatusCode}\n" + 
         $"{Encoding.UTF8.GetString(callDetails.ResponseBodyInBytes)}\n" + 
         $"{new string('-', 30)}\n"); 
      } 
      else 
      { 
       Console.WriteLine($"Status: {callDetails.HttpStatusCode}\n" + 
         $"{new string('-', 30)}\n"); 
      } 
     }); 

    var client = new ElasticClient(settings); 

    if (client.IndexExists("account").Exists) 
     client.DeleteIndex("account"); 

    client.CreateIndex("account", i => i 
     .Settings(s => s 
      .NumberOfShards(2) 
      .NumberOfReplicas(0) 
     ) 
     .Mappings(m => m 
      .Map<AccountType>(map => map 
       .AutoMap() 
       .Properties(p => p 
        .Text(c => c 
         .Name(n => n.Name) 
         .Analyzer("standard") 
        ) 
        .Text(c => c 
         .Name(n => n.Description) 
         .Analyzer("standard") 
        ) 
       ) 
      ) 
     ) 
    ); 

    if (client.IndexExists("product").Exists) 
     client.DeleteIndex("product"); 

    client.CreateIndex("product", i => i 
     .Settings(s => s 
      .NumberOfShards(2) 
      .NumberOfReplicas(0) 
     ) 
     .Mappings(m => m 
      .Map<ProductType>(map => map 
       .AutoMap() 
       .Properties(p => p 
        .Text(c => c 
         .Name(n => n.Title) 
         .Analyzer("standard") 
        ) 
        .Text(c => c 
         .Name(n => n.Description) 
         .Analyzer("standard") 
        ) 
       ) 
      ) 
     ) 
    ); 

    client.IndexMany(new[] { 
     new AccountType { Name = "Name 1", Description = "Description 1" }, 
     new AccountType { Name = "Name 2", Description = "Description 2" }, 
     new AccountType { Name = "Name 3", Description = "Description 3" }, 
     new AccountType { Name = "Name 4", Description = "Description 4" }, 
    }); 

    client.IndexMany(new[] { 
     new ProductType { Title = "Title 1", Description = "Description 1" }, 
     new ProductType { Title = "Title 2", Description = "Description 2" }, 
     new ProductType { Title = "Title 3", Description = "Description 3" }, 
     new ProductType { Title = "Title 4", Description = "Description 4" }, 
    }); 

    var indices = Indices.Index(typeof(ProductType)).And(typeof(AccountType)); 

    client.Refresh(indices); 

    var searchResponse = client.Search<object>(s => s 
     .Index(indices) 
     .Type(Types.Type(typeof(ProductType), typeof(AccountType))) 
     .Query(q => (q 
      .MultiMatch(m => m 
       .Fields(f => f 
        .Field(Infer.Field<ProductType>(ff => ff.Title, 1.5)) 
        .Field(Infer.Field<ProductType>(ff => ff.Description, 0.8)) 
       ) 
       .Operator(Operator.Or) 
       .Query("Title 1") 
      ) && +q 
      .Term("_index", "product")) || (q 
      .MultiMatch(m => m 
       .Fields(f => f 
        .Field(Infer.Field<AccountType>(ff => ff.Name, 3)) 
        .Field(Infer.Field<AccountType>(ff => ff.Description, 0.3)) 
       ) 
       .Operator(Operator.Or) 
       .Query("Name 4") 
      ) && +q 
      .Term("_index", "account")) 
     ) 
    ); 

    foreach (var document in searchResponse.Documents) 
     Console.WriteLine($"document is a {document.GetType().Name}"); 
} 

public class ProductType 
{ 
    public string Title { get; set; } 
    public string Description { get; set; } 
} 

public class AccountType 
{ 
    public string Name { get; set; } 
    public string Description { get; set; } 
} 

Das Ergebnis ist

document is a AccountType 
document is a ProductType 
document is a AccountType 
document is a ProductType 
document is a AccountType 
document is a AccountType 
document is a ProductType 
document is a ProductType 

Es ist viel los hier so lassen Sie mich erklären.Die Suchanfrage JSON wie folgt aussieht:

POST http://localhost:9200/product%2Caccount/producttype%2Caccounttype/_search?pretty=true 
{ 
    "query": { 
    "bool": { 
     "should": [ 
     { 
      "bool": { 
      "must": [ 
       { 
       "multi_match": { 
        "query": "Title 1", 
        "operator": "or", 
        "fields": [ 
        "title^1.5", 
        "description^0.8" 
        ] 
       } 
       } 
      ], 
      "filter": [ 
       { 
       "term": { 
        "_index": { 
        "value": "product" 
        } 
       } 
       } 
      ] 
      } 
     }, 
     { 
      "bool": { 
      "must": [ 
       { 
       "multi_match": { 
        "query": "Name 4", 
        "operator": "or", 
        "fields": [ 
        "name^3", 
        "description^0.3" 
        ] 
       } 
       } 
      ], 
      "filter": [ 
       { 
       "term": { 
        "_index": { 
        "value": "account" 
        } 
       } 
       } 
      ] 
      } 
     } 
     ] 
    } 
    } 
} 

Die Suche über beide product und account Indizes ausgeführt wird, über producttype und accounttype Typen. In den Feldern title und description wird eine Abfrage multi_match ausgeführt, die mit einer Termbuchabfrage mit einer bool-Abfrage kombiniert wird, um die Abfrage auf den Index product zu beschränken. Der Begriff Abfrage befindet sich in einer Filterklausel, da für die Begriffssuche kein Relevanzwert berechnet werden sollte. Diese bool-Abfrage wird mit einer anderen bool-Abfrage kombiniert, die eine multi_match-Abfrage für die Felder name und description ausführt, die mit einer Termabfrage verbunden sind, um die Abfrage auf den account-Index zu beschränken. Die zwei Bool-Abfragen werden mithilfe von SOLL-Klauseln kombiniert, da entweder eine der Bool-Abfragen oder die andere übereinstimmen muss.

object wird als generischer Parameter Typ für die Search<T>() Methodenaufruf verwendet, da ProductType und AccountType (außer object!) Keine gemeinsame Basisklasse teilen die resultierende Dokumentensammlung kann auf dem eingegeben werden. Wir können jedoch aus den Ergebnissen sehen, dass NEST tatsächlich Dokumente mit dem Typ producttype zu Instanzen von ProductType und Dokumente mit dem Typ accounttype zu Instanzen von deserialisiert hat.

Die Abfrage verwendet operator overloading, um Abfragen bündiger zu kombinieren.

+0

Bro Sie sind der Meister von Nest elasticsearch. Jedes Mal, wenn ich eine Frage stelle, erhalte ich eine genaue Antwort von Ihnen. Vielen Dank für Ihre zu detaillierte Antwort. –

+1

Hoffe "wie eine Hölle" ist gut :) Glücklich zu helfen –

+0

Sorry für mein schlechtes Englisch. Es scheint, dass "wie eine Hölle" schlechte Bedeutung hat –

Verwandte Themen