Related
I have a database with Products and each product has an Id, Name, ManufacturerId, CategoryId and UserScore.
I want to retrieve all Products by a given Category sorted by UserScore, but avoiding many products of same Manufacturer listed together.
With the following query they all stuck together:
SELECT
P.ProductId, P.Name, P.ManufacturerId, P.UserScore
FROM Products P
WHERE P.CategoryId = 1
ORDER BY P.UserScore
This is the result in T-SQL
In T-SQL I came up with a solution like the following, where Products are grouped in no more than 2 elements by Manufacturer, and it suits perfectly my needs:
SELECT T.*
FROM (
SELECT
P.ProductId, P.Name, P.ManufacturerId, P.UserScore,
ROW_NUMBER() OVER (PARTITION BY P.ManufacturerId ORDER BY P.UserScore DESC) RN
FROM Products P
WHERE P.CategoryId = 1
) T
ORDER BY T.UserScore / CEILING(RN/2.0) DESC
How could I implement a ElasticSearch Query to mimic this behaviour?
Any ideas?
The index in elasticsearch would be like this, this is just an abstract example:
{"ProductId": "157072", "Name": "Product 157072", "ManufacturerId": "7790", "UserScore": "100000", "CategoryId": "1"},
{"ProductId": "296881", "Name": "Product 296881", "ManufacturerId": "6921", "UserScore": "35400", "CategoryId": "1"},
{"ProductId": "353924", "Name": "Product 353924", "ManufacturerId": "54616", "UserScore": "25000", "CategoryId": "1"},
...
You can use the collapse search function to group all the manufacturers:
https://www.elastic.co/guide/en/elasticsearch/reference/current/collapse-search-results.html
Visit "inner_hits" to control the collapsed results behavior.
# Indexing Documents
POST test_so/_bulk
{ "index" : {} }
{"ProductId": "157072", "Name": "Product 157072", "ManufacturerId": "7790", "UserScore": 100000, "CategoryId": "1"}
{ "index" : {} }
{"ProductId": "296881", "Name": "Product 296881", "ManufacturerId": "6921", "UserScore": 35400, "CategoryId": "1"}
{ "index" : {} }
{"ProductId": "353924", "Name": "Product 353924", "ManufacturerId": "54616", "UserScore": 25000, "CategoryId": "1"}
# Filtering by Category: 1, collapsing by Manufacturer and sorting by UserScore
POST test_so/_search
{
"query": {
"term": {
"CategoryId.keyword": {
"value": "1"
}
}
},
"collapse": {
"field": "ManufacturerId.keyword"
},
"sort": [
{
"UserScore": {
"order": "desc"
}
}
]
}
Results
{
"took": 22,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 3,
"relation": "eq"
},
"max_score": null,
"hits": [
{
"_index": "test_so",
"_id": "0amBPYQBJRm5qR4vd6NE",
"_score": null,
"_source": {
"ProductId": "157072",
"Name": "Product 157072",
"ManufacturerId": "7790",
"UserScore": 100000,
"CategoryId": "1"
},
"fields": {
"ManufacturerId.keyword": [
"7790"
]
},
"sort": [
100000
]
},
{
"_index": "test_so",
"_id": "0qmBPYQBJRm5qR4vd6NE",
"_score": null,
"_source": {
"ProductId": "296881",
"Name": "Product 296881",
"ManufacturerId": "6921",
"UserScore": 35400,
"CategoryId": "1"
},
"fields": {
"ManufacturerId.keyword": [
"6921"
]
},
"sort": [
35400
]
},
{
"_index": "test_so",
"_id": "06mBPYQBJRm5qR4vd6NE",
"_score": null,
"_source": {
"ProductId": "353924",
"Name": "Product 353924",
"ManufacturerId": "54616",
"UserScore": 25000,
"CategoryId": "1"
},
"fields": {
"ManufacturerId.keyword": [
"54616"
]
},
"sort": [
25000
]
}
]
}
}
Try following which is assuming all items in Group have same values. So I used First()
var results = products.Where(x => x.CategoryId == 1)
.OrderByDescending(x => x.UserScore)
.GroupBy(x => x.ManufacturerId)
.Select(x => new {ProductId = x.ProductId.First(), Name = x.Name.First(), ManufacturerId = x.Key, UserScore = x.UserScore.First()})
I've spent a while trying to work out how to desterilise this JSON file and get each products
ID,NAME,COLLECTION COST, AND WEATHER ITS ON OFFER
Im currently attempting this with Newtonsoft.Json in a C# class.
could someone please point me in the right direction.
Many thanks
for the JSON text visit:
https://pastebin.com/bkQwpsAy
{
"_embedded": {
"products": [{
"uuid": "74f1501f-4a52-490a-b2b9-668f16e2db95",
"createdAt": "2020-04-20T13:44:22.000+00:00",
"itemId": "DRN543",
"altItemGroupId": "",
"popularityScore": 101.22,
"itemName": "Coca-Cola Bottles (GB) 6x1.5L",
"jsonFriendlyItemName": "Coca-Cola Bottles (GB) 6x1.5L",
"seoFriendlyItemName": "coca-cola-bottles-gb-6x1-5l",
"brand": "Coca Cola",
"imageLinks": ["https://jjproducts.global.ssl.fastly.net/jjfoodservice/image/upload/v1588074299/products/DRN543/_S/ggktoxjzbplky4uovec4.jpg"],
"price": 9.99,
"depth": 29.0,
"height": 32.0,
"itemNetWeight": 9.0,
"jadMobileItemName": "Coca Cola (GB) 6x1.5L",
"category1Id": "100005",
"category2Id": "200261",
"category3Id": "300194",
"category4Id": "400084",
"category5Id": "",
"category1Name": "Drinks",
"category2Name": "Soft Drinks",
"category3Name": "Fizzy Drinks",
"category4Name": "Cola",
"category5Name": "",
"origin": "United Kingdom",
"catchOrigin": "",
"productDescription": "",
"sellingPoints": "Coca Cola\nCocaCola",
"shelfLife": 135,
"sizeOrCut": "6x1.5l",
"qtyPerLayer": 20.0,
"standardPalletQty": 80.0,
"unitVolume": 17632.0,
"width": 19.0,
"allergensDeclaration": "",
"storageCondition": "Store cool and dry.",
"storedAt": "2020-11-03T23:52:28.789210Z",
"branches": [{
"locationId": "EN-MW",
"locationName": "Enfield Branch",
"warehouseArea": "DA",
"warehouseZone": "GZ"
}, {
"locationId": "LS-MW",
"locationName": "Leicester Branch",
"warehouseArea": "DA",
"warehouseZone": "GZ"
}
],
"branchesBeforeLastUpdate": [{
"locationId": "EN-MW",
"locationName": "Enfield Branch",
"warehouseArea": "DA",
"warehouseZone": "GZ"
}, {
"locationId": "LS-MW",
"locationName": "Leicester Branch",
"warehouseArea": "DA",
"warehouseZone": "GZ"
}
],
"video": [],
"categoryList": "[{\"id\":\"100005\",\"name\":\"Drinks\"},{\"id\":\"200261\",\"name\":\"Soft Drinks\"},{\"id\":\"300194\",\"name\":\"Fizzy Drinks\"}]",
"categoryId": "100005,200261,300194",
"categoryName": "Drinks,Soft Drinks,Fizzy Drinks",
"categoryNormalised": "[Cola Drinks Fizzy Soft]",
"productFeatures": ["Popular", "Ambient", "Vegan", "Vegetarian"],
"unitSize": "1.5L",
"unitPriceDivider": 0.16666,
"unitPriceTypeDisplayText": "each",
"offer": {
"itemId": "DRN543",
"promoForCc": false,
"promoTagId": "Monthly",
"promoTag": "Monthly Special Promotions",
"promoEnd": "31/12/2020",
"promoDisAmt": 0,
"promoDisPct": 0,
"promoDiscountText": [],
"id": "DRN543"
},
"delivery": {
"price": 8.29,
"priceInc": 8.29,
"unitPriceDisplay": "£1.38 each",
"step": 1.0,
"max": 15.0,
"collection": false
},
"collection": {
"price": 7.29,
"priceInc": 7.29,
"unitPriceDisplay": "£1.21 each",
"step": 1.0,
"max": 15.0,
"collection": true
},
"previouslyPurchased": false,
"favourite": false,
"available": true,
"new": false,
"popular": true,
"popularOnCategory1": true,
"popularOnCategory2": true,
"popularOnCategory3": true,
"ageRestriction": false,
"halal": false,
"vegan": true,
"vegeterian": true,
"numberOfPackage": 6,
"numberOfUnitsInPackage": 1.5,
"unitType": "litre",
"CCMAltItemGroup": "",
"JJeBrand": "Coca Cola",
"JadConsumableDepth": 0.0,
"JadConsumableHeight": 0.0,
"JadConsumableWidth": 0.0,
"JJeCategory1Id": "100005",
"JJeCategory2Id": "200261",
"JJeCategory3Id": "300194",
"JJeCategory4Id": "400084",
"JJeCategory5Id": "",
"JJeCategory1": "Drinks",
"JJeCategory2": "Soft Drinks",
"JJeCategory3": "Fizzy Drinks",
"JJeCategory4": "Cola",
"JJeCategory5": "",
"JJeCookingInstruction": "Best served chilled.",
"JJeIngredients": "Carbonated Water, Sugar, Colour (Caramel E150d), Phosphoric Acid, Natural Flavourings including Caffeine.",
"JadIngredientsHTML": "Carbonated Water, Sugar, Colour (Caramel E150d), Phosphoric Acid, Natural Flavourings including Caffeine.",
"JJeOrigin": "United Kingdom",
"JadCatchOrigin": "",
"JJeProductDescription": "",
"JJeSellingPoints": "Coca Cola\nCocaCola",
"JJeShelfLife": 135,
"JJeSizeOrCut": "6x1.5L",
"JadAllergensDeclaration": "",
"JadStorageCondition": "Store cool and dry.",
"JJeEnergyKJ": 180.0,
"JJeEnergyKCAL": 42.0,
"JJeFatG": "0",
"JadFatSaturatesG": "0",
"JJeCarbohydrateG": "10.6",
"JadCarbohydrateSugarsG": "10.6",
"JJeProteinG": "0",
"JadSodiumG": "0",
"IsAgeRestriction": false,
"IsHalal": false,
"IsVegan": true,
"IsVegeterian": true
}
]
},
"_links": {
"maintenance-message": {
"href": "[]"
},
"announcement-message": {
"href": "[]"
}
},
"page": {
"size": 12,
"totalElements": 18,
"totalPages": 2,
"number": 0
}
}
public static List<JJs.ITEMS> JJSGetProductHTML(String Term)
{
string url = "https://www.website.com/api/product-search-agg/api/v1/product/websearch?b=DG-MW&page=0&q=" + Term+ "&size=12&sortType=search&format=json";
WebClient WC = new WebClient();
string JSON = WC.DownloadString(url);
var onject,= JsonConvert.DeserializeObject<dynamic>(JSON);
string s = onject._embedded.products[0].ToString();
List<JJs.ITEMS> products = new List<JJs.ITEMS>();
foreach (var m in onject._embedded.products)
{
JJs.ITEMS newitem = new JJs.ITEMS();
newitem.Name = m.jsonFriendlyItemName.ToString();
newitem.itemId = m.itemId.ToString();
newitem.price = m.collection.price.ToString();
try
{ m.offer.ToString(); newitem.Promoend = m.offer.promoEnd.ToString(); newitem.Promo = true; }
catch
{ newitem.Promo = false; };
products.Add(newitem);
}
return products;
}
As I didnt want all the feilds, i serialised it then used ".jsonFriendlyItemName",".itemId".. to only get the values I was looking for
thanks #JaromandaX, # Peter B and #dbc for your help
I have inserted two json document using documentdb and have them in a list named Hotelwithroomtype.I want to update the created json document with some new values and then recreate them as new document. Here's my code
foreach (var item in Hotelwithroomtype)
{
foreach (var cal in Calendardata)
{
item.CalendarDate = Convert.ToDateTime(cal.CalendarDate);
item.CalendarDay = cal.Calendarday;
item.issweekday = Convert.ToBoolean(cal.isweekday);
item.issweekend = Convert.ToBoolean(cal.isweekend);
foreach (var prop in item.RoomTypes.RoomTypeList)
{
prop.Mon = "0";
prop.Tue = "0";
prop.Wed = "0";
prop.Thur = "0";
prop.Fri = "0";
prop.Sat = "0";
prop.Sun = "0";
prop.Count = "0";
prop.CountType = "0";
}
// var docExists = client.CreateDocumentQuery(UriFactory.CreateDocumentCollectionUri("next", "nextCollection"))
//.Where(doc => doc.Id == item.id)
//.Select(doc => doc.Id)
//.AsEnumerable()
//.Any();
await client.CreateDocumentAsync(collectionLink, item);
}
}
sample Json document which i inserted
{
"HotelCriteria": {
"HotelCode": "101920",
"HotelName": "TestThe Zuri"
},
"RoomTypes": {
"RoomTypeList": [
{
"InvTypeCode": "ZCR",
"Name": "Zuri Club Room",
"BaseOccupancy": 2,
"MaxOccupancy": 3,
"Quantity": 66,
"IsRoomActive": 1,
"RoomDescription": "",
"Availability": 0,
"Mon": "0",
"Tue": "0",
"Wed": "0",
"Thur": "0",
"Fri": "0",
"Sat": "0",
"Sun": "0",
"CountType": "0",
"Count": "0"
},
{
"InvTypeCode": "ZRR",
"Name": "Zuri Room",
"BaseOccupancy": 2,
"MaxOccupancy": 3,
"Quantity": 90,
"IsRoomActive": 1,
"RoomDescription": "",
"Availability": 0,
"Mon": "0",
"Tue": "0",
"Wed": "0",
"Thur": "0",
"Fri": "0",
"Sat": "0",
"Sun": "0",
"CountType": "0",
"Count": "0"
},
{
"InvTypeCode": "ZSR",
"Name": "Zuri Suite Room",
"BaseOccupancy": 2,
"MaxOccupancy": 3,
"Quantity": 4,
"IsRoomActive": 1,
"RoomDescription": "",
"Availability": 0,
"Mon": "0",
"Tue": "0",
"Wed": "0",
"Thur": "0",
"Fri": "0",
"Sat": "0",
"Sun": "0",
"CountType": "0",
"Count": "0"
}
]
},
"RatePlans": {
"RatePlanList": [
{
"RatePlanCode": "B2C00001",
"RatePlanCategory": "B2C",
"RatePlanStatusType": 1,
"RatePlanName": "Channel Rates",
"Description": "Channel Rates",
"InvTypeCode": "ZCR",
"MealPlanCode": "CP",
"MealPlanDesc": "Continental Plan",
"Start": "2016-06-27",
"End": "2017-03-31",
"CurrencyCode": "INR"
},
{
"RatePlanCode": "B2C00001",
"RatePlanCategory": "B2C",
"RatePlanStatusType": 1,
"RatePlanName": "Channel Rates",
"Description": "Channel Rates",
"InvTypeCode": "ZRR",
"MealPlanCode": "CP",
"MealPlanDesc": "Continental Plan",
"Start": "2016-06-27",
"End": "2017-03-31",
"CurrencyCode": "INR"
},
{
"RatePlanCode": "B2C00001",
"RatePlanCategory": "B2C",
"RatePlanStatusType": 1,
"RatePlanName": "Channel Rates",
"Description": "Channel Rates",
"InvTypeCode": "ZSR",
"MealPlanCode": "CP",
"MealPlanDesc": "Continental Plan",
"Start": "2016-06-27",
"End": "2017-03-31",
"CurrencyCode": "INR"
}
]
},
"Inclusions": {
"InclusionList": [
{
"MealPlanCode": "CP",
"MealPlanDesc": "Continental Plan"
}
]
},
"id": "8f236805-d3a5-498b-9cc3-efa7448faa63",
"_rid": "SVcZALakfQAEAAAAAAAAAA==",
"_self": "dbs/SVcZAA==/colls/SVcZALakfQA=/docs/SVcZALakfQAEAAAAAAAAAA==/",
"_etag": "\"00004b09-0000-0000-0000-594a02e80000\"",
"_attachments": "attachments/",
"_ts": 1498022618
}
I want the current document with updated values but need them as a new document.When i tried to create i get the following error "Resource with specified id or name already exists".Any help would be really appreciated.
Thanks
You are trying to instead a new document with id equal to id of the existing document. This is not allowed, so operation fails. Before you insert the modified copy, you need to assign a new id to it.
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've JSON with the following structure:
[
{
"ID": 1,
"Label": "Reg Scheme",
"Colours": [
{
"ID": 1,
"Value": "0x3333cc",
"Result": 1,
"Label": null
},
{
"ID": 2,
"Value": "0x666699",
"Result": 2,
"Label": null
},
{
"ID": 3,
"Value": "0x009966",
"Result": 3,
"Label": null
}
]
},
{
"ID": 2,
"Label": "Spesh Scheme",
"Colours": [
{
"ID": 11,
"Value": "0x59699c",
"Result": 1,
"Label": null
},
{
"ID": 12,
"Value": "0x0070ff",
"Result": 2,
"Label": null
},
{
"ID": 13,
"Value": "0x90865e",
"Result": 3,
"Label": null
}
]
},
and I have an entity dataset whereby I've joined all the relevant information, and am attempting to produce JSON with that structure via a single linq-to-sql EF query to be returned to the webapi method.
My query so far is:
return
DbContext.Schemes
.Join(
DbContext.SchemeColours,
s => s.SchemeID,
sc => sc.SchemeID,
(s, sc) => new
{
s.SchemeID,
s.Label,
sc.Colour,
sc.Result,
sc.ColourID
})
.Select(a =>
new Overlay.ReportColourScheme
{
ID = a.SchemeID,
Label = a.Label,
Colours = new List<Overlay.ReportColour>
{
new Overlay.ReportColour
{
ID = a.ColourID,
Value = a.Colour,
Result = a.Result
}
}
})
.ToArray();
Which is almost there but not quite:
[
{
"ID": 1,
"Label": "Regular Scheme",
"Colours": [
{
"ID": 1,
"Value": "0x3333cc",
"Result": 1,
"Label": null
}
]
},
{
"ID": 1,
"Label": "Regular Scheme",
"Colours": [
{
"ID": 2,
"Value": "0x666699",
"Result": 2,
"Label": null
}
]
},
{
"ID": 1,
"Label": "Regular Scheme",
"Colours": [
{
"ID": 3,
"Value": "0x009966",
"Result": 3,
"Label": null
}
]
},
{
"ID": 2,
"Label": "Protanopia adjusted Scheme",
"Colours": [
{
"ID": 11,
"Value": "0x59699c",
"Result": 1,
"Label": null
}
]
},
{
"ID": 2,
"Label": "Protanopia adjusted Scheme",
"Colours": [
{
"ID": 12,
"Value": "0x0070ff",
"Result": 2,
"Label": null
}
]
},
{
"ID": 2,
"Label": "Protanopia adjusted Scheme",
"Colours": [
{
"ID": 13,
"Value": "0x90865e",
"Result": 3,
"Label": null
}
]
},
As of course it creates a new list for every resultID. The top-level ID is a SchemeID- what I'm looking for is logic along the lines of: "take the first 3 Results with a particular schemeID, add them to a list in Colours, then move on to the next schemeID"
I believe this will produce identical JSON that I started the post with.
Any assistance at all would be greatly appreciated, thank you.
Try the following code:
return
DbContext.Schemes
.Join(
DbContext.SchemeColours,
s => s.SchemeID,
sc => sc.SchemeID,
(s, sc) => new
{
s.SchemeID,
s.Label,
sc.Colour,
sc.Result,
sc.ColourID
})
// After joining you group by SchemeID, in this way you have
// for each SchemeID the group of related items
.GroupBy(a => a.SchemeID)
// You then create your result, starting from the main object
.Select(g =>
new Overlay.ReportColourScheme
{
ID = g.Key,
// I suppose you have at least a child for each SchemeID,
// otherwise you can check if the list is empty
Label = g.FirstOrDefault().Label,
// For each group you create a list of child object
Colours = g.Select(v => new Overlay.ReportColour
{
ID = v.ColourID,
Value = v.Colour,
Result = v.Result
}).ToList()
})
.ToArray();
The main issue is that you are using a Join where actually you need a Group Join:
return DbContext.Schemes
.GroupJoin(DbContext.SchemeColours,
s => s.SchemeID,
sc => sc.SchemeID,
(s, colours) => new Overlay.ReportColourScheme
{
ID = s.SchemeID,
Label = s.Label,
Colours = colours
.Select(sc => new Overlay.ReportColour
{
ID = sc.ColourID,
Value = sc.Colour,
Result = sc.Result,
})
.ToList()
})
.ToArray();
But since you are using Entity Framework, it would be much better and eaiser if you define (if you already haven't) and use a navigation property:
class Scheme
{
// ...
public ICollection<SchemeColour> Colours { get; set; }
}
and then simply
return DbContext.Schemes
.Select(s => new Overlay.ReportColourScheme
{
ID = s.SchemeID,
Label = s.Label,
Colours = s.Colours
.Select(sc => new Overlay.ReportColour
{
ID = sc.ColourID,
Value = sc.Colour,
Result = sc.Result,
})
.ToList()
})
.ToArray();