2015-05-20 12 views
10

Mam JSON, który wygląda tak: Nazwijmy tej dziedzinie Metadaneindeksowania/Searching „kompleks” JSON w elasticsearch

{ 
    "somekey1": "val1", 
    "someotherkey2": "val2", 
    "more_data": { 
    "contains_more": [ 
     { 
     "foo": "val5", 
     "bar": "val6" 
     }, 
     { 
     "foo": "val66", 
     "baz": "val44" 
     }, 
    ], 
    "even_more": { 
     "foz" : 1234, 
    } 
    } 
} 

Jest to tylko prosty przykład. Prawdziwy może stać się jeszcze bardziej złożony. Klawisze mogą pojawiać się wiele razy. Wartości również i mogą być int lub str.

Teraz pierwszy problem polega na tym, że nie jestem do końca pewien, jak poprawnie indeksować to w elastycznym wyszukiwaniu, aby móc znaleźć coś konkretnego.

Używam Django/Haystack gdzie indeks wygląda następująco:

class FooIndex(indexes.SearchIndex, indexes.Indexable): 
    text = indexes.CharField(document=True, use_template=True) 
    metadata = indexes.CharField(model_attr='get_metadata') 
    # and some more specific fields 

a Szablon:

{ 
    "foo": {{ object.foo }}, 
    "metadata": {{ object.metadata}}, 
    # and some more 
} 

Metadane zostaną wypełnione próbki powyżej, a wynik będzie wyglądać w następujący sposób:

{ 
    "foo": "someValue", 
    "metadata": { 
     "somekey1": "val1", 
     "someotherkey2": "val2", 
     "more_data": { 
     "contains_more": [ 
      { 
      "foo": "val5", 
      "bar": "val6" 
      }, 
      { 
      "foo": "val66", 
      "baz": "val44" 
      }, 
     ], 
     "even_more": { 
      "foz" : 1234, 
     } 
     } 
    }, 
    } 

Który przejdzie do kolumny "tekst" w elastycznym wyszuku.

Więc celem jest teraz, aby móc sprawdzić takie rzeczy jak:

  • foo: val5
  • Foz: 12 *
  • bar: val *
  • somekey1: val1
  • i tak dalej:

Drugi problem: Gdy szukam np. dla foo: val5 pasuje do wszystkich obiektów, które mają klucz "foo" i wszystkie obiekty, które mają wartość val5 gdzie indziej w swojej strukturze.

ten sposób mogę szukać w Django:

self.searchqueryset.auto_query(self.cleaned_data['q']) 

Czasami wyniki są „okayish” kiedyś to po prostu całkowicie bezużyteczne.

Mogę potrzebować wskazówki we właściwym kierunku i poznać błędy, które tu popełniłem. Dziękuję Ci!

Edytuj: Dodałem moje ostateczne rozwiązanie jako odpowiedź poniżej!

+1

Wstęp: Nie jestem użytkownikiem django, tylko ES. Zgaduję: pole 'content' jest wypełnione wszystkimi danymi, uniemożliwiając dopasowywanie do konkretnego pola.Jeśli chcesz tego, musisz wyrazić to w swoim filtrze/zapytaniach (ale domyślam się, że nie używasz 'auto_query'). – mark

+0

ma swoje pole metadanych zawsze taką samą strukturę? –

+0

@juliendangers Czasami ma więcej pól lub zawiera wiele elementów w tablicy I czasami nie ma tablic i może być całkiem płaski. Jednak klucze są znane wcześniej i mogą istnieć np. do 30+ różnych – daddz

Odpowiedz

0

Zajęło to trochę czasu, aby dowiedzieć się, że właściwe rozwiązanie działa na mnie

Było to mieszanka obu odpowiedziach dostarczonych przez @juliendangers i @Val i kilka innych konfiguracji.

  1. Wymieniłem Haystack z bardziej konkretnym django-simple-elasticsearch
  2. Dodany niestandardowego get_type_mapping metody modelu

    @classmethod 
    def get_type_mapping(cls): 
        return { 
        "properties": { 
         "somekey": { 
         "type": "<specific_type>", 
         "format": "<specific_format>", 
         }, 
         "more_data": { 
         "type": "nested", 
         "include_in_parent": True, 
         "properties": { 
          "even_more": { 
          "type": "nested", 
          "include_in_parent": True, 
          } 
          /* and so on for each level you care about */ 
         } 
        } 
        } 
    
  3. Dodany zwyczaj get_document metoda modelu

    @classmethod 
    def get_document(cls, obj): 
        return { 
        'somekey': obj.somekey, 
        'more_data': obj.more_data, 
        /* and so on */ 
        } 
    
  4. dodawać własne Searchform

    class Searchform(ElasticsearchForm): 
        q = forms.Charfield(required=False) 
    
        def get_index(self): 
        return 'your_index' 
    
        def get_type(self): 
        return 'your_model' 
    
        def prepare_query(self): 
        if not self.cleaned_data['q']: 
         q = "*" 
        else: 
         q = str(self.cleaned_data['q']) 
    
        return { 
         "query": { 
         "query_string": { 
          "query": q 
         } 
         } 
        } 
    
        def search(self): 
        esp = ElasticsearchProcessor(self.es) 
        esp.add_search(self.prepare_query, page=1, page_size=25, index=self.get_index(), doc_type=self.get_type()) 
        responses = esp.search() 
        return responses[0] 
    

Więc to, co pracował dla mnie i obejmuje moje usecases. Może to może być dla kogoś pomocne.

3

Jedna rzecz, która jest pewne jest to, że najpierw trzeba spreparować niestandardowego odwzorowania na podstawie konkretnych danych i zgodnie z własnymi potrzebami zapytań, moja rada jest taka, że ​​contains_more powinny być nested type tak, że można wydać bardziej precyzyjnych zapytań na twoich polach.

Nie znam dokładnych nazw twoich pól, ale na podstawie tego, co pokazałeś, jedno możliwe odwzorowanie może być czymś podobnym.

{ 
    "your_type_name": { 
    "properties": { 
     "foo": { 
     "type": "string" 
     }, 
     "metadata": { 
     "type": "object", 
     "properties": { 
      "some_key": { 
      "type": "string" 
      }, 
      "someotherkey2": { 
      "type": "string" 
      }, 
      "more_data": { 
      "type": "object", 
      "properties": { 
       "contains_more": { 
       "type": "nested", 
       "properties": { 
        "foo": { 
        "type": "string" 
        }, 
        "bar": { 
        "type": "string" 
        }, 
        "baz": { 
        "type": "string" 
        } 
       } 
       } 
      } 
      } 
     } 
     } 
    } 
    } 
} 

Potem, jak już wspomniano przez znak w swoim komentarzu, auto_query nie będzie go wyciąć, głównie ze względu na wielu poziomach zagnieżdżenia.O ile wiem, Django/Haystack nie obsługuje zagnieżdżonych zapytań po wyjęciu z pudełka, ale możesz rozszerzyć Haystack, aby go obsługiwać. Oto blog, w którym wyjaśniono, jak rozwiązać ten problem: http://www.stamkracht.com/extending-haystacks-elasticsearch-backend. Nie wiem, czy to pomaga, ale powinieneś spróbować i poinformować nas, jeśli potrzebujesz więcej pomocy.

+0

Czy to znaczy, że muszę zdefiniować mapowanie dla wszystkich możliwych "kluczy", jak również ich struktury? Jak napisałem w innym komentarzu, może być ponad 30 różnych. – daddz

+0

Cóż, im więcej instruujesz mapowania, tym dokładniejsze i potężniejsze mogą być twoje zapytania. 30 pól nie jest zabójcą, powiedziałbym. Mam dokumenty z setkami pól i wszystkie są mapowane poprawnie i dokładnie, co muszę zrobić. Najlepiej jest spróbować i zobaczyć, jak Ci pasuje w konkretnym przypadku. – Val

+0

Dziękuję. Spróbuję i zgłoś się ponownie! – daddz

3

indeksowania:

Przede wszystkim należy użyć dynamicznego templates, jeśli chcesz określić konkretny odwzorowanie stosunkowo nazwy klucza lub jeśli dokumenty nie mają taką samą strukturę.

Ale 30 klucz nie jest tak wysoki, i powinien wolisz zdefiniowanie własnego mapowania niż pozwolić Elasticsearch zgadywania go dla Ciebie (w przypadku nieprawidłowych danych, które zostały dodane po pierwsze, mapowanie zostanie określona zgodnie z tymi danymi)

Wyszukany:

nie można szukać

foz: val5 

ponieważ klucz "Foz" nie istnieje.

Ale klucz „metadata.more_data.even_more.foz” ma => wszystkie klawisze są spłaszczyć z korzenia dokumentu

ten sposób będziesz musiał szukać

foo: val5 
metadata.more_data.even_more.foz: 12* 
metadata.more_data.contains_more.bar: val* 
metadata.somekey1: val1 

Używanie query_string np

"query_string": { 
    "default_field": "metadata.more_data.even_more.foz", 
    "query": "12*" 
} 

Lub jeśli chcesz szukać w wielu dziedzinach

"query_string": { 
    "fields" : ["metadata.more_data.contains_more.bar", "metadata.somekey1"], 
    "query": "val*" 
} 
+0

Więc tablice będą również spłaszczone? (np. nie trzeba używać metadata.more_data.contains_more.0.key) – daddz

+1

tak, Elasticsearch wykryje tablicę, a "contains_more.foo" i "contains_more.bar" staną się polami o wielu wartościach –

Powiązane problemy