LINQ expressions for Elasticsearch NEST query - c#

I setting up an index as below. But now I have a requirement because of which i need to tweak the indexing style. (i have to add analyzer field in the below code).
Reference[My previous question and its answer]: Elastic Search using NEST - Results different in debug and browser mode
How can I rewrite
var connectionSettings = new ConnectionSettings(pool)
.DefaultIndex(defaultIndex)
.MapDefaultTypeNames(m => m.Add(typeof(Class1), "omg"))
.PrettyJson()
.DisableDirectStreaming());
with the mapping settings like below.
{
"mappings": {
"Class1": {
"properties": {
"Answer": {
"type": "string",
"analyzer": "english"
}
}
}
}
}
This is my take on answer:
settings = new ConnectionSettings(pool)
.DefaultIndex(defaultIndex)
.MapDefaultTypeNames(m => m.Add(typeof(Class1), "omg"))
.PrettyJson()
.DisableDirectStreaming();
var descriptor = new CreateIndexDescriptor(defaultIndex)
.Mappings(ms => ms
.Map<Class1>(m => m
.Properties(ps => ps
.String(s=>s
.Name(n=>n.Ans)
.Analyzer("english")))));
I think I'm missing a link somewhere between the index creation and mappings. Though it didnt show an error while coding, the output is not as expected.
TIA

A CreateIndexDecriptor<T> is a descriptor for creating an index, but you need to pass it to the IElasticClient.CreateIndex() method in order to create the index in Elasticsearch.
void Main()
{
var defaultIndex = "default-index";
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var settings = new ConnectionSettings(pool, new InMemoryConnection())
.DefaultIndex(defaultIndex)
.MapDefaultTypeNames(m => m.Add(typeof(Class1), "omg"))
.PrettyJson()
.DisableDirectStreaming();
var client = new ElasticClient(settings);
client.CreateIndex("new-index", c => c
.Mappings(ms => ms
.Map<Class1>(m => m
.Properties(ps => ps
.String(s => s
.Name(n => n.Ans)
.Analyzer("english")
)
)
)
)
);
}
public class Class1
{
public string Ans { get; set;}
}
The request to Elasticsearch looks like
{
"mappings": {
"omg": {
"properties": {
"ans": {
"type": "string",
"analyzer": "english"
}
}
}
}
}

Related

How to write the equivalent query in NEST,C# for the date_histogram by week

I need to convert the following query into c# in using NEST.
"aggs": {
"number_of_weeks": {
"date_histogram": {
"field": "#timestamp",
"interval": "week"
}
}
}
in Kibana the output is
I wrote the following query but it give me zero bucket while in Kibana it return many result in buckets
var query3 = EsClient.Search<doc>(q => q
.Index("SomeIndex")
.Size(0)
.Aggregations(agg => agg.DateHistogram("group_by_week", e => e.Field(p => p.timestamp) .Interval(DateInterval.Week)
)) ;
var resultquery3 = query3.Aggregations.DateHistogram("group_by_week");
in vs studio the output is
The problem is likely that
e => e.Field(p => p.timestamp)
does not serialize to the "#timestamp" field in Elasticsearch. For this to work, you would need to either map it with an attribute on the POCO
public class Doc
{
[Date(Name = "#timestamp")]
public DateTime timestamp { get; set; }
}
or map it on ConnectionSettings
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var settings = new ConnectionSettings(pool)
.DefaultMappingFor<Doc>(m => m
.PropertyName(e => e.timestamp, "#timestamp")
);
var client = new ElasticClient(settings);
Alternatively, you can simply pass a string to .Field(), which implicitly converts
.Field("#timestamp")

Elasticsearch nest can't find filter

So I'm trying to configure my index to have certain mappings and filters, but whenever I try to create the index I get the following error:
"[amgindex] failed to create index]; nested: IllegalArgumentException[Custom Analyzer [amgsearch] failed to find filter under name [synonym]"
this is the code I'm using to create the index
public void newIndex() {
var amgBasic = new CustomAnalyzer {
Tokenizer = "edgeNGram",
Filter = new string[] { "lowercase", "worddelimiter", "stemmerEng", "stemmerNl", "stopper", "snowball" } //
};
var amgBasicText = new CustomAnalyzer {
Tokenizer = "standard",
Filter = new string[] { "lowercase", "worddelimiter" }
};
var amgSearch = new CustomAnalyzer {
Tokenizer = "whitespace",
Filter = new string[] { "lowercase", "synonym" }
};
var synonmyfilter = new SynonymTokenFilter() {
Format = "Solr",
SynonymsPath = "analysis/synonym.txt"
};
try {
var result = client.CreateIndex("amgindex", i => i
.Analysis(descriptor => descriptor
.Analyzers(bases => bases
//.Add("amgBasic", amgBasic)
//.Add("amgBasicText", amgBasicText)
.Add("amgsearch", amgSearch)
)
.TokenFilters(c => c.Add("stemmereng", new StemmerTokenFilter() { Language = "english" }))
.TokenFilters(c => c.Add("stemmernl", new StemmerTokenFilter() { Language = "english" }))
.TokenFilters(c => c.Add("stopper", new StopTokenFilter() { Stopwords = new List<string>() { "_english_", "_dutch_" } }))
.TokenFilters(c => c.Add("snowball", new SnowballTokenFilter() { Language = "english" }))
.TokenFilters(c => c.Add("worddelimiter ", new WordDelimiterTokenFilter() { }))
.TokenFilters(c => c.Add("synonym ", synonmyfilter))
)
.AddMapping<general_document>(m => m
.Properties(o => o
.String(p => p.Name(x => x.object_name).IndexAnalyzer("amgSearch"))
.String(p => p.Name(x => x.title).IndexAnalyzer("amgSearch"))
.String(p => p.Name(x => x.Text).IndexAnalyzer("amgSearch"))
)
)
);
Log.Info("Index created? " + result.Acknowledged);
} catch (Exception ex) {
Log.Error("[index-creation] " + ex.Message);
throw;
}
}
everytime I use one of my own filters the error pops up.
Any clue why this is happening?
After spending way to much time on this I found it!! :)
there was a space in the name of 2 Filters, and those were causing the issue;
▼
.TokenFilters(c => c.Add("worddelimiter ", new WordDelimiterTokenFilter() { }))
▼
.TokenFilters(c => c.Add("synonym ", synonmyfilter))

Elasticsearch.NET NEST Object Initializer syntax for a highlight request

I've got:
var result = _client.Search<ElasticFilm>(new SearchRequest("blaindex", "blatype")
{
From = 0,
Size = 100,
Query = titleQuery || pdfQuery,
Source = new SourceFilter
{
Include = new []
{
Property.Path<ElasticFilm>(p => p.Url),
Property.Path<ElasticFilm>(p => p.Title),
Property.Path<ElasticFilm>(p => p.Language),
Property.Path<ElasticFilm>(p => p.Details),
Property.Path<ElasticFilm>(p => p.Id)
}
},
Timeout = "20000"
});
And I'm trying to add a highlighter filter but I'm not that familiar with the Object Initializer (OIS) C# syntax. I've checked NEST official pages and SO but can't seem to return any results for specifically the (OIS).
I can see the Highlight property in the Nest.SearchRequest class but I'm not experienced enough (I guess) to simply construct what I need from there - some examples and explanations as to how to employ a highlighter with OIS would be hot!
This is the fluent syntax:
var response= client.Search<Document>(s => s
.Query(q => q.Match(m => m.OnField(f => f.Name).Query("test")))
.Highlight(h => h.OnFields(fields => fields.OnField(f => f.Name).PreTags("<tag>").PostTags("</tag>"))));
and this is by object initialization:
var searchRequest = new SearchRequest
{
Query = new QueryContainer(new MatchQuery{Field = Property.Path<Document>(p => p.Name), Query = "test"}),
Highlight = new HighlightRequest
{
Fields = new FluentDictionary<PropertyPathMarker, IHighlightField>
{
{
Property.Path<Document>(p => p.Name),
new HighlightField {PreTags = new List<string> {"<tag>"}, PostTags = new List<string> {"</tag>"}}
}
}
}
};
var searchResponse = client.Search<Document>(searchRequest);
UPDATE
NEST 7.x syntax:
var searchQuery = new SearchRequest
{
Highlight = new Highlight
{
Fields = new FluentDictionary<Field, IHighlightField>()
.Add(Nest.Infer.Field<Document>(d => d.Name),
new HighlightField {PreTags = new[] {"<tag>"}, PostTags = new[] {"<tag>"}})
}
};
My document class:
public class Document
{
public int Id { get; set; }
public string Name { get; set; }
}

ElasticSearch C# client (NEST): access nested aggregation results

I have the following query in NEST (ElasticSearch C# client), note the nested aggregation:
var query = _elasticClient.Search<Auth5209>(s => s
.Size(0)
.Aggregations(a=> a
.Terms("incidentID", t=> t
.Field(f=>f.IncidentID)
.Size(5)
.Aggregations(a2 => a2
.Stats("authDateStats", s1=>s1.Field(f=>f.AuthEventDate))
)
)
)
);
This correctly generates the following query:
{
"size": 0,
"aggs": {
"incidentID": {
"terms": {
"field": "incidentID",
"size": 5
},
"aggs": {
"authDateStats": {
"stats": {
"field": "authEventDate"
}
}
}
}
}
}
Which gives me the following results:
"aggregations" : {
"incidentID" : {
"buckets" : [{
"key" : "0A631EB1-01EF-DC28-9503-FC28FE695C6D",
"doc_count" : 233,
"authDateStats" : {
"count" : 233,
"min" : 1401167036075,
"max" : 1401168969907,
"avg" : 1401167885682.6782,
"sum" : 326472117364064
}
}
]
}
}
What I can't figure out is how I access the "authDateStats" section. When I debug I don't see any way to access the data.
Neither the official documentation nor the answers here fully work for nest 2.0+. Although the answer from jhilden did get started me down the right path.
Here is a working example of a similar query which can be used with nest 2.0+:
const string termsAggregation = "device_number";
const string topHitsAggregation = "top_hits";
var response = await _elasticsearchClient.Client.SearchAsync<CustomerDeviceModel>(s => s
.Aggregations(a => a
.Terms(termsAggregation, ta => ta
.Field(o => o.DeviceNumber)
.Size(int.MaxValue)
.Aggregations(sa => sa
.TopHits(topHitsAggregation, th => th
.Size(1)
.Sort(x => x.Field(f => f.Modified).Descending())
)
)
)
)
);
if (!response.IsValid)
{
throw new ElasticsearchException(response.DebugInformation);
}
var results = new List<CustomerDeviceModel>();
var terms = response.Aggs.Terms(termsAggregation);
foreach (var bucket in terms.Buckets)
{
var hit = bucket.TopHits(topHitsAggregation);
var device = hit.Documents<CustomerDeviceModel>().First();
results.Add(device);
}
I'm guessing you already figured this out but you can access the nested aggregations, it's just in a base class, you can see it in Nest.KeyItem.base.base.Aggregations in the debugger.
Here is a full working sample of accessing an inner aggregation:
const string aggName = "LocationIDAgg";
const string aggNameTopHits = "LatestForLoc";
var response = await ElasticClient.SearchAsync<PlacementVerificationES>(s => s
.Query(BuildQuery(filter, null))
.Size(int.MaxValue)
.Aggregations(a=> a
.Terms(aggName, t=> t
.Field(f=>f.LocationID)
.Size(100)
.Aggregations(innerAgg => innerAgg
.TopHits(aggNameTopHits, th=> th
.Size(1)
.Sort(x=>x.OnField(f=> f.Date).Descending())
)
)
)
)
).VerifySuccessfulResponse();
//var debug = response.GetRequestString();
var agBucket = (Bucket)response.Aggregations[aggName];
var output = new List<PlacementVerificationForReporting>();
// ReSharper disable once LoopCanBeConvertedToQuery
// ReSharper disable once PossibleInvalidCastExceptionInForeachLoop
foreach (KeyItem i in agBucket.Items)
{
var topHits = (TopHitsMetric)i.Aggregations[aggNameTopHits];
var top1 = topHits.Hits<PlacementVerificationES>().Single();
var reportingObject = RepoToReporting(top1);
output.Add(reportingObject);
}
return output;

Create custom token filter with NEST

How can I configure Index using NEST with such JSON:
"settings":{
"analysis":{
"filter":{
"name_ngrams":{
"side":"front",
"max_gram":50,
"min_gram":2,
"type":"edgeNGram"
}
},
"analyzer":{
"partial_name":{
"filter":[
"standard",
"lowercase",
"asciifolding",
"name_ngrams"
],
"type":"custom",
"tokenizer":"standard"
}
}
}
I could create my custom analyzer using CustomAnalyzer class, but I could'n find how to create custom filter and register it within my analyzer.
Thanks in advance!
After some searching I've found a solution:
var partialName = new CustomAnalyzer
{
Filter = new List<string> {"lowercase", "name_ngrams", "standard", "asciifolding"},
Tokenizer = "standard"
};
var fullName = new CustomAnalyzer
{
Filter = new List<string> {"standard", "lowercase", "asciifolding"},
Tokenizer = "standard"
};
client.CreateIndex("indexname", c => c
.Analysis(descriptor => descriptor
.TokenFilters(bases => bases.Add("name_ngrams", new EdgeNGramTokenFilter
{
MaxGram = 20,
MinGram = 2,
Side = "front"
}))
.Analyzers(bases => bases
.Add("partial_name", partialName)
.Add("full_name", fullName))
)
.AddMapping<Company>(m => m
.Properties(o => o
.String(i => i
.Name(x => x.Name)
.IndexAnalyzer("partial_name")
.SearchAnalyzer("full_name")
))));

Categories

Resources