Search code examples
elasticsearchfull-text-searchsearch-engine

ElasticSearch: advanced search inside the array of objects


Let's assume that I have the following documents stored in Elasticsearch:

{
  id: '349dec5f-4ddf-4faa-beb0-79a0ea08bad6',
  name: 'Iphone 13',
  criteria: [
    {
      name: 'color',
      options: ['Midnight', 'Blue', 'Green'],
    },
  ]
},
{
  id: '1a63de5a-4335-4e0d-83ab-c820d57709bd',
  name: 'IPhone 14',
  criteria: [
    {
      name: 'color',
      options: ['Midnight', 'Purple', 'Red'],
    },
    {
      name: 'size',
      options: ['128GB', '256GB'],
    },
  ]
},
{
  id: 'eae5672f-2153-4f7d-be68-122e6d9fe5e1',
  name: 'Iphone 14 Pro',
  criteria: [
    {
      name: 'color',
      options: ['Black', 'Blue', 'Red'],
    },
    {
      name: 'size',
      options: ['64GB', '128GB', '256GB'],
    },
  ]
}

And I have the following type mappings for these documents:

"properties": {
  "id": { "type": "keyword", "ignore_above": 36 },
  "name": { "type": "text" },
  "category": { "type": "keyword", "ignore_above": 36 },
  "criteria": {
    "type": "nested",
    "properties": {
      "name": { "type": "keyword", "ignore_above": 500 },
      "options": { "type": "keyword", "ignore_above": 500 }
    }
  }
}

Note that criteria is a nested array of objects with two fields criteria.name and criteria.options, so it can have not only color and size, but other values as well.

And I want to filter these documents by particular color and size, for example, when I get the following filter object in an HTTP request:

{
  search: null,
  filter: {
    criteria: [
      {
        name: 'color',
        options: ['Blue', 'Red'],
        operator: 'OR'
      },
      {
        name: 'size',
        options: ['64GB', '128GB'],
        operator: 'OR'
      }
    ]
  }
}

I need to get documents that have 'Blue' or 'Red' color AND '64GB' OR '128GB' size, so, it looks something like this:

if criteria.name == 'color' THEN
  criteria.options should include one of ['Blue', 'Red']
if criteria.name == 'size' THEN
  criteria.options should include one of ['64GB', '128GB']

So, for example, for the above filter object in the response, I should return two documents, the first with name: 'IPhone 14' and the second with name: 'Iphone 14 Pro' because the first one (name: 'IPhone 14') has a "Red" color and a size of "128GB", and the second (name: 'Iphone 14 Pro') has both "Blue" and "Red" colors and both "64GB" and "128 GB" sizes.

So, my question is how to achieve this?


Solution

  • Try use Nested Queries.

    {
      "query": {
        "bool": {
          "must": [
            {
              "nested": {
                "path": "criteria",
                "query": {
                  "bool": {
                    "must": [
                      {
                        "match": {
                          "criteria.name": "color"
                        }
                      }
                    ], 
                    "filter": [
                      {
                        "terms": {
                          "criteria.options": [
                            "Blue",
                            "Red"
                          ]
                        }
                      }
                    ]
                  }
                }
              }
            },
            {
              "nested": {
                "path": "criteria",
                "query": {
                  "bool": {
                    "must": [
                      {
                        "match": {
                          "criteria.name": "size"
                        }
                      }
                    ], 
                    "filter": [
                      {
                        "terms": {
                          "criteria.options": [
                            "64GB",
                            "128GB"
                          ]
                        }
                      }
                    ]
                  }
                }
              }
            }
          ]
        }
      }
    }