Json group array with a node value - c#

I have a json with below structure.
{
"root": {
"students": [
{
"student": {
"Id": "1",
"Name": "abc"
},
"Type": "Update",
"Time": "20220520050003Z"
},
{
"student": {
"Id": "2",
"Name": "def"
},
"Type": "Update",
"Time": "20220520050009Z"
},
{
"student": {
"Id": "3",
"Name": "ghi"
},
"Type": "Create",
"Time": "20220520050021Z"
},
{
"student": {
"Id": "1",
"Name": "abc"
},
"Type": "Create",
"Time": "20220520050024Z"
}
]
}
}
I want to read the students json array and group them which has same Id under student object
For example Id 1 has two entries
{
"student": {
"Id": "1",
"Name": "abc"
},
"Type": "Update",
"Time": "20220520050003Z"
}
and
{
"student": {
"Id": "1",
"Name": "abc"
},
"Type": "Create",
"eventTime": "20220520050024Z"
}
So I want to group them based on same id and get a list of objects which has record for each unique Id.
I tried with newtonsoft linq , but not able to group them with Id value. I want to know how i can group them
JObject obj = JObject.Parse(jsonasstring);
JObject objRoot = (JObject)obj.SelectToken("root");
var studentsList = from students in objRoot["students"]
select students["student"];

On your example you have different property names("Time", "eventTime") - I assumed that "Time" is a correct one.
Below you can see how grouping was done:
JObject obj = JObject.Parse(jsonasstring);
JObject objRoot = (JObject)obj.SelectToken("root");
var studentsList = from students in objRoot["students"]
group students by students["student"]["Id"] into studentsgroup
select new { StudentId = studentsgroup.Key, Students = studentsgroup.Select(x => new {StudentName = x["student"]["Name"], Type = x["Type"], Time = x["Time"]})};
You get students list, group it by studentId and do selection as you need.
Result:

Select all objects in the students JSON array, then use Linq's GroupBy to bucket them by the student.Id property.
var studentsGroupedById = JObject.Parse(jsonasstring)
.SelectTokens("$.root.students[*]")
.GroupBy(x => x["student"]["Id"]);
In each group, Key is the student.Id property, and you can enumerate through all the entries in the group (the inner foreach loop).
Runnable online version that prints out results to demonstrate the groups
I corrected what I hope is a typo in your JSON - the first student entry has a Time property, not an eventTime property. If that's actually how the data is formatted you'll need to account for it.

Related

Need Neo4j Data Format json in c#

I'm trying to find the shortest path along with relations on the nodes on the path, for which below query is used.
MATCH p = shortestPath((p1:Person { name: 'Kevin Bacon' })-[*..15]-
(p2:Person { name: 'Meg Ryan' }))
UNWIND nodes(p) as n
MATCH (n)-[*]->(q)
RETURN n, q
However i want to return the result as json object with data format as below in c#. I understand we have to use apoc. However can't really understand how to proceed.
{
"results": [
{
"data": [
{
"graph": {
"nodes": [
{
"id": "1",
"labels": ["James"],
"properties": {
"ShortName": "jammy",
"Type": "Person",
"Age": 34
}
},
{
"id": "2",
"labels": ["Brad"],
"properties": {
"name": "Brad",
"PlaceOfBirth": "California",
"Type": "Person",
"description": "Nice actor",
}
},
{
"id": "3",
"labels": ["Titanic"],
"properties": {
"movieName": "Titanic",
"Type": "Movie",
"description": "Tragedy",
}
}
],
"relationships": [
{
"id": "4",
"type": "ACTED_IN",
"startNode": "1",
"endNode": "3",
"properties": {
"from": 1470002400000
}
}
]
}
}
]
}
],
"errors": []
}
You can collect the nodes and relationships separately and add it on the result.
MATCH p = shortestPath((p1:Person { name: 'Kevin Bacon' })-[*..15]-(p2:Person { name: 'Meg Ryan' }))
UNWIND nodes(p) as n
MATCH (n)-[r]->(q)
WITH collect(distinct n) + collect(distinct q) as node_list, collect(distinct r) as rel_list
RETURN {results: {data: {graph: {nodes: node_list, relationships: rel_list}}, error: []}} as output
I wanted the first level incoming and outgoing relations of all the nodes on the path. Slighty modified the answer for anyone looking for something similar in future. Thanks Jose.
MATCH p = shortestpath((p1:Person { name: 'Kevin Bacon' })-[*..30]-
(p2:Person { name: 'Meg Ryan' }))
UNWIND nodes(p) as n
MATCH (n)<-[r*1]->(q)
WITH collect(distinct n) + collect(distinct q) as node_list,
collect(distinct r) as rel_list
RETURN {results: {data: {graph: {nodes: node_list, relationships:
rel_list}}, error: []}} as output

MongoDB, C#. Update multiple array elements with different conditions

There is an entity with custom fields that can change their value. The fields are stored as an array.
public class SomeEntity :...
{
...
public List<CustomField> FieldList { get; set; } = new List<CustomField>();
...
}
I use code like this to update one field:
var filterBuilder = Builders<SomeEntity>.Filter;
var updateFilter =
filterBuilder.Eq(e => e.EntityId, entityToUpdate.EntityId)
& filterBuilder.ElemMatch(e => e.FieldList, f => f.FieldId == updateField.FieldId);
var updateDef = Builders<SomeEntity>.Update.Set(e => e.FieldList[-1].Value, updateField.Value);
var updateRes = await GetEntityCollection().UpdateOneAsync(updateFilter, updateDef);
Sometimes it becomes necessary to update the values of several fields in the database at the same time, but I cannot find a way to do this in one action.
Is it possible in MongoDB to first search for a document using a filter, and then update/delete several elements of its array by their Id's (changes for each element may be different) in one action?
Is it possible to do the same for several documents (for example, remove fields with certain identifiers from all objects matching the filter)?
An example of an object before the change:
{
...
"fieldList": [
{
"id": "3bf2c235-82c3-40e4-91dc-46dc4c1ed177",
"type": 0,
"value": 10
},
{
"id": "5909dabd-fe8f-4edb-a642-c052e23082d8",
"type": 1,
"value": "some value"
},
{
"id": "66805403-d508-4b99-82f3-fa2ed828c19e",
"type": 3,
"value": "2019-08-01T12:00:00"
}
]
}
An example of an object after modification (only one document is updated):
{
...
"fieldList": [
{
"id": "3bf2c235-82c3-40e4-91dc-46dc4c1ed177",
"type": 0,
"value": 500
},
{
"id": "5909dabd-fe8f-4edb-a642-c052e23082d8",
"type": 1,
"value": "new value"
},
{
"id": "66805403-d508-4b99-82f3-fa2ed828c19e",
"type": 3,
"value": "2020-09-10T10:00:00"
}
]
}
An example of an object after a massive change (the document matched the filter, by analogy, all documents that would fit the filter should be updated - delete two fields, add a default value for some field):
{
...
"fieldList": [
{
"id": "3bf2c235-82c3-40e4-91dc-46dc4c1ed177",
"type": 0,
"value": 500,
"defaultValue": 150
}
]
}

Linq query to Json string

starting from a JObject I can get the array that interests me:
JArray partial = (JArray)rssAlbumMetadata["tracks"]["items"];
First question: "partial" contains a lot of attributes I'm not interested on.
How can I get only what I need?
Second question: once succeeded in the first task I'll get a JArray of duplicated items. How can I get only the unique ones ?
The result should be something like
{
'composer': [
{
'id': '51523',
'name': 'Modest Mussorgsky'
},
{
'id': '228918',
'name': 'Sergey Prokofiev'
},
]
}
Let me start from something like:
[
{
"id": 32837732,
"composer": {
"id": 245,
"name": "George Gershwin"
},
"title": "Of Thee I Sing: Overture (radio version)"
},
{
"id": 32837735,
"composer": {
"id": 245,
"name": "George Gershwin"
},
"title": "Concerto in F : I. Allegro"
},
{
"id": 32837739,
"composer": {
"id": 245,
"name": "George Gershwin"
},
"title": "Concerto in F : II. Adagio"
}
]
First question:
How can I get only what I need?
There is no magic, you need to read the whole JSON string and then query the object to find what you are looking for. It is not possible to read part of the JSON if that is what you need. You have not provided an example of what the data looks like so not possible to specify how to query.
Second question which I guess is: How to de-duplicate contents of an array of object?
Again, I do not have full view of your objects but this example should be able to show you - using Linq as you requested:
var items = new []{new {id=1, name="ali"}, new {id=2, name="ostad"}, new {id=1, name="ali"}};
var dedup = items.GroupBy(x=> x.id).Select(y => y.First()).ToList();
Console.WriteLine(dedup);

LINQ: get nested arrays based on a groupby?

Suppose I have this simple object definition:
public class Item
{
public int section { get; set; }
public string item { get; set; }
}
I have some data in a single-depth array. This is JSON, which would be converted to C# objects via Json.NET:
[
{
"section": 0,
"item": "Hello!"
},
{
"section": 1,
"item": "First Steps"
},
{
"section": 1,
"item": "How to Ask for Help"
},
{
"section": 2,
"item": "Your First Program"
},
{
"section": 2,
"item": "The Code"
},
{
"section": 2,
"item": "How It Works"
},
{
"section": 3,
"item": "Where To Go From Here"
}
]
Using Entity Framework or some other method, I have arrived at a simple list of these objects as stated above, contained within a var variable.
Now what I want to do is get the same list, but where each section is grouped as an array within the outer array. For example, the JSON of what I want looks like this:
[
[
{
"section": 0,
"item": "Hello!"
}
],
[
{
"section": 1,
"item": "First Steps"
},
{
"section": 1,
"item": "How to Ask for Help"
}
],
[
{
"section": 2,
"item": "Your First Program"
},
{
"section": 2,
"item": "The Code"
},
{
"section": 2,
"item": "How It Works"
}
],
[
{
"section": 3,
"item": "Where To Go From Here"
}
]
]
My initial thought was to do something with a LINQ query using the groupby statement but I don't think this is what I'm looking for - groupby seems to be analogous to the SQL version so it can only be used for aggregate operations.
The only other option I have found so far is to use a LINQ query to get a list of all of the sections:
var allSections = (from x in myData select x.section).Distinct();
...and then iterate through those IDs and manually build the array:
List<List<Item>> mainList = new List<List<Item>>();
foreach (int thisSection in allSections.ToArray())
{
List<Item> thisSectionsItems = (from x in myData where x.section == thisSection select x).ToList();
mainList.Add(thisSectionsItems);
}
return mainList;
This should result in a proper enumerable that I can feed into JSON.NET and get the expected result, but this seems inefficient.
Is there a more LINQ-ish, or at least more efficient, way to split the items into groups?
You can certainly achieve this with .GroupBy()
var grouped = items
.GroupBy(x => x.section) // group by section
.Select(x => x.ToArray()) // build the inner arrays
.ToArray(); // return collection of arrays as an array

Linq query to d3.js chart

I am looking for a good way to feed a d3.js bubble chart with data from my MVC application. For example the standard bubble chart expects nested data in the form:
{
"name": "flare",
"children": [
{
"name": "analytics",
"children": [
{
"name": "cluster",
"children": [
{
"name": "CNN",
"size": 3938
}
]
},
{
"name": "graph",
"children": [
{
"name": "MTV",
"size": 3534
}
]
}
]
}
]
}
What I have on the server side is this linq query to a SQL database:
var results = from a in db.Durations
where a.Category == "watch"
group a by a.Description
into g
select new
{
name = g.Key,
size = g.Select(d => new{d.Begin, d.End}).Sum(d => SqlFunctions.DateDiff("hh", d.Begin, d.End))
};
return Json(results, JsonRequestBehavior.AllowGet);
The query result, parsed as Json, looks like this:
[{"name":"CNN","size":1950},{"name":"MTV","size":1680}]
I've got stuck in the head on what would be a good way to achieve the correct formatting and to create the nested structure from my query results..
server-side, using anonymous types
server-side, adjusting the linq-query
client-side, using d3.js nest
use a simpler bubble model since for my purpose, the nested
structure with children is not really needed
something totally different and much much cooler than 1-4
Thank you for any input.
Replace your return statement with the following one.
return Json(new
{
name = "Sites",
children = results
},
JsonRequestBehavior.AllowGet);
That will give you the following:
{
"name": "Sites",
"children": [
{
"name": "CNN",
"size": 1950
},
{
"name": "MTV",
"size": 1680
}
]
}
To serve as an example, suppose each website had an additional string Type property, with values such as "News" or "Music". Then you could do the following.
return Json(new
{
name = "Sites",
children = results.GroupBy(site => site.Type).Select(group => new
{
name = group.Key,
children = group
}
},
JsonRequestBehavior.AllowGet);
This would give you something like the following.
{
"name": "Sites",
"children": [
{
"name": "News",
"children": [
{
"name": "CNN",
"size": 1950
},
{
"name": "The Verge",
"size": 1600
}
]
},
{
"name": "Music",
"children": [
{
"name": "MTV",
"size": 1680
},
{
"name": "Pandora",
"size": 2000
}
]
}
]
}

Categories