I am having some trouble with a certain query in elastic search C#.
I have this QueryContainer, with an inner QueryDescriptor and alot of inner QueryContainers \ QueryDescriptors,
but one main QueryContainer => this._QueryContainer that contains all the data.
the thing is, that the field UserID is not unique in this._QueryContainer, so when i return 20 unique users, first time all is ok, but next 20 users (for next page) i wouldn't know where to start this.From...
because the this._QueryContainer has duplicates but return unique because of aggregation. so there is a conflict.
Is there a way to make the query distinct from the start?
results = Client.Search<Users>(s => s
.From(this.From)
.Query(this._QueryContainer)
.Aggregations(a => a
.Terms("unique", te => te
.Field(p => p.UserID)
)
)
.Size(20)
);
The .From() and .Size() within your query do not affect the Terms aggregation that you have, they apply only to the .Query() part and the hits returned from this.
If you need to return lots of values from a Terms aggregation, which is what I think you'd like to do, you can
1.Use partitioning to filter values, returning a large number of terms in multiple requests e.g.
var response = client.Search<Users>(s => s
.Aggregations(a => a
.Terms("unique", st => st
.Field(p => p.UserID)
.Include(partition: 0, numberOfPartitions: 10)
.Size(10000)
)
)
);
// get the next partition
response = client.Search<Users>(s => s
.Aggregations(a => a
.Terms("unique", st => st
.Field(p => p.UserID)
.Include(partition: 1, numberOfPartitions: 10)
.Size(10000)
)
)
);
2.Use a Composite Aggregation with a Terms value source
var response = client.Search<Users>(s => s
.Aggregations(a => a
.Composite("composite", c => c
.Sources(so => so
.Terms("unique", st => st
.Field(p => p.UserID)
)
)
)
)
);
// the following would be in a loop, to get all terms
var lastBucket = response.Aggregations.Composite("composite").Buckets.LastOrDefault();
if (lastBucket != null)
{
// get next set of terms
response = client.Search<Users>(s => s
.Aggregations(a => a
.Composite("composite", c => c
.Sources(so => so
.Terms("unique", st => st
.Field(p => p.UserID)
)
)
.After(lastBucket.Key)
)
)
);
}
Related
My Elasticsearch document looks like this; I have created an average aggregation in the buckets of 60 seconds interval
{
"version" : 4,
"metric1" : 0.688872,
"metric2" : 0.021005,
"metric3" : 0.578913,
"metric4" : 71.57523,
"metric5" : 10.71166,
"Agentid" : "12345",
"epoch" : 1638173827064
}
My Aggregation Code looks like this;
var response = await _client.SearchAsync<MyPOCO>(s => s
.Index(indexName)
.TrackTotalHits(true)
.Size(0)
.Query(q => q
.Bool(b => b
.Must(mu => mu
.Match(m => m
.Field("AgentId")
.Query(Convert.ToString(AgentId))
)
)
.Filter(fi => fi
.DateRange(r => r
.Field("epoch")
.GreaterThanOrEquals(Convert.ToString(StartTime))
.LessThan(Convert.ToString(EndTime))
.Format("epoch_millis")
)
)
)
)
.Aggregations(a => a
.DateHistogram("metricsperminute", ab => ab
.Field("ts")
.FixedInterval("1m")
.Aggregations(x => x
.Average("metric1", m => m
.Field(o => o.metric1)
)
.Average("metric2", n => n
.Field(o => o.metric2)
)
.Average("metric3", o => o
.Field(o => o.metric4)
)
.Average("metric5", p => p
.Field(o => o.metric5)
)
)))
.Sort(sort => sort.Ascending("epoch")));
I am a bit lost in how to fetch more than 10k records/buckets in the aggregations ? How to do pagination or scrolling ? Please help.
There is a search.max_buckets dynamic cluster setting though I'd try to avoid returning too many buckets at once. It's extremely slow
Currently working on a project involving a vehicle database.
We have set up some filters for the end users to select, such as chassis, transmission, price. Which works as intended
Now we are trying to implement a autocomplete feature in the search field. Since the simple query string will require a complete modelname and or brandname, I have looked into completion and that works for an unfinished brand name ie: "tesl" will suggest the respective Tesla models.
What I'm struggling with is getting the suggestions within the specified query.
Thanks!
Solution:
Not sure if this is the correct way to solve it, but I just ran the main query with the suggested ids to get the full payload to get all the data i need for the text search.
var result = await _client.SearchAsync<Models.VehicleModel>(descriptor => descriptor
.Index("vehiclemodel")
.Query(
q =>
q
.Terms(t => t
.Name("ModelName")
.Field("modelName")
.Terms(searchArguments.Models)
)
&&
q
.Terms(t => t
.Name("BrandName")
.Field("brandName")
.Terms(searchArguments.Brands)
)
&&
q
.Range(t => t
.Name("Price")
.Field("modelMinimumPrice")
.GreaterThanOrEquals(searchArguments.MinPrice)
.LessThanOrEquals(searchArguments.MaxPrice)
)
&&
q
.SimpleQueryString(c => c
.Name("textquery")
.Query(searchArguments.Query)
.Boost(2)
.Fields(f => f
.Field(p => p.BrandName + "^2")
.Field(p => p.ModelName + "^2")
.Field(p => p.EnergySource)
.Field("*")
)
)
public async Task<dynamic> GetModelSearchSuggestions(SearchArguments searchArguments = default)
{
var result = await _client.SearchAsync<dynamic>(s => s
.Index("vehiclemodel")
.Suggest(su => su
.Completion("searchsuggest", cs => cs
.Field("suggest")
.Prefix(searchArguments.Query)
.Fuzzy(f => f
.Fuzziness(Fuzziness.Auto)
)
)
)
);
return result.Suggest;
}
mappingDescriptor
.Properties(p => p
.Completion(cp => cp
.Name("suggest")
.Analyzer("standard")
.SearchAnalyzer("standard")
)
.Text(t => t.Name("modelName").Fielddata(true))
.Text(t => t.Name("brandName").Fielddata(true))
);
I quire data from elastic and I get a big bunch of information.
I would like to get only two properties with values (key value pairs): timestamp and value, but I get lots, all the other information too.
How can I require only my properties I want? I tried like I read at elastic.co, but I still get always to full bunch of data.
Here my tries:
var result = ElasticClient.Search<_doc>(document =>
document
.Source(sf => sf
.Includes(i => i
.Fields(
f => f.Timestamp,
f => f.Value
)
)
.Excludes(e => e
.Fields(
f => f.ContextName
)
)
)
.Query(q => q
.Match(m => m
.Field(f => f.DataRecordId)
.Query(search)
)
)
);
Or:
var result = ElasticClient.Search<_doc>(document =>
document
.StoredFields(sf => sf
.Fields(
f => f.Timestamp,
f => f.Value
)
)
.Query(q => q
.Match(m => m
.Field(f => f.DataRecordId)
.Query(search)
)
)
);
Both return a big bag of data, much more than only Timestamp and Value.
So I get still all properties, but the excluded ones are null. I hope I will reduce traffic this way for better performance.
I'm open to other solutions.
var result = ElasticClient.Search<_doc>(document =>
document
.Source(src => src
.Includes(i => i
.Fields(
p => p.Timestamp,
p => p.Value
)
)
.Excludes(e => e
.Fields(
p => p.ComponentId,
p => p.ContextName,
p => p.DataRecordId,
p => p.ResourceId
)
)
)
.Query(q => q
.Match(m => m
.Field(f => f.DataRecordId)
.Query(search)
)
)
);
I'm having trouble configuring stop words in ElasticSearch using the NEST client. Here's what my index definition looks like:
var createIndexResponse = _client.CreateIndex(IndexName, c => c
.Settings(s => s
.Analysis(a => a
.Analyzers(aa => aa.Stop("pfstop", st => st.StopWords("_english_"))
)
)
)
.Mappings(m => m
.Map<SearchTopic>(mm => mm
.Properties(p => p
.Text(t => t
.Name(n => n.Posts)
.Name(n => n.FirstPost)
.Name(n => n.Title)
.SearchAnalyzer("pfstop")
)
)
)
)
);
And here's my query (and yes, I'm only wanting to return the ID):
var searchResponse = _client.Search<SearchTopic>(s => s
.Source(sf => sf.Includes(i => i.Fields(f => f.Id)))
.Query(q => q.MultiMatch(m => m.Query(searchTerm)
.Fields(f => f
.Field(x => x.Title, boost: 20)
.Field(x => x.FirstPost, boost: 2)
.Field(x => x.Posts))))
.Take(pageSize)
.Skip(startRow));
If my searchTerm is "Simon and Diana," I get results from any row that has "and" in it, which should be filtered out by way of the stop words.
Fluent syntax strikes again. After some experimentation, starting with including just one field in the mapping, I learned you have to split out the fields and their analyzer pairings. The correct syntax was this:
.Mappings(m => m
.Map<SearchTopic>(mm => mm
.Properties(p => p
.Text(t => t
.Name(n => n.Posts)
.Analyzer("pfstop")
)
.Text(t => t
.Name(n => n.FirstPost)
.Analyzer("pfstop")
)
.Text(t => t
.Name(n => n.Title)
.Analyzer("pfstop")
)
)
)
)
I want to do a search matching multiple values ( an array of values ) like this :
var result1 = _client.Search<type1>(s => s
.Fields(f => f.trip_id)
.Query(q => q
.Terms(t => t.arg1, value1)).Take(_allData))
.Documents.Select(d => d.arg2).ToArray();
var result2 = _client.Search<type2>(s => s
.Query(q => q
.Terms(t => t.arg3, result1))
.Take(_allData)
).Documents.Select(s => s.ar3).ToList();
How can I do ? I was thinking about facets but I don't see how I can do it.
The only way for now that works is with a foreach iterator which is not really effective...
Thanks for your help.
You can express multiple queries like so:
.Query(q=>q.Terms(t=>t.arg3, result1) && q.Terms(t=>t.arg1, value1))
Be sure to read the documentation on writing queries to discover all the good stuff NEST has to offer.
Orelus,
I'd like to use your solution with
.And( af=>af.Term(...), af=>af.Term(...) )
I don't understand where this fits, here's an example of my non-working filter
var results = client.Search<music>(s => s
.Query(q => q
.Filtered(f => f.
Filter(b => b.Bool(m => m.Must(
t => t
.Term(p => p.artist, artist)
&& t.Term(p2 => p2.year, year)
)
)
)
)
)
);