Dennis Schepers
Dennis Schepers

Reputation: 573

ElasticSearch match combination in array

I'm implementing ElasticSearch into my Laravel application using the php package from ElasticSearch.

My application is a small jobboard and currently my job document is looking like this:

{  
   "_index":"jobs",
   "_type":"job",
   "_id":"19",
   "_score":1,
   "_source":{  
      "0":"",
      "name":"Programmer",
      "description":"This is my first job! :)",
      "text":"Programming is awesome",
      "networks":[  
         {  
            "id":1,
            "status":"PRODUCTION",
            "start":"2015-02-26",
            "end":"2015-02-26"
         },
         {  
            "id":2,
            "status":"PAUSE",
            "start":"2015-02-26",
            "end":"2015-02-26"
         }
      ]
   }
}

As you can see a job can be attached to multiple networks. In my search query I would like to include WHERE network.id == 1 AND network.status == PRODUCTION.

My current query looks like this, however this returns documents where it has a network of id 1, if it has any network of status PRODUCTION. Is there anyway i can enforce both to be true within one network?

$query = [
            'index' => $this->index,
            'type' => $this->type,
            'body' => [
                'query' => [
                    'bool' => [
                        'must' => [
                            ['networks.id' => 1]],
                            ['networks.status' => 'PRODUCTION']]
                        ],
                        'should' => [
                            ['match' => ['name' => $query]],
                            ['match' => ['text' => $query]],
                            ['match' => ['description' => $query]],
                        ],
                    ],
                ],
            ],
        ];

Upvotes: 0

Views: 1697

Answers (1)

Dan Tuffery
Dan Tuffery

Reputation: 5924

You need to specify that the objects in the networks array should be stored as individual objects in the index, this will allow you to perform a search on individual network objects. You can do so using the nested type in Elasticsearch.

Also, if you doing exact matches it is better to use a filter rather than a query as the filters are cached and always give you better performance than a query.

Create your index with a new mapping. Use the nested type for the networks array.

POST /test
{
    "mappings": {
        "job": {
            "properties": {
                "networks": {
                    "type": "nested",
                    "properties": {
                        "status": {
                            "type": "string",
                            "fields": {
                                "raw": {
                                    "type": "string",
                                    "index": "not_analyzed"
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

Add a document:

POST /test/job/1
{
    "0": "",
    "name": "Programmer",
    "description": "This is my first job! :)",
    "text": "Programming is awesome",
    "networks": [
        {
            "id": 1,
            "status": "PRODUCTION",
            "start": "2015-02-26",
            "end": "2015-02-26"
        },
        {
            "id": 2,
            "status": "PAUSE",
            "start": "2015-02-26",
            "end": "2015-02-26"
        }
    ]
}

As you have a nested type you will need to use a nested filter.

POST /test/job/_search
{
    "query": {
        "filtered": {
            "query": {
                "match_all": {}
            },
            "filter": {
                "nested": {
                    "path": "networks",
                    "filter": {
                        "bool": {
                            "must": [
                                {
                                    "term": {
                                        "networks.id": "1"
                                    }
                                },
                                {
                                    "term": {
                                        "networks.status.raw": "PRODUCTION"
                                    }
                                }
                            ]
                        }
                    }
                }
            }
        }
    }
}

Upvotes: 3

Related Questions