I'm a newbie for elasticsearch and we are evaluate elasticsearch for our webstore. One important feature is the usage of synonyms. Unfortunately I'm not able to create a index with synonyms. Please can anybody help me how I can use the synonyms feature. I didn't find any sample for this feature and elasticsearch 2.xx. The goal should be if I search for Hills the entry of Royal will be find.
I use the following code:
private ElasticClient GetClient()
{
var node = new Uri(ES_URI);
var uri = new Uri("http://localhost:9200");
var settings = new ConnectionSettings(uri).DefaultIndex("product");
var client = new ElasticClient(settings);
return client;
}
public void CreateSynonymIndex()
{
Product product = new Product()
{
Id = "2",
ProductName = "Royal",
Description = "Katzenfutter für Nierkranke"
};
var client = GetClient();
client.DeleteIndex("product");
var syn = new[] { "royal, hills => royal" };
ICreateIndexResponse respose = client.CreateIndex("product", c => c
.Mappings(mp => mp.Map<Product>(d => d.
Properties(p => p.String(n => n.Name(name => name.ProductName).Index(FieldIndexOption.Analyzed)))))
.Settings(s => s
.Analysis(an => an
.Tokenizers(at=>at.Pattern("synonymTokenizer",pa=>pa.Pattern("Test")))
.Analyzers(a=>a.Custom("synonymAnalyser",ca =>ca
.Tokenizer("synonymTokenizer")
.Filters(new List<string> { "synonym" })))
.TokenFilters(tf => tf
.Synonym("synonym", sy => sy.Synonyms(syn)
.Tokenizer("whitespace")
.IgnoreCase(true)))))
);
client.Index(product);
}
public void ES_Search()
{
var client = GetClient();
var response = client.Search<Product>(search => search
.Query(q => q.Bool(b => b
.Should(
// s => s.Match(m => m.Query("sometest").Field(f => f.ProductName).Boost(1.1)),
s => s.Match(m => m.Query("hills").Field(f => f.ProductName).Fuzziness(Fuzziness.EditDistance(1)))
))));
var response1 = client.Search<Product>(s => s.Query(q => q.Term(p => p.ProductName, "hills")));
}
Regards,
Dominik
You have created analyzer with synonyms, but you are not using it. You need to tell elasticsearch that ProductName field should use synonymAnalyser analyzer.
.Mappings(mp => mp.Map<Product>(d => d.
Properties(p => p.String(n => n
.Name(name => name.ProductName)
.Analyzer("synonymAnalyser")
.Index(FieldIndexOption.Analyzed)))))
I noticed few more things though:
remeber that document is not immediately available in elasticsearch after calling client.Index(..) method. It will take some miliseconds. Searching just right after indexing document, you may not find it. You can read more about it here
I don't know if you creat ElasticClient with default index, because you didn't share it. If not, you will have to specify it in your search calls e.g.
client.Search<Product>(s => s.Index("product")).
Hope that helps you.
Related
I have a requirement to get data based on combination of properties with OR and AND combination and i am not able to get this query done. Help me with what it should be like in json and also using NEST client.
SQL query:
Select *
From MySourceTable
Where (flag1 In ("Y","N")
And EntityStatus In ("STAT1", "STAT2")
And EntityType In ("T3"))
Or (flag1 In ("N") And EntityType In ("T1", "T2"))
So far my query builds all must queries which resulted in fewer or none results. So theoretically I believe each query is MUST queries and all must queries have to be merged using OR.
Help me build json
You are on a good way, the query will look something like
var searchResponse = await elasticClient.SearchAsync<EsDocument>(s => s
.Query(q =>
(
q.Terms(t => t.Field(f => f.Flag1.Suffix("keyword")).Terms("Y", "N")) &&
q.Terms(t => t.Field(f => f.EntityStatus.Suffix("keyword")).Terms("STAT1", "STAT2")) &&
q.Terms(t => t.Field(f => f.EntityType.Suffix("keyword")).Terms("T3"))
) ||
(
q.Terms(t => t.Field(f => f.Flag1.Suffix("keyword")).Terms("N")) &&
q.Terms(t => t.Field(f => f.EntityType.Suffix("keyword")).Terms("T1", "T2"))
)));
You didn't share your index mapping and query, so hard to say why your query didn't work.
UPDATE
You can compose such query dynamically using the following
var should = new QueryContainer();
should |=
(
new TermsQuery { Field = Infer.Field<EsDocument>(f => f.Flag1.Suffix("keyword")), Terms = new []{ "Y", "N" }} &&
new TermsQuery { Field = Infer.Field<EsDocument>(f => f.EntityStatus.Suffix("keyword")), Terms = new []{ "STAT1", "STAT2" }} &&
new TermsQuery { Field = Infer.Field<EsDocument>(f => f.EntityType.Suffix("keyword")), Terms = new []{ "T3" }}
);
should |=
(
new TermsQuery { Field = Infer.Field<EsDocument>(f => f.Flag1.Suffix("keyword")), Terms = new []{ "N" }} &&
new TermsQuery { Field = Infer.Field<EsDocument>(f => f.EntityType.Suffix("keyword")), Terms = new []{ "T1", "T2" }}
);
var searchResponse = await elasticClient.SearchAsync<EsDocument>(s => s
.Query(q => should));
I'm using NEST query to filter records from elastic.
The following query will filter records based on phrase and list of sourceIds. But I would like to exclude some documents from the result if their URL contains ideaArticles list.
var result = ElasticSearchClientConnection.Client.Search<T>(s => s
.Query(q => q.Match(p => p.Field(f => f.Body).Query(phrase))
&& q.Terms(p => p.Field(f => f.SourceId).Terms(sourceIds))
&& !q.Terms(p => p.Field(f => f.URL).Terms(ideaArticles))
).Take(take));
I resolved this issue with MatchPhrase. But since I had a list of phrases, I had to create a query on the fly.
var result =
ElasticSearchClientConnection.Client.Search<T>(s =>
s.Query(q => q.Match(p => p.Field(f => f.Body).Query(phrase))
&& q.Terms(p => p.Field(f => f.SourceId).Terms(sourceIds))
&& BuildMatchPhraseQueryContainer(q, ideaArticles)).Take(take));
and this is the method to create the query on the fly
private QueryContainer BuildMatchPhraseQueryContainer(QueryContainerDescriptor<T> qd, List<string> phrases)
{
QueryContainer queryContainer = new QueryContainer();
foreach (var phrase in phrases)
{
queryContainer &= !qd.MatchPhrase(m => m.Field(f => f.URL).Query(phrase));
}
return queryContainer;
}
This was my code in the earlier version of ES it used to work. After moving to ES 5.5. It has stopped working and it gives a compiler error.
Error: 'QueryStringQueryDescriptor' does not contain a definition for 'OnFields' and no extension method 'OnFields' accepting a first argument of type 'QueryStringQueryDescriptor'
Below is my code snippet...
public List<EmployeeInfo> SearchText2(string query, List<string> sendersList, int page = 0, int pageSize = 50)
{
try
{
var result = this.client.Search<EmployeeInfo>(s => s
.From(page * pageSize)
.Size(int.MaxValue)
.Query(q => q
.QueryString(qs => qs.Query(query).UseDisMax()
.OnFields(b => b.Subject)
.OnFields(b => b.Body)
))
.SortDescending(f => f.ReceivedTime)
.Filter(f => f.Terms(ak => ak.SenderName, sendersList))
);
...
// Some code here
}
Any tips on how to make this work will be great.
In latest version of Nest library there are some API changes
Instead of OnFields in QueryString you should use Fields
QueryString(qs => qs.Query(string.Empty).UseDisMax()
.Fields(descriptor => descriptor.Fields(b => b.Subject, b => b.Body))
))
Instead of SortDescending you should use Sort
.Sort(descriptor => descriptor.Field(f => f.ReceivedTime, SortOrder.Descending))
Also the filters are not available in elasticsearch starting from version 5 and you should use bool query with filter
Query(descriptor =>
descriptor.Bool(boolQuery =>
boolQuery
.Must(query => query.MatchAll())
.Filter(f => f.Terms(ak => ak.SenderName, sendersList)
)
)
)
List<branch_rating> ObjRat = new List<branch_rating>();
ObjRat=(_Context.branch_rating
.GroupBy(a => a.restaurant_branch_id)
.Select(a => new { rating = a.Sum(b => b.rating), Name = a.Key })
.OrderByDescending(a => a.rating)).ToList();
I tried by putting query in var variable and then adding in object.
Below picture shows the error.
I want to get data from branch_rating table having four columns, I also tried by using var and then adding list data one by one in object.
It will be better if you make a custom class like DOTopResturant which includes data members branch_rating,branch_id and then make a object of type DOTopResturant , add every item in variable q into that object.
List<branch_rating> ObjRat = new List<branch_rating>();
ObjRat = _Context.branch_rating.ToList();
var q = (_Context.branch_rating
.GroupBy(a => a.restaurant_branch_id)
.Select(a => new { branch_rating = a.Sum(b => b.rating), branch_id = a.Key })
.OrderByDescending(a => a.branch_rating)).ToList();
List<DOTopResturant> ObjTopRes = new List<DOTopResturant>();
foreach (var item in q)
{
ObjTopRes.Add(new DOTopResturant() { branch_id = Convert.ToInt32(item.branch_id), branch_rating = Convert.ToInt32(item.branch_rating) });
}
return ObjTopRes;
Just read and understand what the error describes :
your are trying to convert an anonymous type (with 2 properties Name and Rating) to another object type branch_rating.
You have 2 solutions :
work with the anonymous type (using var) :
var ObjRat=(_Context.branch_rating
.GroupBy(a => a.restaurant_branch_id)
.Select(a => new { Rating = a.Sum(b => b.rating), Name = a.Key })
.OrderByDescending(a => a.rating))
.ToList();
And then, in your code, just access to Rating and Name properties of your list members.
work with your type defined branch_rating, by using it in your Select, and use the appropriate constructor.
List<branch_rating> ObjRat = new List<branch_rating>();
ObjRat=(_Context.branch_rating
.GroupBy(a => a.restaurant_branch_id)
.Select(a => new branch_rating() { <Your branch_rating properties assignments here> })
.OrderByDescending(a => a.rating)).ToList();
If your branch_rating object does not have an empty constructor, call the correct constructor with good arguments in the Select.
Change select (third row in the picture) to:
.Select(a => new DOTopRestaurant {rating = a.Sum(b => b.rating), Name = a.Key})
Delete the declaration of ObjRat and use var:
var ObjRat=(_Context.branch_rating
.GroupBy(a => a.restaurant_branch_id)
.Select(a => new { rating = a.Sum(b => b.rating), Name = a.Key })
.OrderByDescending(a => a.rating))
.ToList();
Your list is a list of the anonymous type not of type branch_rating.
If you want to convert it to branch_rating use another Select to create a new branch_rating(...) passing the appropriate parameters to the constructor.
PS You should also look at .NET naming conventions.
In the code below I'd like to do a sum for OBFY and CFY separately within each grouping in deptGroup. Anyone have any ideas, I've tried a coupla things but can't get it. Thanks!!
var deptGroup = prodCostsTotals.AsQueryable()
.Select(r => dt.Rows.Add(new object[]
{
Convert.ToInt32(r.Field<string>("Proc")),
r.Field<string>("UnitL"),
r.Field<Decimal?>("OBFY"),
r.Field<Decimal?>("CFY")
})).GroupBy(g => g.Field<string>("Proc"));
It seems to me you are overusing DataRows. If you had your data in a nice class, you could write it like this:
data.GroupBy(x => x.Proc)
.Select(g => new
{
Proc = g.Key,
OBFYSum = g.Sum(x => x.OBFY),
CFYSum = g.Sum(x => x.CFY)
})
But if you really want to start with a DataTable and add the intermediate result to another DataTable, you could do it like this:
prodCostsTotals.AsQueryable()
.Select(r => dt.Rows.Add(new object[]
{
Convert.ToInt32(r.Field<string>("Proc")),
r.Field<string>("UnitL"),
r.Field<Decimal?>("OBFY"),
r.Field<Decimal?>("CFY")
}))
.GroupBy(g => g.Field<string>("Proc"))
.Select(g => new
{
Proc = g.Key,
OBFYSum = g.Sum(x => x.Field<Decimal?>("OBFY")),
CFYSum = g.Sum(x => x.Field<Decimal?>("CFY"))
})
Note that I have no idea whether your use of AsQueryable() will have any effect on the first part of this query, but it certainly won't have any effect on the grouping.