How to filter with a nested document based on multiple terms? - nest

I am trying to replicate this DSL query in NEST. Basically a structured filter that will return all of the products that have the color red.
{
"query": {
"bool": {
"filter": [
{
"nested": {
"path": "keywordFacets",
"query": {
"bool": {
"filter": [
{ "term": { "keywordFacets.name": "color" } },
{ "term": { "keywordFacets.value": "Red" } }
]
}
}
}
}
]
}
}
}
Here is the POCO with attribute mapping.
[ElasticsearchType]
public class Product
{
[Keyword]
public long ProductId { get; set; }
[Nested]
public List<KeywordFacet> KeywordFacets { get; set; }
// other properties...
}
[ElasticsearchType]
public class KeywordFacet
{
[Keyword]
public string Name { get; set; }
[Keyword]
public string Value { get; set; }
}
I can't figure out how to get the two terms inside the nested filter array. This is my failed attempt so far:
var searchRequest = new SearchDescriptor<Product>()
.Query(q => q
.Bool(b => b
.Filter(bf => bf
.Nested(nq => nq
.Path(nqp => nqp.KeywordFacets)
.Query(qq => qq
.Bool(bb => bb
.Filter(ff => ff
.Term(t => t
.Field(p => p.KeywordFacets.First().Name)
.Value("color")
.Field(p2 => p2.KeywordFacets.First().Value).Value("Red")))))))));
Here is some sample data that is returned when I run the DSL query in Postman:
{
"productId": 183150,
"keywordFacets": [
{
"name": "color",
"value": "Red"
},
{
"name": "color",
"value": "Blue"
},
{
"name": "color",
"value": "Grey"
}
]
}

Here's the proper syntax after fumbling around for a while.
var searchRequest = new SearchDescriptor<Product>()
.Query(q => q
.Bool(b => b
.Filter(bf => bf
.Nested(nq => nq
.Path(nqp => nqp.KeywordFacets)
.Query(qq => qq.Bool(bb => bb
.Filter(ff => ff
.Term(t => t
.Field(p => p.KeywordFacets[0].Name).Value("color"))))
&& qq.Bool(bb => bb
.Filter(ff => ff
.Term(t => t
.Field(p2 => p2.KeywordFacets[0].Value).Value("Red"))))
)
)
)
)
);

Related

GraphQL pagination partial response with error array

I have a query like below
query {
heroes {
node {
name
}
endCursor
}
}
I am trying to understand how GraphQL can handle the error handling and return partial response. I looked at https://github.com/graphql/dataloader/issues/169 and tried to create a resolver like below;
{
Query: {
heroes: async (_) => {
const heroesData = await loadHeroesFromDataWarehouse();
return {
endCursor: heroesData.endCursor;
node: heroesData.map(h => h.name === 'hulk' ? new ApolloError('Hulk is too powerful') : h)
}
}
}
}
I was hoping it would resolve something like below;
{
"errors": [
{
"message": "Hulk is too powerful",
"path": [
"heroes", "1"
],
}
],
"data": {
"heroes": [
{
"name": "spiderman"
},
null,
{
"name": "ironman"
}
]
}
}
but it is completely failing making the heroes itself null like below;
{
"errors": [
{
"message": "Hulk is too powerful",
"path": [
"heroes"
],
}
],
"data": {
"heroes": null
}
}
How can I make resolver to return me the desired partial response?
Found the solution, basically we need a resolver to resolve the edge model itself;
{
Query: {
heroes: (_) => loadHeroesFromDataWarehouse()
},
HeroesEdge {
node: async (hero) => hero.name === 'hulk' ? new ApolloError('Hulk is too powerful') : hero
}
}

IConfiguration.GetSection with Where selector

I'm using IConfiguration.GetSection to retrieve configuration information from a config file:
var loggingProviders = Config.GetSection( "Logging" ).Get<LoggingProviders>();
which works just fine, but I want to only retrieve entries that are enabled, so I want to do either of these:
var loggingProviders = Config.GetSection( "Logging" ).Get<LoggingProviders>().Where( x => x.Enabled == true );
var loggingProviders = Config.GetSection( "Logging" ).Where( x => x.Enabled == true ).Get<LoggingProviders>();
But I keep getting hitting a dead end, any advice would be appreciated!
If you want to use .Where,it needs to be a list,here is a demo:
public class LoggingProviders
{
public int Id { get; set; }
public bool Enabled { get; set; }
}
appsettings.json:
"Logging1": [
{
"Id": "1",
"Enabled": "true"
},
{
"Id": "2",
"Enabled": "true"
},
{
"Id": "3",
"Enabled": "false"
}
]
startup:
public IConfiguration Configuration { get; }
...
List<LoggingProviders> loggingProviders = Configuration.GetSection("Logging1").Get<List<LoggingProviders>>().Where(x => x.Enabled == true).ToList();
result:
If you don't get a list,and want to use .where,you can try to change it to list first.Here is a demo.
appsettings.json:
"Logging1":
{
"Id": "1",
"Enabled": "true"
},
startup:
public IConfiguration Configuration { get; }
...
List<LoggingProviders> l= new List<LoggingProviders>();
l.Add(Configuration.GetSection("Logging1").Get<LoggingProviders>());
List<LoggingProviders> loggingProviders = l.Where(x => x.Enabled == true).ToList();
result:

elasticsearch update by JOIN like SQL using logstash

Create two indices in elasticsearch parent and child
PUT parent/car/sedan
{
"type": "sedan",
"details": {
"wheels": 4,
"doors": 4,
"seats": 5,
"fuel": "gasoline"
}
}
PUT child/toyota/corolla
{
"color": "white",
"type": "sedan",
"details": {
"wheels": 4,
"doors": 4,
"seats": 5,
"fuel": "gasoline"
}
}
SQL UPDATE by JOIN (the corresponding SQL version that we'll perform on elasticsearch using logstash)
update CHILD.doors = PARENT.doors
from PARENT, CHILD
where PARENT.type = CHILD.type
ELASTICSEARCH UPDATE by JOIN (execute logstash with the logstash.conf as mentioned below)
input {
elasticsearch {
docinfo => true
hosts => ["127.0.0.1:9200"]
user => "admin"
password => "pass"
index => "child"
query => '{ "query": { "match": { "type": "sedan" } } }'
}
}
filter {
mutate {
remove_field => ["message","#version","#timestamp"]
}
elasticsearch {
hosts => ["127.0.0.1:9200"]
user => "admin"
password => "pass"
index => "parent"
query => "type:sedan"
fields => { "details.doors" => "parent_doors"
"details.seats" => "parent_seats"
"type" => "parent_type"
}
}
prune {
whitelist_names => ["color","type", "details","parent_doors","parent_seats","parent_type"]
}
}
output {
stdout {
codec => rubydebug
}
elasticsearch {
hosts => ["127.0.0.1:9200"]
user => "admin"
password => "pass"
index => "%{[#metadata][_index]}"
document_type => "%{[#metadata][_type]}"
document_id => "%{[#metadata][_id]}"
action => "update"
doc_as_upsert => true
script_lang => "painless"
script => "if ( ctx._source.type == '%{parent_type}' ) { ctx._source.details.doors = %{parent_doors} }"
}
}
This Works. If you have a better way of achieving the same, please do let us know.

Nest serialize elasticsearch Terms Query request

I'm using NEST 2.0.2 to query ElasticSearch.
Really great API, thanks for the effort, but needs documentation update I think.
Anyways,
I want to serialize my request. I could not find any info, there are some stackoverflow questions but it's about older versions, and api changed.
I want to write a "terms query". But could not succeed.
The working sense DSL is below.
GET myindex/mytype/_search?search_type=count
{
"query": {
"bool": {
"must": [
{
"term": {
"field1": {
"value": 2
}
}
}
],
"must_not": [
{
"terms": {
"field2": [
16,
17,
18,
19
]
}
}
]
}
},
"aggs": {
"termsAggField2": {
"terms": {
"field": "field2",
"size": 20
},
"aggs": {
"sumAggField3": {
"sum": {
"field": "field3"
}
}
}
}
}
}
And the terms query code is below. DSL works in sense, but the query does not working. The "not in" does not filter the output.
List<QueryContainer> must_not = new List<QueryContainer>();
must_not.Add(Query<mytype>.Terms(trms => trms.Terms(new string[] { "16", "17", "18", "19" })));
var resultTermsSum = b1.ElasticClient.Search<mytype>(q=>q.SearchType(SearchType.Count)
.Query(q2 => q2.Bool(
b => b.MustNot(must_not.ToArray())
)
)
.Aggregations(a => a.Terms("termsAggField2", terms => terms.Field("field2").Size(20)
.Aggregations(a2 => a2.Sum("sumAggField3", sum => sum.Field("field3"))))));
ie why I want to see the serialized request and see my problem.
thanks.
regards.
Edit: It's now working with the following update. It'd be great if I could serialize ;)
List<QueryContainer> must_not = new List<QueryContainer>();
short [] valueCollection = new short[] { 16, 19, 99, 100 };
must_not.Add(Query<mytpe>.Terms(trms => trms.Field("field2").Terms(valueCollection)));
var resultTermsSum = b1.ElasticClient.Search<mytype>(q=>q.SearchType(SearchType.Count)
.Query(q2 => q2.Bool(
b => b.MustNot(must_not.ToArray())
)
)
.Aggregations(a => a.Terms("termsAggField2", terms => terms.Field("field2").Size(20)
.Aggregations(a2 => a2.Sum("sumAggField3", sum => sum.Field("field3"))))));

How to pass an object to "Origin" method in NEST (FunctionScore query)?

I am trying to create a function_score elasticsearch query using NEST (gauss function), and have a geo point object to pass as the 'origin', however the "Origin" method in NEST accepts only a string, a result elasticsearch can't parse the query.
How can I write the query in NEST so Elasticsearch can parse it correctly?
var originLoc = JsonConvert.SerializeObject(userLocation.GeoCenter);
var searchDesc = new SearchDescriptor<MyCustomType>().Query(q => q.FunctionScore(fs => fs.Functions(func => func.Gauss("geoCenter", gs => gs.Origin(originLoc).Offset("1km").Scale("500m").Decay(0.99)))));
NEST passes the code above to elasticsearch like this, which elasticsearch can't parse (origin is parsed as string).
"query": {
"function_score": {
"functions": [
{
"gauss": {
"geoCenter": {
"origin": "{\"lat\":29.745703,\"lon\":-95.740514}", //<-- string
"scale": "500m",
"offset": "1km",
"decay": 0.99
}
}
}
]
}
}
Below is the correct query that Elasticsearch can run (origin is parsed as geo point object)
"query": {
"function_score": {
"functions": [
{
"gauss": {
"geoCenter": {
"origin": { //<----- geo point serialized object
"lon": -95.740514,
"lat": 29.745703
},
"scale": "500m",
"offset": "1km",
"decay": 0.99
}
}
}
]
}
i have something like this in my code
.Query(f => f
.FunctionScore(fs => fs
.BoostMode(FunctionBoostMode.Sum)
.Functions(ff => ff
.Linear("location", d => d.Origin(origin).Scale("8km").Decay(0.33))
)
)
)
Where origin -> var origin = object.latitude + "," + object.longitude;