Elasticsearch and C# - query to find exact matches over strings

4.7k views Asked by At

I need a way to search documents using a plain exact match over two or multiple fields which are of type "string" and "integer".

I'd like to avoid standard query as I don't care about scoring or best match, just a yes/no outcome if both the fields match or not.

I know I can do it using filters but I got only example queries using JSON format. I'd like to do such search in a C# environment.

this is my mapping:

{
   "reviewer-test-index": {
      "aliases": {},
      "mappings": {
         "historyRecord": {
            "properties": {
               "groupName": {
                  "type": "string"
               },
               "groupNo": {
                  "type": "integer"
               },
               "instrType": {
                  "type": "integer"
               },
               "instrumentAddress": {
                  "type": "string"
               },
               "insturmentName": {
                  "type": "string"
               },
               "macAddr": {
                  "type": "string"
               },
               "uhhVersion": {
                  "type": "string"
               }
            }
         },         
      "settings": {
         "index": {
            "creation_date": "1434557536720",
            "number_of_shards": "1",
            "number_of_replicas": "0",
            "version": {
               "created": "1050299"
            },
            "uuid": "FfQADLGVQVOPV3913exKsw"
         }
      },
      "warmers": {}
   }
}

I also tried to make a JSON query but I get 0 hits:

GET _search
{
  "query" :{
  "filtered": {
    "query": {
      "match_all": { }
    },
   "filter": {
      "bool" : {
            "must" : [
                {"term" : { "macAddr" : "000A8D810F5A" } },
                {"term" : { "insturmentName" : "Amin's furnace" } },
                {"term" : { "instrumentAddress" : "8D810F5A"}},
                {"term" : { "uhhVersion" :  "v2.5"}},
                {"term" : { "groupName" :  "Amin's Group"}},
                {"term" : { "groupNo" :  2}},
                {"term" : { "instrType" :  60}}
            ]
         }
    }
  }
  }
}

Response:

{
   "took": 3,
   "timed_out": false,
   "_shards": {
      "total": 4,
      "successful": 3,
      "failed": 0
   },
   "hits": {
      "total": 0,
      "max_score": null,
      "hits": []
   }
}
1

There are 1 answers

11
Duc.Duong On BEST ANSWER

You can use filtered query with term filter:

{
  "filtered": {
    "query": {
      "match_all": { }
    },
    "filter": {
      "bool" : {
            "must" : [
                {"term" : { "macaddress" : "your_mac" } },
                {"term" : { "another_field" : 123 } }
            ]
         }
    }
  }
}

NEST version (replace dynamic with your response model):

var res = esclient.Search<dynamic>(q => q
            .Query(fq => fq
                .Filtered(fqq => fqq
                    .Query(qq => qq.MatchAll())
                    .Filter(ff => ff
                        .Bool(b => b
                            .Must(m1 => m1.Term("macaddress", "your_mac"))
                            .Must(m2 => m2.Term("another_field", 123))
                        )
                    )
                )
            )
        );

Updates base on provided mappings & response:

Here's something you need to notice first:

  • We use term when we need to index & search keyword or id. In your case it's "macAddr" but the problem is you index it using standard analyzer (so 000A8D810F5A will be transformed to 000a8d810f5a), and search it using term (which will keep passed data as it ==> 000A8D810F5A), then it will never match the indexed data: 000a8d810f5a. You can solve it by lower case the term before searching, or using match query. It maybe the same for case "instrumentAddress" & "uhhVersion" (I'm not sure because I dont have sample data).

  • For case "insturmentName", "groupName" you're indexing phrase using standard analyzer. So data like "Amin's furnace" will be indexed as two terms amin's, furnace, and none of them match passed term Amin's furnace. In this case we can use match query to search (or query_string if you need more options)

So a quick fix will look like this:

GET _search
{
"query" :{
  "filtered": {
     "query": {
        "match_all": { }
      },
     "filter": {
       "bool" : {
        "must" : [
            {"match" : { "macAddr" : "000A8D810F5A" } },
            {"match" : { "insturmentName" : "Amin's furnace" } },
            {"match" : { "instrumentAddress" : "8D810F5A"}},
            {"term" : { "uhhVersion" :  "v2.5"}},
            {"match" : { "groupName" :  "Amin's Group"}},
            {"term" : { "groupNo" :  2}},
            {"term" : { "instrType" :  60}}
          ]
        }
       }
      }
     }
   }

If you can lower case search term for "macAddr", "instrumentAddress" then you can replace its match query to term query for better performance.