Highlight All fields Nest ElasticSearch - c#

everybody
I'm working with Nest driver of elasticsearch for c#. In my project I don't have any document mapping so, if I want to highlight matching fields I should use this Json part in my query which highlights all fields:
"highlight":{
"fields":{
"*":{}
}
}
but I want to do it with nest. I use this code:
client.Search<dynamic>(s => s
.Index('my index name')
.Type('my doc type name')
.From(page*PageSize)
.Size(PageSize)
.Query(q => q
.QueryString(qs => qs.Query('my query')))
.Highlight(h => h
.OnFields(f => f
.OnAll()
.PreTags("<b style='color:black'>")
.PostTags("</b>")
)));
and it is not working for me, returned result contains hits, but doesn't contain any highlight :(

I guess you are looking for
client.Search<dynamic>(s => s
.Index('my index name')
.Type('my doc type name')
.From(page*PageSize)
.Size(PageSize)
.Query(q => q
.QueryString(qs => qs.Query('my query')))
.Highlight(h => h
.OnFields(f => f
.OnField("*")
.PreTags("<b style='color:black'>")
.PostTags("</b>")
)));
because .OnAll() means .OnField("_all").
Take a look
UPDATE: object initializer syntax(NEST 5.x)
var searchRequest = new SearchRequest
{
Query = ..
Highlight = new Highlight
{
PostTags = new[] {"<a>"},
PreTags = new[] {"</a>"},
Fields = new FluentDictionary<Field, IHighlightField>().Add("*", new HighlightField())
}
};

Related

C# elastic search exact text match with nest

I am using latest c# elastic search NEST library.
I am trying to search with exact text match, but currently it is working searching
for subset match. I want to do exact match.
Following is my code snippet:
public User GetUserByUsername(string username)
{
var client = new ElasticConnectionManager(this.configuration).GetClient(Constant.IndexUsers);
var searchResponse = client.Search<User>(s => s
.Query(q => q
.Bool(bq => bq
.Filter(f => f.Term(t => t.Username, username))
.Must(mt=>mt.Term(t2=> t2.Username, username)))));
//.Must(bs => bs.Term(t => t.Username, username))
if (searchResponse.Documents.Count > 0)
return searchResponse.Documents.First();
else
return null;
}
}
Try using the match_phrase query for exact text match. Your query should be similar to the following:
var searchResponse = client.Search<User>(s => s
.Query(q => q
.MatchPhrase(m => m
.Field(f => f.Username)
.Query(username))));

elasticsearch NEST get nested document

situation is this. I have in elastic a group. each of these groups have a nested list of items.
Both group and items have an attribute named serial, which are unique.
I get a serial for the group and a serial for item, and with those 2 items i'm supposed to return the item.
Currently i'm doing it the following way:
public item findItem(string groupSerial, string itemSerial)
{
var searchResponse = _elasticClient.Search<Group>(s => s
.Index(_config.groupIndexName)
.Query(q => q
.ConstantScore(cs => cs
.Filter(f => f
.Term(t => t
.Field(fi => fi.serial)
.Value(groupSerial)
)
)
)
).Query(q => q
.Nested(c => c
.InnerHits(i => i.Explain())
.Path(p => p.items)
.Query(nq => nq.Term(t => t
.Field(field => field.items.First().serial)
.Value(itemSerial)))))
);
var result = searchResponse.Documents.FirstOrDefault();
return result?.items.Find(item => item.serial == itemSerial);
}
I get the feeling that there is supposed to be a more efficient way. Like getting the item straight from the search in elastic. Does anyone know how?

Elastic-nest.Search with analyzer doesn't work.It can't detect analyzer

I am using elastic with nest for c#.I create analyzers on index time but on search time they don't work.The search query returns me no documents.
Here is my class
public class Car {
Text(Analyzer = "greek",SearchAnalyzer ="custom",Index = true,IncludeInAll = true)]
public string SERVICE_DESC { get; set; }
}
And here is my search query
var response = client.Search<Cars>(n => n
.Index(index)
.Type(type)
.Size(searchSize)
.From(0)
.TrackScores(true)
.Query(q => q
.Match(qs => qs.Field(fieldsForSearchList[0]).Analyzer("custom")
.Operator(Operator.And).Query("*"+searchWord+"*"))));
As I mentioned I have already created the two analyzers ("greek","custom")
var response = client.CreateIndex(index, s => s.Settings(s1 => s1.NumberOfShards(5)
.NumberOfReplicas(5)
.Analysis(a => a.TokenFilters(t => t
.IcuTransform("greeklatin", it => it.Id("Greek-Latin; NFD; [:NonspacingMark:] Remove; NFC")//
.Drection(IcuTransformDirection.Forward)) //
.IcuTransform("latingreek", lg => lg.Id("Greek-Latin; NFD; [:Nonspacing Mark:] Remove; NFC")
.Direction(IcuTransformDirection.Reverse))
.EdgeNGram("greekedge", ed => ed.MaxGram(50)
.MinGram(1)
.Side(EdgeNGramSide.Front))
.Stop("greekstop", sw => sw.StopWords())
.Lowercase("greeklowercase", gl => gl.Language(Language.Greek.ToString()))
.KeywordMarker("greekkeywords", gk => gk.Keywords("")).Stemmer("greekstemmer", gs => gs.Language(Language.Greek.ToString())))
.CharFilters(ma => ma.Mapping("hmap", map => map.Mappings("h=>η"))).Analyzers(a1 => a1.Custom("greek", t =>t.Tokenizer("standard").Filters("greekedge",/* "greekstop",*/ "greeklowercase", "greekkeywords",/*"greekstemmer",*/ "greeklatin", "latingreek")
.CharFilters("hmap")) .Custom("custom", cu => cu.Tokenizer("standard").Filters(/*"greekstop",*/ "greeklowercase", "greekkeywords",/* "greekstemmer" ,*/ "greeklatin", "latingreek")
.CharFilters("hmap"))))));
I don't know why but when you map fields in elastic it tends to lowercase their first character.For example when I used to add a field "SERVICE" it was automatically converted to "sERVICE", etc. So at least the very first character must be lowercased both on mapping and on fields declaration on our class.

find the original key of groupby in linq C#

var aaa = data.GroupBy(o => o.Date).Select(o => new { o }).ToList();
var bbb = aaa.Select(o => o.Key).ToList();
//There is a error of `Key`
Does that mean the Key is only allowed for the original List after GroupBy. Is it possible to obtain the Key for any Select after GroupBy?(Surely, we can storge the Key = o.Key in the Select )
Furthermore,
var aaa = data.GroupBy(o => o.Date).Select(o => o.ToList()}).ToList();
If we change aaa into two dimensional List, Is it possible to obtain the previous Key?
In your first linq expression, in the Select you are wrapping the IGrouping object that you got from the GroupBy with a new anonymous object.
So to get that Key property in your second line you should:
//Original:
var bbb = aaa.Select(o => o.Key).ToList();
//Change to:
var bbb = aaa.Select(o => o.o.Key).ToList();
For second question, if you want to get the Key in this case:
//Original:
var aaa = data.GroupBy(o => o.Date)
.Select(o => o.ToList()})
.ToList();
//Then you should:
var aaa = data.GroupBy(o => o.Date)
.Select(o => o.ToList()})
.Select(x => o.FirstOrDefault().Date)
.ToList();
Reason being is that:
You group your items by Date
First select you convert a IGrouping into a List<YourClass> but now you have an IEnumerable<List<YourClass>> where each record in the IEnumerable, all the inner items will have the same date
In second Select - take whichever item in the inner collections - and get the Date it is the same as getting the Key in the example before
To achieve what you actually what to get (grouping by the date and getting for each group the symbols):
var result = data.GroupBy(item => item.Date)
.Select(group => new { group.Key, Symbols = group.Select(item => item.Symbol).ToList() });
//Or using a different overload of the `GroupBy`:
var result = data.GroupBy(item => item.Date,
(key,group) => return new { Key = key, Symbols = group.Select(item => item.Symbol).ToList() });
Problem is your incorrect syntax. You crated an anonymous object, so you need to access the property with instance name. So you should be doing this.
var bbb = aaa.Select(o => o.o.Key).ToList();
if we change aaa into two dimensional List, Is it possible to obtain
the previous Key?
No, because you have groped values collection not the Key. So result will not contain Key.

Linq Join Multiple Criteria (Number not known beforehand)

I need to join multiple criteria inside a linq query, I have a criteria box like below :
Currently Im using a query than can only handle a single tag :
var c = *text after the t:*
var r = rs.Returns.Where(x => x.Lines.Any(y => y.Tags.Any(z => z.Name.Contains(c))));
I need something like (this may be incorrect) :
var r = rs.Returns.Where(x => x.Lines.Any(y => y.Tags.Any(z => z.Name.Contains(*1st Tag*)) && y.Tags.Any(z.Name.Contains(*2nd Tag*)))); .. etc
So that all the tags the Line has are searched and AND is applied. Is there an easy way of achieving such a thing?
Many thanks in advance.
var r = rs.Returns.Where(x => x.Lines.Any(y => searchTags.All(stag => y.Tags.Any(z => z.Name.Contains(stag)))));
searchTags should contain all tags to search for. No need to use a loop.
I think you are looking for something like this:
List<string> tags = new List<string>() { "tag1", "tag2" };
var query = rs.Returns.AsEnumerable();
foreach(string tag in tags)
{
string tmp = tag;
query = query.Where(x => x.Lines.Any(y => y.Tags.Any(z => z.Name.Contains(tmp))));
}

Categories

Resources