2015-05-20 16 views
10

ho qualche JSON che è simile al seguente: Chiamiamolo campo metadatiIndicizzazione/Cercasi "complesso" JSON in elasticsearch

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

Questo è solo un semplice esempio. Quello reale può diventare ancora più complesso. Le chiavi possono apparire più volte. Valori pure e può essere int o str.

Ora il primo problema è che non sono abbastanza sicuro di come indicarlo correttamente in elasticsearch in modo da poter trovare qualcosa con richieste specifiche.

Sto usando Django/Haystack dove l'indice si presenta così:

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 

E il modello:

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

I metadati sarà poi riempito con il campione di cui sopra e il risultato sarà In questo modo:

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

Che entrerà nella colonna "testo" in elasticsearch.

Quindi l'obiettivo è ora di essere in grado di cercare le cose come:

  • pippo: val5
  • foz: 12 *
  • bar: val *
  • somekey1: val1
  • e così via

Il secondo problema: Quando cerco ad es. per pippo: val5 corrisponde a tutti gli oggetti che hanno solo la chiave "pippo" e tutti gli oggetti che hanno il val5 da qualche altra parte nella sua struttura.

Questo è il modo che cerco in Django:

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

A volte i risultati sono "esplose" a volte è solo completamente inutile.

Potrei avere bisogno di un puntatore nella giusta direzione e conoscere gli errori che ho fatto qui. Grazie!

Modifica: Ho aggiunto la mia soluzione finale come risposta qui sotto!

+1

Prefazione: non sono un utente django, solo ES. La mia ipotesi: il campo 'content' è popolato con tutti i dati, rendendo impossibile fare corrispondenze specifiche per campo.Se lo desideri, devi esprimerlo nel filtro/query (ma la mia ipotesi è: non usare 'auto_query'). – mark

+0

il tuo campo di metadati ha sempre la stessa struttura? –

+0

@juliendangers A volte ha più campi o contiene più elementi nell'array E a volte non ci sono array e può essere piuttosto piatto. I tasti tuttavia sono noti prima e possono esserci per es. fino a 30+ diversi – daddz

risposta

0

Ci sono voluti un po 'per capire la giusta soluzione che funziona per me

E' stato un mix di entrambe le risposte fornite dai @juliendangers e @val e un po 'di personalizzazione.

  1. ho sostituito Pagliaio con la più specifica django-simple-elasticsearch
  2. Aggiunto personalizzato metodo get_type_mapping al modello

    @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. Aggiunto personalizzato get_document metodo per il modello

    @classmethod 
    def get_document(cls, obj): 
        return { 
        'somekey': obj.somekey, 
        'more_data': obj.more_data, 
        /* and so on */ 
        } 
    
  4. Aggiungere personalizzato 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] 
    

Quindi questo è ciò che ha funzionato per me e per i miei copre casi d'uso. Forse può essere di qualche aiuto per qualcuno.

3

L'unica cosa che è certo è che è necessario prima di mettere una mappatura personalizzata, sulla base di dati specifici e secondo le vostre esigenze di query, il mio consiglio è che contains_more dovrebbe essere di nested type in modo da poter eseguire query più precise sui tuoi campi

Non conosco i nomi esatti dei campi, ma in base a ciò che hai mostrato, una possibile mappatura potrebbe essere qualcosa del genere.

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

Poi, come già detto da Mark nel suo commento, auto_query non è tagliato, soprattutto a causa dei molteplici livelli di annidamento.Per quanto ne so, Django/Haystack non supporta le query annidate, ma puoi estendere Haystack per supportarlo. Ecco un post sul blog che spiega come affrontare questo: http://www.stamkracht.com/extending-haystacks-elasticsearch-backend. Non sono sicuro che questo aiuti, ma dovresti fare un tentativo e farci sapere se hai bisogno di più aiuto.

+0

Ciò significa che devo definire la mappatura per tutte le possibili "chiavi" e la loro struttura? Come ho scritto in un altro commento potrebbero esserci oltre 30 diversi. – daddz

+0

Bene, più istruisci la tua mappatura, più precise e potenti possono essere le tue domande. 30 campi non sono un killer, direi. Ho documenti con centinaia di campi e sono tutti mappati in modo corretto e accurato per quello che ho bisogno che facciano. La cosa migliore è fare un tentativo e vedere come va per te nel tuo caso particolare. – Val

+0

Grazie. Ci proverò e segnalerò! – daddz

3

indicizzazione:

Prima di tutto si dovrebbe usare dinamica templates, se si desidera definire mappatura specifica relativamente al nome della chiave, o se i documenti non hanno la stessa struttura.

Ma 30 chiave non è così in alto, e si dovrebbe preferire definire la propria mappatura che lasciare elasticsearch indovinare per voi (nel caso in cui i dati non corretti sono stati aggiunti prima, la mappatura sarebbe definito in base a questi dati)

ricerca:

non è possibile cercare

foz: val5 

dal tasto "Foz" non esiste.

Ma tasto "metadata.more_data.even_more.foz" fa => tutte le vostre chiavi sono appiattire dalla radice del documento

questo modo dovrete cercare

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

Utilizzando QUERY_STRING per esempio

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

Oppure, se si desidera effettuare la ricerca in molteplici campi

0.123.
+0

Quindi anche gli array verranno appiattiti? (ad esempio, non dovendo utilizzare metadata.more_data.contains_more.0.key) – daddz

+1

sì, Elasticsearch rileva l'array e "contains_more.foo" e "contains_more.bar" diventeranno campi a più valori –

Problemi correlati