Using which field matched in a multimatch query in a function score - elasticsearch

I have a multimatch query which I am using across 5 fields. I am also using a function score to combine various factors into the score. I would like to add a factor to this so that results that matched on one of the fields is increased (adding a large number so that matches on this field always have the highest score).
I know that I can use highlighting to find out which fields were matched, but how can I access that information in the function score script?
Here's what I have so far (using NEST, but that shouldn't make a difference).
var searchResponse = client.Search<TopicCollection.Topic>(s => s
.Query(q => q
.FunctionScore(fs => fs
.Name("function_score_query")
.Query(q1 => q1
.MultiMatch(c => c
.Fields(f => f
.Field(p => p.field1)
.Field(p => p.field2) //...etc
.Query(searchTerm)
)
)
.Functions(fun => fun
.ScriptScore(ss => ss.Script(sc => sc
.Inline(
//TODO: add 1000 to normalised _score if match is in field1
)))
).BoostMode(FunctionBoostMode.Replace)
)
).Highlight(h => h
.Fields(p => p.AllField())
)
);

Related

Elasticsearch NEST API, Searching Multiple Indices

If one is seraching several indexes at the same time, is there any way to say that if searching index A, then add this filter and if searching index B then add a different filter.
For example:
var filters = new List<Func<QueryContainerDescriptor<PropertySearchResult>, QueryContainer>>();
filters.Add(fq => fq.Term(t => t.Field(f => f.PromoterId).Value(user.Id)));
filters.Add(fq => fq.Term(t => t.Field(f => f.SubscriptionId).Value(subscriptionId)));
string indicies = String.Join(",", Utils.SupportedCountries.Select(c => c.Key.ToLower()).ToArray());
var result = await ElasticSearchConfig.GetClient().DeleteByQueryAsync<PropertySearchResult>(u => u
.Index(indicies)
.Query(q => q
.Bool(bq => bq.Filter(filters))));
at the moment, all indices will be subject to the same filters but I would like to vary the filters based on which index is being searched.
Add(with &&) a term query to each of your filters
.Term("_index", A)
Check this link
https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-index-field.html

How can I find the total hits for an Elastic NEST query?

In my application I have a query which brings limits the number of hits returned to 50 as follows
var response = await client.SearchAsync<Episode>(s => s
.Source(sf => sf
.Includes(i => i
.Fields(
f => f.Title,
f => f.PublishDate,
f => f.PodcastTitle
)
)
.Excludes(e => e
.Fields(f => f.Description)
)
)
.From(request.Skip)
.Size(50)
.Query(q => q
.Term(t => t.Title, request.Search) || q
.Match(mq => mq.Field(f => f.Description).Query(request.Search))));
I am interested in the total number of hits for the query (i.e. not limited to the size), so that I can deal with pagination on the front-end. Does anyone know how I can do this?
You are looking for Total property on the search response object. Have a look.
So in your particular case that will be response.Total.
For those who are working on indices with more than 10000 documents, Elasticsearch will calculate total hits up to 10000 by default. To get around that, include .TrackTotalHits(true) in your query:
var resp = client.Search<yourmodel>(s => s
.Index(yourindexname)
.TrackTotalHits(true)
.Query(q => q.MatchAll()));

Is it possible to query aggregations on NEST for multiple term fields (.NET)?

Below is the NEST query and aggregations:
Func<QueryContainerDescriptor<ConferenceWrapper>, QueryContainer> query =
q =>
q.Term(p => p.type, "conference")
// && q.Term(p => p.conference.isWaitingAreaCall, true)
&& q.Range(d => d.Field("conference.lengthSeconds").GreaterThanOrEquals(minSeconds))
&& q.DateRange(qd => qd.Field("conference.firstCallerStart").GreaterThanOrEquals(from.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ")))
&& q.DateRange(qd => qd.Field("conference.firstCallerStart").LessThanOrEquals(to.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ")));
Func<AggregationContainerDescriptor<ConferenceWrapper>, IAggregationContainer> waitingArea =
a => a
.Terms("both", t => t
.Field(p => p.conference.orgNetworkId) // seems ignore this field
.Field(p => p.conference.isWaitingAreaCall)
// .Field(new Field( p => p.conference.orgNetworkId + "-ggg-" + p.conference.networkId)
.Size(300)
.Aggregations(a2 => a2.Sum("sum-length", d2 => d2.Field("conference.lengthSeconds"))));
I have called .Field(p => p.conference.orgNetworkId) followed by .Field(p => p.conference.isWaitingAreaCall) But it seems the NEST client tries to ignore the first field expression.
Is is possible to have multiple fields to be the terms group by?
Elasticsearch doesn't support a terms aggregation on multiple fields directly; the calls to .Field(...) within NEST are assignative rather than additive, so the last call will overwrite any previously set values.
In order to aggregate on multiple fields, you can either
Create a composite field at index time that incorporates the values that you wish to aggregate on
or
Use a Script to generate the terms on which to aggregate at query time, by combining the two field values.
The performance of the first option will be better than the second.

Querying NEST 2.0.0-rc1 Aggregation Buckets

Previously in NEST (for Elasticsearch 1.x), after an aggregation query, I had some code that went through and grouped up aggregations by going through all the buckets, similar to this:
var r = (from SingleBucket items1 in result.Aggregation.Values
select (Bucket) agg1
into items1Aggs
from col1 in items1Aggs.Items.Cast<KeyItem>()
....
select new x {}).ToList();
But it seems that now the SingleBucket has to be SingleBucketAggregate, but the Bucketclass used before is now an internal BucketAggregateData class that is no long accessible. Is there any way around this?
Aggregation query being used:
var result = _client.Search<MetaStoreEntry>(s => s
.Aggregations(a => a
.Filter("fullGroupBy", k => fad
.Aggregations(e => e
.Terms("col1", t => t
.Field(f => f.col1)
.Aggregations(b => b
.Terms("col2", u => u
.Field(f => f.col2)
...
).Size(int.MaxValue).CollectMode(TermsAggregationCollectMode.DepthFirst)
) ...
);

Elasticsearch NEST - Phrase search

What methods should I use in order for my query to return hits with at least 2 keywords in the text from an input phrase.
For example, if the input "hello friend" I want the return results to contain documents where "hello" and "friend" somewhere in the text. If the input "hello good friend" I want results where 2 of 3 keyword in the text. Or at least results with best combinations be on top.
If I use code like one below I get results where "hello" or "friend" but not both.
var searchResults = client.Search<Thread>(s => s
.Type("threads")
.From(0)
.Size(100)
.Query(q => q
.Match(qs => qs
.OnField(p => p.Posttext)
.Query("hello friend")
)
)
.Highlight(h => h
.OnFields(
f => f.OnField("posttext").PreTags("<b>").PostTags("</b>").FragmentSize(150)
)
)
);
I can get results I want by code like this one but it is not flexible because phrase can be with arbitrary number of words.
var searchResults = client.Search<Thread>(s => s
.Type("threads")
.From(0)
.Size(100)
.Query(q => q
.Match(qs => qs
.OnField(p => p.Posttext)
.Query("hello")
)
&&
q.Match(qs => qs
.OnField(p => p.Posttext)
.Query("friend")
)
)
.Highlight(h => h
.OnFields(
f => f.OnField("posttext").PreTags("<b>").PostTags("</b>").FragmentSize(150)
)
)
);
I think I am missing something. Please help.
Thanks in advance.
you need to use phrase query..
within the match you need specify the type as phrase ..
https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query.html#query-dsl-match-query-phrase
IF you go through the article above i guess you can find a direction to your question..
PS: I am aware of elasticsearch for javascript...
I found that adding .Operator(Operator.And) to Match query works in my situation. But I need to investigate more on phrase search.

Resources