Ronny vdb
Ronny vdb

Reputation: 2474

Elasticsearch query for multiple terms

I am trying to create a search query that allows to search by name and type. I have indexed the values, and my record in Elasticsearch look like this:

{
  _index: "assets",
  _type: "asset",
  _id: "eAOEN28BcFmQazI-nngR",
  _score: 1,
  _source: {
    name: "test.png",
    mediaType: "IMAGE",
    meta: {
      content-type: "image/png",
      width: 3348,
      height: 1890,
    },
    createdAt: "2019-12-24T10:47:15.727Z",
    updatedAt: "2019-12-24T10:47:15.727Z",
  }
}

so how would I create for example, a query that finds all assets that have the name "test' and are images?

I tried multi_mach query but that did not return the correct results:

{
  "query": {
    "multi_match" : {
      "query":      "*test* IMAGE",
      "type":       "cross_fields",
      "fields":     [ "name", "mediaType" ],
      "operator":   "and" 
    }
  }
}

The query above returns 0 results, and if I change the operator to "or" it returns all this assets of type IMAGE.

Any suggestions would be greatly appreciated. TIA!

EDIT: Added Mapping Below is the mapping:

{
    "assets": {
        "aliases": {},
        "mappings": {
            "properties": {
                "__v": {
                    "type": "long"
                },
                "createdAt": {
                    "type": "date"
                },
                "deleted": {
                    "type": "date"
                },
                "mediaType": {
                    "type": "text",
                    "fields": {
                        "keyword": {
                            "type": "keyword",
                            "ignore_above": 256
                        }
                    }
                },
                "meta": {
                    "properties": {
                        "content-type": {
                            "type": "text",
                            "fields": {
                                "keyword": {
                                    "type": "keyword",
                                    "ignore_above": 256
                                }
                            }
                        },
                        "width": {
                            "type": "long"
                        },
                        "height": {
                          "type": "long"
                      }
                    }
                },
                "name": {
                    "type": "text",
                    "fields": {
                        "keyword": {
                            "type": "keyword",
                            "ignore_above": 256
                        }
                    }
                },
                "originalName": {
                    "type": "text",
                    "fields": {
                        "keyword": {
                            "type": "keyword",
                            "ignore_above": 256
                        }
                    }
                },
                "updatedAt": {
                    "type": "date"
                }
            }
        },
        "settings": {
            "index": {
                "creation_date": "1575884312237",
                "number_of_shards": "1",
                "number_of_replicas": "1",
                "uuid": "nSiAoIIwQJqXQRTyqw9CSA",
                "version": {
                    "created": "7030099"
                },
                "provided_name": "assets"
            }
        }
    }
}

Upvotes: 3

Views: 2758

Answers (2)

Amit
Amit

Reputation: 32376

You are unnecessary using the wildcard expression for this simple query.

First, change your analyzer on name field.

You need to create a custom analyzer which replaces . with space as default standard analyzer doesn't do that, so that you when searching for test you get test.png as there will be both test and png in the inverted index. The main benefit of doing this is to avoid the regex queries which are very costly.

Updated mapping with custom analyzer which would do the work for you. Just update your mapping and re-index again all the doc.

{
    "aliases": {},
    "mappings": {
        "properties": {
            "__v": {
                "type": "long"
            },
            "createdAt": {
                "type": "date"
            },
            "deleted": {
                "type": "date"
            },
            "mediaType": {
                "type": "text",
                "fields": {
                    "keyword": {
                        "type": "keyword",
                        "ignore_above": 256
                    }
                }
            },
            "meta": {
                "properties": {
                    "content-type": {
                        "type": "text",
                        "fields": {
                            "keyword": {
                                "type": "keyword",
                                "ignore_above": 256
                            }
                        }
                    },
                    "width": {
                        "type": "long"
                    },
                    "height": {
                        "type": "long"
                    }
                }
            },
            "name": {
                "type": "text",
                "analyzer" : "my_analyzer"
            },
            "originalName": {
                "type": "text",
                "fields": {
                    "keyword": {
                        "type": "keyword",
                        "ignore_above": 256
                    }
                }
            },
            "updatedAt": {
                "type": "date"
            }
        }
    },
    "settings": {
        "analysis": {
            "analyzer": {
                "my_analyzer": {
                    "tokenizer": "standard",
                    "char_filter": [
                        "replace_dots"
                    ]
                }
            },
            "char_filter": {
                "replace_dots": {
                    "type": "mapping",
                    "mappings": [
                        ". => \\u0020"
                    ]
                }
            }
        },
        "index": {
            "number_of_shards": "1",
            "number_of_replicas": "1"
        }
    }
}

Second, you should change your query to bool query as below:

{
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "name": "test"
                    }
                },
                {
                    "match": {
                        "mediaType.keyword": "IMAGE"
                    }
                }
            ]
        }
    }
}

Which is using must with 2 match queries means, that it would return docs only when there is a match in all the clauses of must query.

I already tested my solution by creating the index, inserting a few sample docs and query them, let me know if you need any help.

Upvotes: 4

vinod_vh
vinod_vh

Reputation: 1061

Did you tried with best_fields ?

{
  "query": {
    "multi_match" : {
      "query":      "Will Smith",
      "type":       "best_fields",
      "fields":     [ "name", "mediaType" ],
      "operator":   "and" 
    }
  }
}

Upvotes: 0

Related Questions