0

I have the following issue in case of a full-text search in Elasticsearch. I would like to search for all indexed attributes. However, one of my Project attributes is a very complex array of hashes/objects:

[
  {
    "title": "Group 1 title",
    "name": "Group 1 name",
    "id": "group_1_id",
    "items": [
      {
        "pos": "1",
        "title": "Position 1 title"
      },
      {
        "pos": "1.1",
        "title": "Position 1.1 title",
        "description": "<p>description</p>",
        "extra_description": {
          "rotation": "2 years",
          "amount": "1.947m²"
        },
        "inputs": {
          "unit_price": true,
          "total_net": true
        },
        "additional_inputs": [
          {
            "name": "additonal_input_name",
            "label": "Additional input label:",
            "placeholder": "Additional input placeholder",
            "description": "Additional input description",
            "type": "text"
          }
        ]
      }
    ]
  }
]

My mappings look like this:

{:title=>{:type=>"text", :analyzer=>"english"},
:description=>{:type=>"text", :analyzer=>"english"},
:location=>{:type=>"keyword"},
:company=>{:type=>"keyword"},
:created_at=>{:type=>"date"},
:due_date=>{:type=>"date"},
:specification=>
 {:type=>:nested,
  :properties=>
   {:id=>{:type=>"keyword"},
    :title=>{:type=>"text"},
    :items=>
     {:type=>:nested,
      :properties=>
       {:pos=>{:type=>"keyword"},
        :title=>{:type=>"text"},
        :description=>{:type=>"text", :analyzer=>"english"},
        :extra_description=>{:type=>:nested, :properties=>{:rotation=>{:type=>"keyword"}, :amount=>{:type=>"keyword"}}},
        :additional_inputs=>
         {:type=>:nested,
          :properties=>
           {:label=>{:type=>"keyword"},
            :placeholder=>{:type=>"text"},
            :description=>{:type=>"text"},
            :type=>{:type=>"keyword"},
            :name=>{:type=>"keyword"}
            }

          }

        }
      }
    }
  }
}

The question is, how to properly seek through it? For no nested attributes, it works as a charm, but for instance, I would like to seek by title in the specification, no result is returned. I tried both:

query:
   { nested:
      { 
        multi_match: {
          query: keyword,
          fields: ['title', 'description', 'company', 'location', 'specification']
        }
      }
  }

Or

  {
      nested: {
        path: 'specification',
        query: {
          multi_match: {
            query: keyword
          }
        }
      }
    }

Without any result.

Edit: It's with elasticsearch-ruby for Ruby.

I am trying to query by: MODEL_NAME.all.search(query: with_specification("Group 1 title")) where with_specification is:

def with_specification(keyword)
    {
        bool: {
          should: [
            {
              nested: {
                path: 'specification',
                query: {
                  bool: {
                    should: [
                      {
                        match: {
                          'specification.title': keyword,
                        }
                      },
                      {
                        multi_match: {
                          query: keyword,
                          fields: [
                            'specification.title',
                            'specification.id'
                          ]
                        }
                      },
                      {
                        nested: {
                          path: 'specification.items',
                          query: {
                            match: {
                              'specification.items.title': keyword,
                            }
                          }
                        }
                      }
                    ]
                  }
                }
              }
            }
          ]
        }
      }
  end
Pigius
  • 31
  • 1
  • 7

1 Answers1

1
  1. Querying on multi-level nested documents must follow a certain schema.
  2. You cannot multi-match on nested & non-nested fields at the same time and/or query on nested fields under different paths.

You can wrap your queries in a bool-should but keep the 2 rules above in mind:

GET your_index/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "nested": {
            "path": "specification",
            "query": {
              "bool": {
                "should": [
                  {
                    "match": {
                      "specification.title": "TEXT"     <-- standalone match
                    }
                  },
                  {
                    "multi_match": {                    <-- multi-match but 1st level path
                      "query": "TEXT",
                      "fields": [
                        "specification.title",
                        "specification.id"
                      ]
                    }
                  },
                  {
                    "nested": {
                      "path": "specification.items",   <-- 2nd level path
                      "query": {
                        "match": {
                          "specification.items.title": "TEXT"
                        }
                      }
                    }
                  }
                ]
              }
            }
          }
        }
      ]
    }
  }
}
Joe - GMapsBook.com
  • 15,787
  • 4
  • 23
  • 68
  • Hm, I tried you solution, but I am getting errors: `Elasticsearch::Transport::Transport::Errors::BadRequest: [400] {"error":{"root_cause":[{"type":"parsing_exception","reason":"[matcher] query malformed, no start_object after query name","line":1,"col":21}],"type":"parsing_exception","reason":"[matcher] query malformed, no start_object after query name","line":1,"col":21},"status":400}`. Even when I switch from `should` to `must` – Pigius Mar 05 '20 at 15:23
  • What's the exact query you've sent to ES? – Joe - GMapsBook.com Mar 05 '20 at 16:15
  • The same you've provided query: your_nested_code(keyword). I changed your "text" query to the attribute. But either hardcoded or not, I cannot send such a query. Found similar issue, but without result:https://stackoverflow.com/questions/43547994/query-malformed-no-start-object-after-query-name – Pigius Mar 05 '20 at 16:33
  • Edited the original post, as I cannot paste in comment so long query. I tried also to send only the first match for specification title in the array, but without any luck. – Pigius Mar 05 '20 at 16:59
  • yea cool but I don’t need your function but the query that gets sent. Your ruby library surely has a method to dump the query before it gets sent. – Joe - GMapsBook.com Mar 05 '20 at 17:47
  • It cannot build a query: `:body=> {:query=> #}},` where `RSpec` is a testing library. – Pigius Mar 05 '20 at 17:50
  • I tried sth easier like `{query:{ "nested": { "path": "specification", "query": { "match": { "specification.title": keyword } } } } }` so it's working: `{:query=> {:nested=> {:path=>"specification", :query=>{:match=>{:"specification.title"=>"Chapter 1"}}}}}}` – Pigius Mar 05 '20 at 18:27
  • That's cool! Familiarize yourself with Kibana and prepare your queries there: https://prnt.sc/rcalkv Once they're working, work your way backwards to construct the ruby function. And don't forget to accept/upvote this answer if it helped you ;) – Joe - GMapsBook.com Mar 06 '20 at 01:05
  • 1
    The query working like a charm, there was some issue in my ruby functions. Thanks a lot for your help and patience! – Pigius Mar 06 '20 at 08:12