How to convert inner json part into object in dart/flutter - flutter

I need to convert every element of 'content' array to object.
Here is json:
{
"content": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"author": {
"userId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
},
"createDate": "2020-01-30T20:18:29.764Z",
"executor": {
"userId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
},
}
],
"first": true,
"numberOfElements": 0,
}
The problem is that 'content' array is inside json, and its parts as executor and author has to be objects too, and I don't know how to reach it and parse.
How it can be done? Any help, thanks.

You access the elements like this:
var decoded = json.decode(j);
var inner = decoded['content'][0]; // if you expect more than one entry, iterate the list
print(inner['id']); // -> 3fa85f64-5717-4562-b3fc-2c963f66afa6
print(inner['createDate']);
print(inner['author'].runtimeType); // another Map<String, dynamic> as expected
print(inner['author']['userId']);
You can create Dart classes to model, for example, a 'user' if you want.

Related

Decode a nested array with circe-optics

I have JSON like this:
"data": {
"project": {
"activityChildren": [
{
"id": 2,
"parents": [
{
"id": 1
}
]
},
]
}
}
I'd like to decode this to List[(Long, List[Long])] with circe-optics. I got as far as:
val activityParents: Map[Long, List[Long]] = root.data.activityChildren.each.json
.getAll(json)
.flatMap { activity =>
root.id.long.getOption(activity).map(_ -> root.parents.each.long.getAll(activity))
}
.toMap
I wonder whether it's possible to define a single lens for this that just turns the JSON into the desired map without explicitly mapping over the intermediate array. If so, how?

Update a nested array mongoose

I've asked this before, but people just want to label this a duplicate instead of answering it.
Looking for a simple answer here. Can the below be done or not?
I have a huge schema that I inherited. It has an array of objects and I am targeting an array of objects nested within that. cssClass is what I'm trying to update. Here is the schema
board: {
"lists": [
{ "id": 12,
"cards": [
{ "id": 123,
"cssClass": "default"
},
{ "id": 124,
"cssClass": "default"
}
]
}
]
}

update MongoDB document sub object without replacing

So I have this document in my database like below
{
"_id": {
"$oid": "59a8668f900bea0528b63fdc"
},
"userId": "KingSlizzard",
"credits": 15,
"settings": {
"music": 1,
"sfx": 0
}
}
I have this method for updating just specific fields in a document
function setPlayerDataField(targetUserId, updateObject) {
playerDataCollection.update({
"userId": targetUserId //Looks for a doc with the userId of the player
}, { $set: updateObject }, //Uses the $set Mongo modifier to set value at a path
false, //Create the document if it does not exist (upsert)
true //This query will only affect a single object (multi)
);
}
It works fine if I do a command like
setPlayerDataField("KingSlizzard",{"credits": 20});
It would result in the document like this
{
"_id": {
"$oid": "59a8668f900bea0528b63fdc"
},
"userId": "KingSlizzard",
"credits": 20,
"settings": {
"music": 1,
"sfx": 0
}
}
The value for credits is now 20 yay! This is desired.
However, if I do this command...
setPlayerDataField("KingSlizzard",{"settings": {"music":0}});
It would result in the document like this
{
"_id": {
"$oid": "59a8668f900bea0528b63fdc"
},
"userId": "KingSlizzard",
"credits": 20,
"settings": {
"music": 0
}
}
All I wanted to do was set only the settings/music value to 0. This result is NOT desired since we lost the sfx value.
So my question is, how do I update a value in a sub object without replacing the whole sub object itself?
To set a specific property of a child document, use dot notation. In your example, write it as:
setPlayerDataField("KingSlizzard", {"settings.music": 0});
See the example in the MongoDB docs.
To specify a <field> in an embedded document or in an array, use dot notation.

Elasticsearch for normalized data

I have some data from facebook api's...
I have a FB page and thats part of multiple country...
For example:-
Assume company-x operated in multiple countries - USA, UK, India, China
Now, a page can be posted on multiple country pages.
For example:- Company-x new innovation will be displayed in all the 4 country pages...
Each of the pages will get its over comments, likes...etc...
So, basically its a relational data.
Company(1) - Country(n)- Post(n) - LIkes(n) - Comments(n)...
I would like to know what would be the best way to store this data in elastic search and implement the search engine..
As you can't use "classic" (relational) JOINs in Elasticsearch, IMHO you only can choose between storing the (sub-)objects as flat objects, parent-child objects or nested objects in the index/type.
I think that you should consider the first two option. I personally would opt for flat objects, as they are easier to load, and also get returned from the FB Graph API in that way ("flat"). What you would have to add in you application is the mapping of the page to Company -> Country, because FB doesn't know about that.
See
https://www.elastic.co/guide/en/elasticsearch/guide/current/nested-mapping.html
https://www.elastic.co/guide/en/elasticsearch/guide/current/parent-child-mapping.html
As a query for the posts, you could use something like
/?ids={page1_id},{page2_id},{page3_id}&fields=id,posts.fields(id,message,created_time,link,picture,place,status_type,shares,likes.summary(true).limit(0),comments.summary(true).limit(0))
which will return something like
{
"id": "7419689078",
"posts": {
"data": [
{
"id": "7419689078_10153348181604079",
"message": "Gotta find them all in the real world soon.",
"created_time": "2015-09-10T06:40:12+0000",
"link": "http://venturebeat.com/2015/09/09/nintendo-takes-pokemon-into-mobile-gaming-in-partnership-with-google-niantic/",
"picture": "https://fbexternal-a.akamaihd.net/safe_image.php?d=AQDvvzpCAM1WkJZS&w=130&h=130&url=http%3A%2F%2Fi0.wp.com%2Fventurebeat.com%2Fwp-content%2Fuploads%2F2013%2F04%2Fpokemon_mystery_dungeon_gates_to_infinity_art.jpg%3Ffit%3D780%252C9999&cfs=1",
"status_type": "shared_story",
"likes": {
"data": [
],
"summary": {
"total_count": 0,
"can_like": true,
"has_liked": false
}
},
"comments": {
"data": [
],
"summary": {
"order": "ranked",
"total_count": 0,
"can_comment": true
}
}
}
],
"paging": {
"previous": "https://graph.facebook.com/v2.4/7419689078/posts?fields=id,message,created_time,link,picture,place,status_type,shares,likes.summary%28true%29.limit%280%29,comments.summary%28true%29.limit%280%29&limit=1&since=1441867212&access_token=&__paging_token=&__previous=1",
"next": "https://graph.facebook.com/v2.4/7419689078/posts?fields=id,message,created_time,link,picture,place,status_type,shares,likes.summary%28true%29.limit%280%29,comments.summary%28true%29.limit%280%29&limit=1&access_token=&until=1441867212&__paging_token="
}
}
}
You can then use some application-side JSON manipulation to
Add the Company -> Country -> Page mapping info to the JSON
Get rid of unwanted fields such as paging
Flatten the structure before saving (e.g. posts.data as posts)
before you save it to Elasticsearch. See the JSFiddle I prepared (fill in the access token!):
http://jsfiddle.net/7x9xuo8L/
Then, you can use the bulk load feature to load the data to Elasticsearch:
https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html
Sample JavaScript code:
var pageMapping = {
"venturebeat": {
"country": "United States",
"company": "Venture Beat"
},
"techcrunch": {
"country": "United States",
"company": "TechCrunch"
}
};
//For bulk load
var esInfo = {
"index": "socialmedia",
"type": "fbosts"
};
var accessToken = "!!!FILL_IN_HERE_BEFORE_EXECUTING!!!";
var requestUrl = "https://graph.facebook.com/?ids=venturebeat,techcrunch&fields=id,name,posts.fields(id,message,created_time,link,picture,place,status_type,shares,likes.summary(true).limit(0),comments.summary(true).limit(0)).limit(2)&access_token=" + accessToken;
$.getJSON(requestUrl, function(fbResponse) {
//Array to store the bulk info for ES
var bulkLoad = [];
//Iterate over the pages
Object.getOwnPropertyNames(fbResponse).forEach(function(page, idx, array) {
var pageData = fbResponse[page];
var pageId = pageData.id;
pageData.posts.data.forEach(function(pagePostObj, idx, array) {
var postObj = {};
postObj.country = pageMapping[page].country;
postObj.company = pageMapping[page].company;
postObj.page_id = pageData.id;
postObj.page_name = pageData.name;
postObj.post_id = pagePostObj.id;
postObj.message = pagePostObj.message;
postObj.created_time = pagePostObj.created_time;
postObj.link = pagePostObj.link;
postObj.picture = pagePostObj.picture;
postObj.place = pagePostObj.place;
postObj.status_type = pagePostObj.status_type;
postObj.shares_count = pagePostObj.shares.count;
postObj.likes_count = pagePostObj.likes.summary.total_count;
postObj.comments_count = pagePostObj.comments.summary.total_count;
//Push bulk load metadata
bulkLoad.push({ "index" : { "_index": esInfo.index, "_type": esInfo.type } })
//Push actual object data
bulkLoad.push(postObj);
});
});
//You can now take the bulkLoad object and POST it to Elasticsearch!
console.log(JSON.stringify(bulkLoad));
});

Meteor. Sorting my collection by a deeply nested value

In my application I have a list of tiles representing each project in a portfolio. This is the main list view for the app and all projects are fetched from the collection without any sorting or ordering.
When I have an optional slug parameter specified in my route (for the category assigned to the project) I want to be able to display the projects within the UI that match that category first, and then display the other ones that don't match the category.
For reference, I have included the code for the route below:
/**
* Project list view (all projects) with optional
* filter parameter for showing projects only by
* their category name.
*/
this.route('list', {
path: '/:_category_slug?',
template: 'template_main',
action: function() {
if(this.ready()) {
this.render();
}
},
waitOn: function() {
return [
Meteor.subscribe('projects'),
Meteor.subscribe('formations'),
Meteor.subscribe('categories')
];
},
data: function() {
if(this.params._category_slug) {
/**
* Building up the query given the category slug and the language
*/
var query = {};
query['slug.' + App.language] = this.params._category_slug;
/**
* Grab the category given the query, so we can get its 'id'
*/
var category = App.models.categories.findOne(query);
/**
* This is the query I need to work on so that I can achieve what I want
*/
return App.models.projects.find({}).fetch();
}
else {
return App.models.projects.find({}).fetch();
}
},
yieldTemplates: {
'components_header': {to: 'header'},
'views_list': {to: 'content'},
'components_footer': {to: 'footer'}
}
});
For reference, I have also included a sample of the data for three projects that is relevant to this question.
{
"id": 10,
"slug": {
"en": "sample-english-slug",
},
"title": {
"en": "Sample English Title",
},
"description": {
"en": "A good description.",
},
"category_ids": [
{
"id": 5
},
{
"id": 6
}
],
},
{
"id": 12,
"slug": {
"en": "another-sample-slug",
},
"title": {
"en": "Another sample title",
},
"description": {
"en": "Sample description three",
},
"category_ids": [
{
"id": 1
},
{
"id": 4
}
],
},
{
"id": 11,
"slug": {
"en": "another-sample-slug",
},
"title": {
"en": "A sample title",
},
"description": {
"en": "Sample description",
},
"category_ids": [
{
"id": 2
},
{
"id": 5
}
],
}
So what I would want to do is make sure that given a category with an ID of 5, I want those first two projects to be the first two that appear.
Can this be done in meteor, without having to resort to writing extra logic in JS? One approach I did have once was to update each project from within the Client side collection (something I no longer do) and set a few extra attributes, then sort after that.
When dealing with syncing client and server collections, this is not really feasible.
From the mongodb docs:
Use the dot notation to match by specific fields in an embedded document. Equality matches for specific fields in an embedded document will select documents in the collection where the embedded document contains the specified fields with the specified values. The embedded document can contain additional fields.
I don't know if you can do it with a single query, but you can concat two complementary queries that use dot notation.
var selected = App.models.projects.find({'category_ids.id': category._id}).fetch();
var other = App.models.projects.find({'category_ids.id': {$ne: category._id}}).fetch();
return selected.concat(other);