Storing nested Json in parse.com classes - json

I'm setting up my data structure for my first parse.com app. I am from a relational data background, so I'm not sure what I can and can't do when storing data in parse.
So my question is, can I store the Json below in a parse class as one object or would I have to split it into multiple classes / objects for the "fixtures" and "location" fields?
{
"name": "fast five",
"rules": "round robin",
"location": [
{
"long":"18.633456",
"lat":"-33.880178",
"venue":"Velodrome, Cape Town",
"date_time":"2011-08-21T18:02:52.249Z"
}
],
"teams": [
"gauteng west",
"gauteng north"
],
"fixtures": [
{
"teamA":"gauteng west",
"teamB":"gauteng west",
"court":"court 5",
"date_time":"2011-08-21T18:02:52.249Z"
},
{
"teamA":"gauteng west",
"teamB":"gauteng west",
"court":"court 5",
"date_time":"2011-08-21T18:02:52.249Z"
}
]
}

Parse supports storing JSON in a column on a Parse Object, but you won't be able to query based on values inside that. It's tough to quickly sketch out the perfect schema for using more of Parse Data, but it could be something like this:
Location class
-venue : "Velodrome, Cape Town"
-date : a date object
-location: a Parse GeoPoint object with that lat/lon
Team class:
-name: "gauteng west"
Fixture class:
-teamA : a Team class object
-teamB : a Team class object
-location: a Location class object
-court : "court 5"
-date : a date object
Event class
-name : "fast five"
-rules : "round robin"
-teams : an array of Team class objects
-location: a Location class object
-fixtures: an array of Fixture class objects
With this separation, you could get all the data for an event at once:
var query = new Parse.Query("Event");
query.include(['teams', 'fixtures', 'location']);
query.first().then(function(event) {
var teams = event.get('teams');
console.log(teams[0].get('name'));
});
Or query for events near a given location:
var locQuery = new Parse.Query("Location");
locQuery.near("location", a Parse GeoPoint object);
var query = new Parse.Query("Event");
query.matchesQuery("location", locQuery);
query.find().then(function(results) {
// has all events sorted by distance from provided geopoint
}, function(err) {
// error
});
And many other benefits..

Related

Trying to get values form a list inside a JSON respons

My goal is to loop over a JSON response, grab two values, and build out an API call to load information into a POST to create a policy I am building.
To start out on this, I am trying to get two values from a JSON response to assign as variables to build the POST call, which will be the second step to this. As each different "id" and "name" key is assigned, I would like to build out a JSON payload and send the POST calls one at a time. The keys "id" and "name" occurs multiple times in the response payload and I am having issues capturing the two keys.
JSON response
data = {
"data":[
{
"id":"02caf2be-3245-4d3d",
"name":"ORA-FIN-ACTG",
"description":"Oracle",
"links":{
"web":"https://com/",
"api":"https://com/"
}
},
{
"id":"03af2f46-fad6-41a1",
"name":"NBCMAINFRAME",
"description":"Network",
"links":{
"web":"https://com/",
"api":"https://com/"
}
},
{
"id":"0649628b-0e3b-48df",
"name":"CAMS",
"description":"Customer",
"links":{
"web":"https://com/",
"api":"https://com/"
}
},
{
"id":"069d4bcf-3e50-4105",
"name":"SHAREPOINTSITES",
"description":"Sharepoint",
"links":{
"web":"https://com/",
"api":"https://com/"
}
}
],
"took":0.013,
"requestId":"1f364470"
}
I have tried various "for loops" to grab the data. Here is one of the loops below:
data = json.loads(data)
data[0]['data'][0]['name']
for item in range(len(data)):
print(data[item]['data'][0]['name'])
I have also tried reading it as a dictionary:
for data_dict in data:
for key, value in data_dict.items():
team.append(key)
id.append(value)
print('name = ', team)
print('id = ', id)
I am also getting KeyError's and TypeError: the JSON object must be str, bytes or bytearray, not 'dict'.
Any help is appreciated.
FYI, this is the payload I am wanting to populate with the "name" and "id" values:
data= {
"type":"alert",
"description":"policy",
"enabled":"true",
"filter":{
"type":"match-any-condition",
"conditions":[
{
"field":"extra-properties",
"key":"alertOwner",
"operation":"equals",
"expectedValue":name
}
]
},
"ignoreOriginalResponders": "true",
"ignoreOriginalTags": "true",
"continue": "true",
"name": str(name) + " Policy",
"message":"{{message}}",
"responders":[{"type":"team","id":id}],
"alias":"{{alias}}",
"tags":["{{tags}}"],
"alertDescription":"{{description}}"
}
The JSON response which you have given is already a dictionary so no need to use json.loads for that. The multiple item list is actually nested under the data key. So you could just simply iterate through the array of items like this:
for item in data["data"]:
print("{} : {}".format(item["id"],item["name"]))
This is the output:
02caf2be-3245-4d3d : ORA-FIN-ACTG
03af2f46-fad6-41a1 : NBCMAINFRAME
0649628b-0e3b-48df : CAMS
069d4bcf-3e50-4105 : SHAREPOINTSITE

How to process nested json with FOSRestBundle and symfony forms

By nested json I mean something that keeps address data in its own "address" array:
{
"user": {
"id": 999,
"username": "xxxx",
"email": "xxxx#example.org",
"address": {
"street": "13th avenue",
"place": 12
}
}
}
instead of flat one
{
"user": {
"id": 999,
"username": "xxxx",
"email": "xxxx#example.org",
"street": "13th avenue",
"place": 12
}
}
Flat one is processed fine there using User entity and it's properties: "id", "username" and "email". It is nicely validated using symfony form feature:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('username');
$builder->add('email', 'email');
$builder->add('password', 'password');
$builder->add('street');
$builder->add('place');
}
I want to have both "street" and "place" as properties in User entity, to store it all in one user table in the database, using doctrine. But the json I get comes from third party, so I can not modify it.
Is there any way of constructing the form so it can validate the json with "address" field correctly, still being able to keep all the user data in one table?
This is a pretty good question. One solution that comes to mind is making an unmapped form and binding data manually using a form event, for example:
public function buildForm(FormBuilderInterface $builder, array $options)
{
// Make a "nested" address form to resemble JSON structure
$addressBuilder = $builder->create('address', 'form')
->add('place')
->add('street');
$builder->add('username');
$builder->add('email', 'email');
$builder->add('password', 'password');
// add that subform to main form, and make it unmapped
// this will tell symfony not to bind any data to user object
$builder->add($addressBuilder, null, ['mapped' => false]);
// Once form is submitted, get data from address form
// and set it on user object manually
$builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
$user = $event->getData();
$addressData = $event->getForm()->get('address')->getData();
$user->setStreet($addressData['street']);
$user->setPlace($addressData['place']);
})
}

Access object property while working with another object in ng-repeat

In my project I got a JSON response via GET request. The subTopics will be selected by the user and stored. Afterwards I send a POST request to the server with the selected ids.
Example JSON1: (from GET request)
{
"TopicList" :
[{
"id": "1234",
"name": "topic1",
"number": "1",
"subTopics": [
{
"id": "4567",
"name": "subTopic1.1",
"number": "1.1"
},
{
"id": "9876",
"name": "subTopic1.2",
"number": :1.2"
}
]
}]
}
In the POST response I get another JSON object from the server, which I have to show in my HTML view as a table. In the response JSON I have the subTopics id (selected by the user) but I do not have the subTopic name associated with the id.
I have to show the subTopic name in my table which is available in a separate object(see above JSON file). I don't know how to access the first JSON object while working with another.
My table view looks like this,
<tr ng-repeat-start="tableRow in searchCtrl.tableViewData" ng-click="tableRow.expanded = !tableRow.expanded">
<td>{{tableRow.project.name}}</td>
<td>{{tableRow.project.number}}</td>
<td>{{tableRow.project.endDate | date}}</td>
<td>{{tableRow.topicIds[0]}}</td>
<td>{{tableRow.matching.score}}</td>
</tr>
As you can see the 4th row: <td>{{tableRow.topicIds[0]}}</td> shows the id. How can I show the topicName?
Any help would be appreciable.
EDIT
In my controller this variable contains the above JSON object.
if (!self.topic) {
searchService.getTopic().then(
function (response) {
self.topic = response.data;
},
function (error) {
alert("Server is not found");
}
);
}
So, the topic variable contains the response JSON object. Maybe it will help.
You can create a function that takes an id and returns the subTopic.
$scope.getSubTopic = function(id) {
var selectedSubTopic = {};
angular.forEach(subTopics, function(subTopic) {
// loop through subTopics until a matching id is found
if (subTopic.id === id) {
selectedSubTopic = subTopic;
return;
}
});
return selectedSubTopic;
};
then you can update your fourth row to:
<td>{{getSubTopic(tableRow.topicIds[0]).name}}</td>
This assumes you have an array named subTopics.
Edit
As mentioned in my comment this will end up performing pretty slow for heavy pages and/or large datasets. You will likely want to generate a map object for the subTopics for quick access. The downside being you have to generate this each time the TopicList is modified.
function generateSubTopicMap(topics) {
var map = {};
angular.forEach(topics, function(topic) {
angular.forEach(topic.subTopics, function(subTopic) {
// use this if you want the map to reference the same data
// (i.e. updating subTopic.name will update the map at the same time)
map[subTopic.id] = subTopic;
// use this if you don't want the map to reference the same data
// map[subTopic.id] = {};
// angular.copy(subTopic, map[subTopic.id]);
// you can also add the parent id here if you need access to it
// this will modify the original object if you use the first method!
// map[subTopic.id].parentId = topic.id
});
});
return map;
}
The output looks like:
{
"4567": {
"id": "4567",
"name": "subTopic1.1",
"number": "1.1"
},
"9876": {
"id": "9876",
"name": "subTopic1.2",
"number": :1.2"
}
}
With this you would call it after every GET request and pass it the array of topics.
// where topics is the response from the GET request
$scope.subTopics = generateSubTopicMap(topics);
And finally to display you just need:
<td>{{subTopics[tableRow.topicIds[0])].name}}</td>
Edit 2
Here is a jsfiddle showing how to use the second method. All you have to do is pass the array containing your TopicList to generateSubTopicMap and it returns an object with the keys as subTopic ids and the value as the subTopic itself.
I wouldn't worry about my first solution. It isn't going to be performant inside an ng-repeat or grabbing 2nd level objects.

Push new items into JSON array

Let's say this is the table inside my collection:
{
"_id" : ObjectId("557cf6bbd8efe38c627bffdf"),
"name" : "John Doe",
"rating" : 9,
"newF" : [
"milk",
"Eggs",
"Beans",
"Cream"
]
}
Once a user types in some input, it is sent to my node server, and my node server then adds that item to the list "newF", which is then sent back to my MongoDB and saved.
I'm trying to use update, which can successfully change the values inside of this table, but I'm not sure how to add new items onto that list. I did it with $push inside the MongoDB shell, but not sure how to do it on node.
Here's a snippet of my code:
db.collection('connlist').update({ _id: new ObjectId("e57cf6bb28efe38c6a7bf6df")}, { name: "JohnDoe", rating: 9, newF: ["Milk, Eggs", "Beans"] }, function(err,doc){
console.log(doc);
});
Well the syntax for adding new items is just the same as in the shell:
// make sure you actually imported "ObjectID"
var ObjectId = require('mongodb').ObjectID;
db.collection('conlist').update(
{ "_id": new ObjectId("e57cf6bb28efe38c6a7bf6df") },
{ "$push": { "newF": { "$each": [ "cream", "butter" ] } } },
function(err,numAffected) {
// do something in the callback
}
)
Or perhaps use .findOneAndUpdate() if you want to return the modified document instead of just making the alteration.
Of course use $push and possibly with $each which allows multiple array elements to be added when adding to an array. If you want "unique" items then use $addToSet where your operation allows.
And generally speaking for other items you should use $set or other operators in the update portion of your document. Without these operators you are just "replacing" the document content with whatever structure you place in the "update" portion of your statement.

Changing the labels jsonBuilder creates automatically in Groovy after converting Obj to JSON

I have a class called Case which is like:
class Case {
String caseId;
Map <String, List<String[]>> listOfCases = new HashMap<String, ArrayList<String[]>>();
}
I created several of these cases and add them to a list. Eventually I want to print the list in JSON format in groovy like:
for (...){
// creating many of these cases in a loop and adding to caseList
caseList.add(myCase);
}
// Finally printing them
println new JsonBuilder( caseList ).toPrettyString()
Result looks like this, I chose one only:
{
"caseId": "Case_1",
"listOfCases": {
"server-config.xml": [
[
"Core",
"8"
]
],
"server-beans.xml": [
[
"plugin",
"mmap"
],
[
"maxIdle",
"16"
],
[
"minIdle",
"16"
],
[
"maxCon",
"16"
]
]
}
}
I want the listOfCases be replaced with Case_1 so each time I create a new Case with the number incremented, the name follows. Is there anyway to customize the jsonBuilder in groovy to do this ?
As stated in How to modify JSON in groovy changing the content is dangerous, as it attempts to change the original object. The solution presented there is to send the proper map.
You can prepare a map from your data and pass this like this:
class Case {
String caseId
Map<String,List<List<String>>> listOfCases
}
def caseList = (1..3).collect{
new Case(caseId: "Case$it", listOfCases: [ 'abcde': [['1','2'],['4','5']]])
}
println new groovy.json.JsonBuilder(caseList.collect{[caseId: it.caseId, "$it.caseId": it.listOfCases]}).toPrettyString()
results in
[
{
"caseId": "Case1",
"Case1": {
"abcde": [
...