I have implemented a Search Query through NEST client and was able to get the records. The code is as follows.
var response = clientProvider.Client.Search<ProjectModel>(s => s
.Index("project_index")
.Type("projects")
.Source(so => so.Excludes(f => f.Field(x => x.FileInfo.FileBase64Data)))
.Size(100)
.Query(q => q
.Bool(b => b
.Should(
m => m.QueryString(qs => qs
.Query(searchOptions.SearchTerm)
.Fields(ff => ff.Fields(fields))
.Fuzziness(Fuzziness.Auto)
),
m => m.MultiMatch(qs => qs
.Query(searchOptions.SearchTerm)
.Type(Nest.TextQueryType.PhrasePrefix)
.Fields(ff => ff.Fields(fields))
)
)
)
)
.Sort(ss => ss.Descending(SortSpecialField.Score))
);
And I am mapping the response to my Project Model as follows.
var project = response.Hits.Select(h =>
{
h.Source._id = h.Id;
h.Source.Score = h.Score;
return h.Source;
}).ToList();
When I am trying to implement the same Search in Async way that is
var response = clientProvider.Client.SearchAsync<ProjectModel>(s => s
.Index("project_index")
.Type("projects")
.Source(so => so.Excludes(f => f.Field(x => x.FileInfo.FileBase64Data)))
.Size(100)
.Query(q => q
.Bool(b => b
.Should(
m => m.QueryString(qs => qs
.Query(searchOptions.SearchTerm)
.Fields(ff => ff.Fields(fields))
.Fuzziness(Fuzziness.Auto)
),
m => m.MultiMatch(qs => qs
.Query(searchOptions.SearchTerm)
.Type(Nest.TextQueryType.PhrasePrefix)
.Fields(ff => ff.Fields(fields))
)
)
)
)
.Sort(ss => ss.Descending(SortSpecialField.Score))
);
I am not getting any errors while executing it. But I am not able to get the response.Hits objects to map it back to my original Project Model.
Thanks In advance
In SearchAsync<T>(), response is Task<ISearchResponse<T>>, so you probably want to await it
Map all documents, found in products index, onto ProductDto type:
var result = await _elasticClient.SearchAsync<ProductDto>(x => x.Index("products").MatchAll());
var documents = result.Documents;
Related
Does anyone know the syntax for using MultiSearch using the NEST library version 7.6.
We have tried:
var result = client.MultiSearch(ms => ms
.Search<ElasticsearchProject>("projects", s => s.MatchAll())
.Search<Person>("people", s => s.MatchAll())
);
It seems this is not valid anymore in version 7.6
MultiSearch expects an Indices as the first parameter, although it is an optional parameter. To pass just the delegate, label the parameter
var result = client.MultiSearch(selector: ms => ms
.Search<ElasticsearchProject>("projects", s => s.MatchAll())
.Search<Person>("people", s => s.MatchAll())
);
var d = new MultiSearchDescriptor();
d.Search<ElasticsearchProject>("projects", s => s
.Index("<indexname>")
.Query(q => q
.MatchAll()
)
.From(1)
.Size(10)
);
d.Search<Person>("people", s => s
.Index("<indexname>")
.Query(q => q
.MatchAll()
)
.From(1)
.Size(10)
);
var re = _elasticClient.MultiSearch(d);
Here is the query I use to search:
var response = await client.SearchAsync<MenuForElasticSearch>(searchDescriptor => searchDescriptor
.Query(queryContainerDescriptor => queryContainerDescriptor
.Bool(queryDescriptor => queryDescriptor
.Should(queryStringQuery => queryStringQuery.Match(match => match.Field(fld => fld.DisplayName).Query(query)),
queryStringQuery => queryStringQuery.Wildcard(wildcard => wildcard.Field(flds => flds.DisplayName).Value($"*{query}*")),
queryStringQuery => queryStringQuery.Fuzzy(fuzzy => fuzzy.Field(flds => flds.DisplayName).Value(query)))
)));
There are three documents with displayName = NPW-711, NPW-677 and NPW-777. When I search NPW-711 it returns all three documents.
Can adding DefaultOperator(Elasticsearch.Net.DefaultOperator.And) help? If yes, where it fits?
Match query with AND operator will give you what you are looking for
var results = await client.SearchAsync<Document>(s => s
.Query(q => q
.Match(m => m
.Field("name")
.Query(query)
.Operator(Operator.And))));
output:
Results for query "NPW-777": NPW-777
Results for query "NPW": NPW-711,NPW-677,NPW-777
Results for query "677": NPW-677
Hope that helps.
Good day:
I"m trying to accomplish loading parent and children in one go. My query is this:
ISearchResponse<Models.Facilities.Facility> response = await this._elasticClient.SearchAsync<Models.Facilities.Facility>(s =>
s.Query(q => (q.GeoDistance(g => g.Boost(1.1)
.Field(f => f.BusinessLocation)
.DistanceType(GeoDistanceType.Arc)
.Distance(search.location.distance.ToString() + "m")
.Location((double)search.location.latitude, (double)search.location.longitude)
)
&&
q.HasChild<Models.Facilities.FacilityType>(c => c.Query(qq => qq.MatchAll()).Name("FacilityType")))
||
q.GeoDistance(g => g.Boost(1.1)
.Field(f => f.ServiceAreas)
.DistanceType(GeoDistanceType.Arc)
.Distance(search.location.distance.ToString() + "m")
.Location((double)search.location.latitude, (double)search.location.longitude))
)
.Aggregations(a =>
a.Children<Models.Facilities.FacilityType>("Capacity", child => child.Aggregations(ca => ca.Histogram("Capacity",
h => h.Field(p => p.Capacity)
.Interval(10)
.Missing(0))))
.Histogram("Distance", d => d.Field(f => f.BusinessLocation)
.Interval(10)
.Order(HistogramOrder.CountDescending)))
.Sort(g => g.GeoDistance(g => g.Field(f => f.BusinessLocation)
.DistanceType(GeoDistanceType.Arc)
.Order(SortOrder.Descending)
.Unit(DistanceUnit.Miles)).GeoDistance(g => g.Field(f => f.ServiceAreas)
.DistanceType(GeoDistanceType.Arc)
.Order(SortOrder.Descending))));
Facility and FacilityType extends from a parent Document. I'm trying to load the parent Facility along with its children FacilityType. Essentially if I could achieve an object like this facilityObj.facilityTypes. Let me know if this is possible.
InnerHits works for both Nested and Parent/Child relationships.
Lets say i have an index that contains documents that represent a Message in a discussion.
that document owns a discussionId property.
(it also has its own ID "that represent MessageId")
now, i need to find all discussionIds that have no documents (messages) that match a query.
for example:
"Find all discussionIds , that have no message that contains the text 'YO YO'"
how can i do that?
the class is similar to this:
public class Message
{
public string Id{get;set}
public string DiscussionId {get;set}
public string Text{get;set}
}
You just need to wrap the query that would find matches for the phrase "YO YO" in a bool query must_not clause.
With NEST
client.Search<Message>(s => s
.Query(q => q
.Bool(b => b
.MustNot(mn => mn
.MatchPhrase(m => m
.Field(f => f.Text)
.Query("YO YO")
)
)
)
)
);
which, with operator overloading, can be shortened to
client.Search<Message>(s => s
.Query(q => !q
.MatchPhrase(m => m
.Field(f => f.Text)
.Query("YO YO")
)
)
);
Both produce the query
{
"query": {
"bool": {
"must_not": [
{
"match": {
"text": {
"type": "phrase",
"query": "YO YO"
}
}
}
]
}
}
}
To only return DiscussionId values, you can use source filtering
client.Search<Message>(s => s
.Source(sf => sf
.Includes(f => f
.Field(ff => ff.DiscussionId)
)
)
.Query(q => !q
.MatchPhrase(m => m
.Field(f => f.Text)
.Query("YO YO")
)
)
);
And, if you want to get them all, you can use the scroll API
var searchResponse = client.Search<Message>(s => s
.Scroll("1m")
.Source(sf => sf
.Includes(f => f
.Field(ff => ff.DiscussionId)
)
)
.Query(q => !q
.MatchPhrase(m => m
.Field(f => f.Text)
.Query("YO YO")
)
)
);
// fetch the next batch of documents, using the scroll id returned from
// the previous call. Do this in a loop until no more docs are returned.
searchResponse = client.Scroll<Message>("1m", searchResponse.ScrollId);
In an older version of Nest I was able to do this:
var newIndexRequest = client.CreateIndex(ci => ci.Index(IndexName.ToLower())
.Analysis(a => a
.Analyzers(an => an
.Add("search_ngram_analyzer", customAnalyzer))
.Tokenizers(t => t
.Add("search_ngram_tokenizer", new NGramTokenizer
{
MaxGram = 500, // qty of chars to separate into tokens
MinGram = 2
})))
.AddMapping<Models.CustomerSearchResult>(m => m
.MapFromAttributes()
.Properties(p => p
.String(s => s.Name("short_name").CopyTo("search").Index(FieldIndexOption.NotAnalyzed))
.String(s => s.Name("legacy_name").CopyTo("search").Index(FieldIndexOption.NotAnalyzed))
.String(s => s.Name("legacy_contact_name").CopyTo("search").Index(FieldIndexOption.NotAnalyzed))
.String(s => s.Name("search").Index(FieldIndexOption.Analyzed).Analyzer("search_ngram_analyzer"))
)
));
Since the documentation hasn't been updated (or I haven't been able to find it), I can't figure out how to do the same thing in Nest 2.x.)
.. I know I need to use AutoMap instead of MapFromAttributes, but don't know how to properly set the Analyzer, Tokenizer, and CopyTo function.
This is all I have so far:
var customerSearchIdxDesc = new CreateIndexDescriptor(Constants.ElasticSearch.CustomerSearchIndexName)
.Mappings(ms => ms.Map<SO.Services.Data.ServiceModel.DtoTypes.Customer.SearchResult>(m => m.AutoMap()));
var response = client.CreateIndex(customerSearchIdxDesc);
Update
Getting closer I think :/
var customerSearchIdxDesc = new CreateIndexDescriptor(Constants.ElasticSearch.CustomerSearchIndexName)
.Settings((f) =>
{
return f.Analysis(analysis => analysis
.Analyzers(analyzers => analyzers
.Custom("search_ngram_analyzer", a => a
.Filters("lowercase")
.Tokenizer("search_ngram_tokenizer")))
.Tokenizers(tokenizers => tokenizers
.NGram("search_ngram_tokenizer", t => t
.MinGram(2)
.MaxGram(500)
.TokenChars(TokenChar.Digit, TokenChar.Letter, TokenChar.Punctuation, TokenChar.Punctuation, TokenChar.Symbol))));
})
.Mappings(ms => ms.Map<SO.Services.Data.ServiceModel.DtoTypes.Customer.SearchResult>(m => m.AutoMap()));
This is my solution
var customerSearchIdxDesc = new CreateIndexDescriptor(Constants.ElasticSearch.CustomerSearchIndexName)
.Settings(f =>
f.Analysis(analysis => analysis
.Analyzers(analyzers => analyzers
.Custom(Constants.ElasticSearch.AnalyzerNames.LowercaseNGram, a => a
.Filters("lowercase")
.Tokenizer(Constants.ElasticSearch.TokenizerNames.NoWhitespaceNGram)))
.Tokenizers(tokenizers => tokenizers
.NGram(Constants.ElasticSearch.TokenizerNames.NoWhitespaceNGram, t => t
.MinGram(2)
.MaxGram(500)
.TokenChars(TokenChar.Digit, TokenChar.Letter, TokenChar.Punctuation, TokenChar.Punctuation, TokenChar.Symbol)
)
)
)
)
.Mappings(ms => ms.Map<ServiceModel.DtoTypes.Customer.SearchResult>(m => m
.AutoMap()
.Properties(p => p
.String(n => n.Name(c => c.CustomerName).CopyTo(f =>
{
return new FieldsDescriptor<string>().Field("search");
}).Index(FieldIndexOption.Analyzed).Analyzer(Constants.ElasticSearch.AnalyzerNames.LowercaseNGram))
.String(n => n.Name(c => c.ContactName).CopyTo(fs => fs.Field(Constants.ElasticSearch.CombinedSearchFieldName)).Index(FieldIndexOption.Analyzed).Analyzer(Constants.ElasticSearch.AnalyzerNames.LowercaseNGram))
.String(n => n.Name(c => c.City).CopyTo(fs => fs.Field(Constants.ElasticSearch.CombinedSearchFieldName)).Index(FieldIndexOption.Analyzed).Analyzer(Constants.ElasticSearch.AnalyzerNames.LowercaseNGram))
.String(n => n.Name(c => c.StateAbbreviation).CopyTo(fs => fs.Field(Constants.ElasticSearch.CombinedSearchFieldName)).Index(FieldIndexOption.Analyzed).Analyzer(Constants.ElasticSearch.AnalyzerNames.LowercaseNGram))
.String(n => n.Name(c => c.Country).CopyTo(fs => fs.Field(Constants.ElasticSearch.CombinedSearchFieldName)).Index(FieldIndexOption.Analyzed).Analyzer(Constants.ElasticSearch.AnalyzerNames.LowercaseNGram))
.String(n => n.Name(c => c.PostalCode).CopyTo(fs => fs.Field(Constants.ElasticSearch.CombinedSearchFieldName)).Index(FieldIndexOption.Analyzed).Analyzer(Constants.ElasticSearch.AnalyzerNames.LowercaseNGram))
.String(n => n.Name(Constants.ElasticSearch.CombinedSearchFieldName).Index(FieldIndexOption.Analyzed).Analyzer(Constants.ElasticSearch.AnalyzerNames.LowercaseNGram))
)
)
);
var response = client.CreateIndex(customerSearchIdxDesc);