adig14
adig14

Reputation: 65

Matching multiple phrases to one field in Elastic Search

I have built an application in C# which searches my Elasticsearch documents and this all works fine using this code....

List<AuditLog> resultsList = Client.SearchAsync<AuditLog>(s => s
    .From(0)
    .Take(noRows)
    .Query(q => q
        .Bool(b => b
            .Must(mu => mu.MatchPhrase(mp => mp.Field("audit_Event").Query(auditEvents)),
                mu => mu.Match(ma => ma.Field(field).Query(value))
             )
          )
     )).Result.Documents.ToList();

This all works fine but there is a new requirement to search the audit_Event field against multiple phrases, such as "User Login", "Add Employee" etc

I have found reference to multi_match but this appears to be more related to searching across multiple fields rather than multiple values in one field.

Following discussions below, TermQuery would seem to do the job but doesn't return any values. Any ideas?

QueryContainer queryAnd = new TermQuery() { Field = "audit_Application", Value = "MyApplication" };
QueryContainer queryOr = new TermQuery() { Field = "audit_Event", Value = "Employee Inserted" };
queryOr |= new TermQuery() { Field = "audit_Event", Value = "Employee Updated" }; 
QueryContainer queryMain = queryAnd & queryOr;

resultsList = Client.SearchAsync<AuditLog>(s => s
    .From(0)
    .Take(noRows)
    .Query(q => q
        .Bool(b => b
            .Must(queryMain)
        )
    )).Result.Documents.ToList();

I've also checked the two queries using Kibana, the first one returns data but the second one doesn't, and am now wondering if it a problem with how the data is indexed....

GET auditlog/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match_phrase": {
            "audit_Event": "Employee Updated"
          }
        },
        {
          "match_phrase": {
            "audit_Event": "Employee Inserted"
          }
        }
      ]
    }
  }
}

GET auditlog/_search
{
  "query" : {
    "terms": {
        "audit_Event": ["Employee Updated","Employee Inserted"]
    }
  }
}

Upvotes: 2

Views: 2326

Answers (3)

Sai Gummaluri
Sai Gummaluri

Reputation: 1394

Based on your statement that the OR query works as expected, below is a way you can build the query using NEST. You may make any specific modifications as needed.

This snippet of code is responsible for triggering the search call

var searchDescriptor = new SearchDescriptor<AuditLog>()
    .Query(q => Blah(auditEventsList))
    .From(0)
    .Take(noRows);
    // other methods you want to chain here

var response = Client.searchAsync<AuditLog>(searchDescriptor);

The search query is generated here. Notice the use of SHOULD and the call to another method that is responsible for dynamically generating the match_phrase queries.

private static QueryContainer Blah(List<string> auditEventsList)
{
    return new QueryContainerDescriptor<AuditLog>().Bool(
        b => b.Should(
            InnerBlah(auditEventsList)
        )
    );
}

This method generates an array of match_phrase queries that are passed to should in the above snippet. These are generated by iterating over all the audit events that are received.

private static QueryContainer[] InnerBlah(List<string> auditEventsList)
{
    QueryContainer orQuery = null;
    List<QueryContainer> queryContainerList = new List<QueryContainer>();
    foreach(var item in auditEventsList)
    {
        orQuery = new MatchPhraseQuery { Field = "audit_Event", Query = item };
        queryContainerList.Add(orQuery);
    }
    return queryContainerList.ToArray();
}

Upvotes: 1

Muzaffer Galata
Muzaffer Galata

Reputation: 642

Please try this:

QueryContainer queryOr = new MatchPhraseQuery() { Field = "audit_Event", Query = "Employee Inserted" };
        queryOr |= new MatchPhraseQuery() { Field = "audit_Event", Query = "Employee Updated" };
        ISearchResponse<AuditLog> resultAuditLog = Client.Search<AuditLog>(s => s
                    .RequestConfiguration(r => r.DisableDirectStreaming())
                    .From(0)
                    .Size(100)
                    .Query(q2 => q2
                        .Bool(b => b
                        .Should(queryOr))
                    )
        );

Upvotes: 0

Bhavya
Bhavya

Reputation: 16172

If you want to match multiple phrases against a single field, then you can use the following combinations of bool query with match_phrase

{
  "query": {
    "bool": {
      "must": [
        {
          "match_phrase": {
            "audit_Event": "User Login"
          }
        },
        {
          "match_phrase": {
            "audit_Event": "Add Employee"
          }
        }
      ]
    }
  }
}

Upvotes: 0

Related Questions