Nest NamedFilters requires promise - elasticsearch

I have simple elastic request:
var request4 =
_client.Search<T>(s => s
.Aggregations(aggs =>
aggs.Filters("FacetedSearch",
f => f
.NamedFilters(g =>
{
var namedFilters = new NamedFiltersContainer();
foreach (var facet in _facets)
{
namedFilters.Add(facet.Key,
Query<T>.Terms(p => new Nest.TermsQuery
{Field = facet.Key, Terms =
new[] {facet.Value}}));
}
return namedFilters;
})
However, compiler requires Nest.IPromise<Nest.INamedFiltersContainer> I haven't found any constructor which could construct a promise of a given type. Is there any way to cast the NamedFiltersContainer into a prmosie ?

You can use the parameters passed to the delegates to compose the named filters
var client = new ElasticClient();
var _facets = new Dictionary<string, string>
{
["foo"] = "bar",
["baz"] = "quux"
};
var request4 = client.Search<object>(s => s
.Aggregations(aggs =>
aggs.Filters("FacetedSearch", f => f
.NamedFilters(g =>
{
foreach (var facet in _facets)
{
g.Filter(facet.Key, Query<object>.Terms(p => p
.Field(facet.Key)
.Terms(new[] { facet.Value })
)
);
}
return g;
})
)
)
);
yields
{
"aggs": {
"FacetedSearch": {
"filters": {
"filters": {
"foo": {
"terms": {
"foo": ["bar"]
}
},
"baz": {
"terms": {
"baz": ["quux"]
}
}
}
}
}
}
}

Related

Is there a way to filter json data format field in strapi?

Hi Guys I'm trying to filter post with data json format field?
"categoryList": ["cat", "cat1"]
For anyone still looking for a solution, this is what I have done for a json type field called tags of a collection type called Articles.
I have two articles in the database with one article having the following values set:
title: "lorem ipsum 1",
tags: [
"test",
"rest"
]
The other article has the following values set:
title: "lorem ipsum 2",
tags: [
"test",
"graphql"
]
My graphql query looks like this:
query {
articlesByTag(limit: 2, where: {tags_include: ["test", "rest"]}, start: 0, sort: "title:asc") {
title,
tags
}
}
While my rest query looks like this:
http://localhost:1337/articlesByTag?limit=2&tags_include[]=test&tags_include[]=rest
This is my articles.js service file:
const { convertRestQueryParams, buildQuery } = require('strapi-utils');
const _ = require('lodash');
const { convertToParams, convertToQuery } = require('../../../node_modules/strapi-plugin-graphql/services/utils');
module.exports = {
async findByTag(ctx) {
let tags_include;
if (ctx.where && ctx.where.tags_include && ctx.where.tags_include.length > 0) {
tags_include = ctx.where.tags_include;
delete ctx.where.tags_include;
} else if (ctx.query && ctx.query.tags_include && ctx.query.tags_include.length > 0) {
tags_include = ctx.query.tags_include;
delete ctx.query.tags_include;
}
if (!Array.isArray(tags_include)) {
tags_include = [tags_include];
}
let filters = null;
if (ctx.query) {
filters = convertRestQueryParams({
...convertToParams(ctx.query)
});
} else {
filters = convertRestQueryParams({
...convertToParams(_.pick(ctx, ['limit', 'start', 'sort'])),
...convertToQuery(ctx.where),
});
}
const entities = await strapi.query('articles').model.query(qb => {
buildQuery({ model: strapi.query('articles').model, filters: filters })(qb);
if (tags_include.length > 0) {
tags_include.forEach((tag) => {
if (tag && tag.length > 0) {
const likeStr = `%"${tag}"%`;
qb.andWhere('tags', 'like', likeStr);
}
});
}
}).fetchAll();
return entities;
},
};
This is the entry needed in routes.js
{
"method": "GET",
"path": "/articlesByTag",
"handler": "articles.findByTag",
"config": {
"policies": []
}
}
This is the controller articles.js
const { sanitizeEntity } = require('strapi-utils');
module.exports = {
async findByTag(ctx) {
const entities = await strapi.services.articles.findByTag(ctx);
return entities.map(entity => sanitizeEntity(entity, { model: strapi.models.articles }));
},
};
And finally this is the schema.graphql.js
module.exports = {
query: `
articlesByTag(sort: String, limit: Int, start: Int, where: JSON): [Articles]
`,
resolver: {
Query: {
articlesByTag: {
description: 'Return articles filtered by tag',
resolverOf: 'application::articles.articles.findByTag',
resolver: async (obj, options, ctx) => {
return await strapi.api.articles.controllers.articles.findByTag(options);
},
},
},
},
};
There is not currently a way to filter the JSON fields yet as of beta.17.8 (latest)
Probably something like that?
strapi.query('cool_model').find({ categoryList: { $all: [ "cat" , "cat1" ] } })

How to merge the result of two observations (with different type)

Assume I have these two observations:
const thread = of({
thread: {
name: "Name",
author: null
}
})
const author = of({name:"Snoob"})
How can i get the merged result of these observations:
const threadWithAuthor = .....;
threadWithAuthor.subscribe(it=>console.log(it))
// {
// thread: {
// name: "Name",
// author: { name: "Snoob" }
// }
// }
Here's an example of how you can do it using combineLatest, pipe, and map:
var {of, combineLatest } = require('rxjs')
var { map } = require('rxjs/operators')
var mergeByAuthor = ([t, a]) => {
var x = Object.assign({}, t)
x.thread.author = a
return x
}
var thread = of({
thread: {
name: 'Name',
author: null
}
})
var author = of({name:'Snoob'})
var threadWithAuthor = combineLatest(thread, author).pipe(
map(mergeByAuthor)
)
threadWithAuthor.subscribe(x => console.log(JSON.stringify(x, null, 2)))
Output
{
"thread": {
"name": "Name",
"author": {
"name": "Snoob"
}
}
}

Aggregation by nested objects with filter in ElasticSearch 6

I have set of documents that represent property units in ElasticSearsh 6. Every property has nested array of weekly rates:
{
"name" : "Completely Awesome Cabin"
"rates" : [
{
"start": "2018-06-09T00:00:00",
"end": "2018-06-16T00:00:00",
"weeklyRate": 100.0,
},
{
"start": "2018-06-16T00:00:00",
"end": "2018-06-23T00:00:00",
"weeklyRate": 200.0,
}
...
]
...
}
I am performing some filtering by several options including dates. I need to add aggregations that would give me minimum and maximum weeklyRate between all units that passed the filters. It should be some kind of nested aggregation with filter, I suppose. How can I do this?
Here's a complete example running with NEST 6.1.0
private static void Main()
{
var index = "default";
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var connectionSettings = new ConnectionSettings(pool)
.DefaultIndex(index);
var client = new ElasticClient(connectionSettings);
if (client.IndexExists(index).Exists)
client.DeleteIndex(index);
client.CreateIndex(index, c => c
.Mappings(m => m
.Map<MyDocument>(mm => mm
.AutoMap()
.Properties(p => p
.Nested<Rate>(n => n
.AutoMap()
.Name(nn => nn.Rates)
)
)
)
)
);
client.Bulk(b => b
.IndexMany(new[] {
new MyDocument
{
Name = "doc 1",
Rates = new []
{
new Rate
{
Start = new DateTime(2018, 6, 9),
End = new DateTime(2018, 6, 16),
WeeklyRate = 100
},
new Rate
{
Start = new DateTime(2018, 6, 16),
End = new DateTime(2018, 6, 23),
WeeklyRate = 200
}
}
},
new MyDocument
{
Name = "doc 2",
Rates = new []
{
new Rate
{
Start = new DateTime(2018, 6, 9),
End = new DateTime(2018, 6, 16),
WeeklyRate = 120
},
new Rate
{
Start = new DateTime(2018, 6, 16),
End = new DateTime(2018, 6, 23),
WeeklyRate = 250
}
}
}
})
.Refresh(Refresh.WaitFor)
);
var searchResponse = client.Search<MyDocument>(s => s
// apply your filtering in .Query(...) e.g. applicable date range
.Query(q => q.MatchAll())
// don't return documents, just calculate aggregations
.Size(0)
.Aggregations(a => a
.Nested("nested_start_dates", n => n
.Path(f => f.Rates)
.Aggregations(aa => aa
.DateHistogram("start_dates", dh => dh
.Field(f => f.Rates.First().Start)
.Interval(DateInterval.Day)
.MinimumDocumentCount(1)
.Aggregations(aaa => aaa
.Min("min_rate", m => m
.Field(f => f.Rates.First().WeeklyRate)
)
.Max("max_rate", m => m
.Field(f => f.Rates.First().WeeklyRate)
)
)
)
)
)
)
);
var nested = searchResponse.Aggregations.Nested("nested_start_dates");
var startBuckets = nested.DateHistogram("start_dates").Buckets;
foreach(var start in startBuckets)
{
var min = start.Min("min_rate").Value;
var max = start.Max("max_rate").Value;
Console.WriteLine($"{start.KeyAsString} - min: {min}, max: {max}");
}
}
public class MyDocument
{
public string Name {get;set;}
public IEnumerable<Rate> Rates {get;set;}
}
public class Rate
{
public DateTime Start {get;set;}
public DateTime End {get;set;}
public double WeeklyRate {get;set;}
}
which prints the following to the console
2018-06-09T00:00:00.000Z - min: 100, max: 120
2018-06-16T00:00:00.000Z - min: 200, max: 250
You may also be interested in other metrics aggregations like Stats Agggregation
Just in addition to very usefull and helpfull answer from Russ Cam I want to post the final implementation, that does exactly what I needed. This is NEST aggregation:
.Aggregations(a => a
.Nested("budget_bound", n => n
.Path(p => p.Rates)
.Aggregations(aa => aa
.Filter("by_start_date", fl => fl
.Filter(fld => fld
.DateRange(dr => dr
.Field(f => f.Rates.First().Start)
.GreaterThanOrEquals(checkIn)
.LessThanOrEquals(checkOut)))
.Aggregations(md => md
.Min("min_budget", m => m
.Field(f => f.Rates.First().WeeklyRate))
.Max("max_budget", m => m
.Field(f => f.Rates.First().WeeklyRate))
)
)
)
)
Here is the corresponding ES query:
"aggs": {
"budget_bound": {
"nested": {
"path": "rates"
},
"aggs": {
"by_start_date": {
"filter": {
"range": {
"rates.start": {
"gte": "2018-06-29T00:00:00+07:00", // parameter values
"lte": "2018-07-06T00:00:00+07:00" // parameter values
}
}
},
"aggs": {
"min_budget": {
"min": {
"field": "rates.weeklyRate"
}
},
"max_budget": {
"max": {
"field": "rates.weeklyRate"
}
}
}
}
}
}}
The thing for me was to figure out how to nest aggregations to add filtering of nested collection before getting minimum and maximum aggregation.

NEST - How can i do multiple nested aggregation?

How can I do multiple nested aggregation?
I have tried something like this:
Aggregations(x => x
.Nested("Facets", y => y.Path("categories")
.Aggregations(r => r.Terms("categories", w => w.Field(q => q.Categories.FirstOrDefault().Id))
)).Nested("Facets2", s => s.Path("brand")
.Aggregations(e => e.Terms("brand", w => w.Field(q => q.Brand.Id))
)));
But it returns Facets2 as a child of Facets
Can anyone help?
The aggregations that you have work as expected with NEST client version 1.7.1 as demonstrated with this example
void Main()
{
var settings = new ConnectionSettings();
var connection = new InMemoryConnection(settings);
var client = new ElasticClient(connection : connection);
var response = client.Search<Example>(s => s
.Aggregations(aggs => aggs
.Nested("Facets", nested => nested
.Path(p => p.Categories)
.Aggregations(r => r
.Terms("categories", w => w
.Field(q => q.Categories.FirstOrDefault().Id)
)
)
)
.Nested("Facets2", nested => nested
.Path(p => p.Brand)
.Aggregations(e => e
.Terms("brand", w => w
.Field(q => q.Brand.Id)
)
)
)
)
);
Console.WriteLine(Encoding.UTF8.GetString(response.RequestInformation.Request));
}
public class Example
{
public IList<Category> Categories { get; set; }
public Brand Brand { get; set; }
}
public class Brand
{
public int Id { get; set; }
}
public class Category
{
public int Id { get; set; }
}
This outputs the following request query
{
"aggs": {
"Facets": {
"nested": {
"path": "categories"
},
"aggs": {
"categories": {
"terms": {
"field": "categories.id"
}
}
}
},
"Facets2": {
"nested": {
"path": "brand"
},
"aggs": {
"brand": {
"terms": {
"field": "brand.id"
}
}
}
}
}
}

NEST Returns Nothing on Fuzzy Search

I use the following query in Sense and I get some results back:
POST myindex/mytype/_search
{
"query": {
"fuzzy_like_this_field" : {
"BookLabel" : {
"like_text" : "myBook",
"max_query_terms" : 12
}
}
}
}
But with the following code using Nest I get nothing:
var docs = client.Search<dynamic>(b => b
.Index("myindex")
.Type("mytype")
.Query(q => q
.Fuzzy(fz => fz
.OnField("BookLabel")
.Value("myBook")
)
)
).Documents.ToList();
I can't see the difference between them. What am I missing?
You NEST query you have above produces the following query DSL
{
"query": {
"fuzzy": {
"BookLabel": {
"value": "myBook"
}
}
}
}
To get the nearest equivalent to a fuzzy_like_this_field query (which is deprecated in Elasticsearch 1.6.0 and will be removed in 2.0), you can run a fuzzy_like_this query only on the field you're interested in
void Main()
{
var settings = new ConnectionSettings(new Uri("http://localhost:9200"));
var connection = new InMemoryConnection(settings);
var client = new ElasticClient(connection: connection);
var docs = client.Search<dynamic>(b => b
.Index("myindex")
.Type("mytype")
.Query(q => q
.FuzzyLikeThis(fz => fz
.LikeText("myBook")
.MaxQueryTerms(12)
.OnFields(new [] { "BookLabel" })
)
)
);
Console.WriteLine(Encoding.UTF8.GetString(docs.RequestInformation.Request));
}
This outputs the following query DSL
{
"query": {
"flt": {
"fields": [
"BookLabel"
],
"like_text": "myBook",
"max_query_terms": 12
}
}
}
which should yield the same results as you see in Sense.

Resources