web-dev-qa-db-fra.com

NEST Requête de filtre conditionnelle avec plusieurs termes

Je voudrais faire une requête ElasticSearch comme ceci:

{
    "query" :
    {
        "bool" :
        {
            "filter" : [
                {
                    "terms" :
                    {
                        "name" : ["name1", "name2"]
                    }
                },
                {
                    "terms" :
                    {
                        "color" : ["orange", "red"]
                    }
                }
            ]
        }
    }
}

J'ai essayé de l'implémenter dans NEST comme ceci:

_elasticClient
    .SearchAsync<MyDocument>(s =>
        s.Index("myindex")
            .Query(q => q
                .Bool(bq => bq
                    .Filter(fq =>
                    {
                        QueryContainer query = null;

                        if (nameList.Any()) {
                            query &= fq.Terms(t => t.Field(f => f.Name).Terms(nameList));
                        }

                        if (colorList.Any()) {
                            query &= fq.Terms(t => t.Field(f => f.Color).Terms(colorList));
                        }

                        return query;
                    })
                )
            )
    );

Mais cela me donne une requête comme celle-ci où les filtres sont encapsulés dans un bool must :

{
    "query" :
    {
        "bool" :
        {
            "filter" : [
                {
                    "bool" :
                    {
                        "must" : [
                            {
                                "terms" :
                                {
                                    "name" : ["name1", "name2"]
                                }
                            },
                            {
                                "terms" :
                                {
                                    "color" : ["orange", "red"]
                                }
                            }
                        ]
                    }
                }
            ]
        }
    }
}

Comment dois-je changer mon code NEST pour me donner la bonne requête? Dois-je ajouter mes termes à quelque chose d'autre qu'un QueryContainer ?

10
dhrm

Vous pouvez créer une liste de filtres avant de faire une requête si vous souhaitez vérifier les filtres conditionnels comme indiqué ci-dessous:

var nameList = new[] {"a", "b"};
var colorList = new[] {1, 2};

var filters = new List<Func<QueryContainerDescriptor<MyDocument>, QueryContainer>>();
if (nameList.Any())
{
     filters.Add(fq=> fq.Terms(t => t.Field(f => f.Name).Terms(nameList)));
}

if (colorList.Any())
{
    filters.Add(fq => fq.Terms(t => t.Field(f => f.Color).Terms(colorList)));
}

ISearchResponse<Property> searchResponse =
     elasticClient.Search<MyDocument>(x => x.Query(q => q
     .Bool(bq => bq.Filter(filters))));

Si vous n'avez besoin de vérifier aucune condition avant de créer une requête de filtrage, vous pouvez avoir quelque chose comme ça:

ISearchResponse<MyDocument> searchResponse =
elasticClient.Search<MyDocument>(x => x.Query(q => q
.Bool(bq => bq
.Filter(
        fq => fq.Terms(t => t.Field(f => f.Name).Terms(nameList)),
        fq => fq.Terms(t => t.Field(f => f.Color).Terms(colorList))
        ))));
16
Adam Łepkowski

La méthode Filter d'une requête bool utilise un params Func<QueryContainerDescriptor<T>, QueryContainer>[] afin que vous puissiez lui transmettre plusieurs expressions pour représenter plusieurs filtres.

var nameList = new string[] { "name1", "name2" };
var colorList = new string[] { "orange", "red" };

client.SearchAsync<MyDocument>(s => s
        .Index("myindex")
        .Query(q => q
            .Bool(bq => bq
                .Filter(
                    fq => fq.Terms(t => t.Field(f => f.Name).Terms(nameList)),
                    fq => fq.Terms(t => t.Field(f => f.Color).Terms(colorList))
                )
            )
        )
);

qui se traduit par 

{
  "query": {
    "bool": {
      "filter": [
        {
          "terms": {
            "name": [
              "name1",
              "name2"
            ]
          }
        },
        {
          "terms": {
            "color": [
              "orange",
              "red"
            ]
          }
        }
      ]
    }
  }
}

NEST utilise également le concept de requêtes sans condition , c'est-à-dire que si une requête est considérée comme étant sans condition, elle sera not sera sérialisée dans le cadre de la requête. 

Qu'est-ce que cela signifie d'être sans condition? Cela dépend de la requête. par exemple, dans le cas d'une requête terms, elle est considérée comme étant sans condition si l'une des conditions suivantes est vraie

  • la field n'a pas de valeur 
  • la liste des valeurs de terme est null 
  • la valeur des termes est une collection vide
  • la liste des valeurs de termes a des valeurs mais elles sont toutes des chaînes null ou vides

Démontrer

var emptyNames = new string[] {};
string[] nullColors = null;

client.SearchAsync<MyDocument>(s =>
s.Index("myindex")
    .Query(q => q
        .Bool(bq => bq
            .Filter(
                fq => fq.Terms(t => t.Field(f => f.Name).Terms(emptyNames)),
                fq => fq.Terms(t => t.Field(f => f.Color).Terms(nullColors)))
        )
    )
);

résulte en

{}

Les requêtes sans condition servent à faciliter l'écriture des requêtes NEST dans la mesure où vous n'avez pas besoin de vérifier si la collection a des valeurs avant de construire une requête. Vous pouvez modifier la sémantique sans condition sur la base d'une requête à l'aide de .Strict() et de .Verbatim() .

11
Russ Cam
var searchResponse = client.Search<EventData>(s => s
            .From(0)
            .Query(q => q
                    .Bool(bq => bq
                    .Filter(
                            fq => fq.Terms(t => t.Field(f => f.Client.Id).Terms(17)),
                            fq => fq.Terms(t => t.Field(f => f.Item.Id).Terms(**new[] { 34983, 35430, 35339, 35300 }**)), 
                            fq => fq.Terms(t=>t.Field(f=>f.Event).Terms("Control de Panico")),
                            fq => fq.DateRange(dr => dr.Field(f => f.DateTime)
                                .GreaterThanOrEquals(new DateTime(2018, 07, 01))
                                .LessThanOrEquals(new DateTime(2018, 10, 02)))
                            )
                  ))
            .Size(2000)
            .Sort(g => sortDescriptor)
            );
0
user734862