I have been searching here and I didn't find anything similar... However I apologize in advanced if it have escaped me, and I hope you can help out finding the correct direction.
I was looking for a way to implement the following in NEST C#:
"aggs": {
"sys_created_on_max": {
"max": {
"field": "sys_created_on"
}
},
"sys_created_on_min":{
"min": {
"field": "sys_created_on"
}
},
"sys_updated_on_max": {
"max": {
"field": "sys_updated_on"
}
},
"sys_updated_on_min":{
"min": {
"field": "sys_updated_on"
}
}
}
Meaning that I want to perform, in the same statement:
Max and Min aggregated value for "sys_created_on" field
and also
Max and Min aggregated value for "sys_updated_on" field
Thanks!
What you want is Stats Aggregation.
Here is an example input/output
INPUT
GET devdev/redemption/_search
{
"size": 0,
"aggs": {
"a1": {
"stats": {
"field": "reporting.campaign.endDate"
}
}
}
}
Result
{
"took": 97,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 146,
"max_score": 0,
"hits": []
},
"aggregations": {
"a1": {
"count": 11,
"min": 1443675599999,
"max": 1446353999999,
"avg": 1445607818180.818,
"sum": 15901685999989,
"min_as_string": "1443675599999",
"max_as_string": "1446353999999",
"avg_as_string": "1445607818180",
"sum_as_string": "15901685999989"
}
}
}
I've figured it out. In case of someone have the same doubt:
1) create a AggregationContainerDescriptor:
Func<AggregationContainerDescriptor<dynamic>, IAggregationContainer> aggregationsSelector = null;
2) Fill it up:
foreach (var field in requestList)
{
aggregationsSelector += ms => ms.Max(field.MaxAggregationAlias, mx => mx.Field(field.Name))
.Min(field.MinAggregationAlias, mx => mx.Field(field.Name));
}
3) Query it:
var esResponse = _esClient.Raw.Search<dynamic>(indexName.ToLower(), new PostData<dynamic>(jsonStr), null);
Cheers!
Related
i am new on MongoDB and i am trying to use it in C# context. Let´s say, i have documents like this:
[
{
"Number": "2140007529",
"Name": "ABC",
"IsInactive": true,
"EntryList": [
{
"Timestamp": "2022-06-01T14:00:00.000+00:00",
"Value": 21564.0
},
{
"Timestamp": "2022-07-01T21:31:00.000+00:00",
"Value": 21568.0
},
{
"Timestamp": "2022-08-02T21:21:00.000+00:00",
"Value": 21581.642
},
{
"Timestamp": "2022-09-02T15:42:00.000+00:00",
"Value": 21593.551
},
{
"Timestamp": "2022-09-26T13:00:00.000+00:00",
"Value": 21603
}
]
},
{
"Number": "2220000784",
"Name": "XYZ",
"IsInactive": false,
"EntryList": [
{
"Timestamp": "2022-09-26T13:00:00.000+00:00",
"Value": 0.0
},
{
"Timestamp": "2022-10-01T08:49:00.000+00:00",
"Value": 5.274
},
{
"Timestamp": "2022-11-01T09:56:00.000+00:00",
"Value": 76.753
},
{
"Timestamp": "2022-12-01T19:43:00.000+00:00",
"Value": 244.877
},
{
"Timestamp": "2023-01-01T11:54:00.000+00:00",
"Value": 528.56
},
{
"Timestamp": "2023-02-01T17:21:00.000+00:00",
"Value": 802.264
}
]
}
]
I want to get the document where the IsInactive flag is false. But for the EntryList there should be returned entries greater than Timestamp 2022-12-31 only.I should look like this:
{
"Number": "2220000784",
"Name": "XYZ",
"IsInactive": false,
"EntryList": [
{
"Timestamp": "2023-01-01T11:54:00.000+00:00",
"Value": 528.56
},
{
"Timestamp": "2023-02-01T17:21:00.000+00:00",
"Value": 802.264
}
]
}
So, here is my question. How can i filter nested arrays in return value with C#. Thanks for help!
I tried to get the result with aggregate function of MongoDB in MongoDB Compass. I got it work with but not in C#.
I think you are looking for a query similar to this one.
So you can try something like this code:
var desiredTimestamp = new DateTime(2022, 12, 31);
var results = collection.AsQueryable()
.Where(x => x.IsInactive == false && x.EntryList.Any(e => e.Timestamp >= desiredTimestamp))
.Select(obj => new
{
Number = obj.Number,
Name = obj.Name,
IsInactive = obj.IsInactive,
EntryList = obj.EntryList
.Where(e => e.Timestamp >= desiredTimestamp)
.ToList()
}).ToList()
Note that I'm assuming your Timestamp is a date type, otherwise you can't compare date and string.
I have a json file like this:
[
{
"key1": {
"find": 5,
"count": 65,
"name": "Parser"
},
"init": {
"key2": {
"find": 5,
"count": 15,
"name": "Some"
},
"arr": [
{
"key2": {
"find": 8,
"count": 32,
"name": "Object"
},
"temp": {
"pay": null
}
}
]
}
},
{
"key3": {
"find": 5,
"count": 23,
"name": "String"
},
"classes": [],
}
]
And I want to get list of all nodes that contains key "find" and value "5". The result have to be:
{
"find": 5,
"count": 65,
"name": "Parser"
},
{
"find": 5,
"count": 15,
"name": "Some"
},
{
"find": 5,
"count": 23,
"name": "String"
}
The difficulty is that the nesting can be any, but I need to get only those nodes that contain key "find" and the value "5" for it. How can I go through the entire file and get the nodes I need?
You can use JToken for this purpose, use the below function to find the nodes.
public void FindNodes(JToken json, string name, string value, List<JToken> nodes)
{
if (json.Type == JTokenType.Object)
{
foreach (JProperty child in json.Children<JProperty>())
{
if (child.Name == name && child.Value.ToString() == value)
{
nodes.Add(child);
}
FindNodes(child.Value, name, value, nodes);
}
}
else if (json.Type == JTokenType.Array)
{
foreach (JToken child in json.Children())
{
FindNodes(child, name, value, nodes);
}
}
}
Use the method in this way,
var json = JsonConvert.DeserializeObject<JToken>(jsonString);
var nodes = new List<JToken>();
FindNodes(json, "find", "5", nodes);
I'm facing a challenge while aggregating data by the "Week of Year" number.
The use case is:
The first day of the week is set as Sunday on my PC.
Create a data range:
Start='2018-06-24 Sunday' and End='2018-06-30 Saturday'
Expected: I can only see one week aggregated
Actual: I can see two weeks aggregated
The Elastic Query:
{
"size": 0,
"aggs": {
"groupby": {
"terms": {
"script": {
"source": "ZonedDateTime.ofInstant(Instant.ofEpochMilli(doc['CLOSED_DATE'].value.millis), ZoneId.of('UTC')).get(IsoFields.WEEK_OF_WEEK_BASED_YEAR)"
},
"size": 100
}
}
},
"query": {
"bool": {
"must": [
{
"range": {
"CLOSED_DATE": {
"gte": "2018-06-24T00:00:01",
"lte": "2018-06-30T23:59:59",
"time_zone": "UTC"
}
}
}
]
}
}
}
I also tried this, and got the same results:
{
"size": 0,
"aggs": {
"groupby": {
"terms": {
"script": {
"source": "doc['CLOSED_DATE'].value.getWeekOfWeekyear()"
},
"size": 100
}
}
},
"query": {
"bool": {
"must": [
{
"range": {
"CLOSED_DATE": {
"gte": "2018-06-24T00:00:01",
"lte": "2018-06-30T23:59:59",
"time_zone": "UTC"
}
}
}
]
}
}
}
To be fair, this query works alright for most use cases.
It's failing is scenarios like this use case above.
Any tip will be welcome!!!
Thanks
This did the trick:
def date = ZonedDateTime.ofInstant(Instant.ofEpochMilli(doc[params.dt_field_name].value), ZoneId.of(params.timezone)); DayOfWeek dayOfWeek = DayOfWeek.valueOf(params.first_day_week); date = date.with(TemporalAdjusters.previousOrSame(dayOfWeek)).plusWeeks(1); return date.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR);
I have a JSON object as below
[
{
"Id": 7,
"Name": "Colocation Folder",
"ParentId": 1,
"depth": 0
},
{
"Id": 8,
"Name": "CoLo Real Estate",
"ParentId": 7,
"depth": 1
},
{
"Id": 10,
"Name": "CoLo: Burst",
"ParentId": 7,
"depth": 1
},
{
"Id": 34,
"Name": "CoLo Dedicated Bandwidth",
"ParentId": 7,
"depth": 1
},
{
"Id": 10035,
"Name": "Infrastructure as a Service",
"ParentId": 7,
"depth": 1
},
{
"Id": 10037,
"Name": "Software as a Service",
"ParentId": 7,
"depth": 1
},
{
"Id": 10038,
"Name": "IaaS Component Upgrade",
"ParentId": 7,
"depth": 1
},
{
"Id": 668,
"Name": "CoLo Misc Folder",
"ParentId": 7,
"depth": 1
},
{
"Id": 758,
"Name": "CoLo: Conduit Fee",
"ParentId": 668,
"depth": 2
},
{
"Id": 765,
"Name": "CoLo: Private VLAN",
"ParentId": 668,
"depth": 2
}
]
The Id and ParentId fields show the relation between the items. I need to make it as a nested JSON using C#.
Since there will be many such models, I don't want to create individual classes for each model. Is there a generic approach in C# that will take a flat JSON array, take the ID and ParentId fields as input and then return me a nested JSON with all other fields in the array as well? For example, I am looking for an output of nested JSON as below:
[
{
"Id": 7,
"Name": "Colocation Folder",
"items": [
{
"Id": 8,
"Name": "CoLo Real Estate",
"ParentId": 7
},
{
"Id": 10,
"Name": "CoLo: Burst",
"ParentId": 7
},
{
"Id": 34,
"Name": "CoLo Dedicated Bandwidth",
"ParentId": 7
},
{
"Id": 10035,
"Name": "Infrastructure as a Service",
"ParentId": 7
},
{
"Id": 10037,
"Name": "Software as a Service",
"ParentId": 7
},
{
"Id": 10038,
"Name": "IaaS Component Upgrade",
"ParentId": 7
},
{
"Id": 668,
"Name": "CoLo Misc Folder",
"ParentId": 7,
"items": [
{
"Id": 758,
"Name": "CoLo: Conduit Fee",
"ParentId": 668
},
{
"Id": 765,
"Name": "CoLo: Private VLAN",
"ParentId": 668
}
]
}
]
}
]
If you use Json.Net, you can do this conversion in a generic way using the LINQ-to-JSON API (JObjects). The idea is to parse the JSON array and add all the individual items to a dictionary keyed by Id. Then, loop over the dictionary items, and for each one, try to look up the parent. If the parent is found, add the item to the parent's items array (creating it if needed). Otherwise, add the item to the root array. Along the way, remove the depth property from each item, since you don't seem to want that in the output. Lastly, just dump the root array to string to get the final result.
var dict = JArray.Parse(json)
.Children<JObject>()
.ToDictionary(jo => (string)jo["Id"], jo => new JObject(jo));
var root = new JArray();
foreach (JObject obj in dict.Values)
{
JObject parent;
string parentId = (string)obj["ParentId"];
if (parentId != null && dict.TryGetValue(parentId, out parent))
{
JArray items = (JArray)parent["items"];
if (items == null)
{
items = new JArray();
parent.Add("items", items);
}
items.Add(obj);
}
else
{
root.Add(obj);
}
JProperty depth = obj.Property("depth");
if (depth != null) depth.Remove();
}
Console.WriteLine(root.ToString());
Fiddle: https://dotnetfiddle.net/Buza6T
You can use a dynamic object with JSON.Net like so to detect your properties dynamically then you could build a new json object with the desired nesting:
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
dynamic d = JArray.Parse(stringy);
foreach(var ob in d)
{
if(ob.ParentID != ob.Id)
{
string debug = "oh snapple, it's a child object";
}
}
Share my working code for you at jsFiddle full source code
recursive function is:
function getNestedChildren(arr, parent) {
var out = []
for(var i in arr) {
if(arr[i].parent == parent) {
var children = getNestedChildren(arr, arr[i].id)
if(children.length) {
arr[i].children = children
}
out.push(arr[i])
}
}
return out
}
full source code:
function getNestedChildren(arr, parent) {
var out = []
for(var i in arr) {
if(arr[i].ParentId == parent) {
var items = getNestedChildren(arr, arr[i].Id)
if(items.length) {
arr[i].items = items
}
out.push(arr[i])
}
}
return out
}
var flat_array = [
{
"Id": 7,
"Name": "Colocation Folder",
"ParentId": 1,
"depth": 0
},
{
"Id": 8,
"Name": "CoLo Real Estate",
"ParentId": 7,
"depth": 1
},
{
"Id": 10,
"Name": "CoLo: Burst",
"ParentId": 7,
"depth": 1
},
{
"Id": 34,
"Name": "CoLo Dedicated Bandwidth",
"ParentId": 7,
"depth": 1
},
{
"Id": 10035,
"Name": "Infrastructure as a Service",
"ParentId": 7,
"depth": 1
},
{
"Id": 10037,
"Name": "Software as a Service",
"ParentId": 7,
"depth": 1
},
{
"Id": 10038,
"Name": "IaaS Component Upgrade",
"ParentId": 7,
"depth": 1
},
{
"Id": 668,
"Name": "CoLo Misc Folder",
"ParentId": 7,
"depth": 1
},
{
"Id": 758,
"Name": "CoLo: Conduit Fee",
"ParentId": 668,
"depth": 2
},
{
"Id": 765,
"Name": "CoLo: Private VLAN",
"ParentId": 668,
"depth": 2
}
]
var nested = getNestedChildren(flat_array, 1)
console.log(nested)
I created an elastic search index and the result of a simple search looks like:
{
"took": 2,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 11,
"max_score": 1,
"hits": [
{
"_index": "shop-bestellung",
"_type": "bestellung",
"_id": "dc144b04-8e73-4ea5-9f73-95c01768fd26",
"_score": 1,
"_source": {
"id": "dc144b04-8e73-4ea5-9f73-95c01768fd26",
"bestellnummer": "B-20170302-026",
"shopid": "0143d767-8986-432a-a15d-00e1c4862b24",
"shopname": "DeeDa",
"erstelltVon": "5663bb4b-fc44-46ca-b875-a3487b588b24",
"bestellername": "Max Mann",
"bestelldatum": "2017-01-30T23:00:00Z",
"bestellpositionen": []
}
}
]
}
}
I tried to create a filter which should consits of following three restrictions:
Query text
Date range
Filter on a specific field: "erstelltVon"
My filter only consits of query text and date range:
{
"query":{
"query_string":{
"fields":[
"bestellnummer",
"bestellername",
"bestelldatum",
"erstelltVon",
"bestellpositionen.artikelname",
"bestellpositionen.artikelnummer",
"bestellpositionen.referenznummer"
],
"query":"*"
}
},
"filter": {
"range" : {
"bestelldatum" : {
"gte": "2017-02-04T23:00:00Z",
"lte": "now",
"time_zone": "+01:00"
}
}
}
}
I would like to add the third filter:
"erstelltVon": "5663bb4b-fc44-46ca-b875-a3487b588b24"
How can I do that?
You need to use a boolean filter.
Here is how to use it:
"filter": {
"bool" : {
"must": [
// FIRST FILTER
{
"range" : {
"bestelldatum" : {
"gte": "2017-02-04T23:00:00Z",
"lte": "now",
"time_zone": "+01:00"
}
}
},
{
// YOUR OTHER FILTER HERE
}
]
}
change "must" to "should" if you want to use a OR instead of an AND.