40

I want to exactly match the string ":Feed:" in a message field and go back a day pull all such records. The json I have seems to also match the plain word " feed ". I am not sure where I am going wrong. Do I need to add "constant_score" to this query JSON? The JSON I have currently is as shown below:

{
    "query": {
        "bool": {
            "must": {
                "query_string": {
                    "fields": ["message"],
                    "query": "\\:Feed\\:"
                }
            },
            "must": {
                "range": {
                    "timestamp": {
                        "gte": "now-1d",
                        "lte": "now"
                    }
                }
            }
        }
    }
}
Dhanesh
  • 1,009
  • 1
  • 11
  • 16
  • try setting message to {index : "not_analyzed"} in the mapping – keety Jun 18 '16 at 14:22
  • @keety thanks for the input. I do not have permissions to change the indexing unfortunately. I believe I should be searching for something like `*\\:Feed\\:*` if the field was "not_analyzed". So looks like I am out of luck. – Dhanesh Jun 18 '16 at 19:49

4 Answers4

25

As stated here: Finding Exact Values, since the field has been analyzed when indexed - you have no way of exact-matching its tokens (":"). Whenever the tokens should be searchable the mapping should be "not_analyzed" and the data needs to be re-indexed.

If you want to be able to easily match only ":feed:" inside the message field you might want to costumize an analyzer which doesn't tokenize ":" so you will be able to query the field with a simple "match" query instead of wild characters.

israelst
  • 1,042
  • 9
  • 7
  • This is simply not true. Have a look below at several ways to do this..some, without even having to reindex (my fav is Ashsh Sondagar's solution). not sure how/why this is the accepted answer. – Midiman Mar 13 '23 at 21:05
  • See my comment on Ashsh solution which explains why his solution is based on assumption which is not the case for the question, and so are the other answers. They assume the "message" field has a "keyword" mapping (which is indeed the current ES default) - but its not the case for this question! In the question, the "message" field does not have this mapping which will fail the other "solution"! In the question, the mapping is "analyzed" only, hence the only solution is to re-map with "keyword" and then re-index. Next time, please refrain from quick judgments ("this is simply not true"). – israelst Mar 14 '23 at 06:00
15

Not able to do this with query_string but managed to do so by creating a custom normalizer and then using a "match" or "term" query.

The following steps worked for me.

  1. create a custom normalizer (available >V5.2)
"settings": {
  "analysis": {
    "normalizer": {
      "my_normalizer": {
        "type": "custom",
        "filter": ["lowercase"]
      }
    }
  }
}
  1. Create a mapping with type "keyword"
{
  "mappings": {
    "default": {
      "properties": {
        "title": {
          "type": "text",
          "fields": {
            "normalize": {
              "type": "keyword",
              "normalizer": "my_normalizer"
            },
            "keyword" : {
              "type": "keyword"
            }
          }
        }
      }
    }
  }     
  1. use match or term query
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "title.normalize": "string to match"
          }
        }
      ]
    }
  }
}
MichaelMao
  • 2,596
  • 2
  • 23
  • 54
Arvind Krmar
  • 2,154
  • 2
  • 13
  • 18
  • Thank you! I slightly modified your JSON to handle accents in the normalizer : `"filter": ["lowercase", "asciifolding"]`. Note that you have to reindex your documents to make all this work. – Sylvain Tch Oct 16 '21 at 17:12
9

Use match phrase

GET /_search
{
    "query": {
        "match_phrase": {
            "message": "7000-8900"
        }
    }
}

In java use matchPhraseQuery of QueryBuilder

QueryBuilders.matchPhraseQuery(fieldName, searchText);
Harshad Panmand
  • 410
  • 5
  • 19
7
  • Simple & Sweet Soln:
  • use term query..
GET /_search
{
    "query": {
        "term": {
            "message.keyword": "7000-8900"
        }
    }
}
  • use term query instead of match_phrase,
  • match_phrase this find/match with ES-document stored sentence, It will not exactly match. It matches with those sentence words!
Ashish Sondagar
  • 917
  • 1
  • 9
  • 16
  • This response deserves some love. This should be the accepted answer. There's lots of ways to solve this (except ironically the currently accepted answer!), but this use of .keyword is elegantly simple and effective. – Midiman Mar 13 '23 at 21:05
  • 2
    This "solution" assumes the "message" field is mapped with the "keyword" field (="not analyzed") which is the current default of ES. But this is not the case in the question! The question is about an "analyzed" field, hence the solution is only to re-map the field as "keyword" and reindex. And then, BTW, it will work also with "query_string" and its not related to the "term" query specifically... @Midiman, please remove your note about the current accepted answer as it is the correct answer for the question. – israelst Mar 14 '23 at 05:53