How I can use double grouped elements using LINQ? - c#

In my project I would like to group double time first by project then group by daily.
For ex: Project X is using for 06.11.2020 (300$ price), for 07.11.2020 (250 $) etc.
I would like to achieve some result like this:
[
{
projectId: 1,
projectName: 'Test',
data: [
{
price: 22.000000176,
startDate: '2020-11-06T00:00:00'
},
{
price: 42.000000176,
startDate: '2020-11-07T00:00:00'
},
{
price: 62.000000176,
startDate: '2020-11-08T00:00:00'
},
],
},
{
projectId: 2,
projectName: 'Test 2',
data: [
{
price: 333.000000176,
startDate: '2020-11-06T00:00:00'
},
{
price: 3333.000000176,
startDate: '2020-11-07T00:00:00'
},
{
price: 3333.000000176,
startDate: '2020-11-08T00:00:00'
},
],
},
]
What I am trying in my code
public async Task<List<GroupedBillingInfo>> Handle(GroupedBillingListQuery request,
CancellationToken cancellationToken)
{
var billing = _context.BillingSummaries.Where(x => x.Project.Organization.IsDeleted == false)
.AsQueryable();
var demo = billing.ToList();
var result = demo.GroupBy(x => new {
ProjectId = x.ProjectId,
Year = x.BeginApply.Year,
Month = x.BeginApply.Month,
Day = x.BeginApply.Day
})
.Select(y => new GroupedBillingInfo
{
ProjectId = y.Key.ProjectId,
ProjectName = y.Select(d => d.Project.Name).FirstOrDefault(),
Data = new List<GroupedBillings>
{
new GroupedBillings
{
StartDate = $"{y.Key.Year}/{y.Key.Month}/{y.Key.Day}",
Price = y.Sum(s => s.Price)
}
}
}).ToList();
return result;
}
public class GroupedBillings
{
public string StartDate { get; set; }
public decimal Price { get; set; }
}
public class GroupedBillingInfo
{
public List<GroupedBillings> Data { get; set; }
public int ProjectId { get; set; }
public string ProjectName { get; set; }
}
And after this I am getting results:
[
{
"data": [
{
"startDate": "2020/11/6",
"price": 26.66666688
}
],
"projectId": 50,
"projectName": "test-bug"
},
{
"data": [
{
"startDate": "2020/11/6",
"price": 33.333333599999996
}
],
"projectId": 38,
"projectName": "tcpro-demo"
},
{
"data": [
{
"startDate": "2020/11/6",
"price": 40.00000032
}
],
"projectId": 53,
"projectName": "loki-test"
},
{
"data": [
{
"startDate": "2020/11/6",
"price": 12.000000096
}
],
"projectId": 64,
"projectName": "chaos"
},
{
"data": [
{
"startDate": "2020/11/7",
"price": 26.66666688
}
],
"projectId": 64,
"projectName": "chaos"
},
{
"data": [
{
"startDate": "2020/11/7",
"price": 32.000000256
}
],
"projectId": 50,
"projectName": "test-bug"
},
{
"data": [
{
"startDate": "2020/11/7",
"price": 40.00000032
}
],
"projectId": 38,
"projectName": "tcpro-demo"
},
{
"data": [
{
"startDate": "2020/11/7",
"price": 48.000000384
}
],
"projectId": 53,
"projectName": "loki-test"
},
{
"data": [
{
"startDate": "2020/11/8",
"price": 32.000000256
}
],
"projectId": 64,
"projectName": "chaos"
},
{
"data": [
{
"startDate": "2020/11/8",
"price": 32.000000256
}
],
"projectId": 50,
"projectName": "test-bug"
},
{
"data": [
{
"startDate": "2020/11/8",
"price": 40.00000032
}
],
"projectId": 38,
"projectName": "tcpro-demo"
},
{
"data": [
{
"startDate": "2020/11/8",
"price": 48.000000384
}
],
"projectId": 53,
"projectName": "loki-test"
},
{
"data": [
{
"startDate": "2020/11/9",
"price": 17.333333472
}
],
"projectId": 64,
"projectName": "chaos"
},
{
"data": [
{
"startDate": "2020/11/9",
"price": 18.666666816
}
],
"projectId": 50,
"projectName": "test-bug"
},
{
"data": [
{
"startDate": "2020/11/9",
"price": 23.333333519999996
}
],
"projectId": 38,
"projectName": "tcpro-demo"
},
{
"data": [
{
"startDate": "2020/11/9",
"price": 28.000000224
}
],
"projectId": 53,
"projectName": "loki-test"
}
]
So in short I would like (project and inside object -> daily datetimes and price sum,
Project x -> (06nov2020 -> price 300 || 07nov2020 -> price 100)

You should not group it at once. You want select a records with unique projectId, and group by date. It should be two different groupings instead
var result = demo
.GroupBy(x => new {
ProjectId = x.ProjectId,
Year = x.BeginApply.Year,
Month = x.BeginApply.Month,
Day = x.BeginApply.Day
})
.GroupBy(x => new {
ProjectId = x.Key.ProjectId
})
.Select(y => new GroupedBillingInfo
{
ProjectId = y.Key.ProjectId,
ProjectName = y.First().Select(d => d.Project.Name).First(),
Data = y.Select(z => new GroupedBillings
{
StartDate = $"{z.Key.Year}/{z.Key.Month}/{z.Key.Day}",
Price = z.Sum(s => s.Price)
}).ToList()
}).ToList();

Related

Multiple conditions in MongoDB group aggregation

I have the documents in the below format. I would like to count production deployments from the collection. Mongo Playground link is also attached here.
A document is considered as a production deployment when ANY of the following is true.
deployments.steps.environments.name contains Production OR Prod OR Prd
deployments.steps.stages contains Production OR Prod OR Prd
Any help on incorporating the above condition into the query to calculate TotalCount, SucceededCount etc. please?
Update: I have updated the query here. Am I right?
[
{
"productId": "613a5114b24382575e7e7668",
"deployments": [
{
"projectId": "613a5083b24382575e7e765f",
"title": "Release-4",
"steps": [
{
"releaseId": 8168,
"title": "UnitTest-Release-004",
"environments": [
{
"envId": 61553,
"name": "Production"
}
],
"stages": []
},
{
"releaseId": 7376,
"title": "UnitTest-Release-005",
"environments": [],
"stages": [
"Prod"
]
}
]
}
],
"createdAt": ISODate("2021-11-03T07:55:57.486Z"),
"deploymentStatus": "Succeeded",
"completedAt": ISODate("2021-11-03T07:29:00.907Z"),
"startedAt": ISODate("2021-11-03T07:26:53.761Z"),
},
{
"productId": "613a5114b24382575e7e7668",
"deployments": [
{
"projectId": "613a5083b24382575e7e765f",
"title": "Release-4",
"steps": [
{
"releaseId": 8168,
"title": "UnitTest-Release-004",
"environments": [
{
"envId": 61553,
"name": "Production"
}
],
"stages": []
},
{
"releaseId": 7376,
"title": "UnitTest-Release-005",
"environments": [],
"stages": []
}
]
}
],
"createdAt": ISODate("2021-11-03T07:55:57.486Z"),
"deploymentStatus": "Failed",
"completedAt": ISODate("2021-11-03T07:29:00.907Z"),
"startedAt": ISODate("2021-11-03T07:26:53.761Z"),
}
]
Here is the query.
db.collection.aggregate([
{
$match: {
$and: [
{
"createdAt": {
$gte: ISODate("2020-11-01")
}
},
{
"createdAt": {
$lte: ISODate("2021-11-17")
}
}
],
$or: [
{
"deployments.steps.environments.name": {
"$in": [
"Prd",
"Prod",
"Production"
]
}
},
{
"deployments.steps.stages.name": {
"$in": [
"Prd",
"Prod",
"Production"
]
}
}
]
}
},
{
$group: {
_id: "$productId",
TotalCount: {
$sum: 1
},
SucceededCount: {
$sum: {
"$cond": {
"if": {
$eq: [
"$deploymentStatus",
"Succeeded"
]
},
"then": 1,
"else": 0
}
}
},
FailedCount: {
$sum: {
"$cond": {
"if": {
$eq: [
"$deploymentStatus",
"Failed"
]
},
"then": 1,
"else": 0
}
}
},
CancelledCount: {
$sum: {
"$cond": {
"if": {
$eq: [
"$deploymentStatus",
"Cancelled"
]
},
"then": 1,
"else": 0
}
}
},
NotStartedCount: {
$sum: {
"$cond": {
"if": {
$eq: [
"$deploymentStatus",
"NotStarted"
]
},
"then": 1,
"else": 0
}
}
}
}
}
])
MongoPlayground
$cond - if - then - else = without quotes

Performing an update on objects from an array by using object own values

Having a collection sampled by next:
{
"_id": {
"$oid": "6139b08ee5a3119445892f94"
},
"type": "bike",
"specs": [
{
"name": "giant",
"models": [
{
"name": "v1",
"categories": [
{
"name": "v11",
"price": 0
},
{
"name": "v12",
"price": 0.1
},
{
"name": "v13",
"price": 0.1
}
]
},
{
"name": "v2",
"categories": [
{
"name": "v21",
"price": 1
},
{
"name": "v22",
"price": 0.1
}
]
}
]
},
{
"name": "sputnik",
"models": [
{
"name": "s1",
"categories": [
{
"name": "s11",
"price": 20
},
{
"name": "s12",
"price": 0.9
},
{
"name": "s13",
"price": 1.1
}
]
},
{
"name": "s2",
"categories": [
{
"name": "s31",
"price": 1
},
{
"name": "s32",
"price": 0.1
}
]
}
]
}
]
}
In order to edit models items by adding a new subdocument with next structure:
"valid": {
"isValid": true,
"currentName": [value from name field]
}
and remove field name
Almost achieved that by using Aggregations:
var dineInOptionsDefinition =
BsonDocument.Parse("{ isValid: 5, currentName: '$specs.name'}");
var addNewFieldsDefinition = new BsonDocument
{
{ "$addFields", new BsonDocument
{
{
"specs.models.valid", dineInOptionsDefinition
}
}
}
};
var t1 = collection
.Aggregate()
.Match(filterDefinition)
.AppendStage<BsonDocument>(addNewFieldsDefinition)
.Merge(collection);
The problem I'm facing is the value for currentName - as result it's an array of concatenated name fields values. Any clue how to get the value from the current document field value?

Entity Framework Core Parent/Child Clean query with multi level child [duplicate]

This question already has an answer here:
Map category parent id self referencing table structure to EF Core entity
(1 answer)
Closed 2 years ago.
I have an issue on cleaning querying my parent child without repeating the children at its own root of the list.
Object
public class Office
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
public bool Inactive { get; set; }
public int? ParentId { get; set; }
[ForeignKey("ParentId")]
public Office Parent { get; set; }
[InverseProperty("Parent")]
public virtual ICollection<Office> Children { get; set; }
}
}
The Query
public async Task<IEnumerable<Office>> GetOffices()
{
return await _context.Offices.Where(os => !os.Inactive)
.Include(o => o.Parent)
.Include(o => o.Children).ToListAsync();
}
And the result
{
"id": 5,
"name": "AA",
"inactive": false,
"parentId": null,
"parent": null,
"children": []
},
{
"id": 10,
"name": "BAA",
"inactive": false,
"parentId": 2,
"parent": {
"id": 2,
"name": "BA",
"inactive": false,
"parentId": null,
"parent": null,
"children": []
},
"children": [
{
"id": 1011,
"name": "BAAA",
"inactive": false,
"parentId": 10,
"children": []
}
]
},
{
"id": 1011,
"name": "BAAA",
"inactive": false,
"parentId": 10,
"parent": {
"id": 10,
"name": "BAA",
"inactive": false,
"parentId": 2,
"parent": {
"id": 2,
"name": "BA",
"inactive": false,
"parentId": null,
"parent": null,
"children": []
},
"children": []
},
"children": []
},
,
{
"id": 2,
"name": "BA",
"inactive": false,
"parentId": null,
"parent": null,
"children": [
{
"id": 10,
"name": "BAA",
"inactive": false,
"parentId": 2,
"children": [
{
"id": 1011,
"name": "BAAA",
"inactive": false,
"parentId": 10,
"children": []
}
]
}
]
}
This also has child objects at the root level so I tried this
public async Task<IEnumerable<Office>> GetOffices()
{
return await _context.Offices.Where(os => !os.Inactive && !os.ParentId.HasValue).Include(o => o.Parent).Include(o => o.Children).ToListAsync();
}
And this is the result of the other query
{
"id": 5,
"name": "AA",
"inactive": false,
"parentId": null,
"parent": null,
"children": []
},
{
"id": 2,
"name": "BA",
"inactive": false,
"parentId": null,
"parent": null,
"children": [
{
"id": 10,
"name": "BAA",
"inactive": false,
"parentId": 2,
"children": null
}
]
}
This leaves out id 1010 BAAA child object. Idealy I would like to have it come out as
{
"id": 5,
"name": "AA",
"inactive": false,
"parentId": null,
"parent": null,
"children": []
},
{
"id": 2,
"name": "BA",
"inactive": false,
"parentId": null,
"parent": null,
"children": [
{
"id": 10,
"name": "BAA",
"inactive": false,
"parentId": 2,
"children": [
{
"id": 1011,
"name": "BAAA",
"inactive": false,
"parentId": 10,
"children": []
}
]
}
]
}
No matter how I stack the query I can't seem to get this to display this way. Is there a way to adjust the query to allow this or an easy post query command to trim only the root level objects that are a child?
If you want limited number of child levels (2 in this example) fetched you can try:
await _context.Offices
.Where(os => !os.Inactive && !os.ParentId.HasValue)
.Include(o => o.Parent)
.Include(o => o.Children)
.ThenInclude(o => o.Children)
.ToListAsync()

Elastic .Nest Equivelent of field_value_factor

I need to revise a method that builds a SearchDescriptor using .Nest so that the score is higher for
product search results for items having a contract price (value of zero).
I captured the serialized version of the query added "field_value_factor" to return the results in the desired order. I have not determined how to achieve this in the .Nest query statement.
Can someone recommend how to revise the .NEST client statements to produce the same search descriptor?
Thank you
Below is the query we want to achieve where you will see field_value_factor at the bottom:
{
"from": 0,
"size": 3000,
"sort": [
{
"_score": {
"order": "desc"
}
},
{
"priceStatus": {
"order": "asc"
}
},
{
"unitPrice": {
"order": "asc"
}
}
],
"aggs": {
"PriceStatus": {
"terms": {
"field": "priceStatus",
"size": 5
}
},
"VendorName": {
"terms": {
"field": "vendorName",
"size": 5
}
},
"CatalogName": {
"terms": {
"field": "catalogName",
"size": 5
}
},
"ManufacturerName": {
"terms": {
"field": "manufacturerName",
"size": 5
}
},
"IsGreen": {
"terms": {
"field": "isGreen",
"size": 5
}
},
"IsValuePack": {
"terms": {
"field": "isValuePack",
"size": 5
}
},
"UnitOfMeasure": {
"terms": {
"field": "unitOfMeasure",
"size": 5
}
},
"Attributes": {
"nested": {
"path": "attributes"
},
"aggs": {
"TheAttributeName": {
"terms": {
"field": "attributes.name",
"size": 10
},
"aggs": {
"TheAttributeValue": {
"terms": {
"field": "attributes.value",
"size": 5
}
}
}
}
}
}
},
"query": {
"function_score": {
"query": {
"bool": {
"should": [
{
"multi_match": {
"type": "phrase",
"query": "pen",
"slop": 3,
"boost": 16.0,
"fields": [
"itemNumber*^4",
"shortDescription*^4",
"subCategory1Name*^1.5",
"subCategory2Name*^2.0",
"categoryName*^0.9",
"longDescription*^0.6",
"catalogName*^0.30",
"manufactureName*^0.20",
"vendorName*^0.15",
"upcCode*^0.10"
]
}
},
{
"multi_match": {
"query": "pen",
"boost": 15.0,
"minimum_should_match": "75%",
"fields": [
"itemNumber*^4",
"shortDescription*^4",
"subCategory1Name*^1.5",
"subCategory2Name*^2.0",
"categoryName*^0.9",
"longDescription*^0.6",
"catalogName*^0.30",
"manufactureName*^0.20",
"vendorName*^0.15",
"upcCode*^0.10"
]
}
},
{
"multi_match": {
"query": "pen",
"fuzziness": 1.0,
"slop": 2,
"minimum_should_match": "75%",
"fields": [
"itemNumber*^4",
"shortDescription*^4",
"subCategory1Name*^1.5",
"subCategory2Name*^2.0",
"categoryName*^0.9",
"longDescription*^0.6",
"catalogName*^0.30",
"manufactureName*^0.20",
"vendorName*^0.15",
"upcCode*^0.10"
]
}
}
]
}
},
"filter": {
"bool": {
"must": [
{
"terms": {
"catalogId": [
"fbb3dd2c-f81c-4ff3-bd5b-9c2cffc51540"
]
}
}
]
}
},
"field_value_factor": {
"field": "priceStatus",
"factor": -1,
"modifier": "none"
}
}
}
}
Below is the current method that builds the SearchDescriptor:
private SearchDescriptor<SearchItem> BuildSearchDescriptor(
string searchTerm,
IList<Guid> catalogIds,
int from,
int size,
string index,
string preference,
int attrSize,
int valueSize,
Dictionary<string, string[]> filterProps,
Dictionary<string, string[]> filterAttrs,
Guid? categoryId)
{
var searchDescriptor = new SearchDescriptor<SearchItem>()
.From(from)
.Size(size)
.Query(q =>
q.Filtered(fd => BuildFilterTerms(fd, filterProps, filterAttrs, catalogIds, categoryId)
.Query(iq => BuildQueryContainer(iq, searchTerm))
)
)
.Index(index)
.Preference(preference)
.Aggregations(agg => BuildAggregationDescriptor(agg, attrSize, valueSize, catalogIds.Count))
.Sort(sort => sort.OnField("_score").Descending())
.SortAscending(p=> p.PriceStatus)
.SortAscending(p => p.UnitPrice);
// Debug the raw string that will post to the ES servers i.e. use this in postman
//var str = System.Text.Encoding.UTF8.GetString(client.Serializer.Serialize(searchDescriptor));
return searchDescriptor;
}
Your JSON query isn't valid; field_value_factor is a function of a function_score query. In NEST 1.x, this would look like
var response = client.Search<Document>(x => x
.Query(q => q
.FunctionScore(fs => fs
.Functions(fu => fu
.FieldValueFactor(fvf => fvf
.Field(f => f.PriceStatus)
.Factor(-1)
.Modifier(FieldValueFactorModifier.None)
)
)
)
)
);
public class Document
{
public string Title { get; set; }
public int PriceStatus { get; set; }
}
which produces the query
{
"query": {
"function_score": {
"functions": [
{
"field_value_factor": {
"field": "PriceStatus",
"factor": -1.0,
"modifier": "none"
}
}
]
}
}
}

Not able to convert string to json

i am getting measurements from withings and want to show them in graphs but not able to convert it to json. i also try JsonConvert.SerializeObject(myString) using Newtonsoft.dlland simple
System.Web.Script.Serialization.JavaScriptSerializer sr = new System.Web.Script.Serialization.JavaScriptSerializer();
sr.Serialize(myString);
but it is not converting.
My withings measurement string is as follows.
{
"status": 0,
"body": {
"updatetime": 1392764547,
"measuregrps": [
{
"grpid": 17945868,
"attrib": 0,
"date": 139984270,
"category": 1,
"measures": [
{
"value": 72,
"type": 9,
"unit": 0
},
{
"value": 152,
"type": 10,
"unit": 7
},
{
"value": 87,
"type": 17,
"unit": 0
}
]
},
{
"grpid": 176587495,
"attrib": 0,
"date": 13915689,
"category": 1,
"measures": [
{
"value": 94,
"type": 9,
"unit": 0
},
{
"value": 145,
"type": 10,
"unit": 0
},
{
"value": 109,
"type": 11,
"unit": 0
}
]
},
{
"grpid": 179262494,
"attrib": 0,
"date": 1391369607,
"category": 1,
"measures": [
{
"value": 77,
"type": 9,
"unit": 0
},
{
"value": 121,
"type": 10,
"unit": 0
},
{
"value": 87,
"type": 11,
"unit": 0
}
]
},
{
"grpid": 179258492,
"attrib": 0,
"date": 1391171167,
"category": 1,
"measures": [
{
"value": 61,
"type": 9,
"unit": 0
},
{
"value": 107,
"type": 10,
"unit": 0
},
{
"value": 80,
"type": 11,
"unit": 0
}
]
},
{
"grpid": 179089150,
"attrib": 0,
"date": 1391167537,
"category": 1,
"measures": [
{
"value": 69,
"type": 9,
"unit": 0
},
{
"value": 112,
"type": 10,
"unit": 0
},
{
"value": 67,
"type": 11,
"unit": 0
}
]
},
{
"grpid": 179079661,
"attrib": 2,
"date": 1391164672,
"category": 1,
"measures": [
{
"value": 720,
"type": 1,
"unit": -1
}
]
},
{
"grpid": 17998560,
"attrib": 2,
"date": 146989672,
"category": 1,
"measures": [
{
"value": 284,
"type": 4,
"unit": -2
}
]
}
]
}
}
It seems, you want to deserialize your json string, not serialize:
var obj = JsonConvert.DeserializeObject<Withings.RootObject>(json);
public class Withings
{
public class Measure
{
public int value { get; set; }
public int type { get; set; }
public int unit { get; set; }
}
public class Measuregrp
{
public int grpid { get; set; }
public int attrib { get; set; }
public int date { get; set; }
public int category { get; set; }
public List<Measure> measures { get; set; }
}
public class Body
{
public int updatetime { get; set; }
public List<Measuregrp> measuregrps { get; set; }
}
public class RootObject
{
public int status { get; set; }
public Body body { get; set; }
}
}
JsonConvert.SerializeObject(myString) takes an object an returns a string. If you want to turn a string into an object you want to use Deserialize<T>(sting json). Given the arguments name in your sample is myString I would assume you're using the method wrong.
To deserialize you need an equivalent type like;
public class myObject
{
public int status { get; set; }
public Body body { get; set; }
}
public class Body
{
//other parameters ect
}
Your object model needs to exactly match the json in order by Deserialize<T> to behave correctly.

Categories