If I run the code below it will create a mapping on ALL indices, which I don't want. I am unable to find the documentation for specifying just the index I want.
How do I specify which index to apply this mapping to?
var client = new ElasticClient();
var response = client.Map<Company>(m => m
.Properties(props => props
.Number(n => n
.Name(p => p.ID)
.Type(NumberType.Integer)
)
)
);
Add .Index() to the put mapping descriptor
var response = client.Map<Company>(m => m
.Index("index-name")
.Properties(props => props
.Number(n => n
.Name(p => p.ID)
.Type(NumberType.Integer)
)
)
);
This puts a mapping into an existing index. If an index doesn't yet exist, you can create it and define a mapping for it in one request. For example
var createIndexResponse = client.CreateIndex("index-name", c => c
// settings for the index
.Settings(s => s
.NumberOfShards(3)
.NumberOfReplicas(1)
.RefreshInterval("5s")
)
// mappings for the index
.Mappings(m => m
.Map<Company>(mc => mc
.Properties(props => props
.Number(n => n
.Name(p => p.ID)
.Type(NumberType.Integer)
)
)
)
)
);
Something like this also will do.
string IndexName = "my_index";
this.client.CreateIndex(IndexName, c =>
c.AddMapping<CForm>
(m => m.Properties(ps => ps.Attachment
(a => a.Name(o => o.Document)
.TitleField(t => t.Name(x => x.Name)
.TermVector(TermVectorOption.WithPositionsOffsets))))));
// Create Mappings for the fields with specific properties.
// You can also make field1 a multi-field and make it both analyzed and not_analyzed
// to get the best of both worlds (i.e. text matching on the analyzed field + aggregation on the exact value
// of the not_analyzed raw sub-field).
// Field: Plan
var result = this.client.Map<CForm>(m => m
.Properties(props => props
.MultiField(s => s
.Name(p => p.Plan)
.Fields(pprops => pprops
.String(ps => ps.Name(p => p.Plan).Index(FieldIndexOption.NotAnalyzed))
.String(ps => ps.Name("original").Index(FieldIndexOption.Analyzed))
)
)
)
);
Related
I'm trying to build a query that basically searches over all full text fields, boosting a few, but leaving all others at the default boost of 1.
When I don't include any fields, everything has a boost of 1 (we're on version 6.4.2 which supports default when no fields are specified):
var results = await _ElasticClient.SearchAsync<dynamic>(s => s
.Query(q => q
.MultiMatch(m => m
.Query(request.Query)
)
)
);
However, as soon as I try to boost a single field, it removes the defaults on all the other fields, only searching on the explicit field:
var results = await _ElasticClient.SearchAsync<dynamic>(s => s
.Query(q => q
.MultiMatch(m => m
.Fields(f => f.Field("firstName^20"))
.Query(request.Query)
)
)
);
I tried adding a wildcard, but this still just matches on firstName (then again, the wildcard on its own doesn't match anything, so assuming I have the syntax wrong on that):
var results = await _ElasticClient.SearchAsync<dynamic>(s => s
.Query(q => q
.MultiMatch(m => m
.Fields(f => f.Field("*.*^1"))
.Fields(f => f.Field("firstName^20"))
.Query(request.Query)
)
)
);
I also tried Booling them together, but this also just matches on firstName:
var results = await _ElasticClient.SearchAsync<dynamic>(s => s
.Query(q => q
.Bool(b => b
.Should(m => m
.MultiMatch(mm => mm
.Query(request.Query)
)
)
.Should(m => m
.MultiMatch(mm => mm
.Fields(f => f.Field("firstName^20"))
.Query(request.Query)
)
)
)
)
);
I'm starting to think this isn't possible. For context, the reason I'm trying to do this is to be able to add other full text fields to the index without having to include every field in our queries, but still be able to boost certain fields.
Figured out my problem. I was chaining multiple .Fields() (plural) together, where I should only have a single .Fields() (plural) and then chain multiple .Field() (singular) together:
var results = await _ElasticClient.SearchAsync<dynamic>(s => s
.Query(q => q
.MultiMatch(m => m
.Fields(f => f
.Field("firstName^20")
.Field("*.*^1")
)
.Query(request.Query)
)
)
);
I am successfully creating Aggregations with my Elastic NEST query but I would like to pass some additional information in I have seen that I can use Meta for this, but I cannot find any documentation for doing it in NEST.
HERE is my nest Aggregations code
.Aggregations(aa => aa.Terms("groups", sp => sp.Field(p => p.ProductSpecification.Suffix("name"))
.Aggregations(aaa => aaa
.Terms("attribute", tt => tt.Field(ff => ff.ProductSpecification.Suffix("value"))))
Basically id like to be add say SpecificationId to the meta any hints?
You can add meta per aggregation, and it'll be returned on the aggregation response
var response = client.Search<User>(s => s
.Size(0)
.Query(q => +q
.Term(m => m
.Field(f => f.Badges.First().Name)
.Value("c#")
)
)
.Aggregations(a => a
.SignificantTerms("badges", st => st
.Field(f => f.Badges.First().Name)
.Meta(m => m
.Add("meta_1", "value_1")
.Add("meta_2", 2)
)
)
)
);
Ive now successfully got my c# Test win forms app working with aggregations and now I want to be able to filter on said aggregations if someone selects one (or more) of them.
Here is my query that is working exactly as I want and I am getting both price range buckets and buckets for each of the specification terms that it can find in the index.
.Aggregations(a => a
.Nested("specifications", n => n
.Path(p => p.ProductSpecification)
.Aggregations(aa => aa.Terms("groups", sp => sp.Field(p => p.ProductSpecification.Suffix("name"))
.Aggregations(aaa => aaa
.Terms("attribute", tt => tt.Field(ff => ff.ProductSpecification.Suffix("value"))))
)
)
)
.Range("price_range", ra => ra
.Field(p => p.Price)
.Ranges(
r => r.To(50),
r => r.From(50).To(100),
r => r.From(100).To(150),
r => r.From(150).To(200),
r => r.From(200).To(250),
r => r.From(250)
)
))
.Index("myindex")
.Type("product")
.Query(q => q
.MultiMatch(m => m
.Query(searchBox.Text + "*")
.Fields(ff => ff
.Field(f => f.Name, boost: nameBoost)
.Field(f => f.Description, boost: descriptionBoost)
.Field(f => f.ProductCode)))));
Can someone point me in the direction of how I can go about filtering down results based on selecting any of these buckets.
UPDATE (30/01/18) I have now added this to my query
&& q.Nested(n => n
.Path(p => p.ProductSpecification)
.Query(q2 => q2
.Terms(t => t
.Field(f => f.ProductSpecification.Suffix("name"))
.Terms("Guarantee",)
)).Query(q3 => q3
.Terms(t2 => t2
.Field(f2 => f2.ProductSpecification.Suffix("value"))
.Terms("3 years","10 years")
)))
));
This is enabling me to pass multiple values into one Spec filter, but what im not sure on how to achieve is how to filter on multiple specs so the above filters on Guarantee with values of either 3 or 10 years
but if I also wanted to pass values of "Grey" and "Copper" to the spec of "Colour"
Adding "Colour" into My first set of terms and "Grey" and "Copper" into my second terms list breaks all filtering.
I think im close here just need a little direction
If I understand your question correctly, when the user selects a price range from the price range facets that are applicable to the query that is being executed, you'd like to apply this price range to the query?
The Post Filter serves this purpose, by applying a filter to the search results after aggregations have been calculated.
Assuming a user has selected the 50-100 price range, your query would like something like
var response = client.Search<Product>(s => s
.Aggregations(a => a
.Nested("specifications", n => n
.Path(p => p.ProductSpecification)
.Aggregations(aa => aa
.Terms("groups", sp => sp
.Field(p => p.ProductSpecification.Suffix("name"))
.Aggregations(aaa => aaa
.Terms("attribute", tt => tt
.Field(ff => ff.ProductSpecification.Suffix("value"))
)
)
)
)
)
.Range("price_range", ra => ra
.Field(p => p.Price)
.Ranges(
r => r.To(50),
r => r.From(50).To(100),
r => r.From(100).To(150),
r => r.From(150).To(200),
r => r.From(200).To(250),
r => r.From(250)
)
)
)
.Index("myindex")
.Type("product")
.Query(q => q
.MultiMatch(m => m
.Query(searchBox.Text + "*")
.Fields(ff => ff
.Field(f => f.Name, boost: nameBoost)
.Field(f => f.Description, boost: descriptionBoost)
.Field(f => f.ProductCode)
)
)
)
.PostFilter(pf => pf
.Range(r => r
.Field(f => f.Price)
.GreaterThanOrEquals(50)
.LessThan(100)
)
)
);
I am using ElasticClient 2.4.4.
I have one query
var sample = client.Search<TestMenuItem>(s => s
.Query(q =>
q.Term(p => p.RegionSegments, "s1")
&& q.Term(p => p.RegionSegments, "s2")
&& q.Term(p => p.RegionSegments, "s3")
));
than I wanted to count how many items are with RegionSegments = s1.
As I understand I should use Aggregations but cant find good example how solve this issue.Any ideas?
If you simply need a count of each of s1, s2 and s3 RegionSegment documents, then you can do a multi_search for this, specifying a count search_type for all
var multiSearchResponse = client.MultiSearch(ms => ms
// this is the default index for multi_search. Can override
// on each Search call if needed
.Index<TestMenuItem>()
// this is the default type for mulit_search. Can override
// on each search call if needed
.Type<TestMenuItem>()
// this is the default search_type. Can override on
// each search call if needed
.SearchType(SearchType.Count)
.Search<TestMenuItem>("s1", s => s
.Query(q => +q.Term(p => p.RegionSegments, "s1"))
)
.Search<TestMenuItem>("s2", s => s
.Query(q => +q.Term(p => p.RegionSegments, "s2"))
)
.Search<TestMenuItem>("s3", s => s
.Query(q => +q.Term(p => p.RegionSegments, "s3"))
)
);
This produces the following request
POST http://localhost:9200/testmenuitem-index/testmenuitem/_msearch?search_type=count
{}
{"query":{"bool":{"filter":[{"term":{"regionSegments":{"value":"s1"}}}]}}}
{}
{"query":{"bool":{"filter":[{"term":{"regionSegments":{"value":"s2"}}}]}}}
{}
{"query":{"bool":{"filter":[{"term":{"regionSegments":{"value":"s3"}}}]}}}
The to get the total for each search result is similar to the following
var s1Total = multiSearchResponse.GetResponse<TestMenuItem>("s1").Total;
Let´s say I have
class Product
{
string name;
List<Order> orders;
}
class Order
{
string name;
}
If I try to map analyzers to Product name it work, but not for Order.name
//This work and adds the analyzer on the mapping list.
var resp = client.Map<Product>(map => map
.Properties(props => props
.String(s =>s
.Name(p => p.Name)
.IndexAnalyzer("normalize")
)
));
//This does not.
var resp = client.Map<Product>(map => map
.Properties(props => props
.String(s =>s
.Name(p => p.orders.First().Name)
.IndexAnalyzer("normalize")
)
));
Am I doing something wrong, or is this a bug?
Some more info:
Those classes are just an example to show the problem.
If I add [ElasticProperty(Analyzer = "normalize")] on the variable it works.
Actually the setup look something like Product Inherists from BaseProdcuts and BaseProductis is the one who has the List
As answered here, .Name(p => p.Orders.First().Name) is telling ES to map the field 'Name' on the Product document. Instead, you want to map to the 'Name' field on Orders, which is an array in your Product document.
Try this instead:
client.Map<Product>(m => m
.Properties(pp => pp
// Map Product.Name
.String(s => s
.Name(p => p.Name)
.IndexAnalyzer("normalize")
)
// Map Product.Orders.Name
.Object<List<Order>>(o => o
.Name(p => p.Orders)
.Properties(op => op
.String(s => s
.Name(os => os.First().Name)
.IndexAnalyzer("normalize"))))
));