Search code examples
c#asp.net-corenest

How to take Range GreaterThan Or LessThan on condition base in one Nest query?


I need to write one range query with different condition base

await _elasticClient.SearchAsync<T>(s => s
                            .Index(IndexName)
                            .From(From)
                            .Size(Size)
                            .Aggregations(ag => ag
                               .Cardinality("SumName", ca => ca.Field("CardinalField")))
                            .Query(q => q
                                .Bool(b => b
                                    .Must(m => m
                                            .QueryString(qs => qs                                                        .Fields("FieldNames").Query("FieldValue")),
                                          m => m.Terms(tf => tf
                                                       .Field("TermField")
                                                       .Terms("TermFeildVal")),
                                          m => m.Range(ra => ra.Field("RangeField").GreaterThanOrEquals("RangeVal")))))
                            ).ConfigureAwait(false);

How can I use GreaterThanOrEquals, GreaterThan, LessThanOrEquals & GreaterThan on the basis of my input string, e.g., if someone wants with gte, use GreaterThanOrEquals, if they want lte, use LessThanOrEquals, etc.?

Like

await _elasticClient.SearchAsync<T>(s => s
                            .Index(objFeasibilityRangeVM.IndexName)
                            .From(objFeasibilityRangeVM.From)
                            .Size(objFeasibilityRangeVM.Size)
                            .Aggregations(ag => ag
                               .Cardinality(objFeasibilityRangeVM.SumName, ca => ca.Field(objFeasibilityRangeVM.CardinalField)))
                            .Query(q => q
                                .Bool(b => b
                                    .Must(m => m
                                            .QueryString(qs => qs
                                                        .Fields(objFeasibilityRangeVM.FieldNames).Query(objFeasibilityRangeVM.FieldValue)),
                                          m => m.Terms(tf => tf
                                                       .Field(objFeasibilityRangeVM.TermField)
                                                       .Terms(objFeasibilityRangeVM.TermFeildVal)),
                                          m => m.Range(ra => ra.Field(objFeasibilityRangeVM.RangeField)
                                          FieldValue == "gte"?
                                          .GreaterThanOrEquals(RangeVal)
                                          : .LessThanOrEquals(RangeVal)
                                          ))))
                            ).ConfigureAwait(false);

Solution

  • One option could be to move range part into method like this

    QueryContainer Range(string condition)
    {
        return condition == "gte" 
            ? new NumericRangeQuery { Field = "RangeField", GreaterThanOrEqualTo = 1 }
            : new NumericRangeQuery { Field = "RangeField", LessThanOrEqualTo = 1 };
    }
    

    and then your query becomes something like

    await _elasticClient.SearchAsync<T>(s => s
        .Index(IndexName)
        .From(From)
        .Size(Size)
        .Aggregations(ag => ag
            .Cardinality("SumName", ca => ca.Field("CardinalField")))
        .Query(q => q
            .Bool(b => b
                .Must(m => m
                        .QueryString(qs => qs.Fields("FieldNames").Query("FieldValue")),
                    m => m.Terms(tf => tf
                        .Field("TermField")
                        .Terms("TermFeildVal")),
                    _ => Range("gte"))))
    ).ConfigureAwait(false);
    

    With second idea you could inline your logic like below using the fact that in case to / from values are null NEST will omit those in generated query to Elasticsearch

    var searchResponse = await client.SearchAsync<object>(s => s
        .Index("test")
        .From(0)
        .Size(10)
        .Aggregations(ag => ag
            .Cardinality("SumName", ca => ca.Field("CardinalField")))
        .Query(q => q
            .Bool(b => b
                .Must(m => m
                        .QueryString(qs => qs.Fields("FieldNames").Query("FieldValue")),
                    m => m.Terms(tf => tf
                        .Field("TermField")
                        .Terms("TermFeildVal")),
                    m =>
                    {
                        double? gtValue = null;
                        double? ltValue = null;
    
                        if (FieldValue == "gte")
                        {
                            gtValue = 1;
                        }
    
                        if (FieldValue == "lte")
                        {
                            ltValue = 2;
                        }
                        
                        return m.Range(ra => ra.Field("RangeField").LessThanOrEquals(ltValue).GreaterThanOrEquals(gtValue));
                    })))
    );