2015-05-20 19 views
10

Tôi có một số JSON trông giống như sau: Chúng ta hãy gọi đó là lĩnh vực metadataIndexing/Tìm kiếm "phức tạp" JSON trong elasticsearch

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

Đây chỉ là một ví dụ đơn giản. Người thực có thể phát triển phức tạp hơn. Phím có thể xuất hiện nhiều lần. Giá trị là tốt và có thể là int hoặc str.

Bây giờ vấn đề đầu tiên là tôi không hoàn toàn chắc chắn làm thế nào tôi phải chỉ số chính xác điều này trong elasticsearch vì vậy tôi có thể tìm thấy một cái gì đó với các yêu cầu cụ thể.

Tôi đang sử dụng Django/Haystack nơi chỉ số trông như thế này:

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 

Và mẫu:

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

Các siêu dữ liệu sau đó sẽ được lấp đầy với các mẫu ở trên và kết quả sẽ trông như thế này:

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

Mà sẽ đi vào cột 'văn bản' trong elasticsearch.

Vì vậy, mục tiêu bây giờ là có thể tìm kiếm cho những thứ như:

  • foo: val5
  • Foz: 12 *
  • thanh: val *
  • somekey1: VAL1
  • và như vậy trên

Vấn đề thứ hai: Khi tôi tìm kiếm ví dụ: cho foo: val5 nó phù hợp với tất cả các đối tượng chỉ có khóa "foo" và tất cả các đối tượng có val5 ở một nơi khác trong cấu trúc của nó.

Đây là cách tôi tìm kiếm trong Django:

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

Đôi khi kết quả là "okayish" đôi khi nó chỉ là hoàn toàn vô dụng.

Tôi có thể cần một con trỏ đúng hướng và tìm hiểu những sai lầm mà tôi đã thực hiện ở đây. Cảm ơn bạn!

Chỉnh sửa: Tôi đã thêm giải pháp cuối cùng của tôi làm câu trả lời bên dưới!

+1

Lời nói đầu: Tôi không phải là người dùng django, chỉ là ES. Đoán của tôi: trường 'nội dung' được điền với tất cả dữ liệu, khiến cho không thể thực hiện các kết quả phù hợp với từng trường cụ thể.Nếu bạn muốn điều đó, bạn cần phải thể hiện rằng trong bộ lọc/truy vấn của bạn (nhưng tôi đoán là: không sử dụng 'auto_query'). – mark

+0

trường siêu dữ liệu của bạn luôn có cấu trúc giống nhau không? –

+0

@juliendangers Đôi khi nó có nhiều trường hoặc chứa nhiều phần tử trong mảng Và đôi khi không có mảng và nó có thể khá phẳng. Tuy nhiên, các khóa được biết trước và có thể có, ví dụ: lên đến 30 + những cái khác nhau – daddz

Trả lời

0

Phải mất một thời gian để tìm ra các giải pháp phù hợp mà công trình đối với tôi

Đó là một sự pha trộn của cả hai câu trả lời cung cấp bởi @juliendangers@Val và một số tùy biến hơn.

  1. tôi thay Haystack với cụ thể hơn django-simple-elasticsearch
  2. Added tùy chỉnh phương pháp get_type_mapping với mô hình

    @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. Added tùy chỉnh get_document phương pháp để mô hình

    @classmethod 
    def get_document(cls, obj): 
        return { 
        'somekey': obj.somekey, 
        'more_data': obj.more_data, 
        /* and so on */ 
        } 
    
  4. Thêm tùy chỉnh Biểu mẫu tìm kiếm

    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] 
    

Vì vậy, đây là những gì làm việc cho tôi và bao gồm usecases tôi. Có lẽ nó có thể giúp ích cho ai đó.

3

Có một điều mà chắc chắn là lần đầu tiên bạn cần đưa ra một bản đồ tùy chỉnh dựa trên dữ liệu cụ thể của bạn và theo nhu cầu truy vấn của bạn, lời khuyên của tôi là contains_more nên có nested type để bạn có thể phát hành các truy vấn chính xác hơn trên các lĩnh vực của bạn.

Tôi không biết tên chính xác của các trường của bạn, nhưng dựa trên những gì bạn đã hiển thị, một bản đồ có thể có thể giống như thế này.

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

Sau đó, như đã đề cập trong đánh dấu của anh ấy, auto_query sẽ không cắt, chủ yếu là do nhiều cấp độ lồng nhau.Theo như tôi biết, Django/Haystack không hỗ trợ các truy vấn lồng nhau ra khỏi hộp, nhưng bạn có thể mở rộng Haystack để hỗ trợ nó. Đây là một bài đăng trên blog giải thích cách giải quyết vấn đề này: http://www.stamkracht.com/extending-haystacks-elasticsearch-backend. Không chắc chắn nếu điều này giúp, nhưng bạn nên thử và cho chúng tôi biết nếu bạn cần thêm trợ giúp.

+0

Điều này có nghĩa là tôi phải xác định để ánh xạ cho tất cả các 'khóa' có thể có cũng như cấu trúc của chúng? Như tôi đã viết trong một bình luận khác có thể có hơn 30 người khác nhau. – daddz

+0

Vâng, bạn càng hướng dẫn lập bản đồ của bạn, các truy vấn của bạn càng chính xác và mạnh mẽ hơn. 30 lĩnh vực không phải là một kẻ giết người, tôi muốn nói. Tôi có tài liệu với hàng trăm trường và tất cả đều được lập bản đồ chính xác và chính xác cho những gì tôi cần chúng làm. Tốt nhất là để cho nó một thử và xem nó như thế nào cho bạn trong trường hợp cụ thể của bạn. – Val

+0

Cảm ơn bạn. Tôi sẽ thử và báo cáo lại! – daddz

3

Indexing:

Trước hết bạn nên sử dụng động templates, nếu bạn muốn xác định bản đồ cụ thể tương đối để tên chủ chốt, hoặc nếu tài liệu của bạn không có cấu trúc tương tự.

Nhưng 30 Điều quan trọng là không phải là cao, và bạn sẽ thích định lập bản đồ của riêng bạn hơn là để Elasticsearch đoán điều đó cho bạn (trong trường hợp dữ liệu không chính xác đã được thêm vào đầu tiên, lập bản đồ sẽ được xác định theo những dữ liệu này)

Searching:

Bạn không thể tìm kiếm

foz: val5 

vì "Foz" chìa khóa không tồn tại.

Nhưng chìa khóa "metadata.more_data.even_more.foz" không => tất cả các phím của bạn được flatten từ thư mục gốc của tài liệu của bạn

cách này bạn sẽ phải tìm kiếm

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

Sử dụng QUERY_STRING ví dụ

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

Hoặc nếu bạn muốn tìm kiếm trong nhiều lĩnh vực

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

Vì vậy, mảng cũng sẽ được làm phẳng? (ví dụ: không phải sử dụng siêu dữ liệu.more_data.contains_more.0.key) – daddz

+1

có, Elasticsearch sẽ phát hiện mảng và "contains_more.foo" và "contains_more.bar" sẽ trở thành các trường có nhiều giá trị –

Các vấn đề liên quan