elasticsearch multimatch on nested items

1.2k views Asked by At

I get a string from the client. This string can match either one of the strings of my group, or one of the strings of one of the underlaying items. This string is a must query.

i got the multimatch working for both the nested querycontainers and the normal ones. My question is: How do I get both the nested and the top layer in a single multimatch?

queryList.Add(q => q
            .MultiMatch(mm => mm
                .Fields(fs => fs
                    .Field(f => f.ExtraText)
                    .Field(f => f.SomeText)
                    .Field(f => f.MoreInfo)
                    .Field(f => f.SEOText)
                    .Field(f => f.Title))
                .Query(filter.SearchString)));
queryList.Add(q => q
            .Nested(c => c
                .Path(p => p.Items)
                .Query(nq => nq
                    .MultiMatch(mm => mm
                        .Fields(fs => fs
                            .Field(f => f.Items.First().Subtitle)
                            .Field(f => f.Items.First().Name)
                            .Field(f => f.Items.First().MoreInfo)
                            .Field(f => f.Items.First().ExtraText)
                            .Field(f => f.Items.First().SEOText))
                        .Query(filter.SearchString)))));

Expansion: This search query is an expansion on an already existing number filters. Next to the search on text there is also the filter on categories or from which country the group is:

if (filter.categories != null && filter.categories.Any())
        {
            foreach (string category in filter.categories)
            {
                queryList.Add(d => d
                    .Nested(nd => nd
                        .Path(np => np.Categories)
                        .Query(nq => nq
                            .Bool(nb => nb
                                .Must(fm => fm
                                    .Match(q => q
                                        .Field(f => f.Categories.First().UrlSlug)
                                        .Query(category)))))));
            }
        }
 if (!string.IsNullOrEmpty(filter.country))
        {
            queryList.Add(d => d.Term(p => p.country, filter.country));
        }

then this list of queries is fed to the search:

 _elasticClient.Search<ProductModel>(d => d
                        .Index(_config.GroupIndexName)
                            .Query(q => q
                                .Bool(b => b.Must(queryList))
                            )
                            .Sort(s => s
                                .Field(f => f
                                    .Field(GetSortField(filter.SortOption))
                                    .Order(filter.SortOrder)
                                )
                            )
                            .Skip((filter.Page - 1) * filter.RecordsPerPage)
                            .Take(filter.RecordsPerPage)
                        );

What i need is the filter for the searchstring to be 1 single query. So that on the 'must' level they don't cancel each other out. (if the string matches the group but not one of his underlying items, neither are shown).

1

There are 1 answers

2
Russ Cam On

Use a bool query. It looks like you're probably looking to combine them as should clauses i.e. match one or the other query

client.Search<MyDocument>(s => s
    .Query(q => q
        .Bool(b => b
            .Should(sh => sh
                .MultiMatch(mm => mm
                    .Fields(fs => fs
                        .Field(f => f.ExtraText)
                        .Field(f => f.SomeText)
                        .Field(f => f.MoreInfo)
                        .Field(f => f.SEOText)
                        .Field(f => f.Title)
                    )
                    .Query(filter.SearchString)
                ), sh => sh
                .Nested(c => c
                    .Path(p => p.Items)
                    .Query(nq => nq
                        .MultiMatch(mm => mm
                            .Fields(fs => fs
                                .Field(f => f.Items.First().Subtitle)
                                .Field(f => f.Items.First().Name)
                                .Field(f => f.Items.First().MoreInfo)
                                .Field(f => f.Items.First().ExtraText)
                                .Field(f => f.Items.First().SEOText))
                            .Query(filter.SearchString)
                        )
                    )
                )
            )
        )
    )
);

which can be more succinctly expressed

client.Search<MyDocument>(s => s
    .Query(q => q
        .MultiMatch(mm => mm
            .Fields(fs => fs
                .Field(f => f.ExtraText)
                .Field(f => f.SomeText)
                .Field(f => f.MoreInfo)
                .Field(f => f.SEOText)
                .Field(f => f.Title)
            )
            .Query(filter.SearchString)
        ) || q
        .Nested(c => c
            .Path(p => p.Items)
            .Query(nq => nq
                .MultiMatch(mm => mm
                    .Fields(fs => fs
                        .Field(f => f.Items.First().Subtitle)
                        .Field(f => f.Items.First().Name)
                        .Field(f => f.Items.First().MoreInfo)
                        .Field(f => f.Items.First().ExtraText)
                        .Field(f => f.Items.First().SEOText))
                    .Query(filter.SearchString)
                )
            )
        )
    )
);