cloudant view for nested json documents - json

I am trying to create a view in Cloudant DB which will pick up all the JSON documents based on the value of one field (SAVE_TYPE_SUBMIT). My problem is that, the JSON document contains nested fields. Please take a look at the sample document below.
{
"_id ": "70f79cc9309fd8b2bcca90efd871f993 ",
"_rev": "1-18fe726fc3d99f50a945ab30c9ffeb4b",
"NAME": "qqq",
"EMAIL": "qqq",
"TITLE": "qq",
"DATE_OF_REPORT": "2017/08/17",
"PUBLIC_OFFICIALS_CONTACTED": [{
"NAME_PUBLIC_OFFICIAL": "qq"
},
{
"TITLE_PUBLIC_OFFICIAL": "qq"
}
],
"MANAGER": "qq",
"SAVE_TYPE_SUBMIT": "Submit"
}
The view created is :
function(doc) {
if (("SAVE_TYPE_SUBMIT" in doc) && (doc.SAVE_TYPE_SUBMIT == "Submit")) {
emit (doc.LAST_UPDATE_BY, [doc.NAME, doc.EMAIL, doc.TITLE, doc.DATE_OF_REPORT, doc.PUBLIC_OFFICIALS_CONTACTED, doc.MANAGER]);
}
}
When I try to fetch the data from this view into my application, I do not get the value of the nested fields, i.e. NAME_PUBLIC_OFFICIAL and TITLE_PUBLIC_OFFICIAL. I see those fields as [object,object].
Please note that PUBLIC_OFFICIALS_CONTACTED can contain multiple Name and Title fields.
Please help understand how the view needs to be customized to get the value of the nested fields. I am having a hard time with this and any guidance or material will be highly appreciated!

Create a map function of this form:
function(doc) {
if (("SAVE_TYPE_SUBMIT" in doc) && (doc.SAVE_TYPE_SUBMIT == "Submit")) {
emit(doc.LAST_UPDATE_BY, { name:doc.NAME, email: doc.EMAIL, title: doc.TITLE, date: doc.DATE_OF_REPORT, officials: doc.PUBLIC_OFFICIALS_CONTACTED, manager: doc.MANAGER});
}
}
This is very similar to your map function except that it emits a value which is an Object instead of an array. This object can represent a subset to the original document.
If you need ALL the fields from the original document, then you could modify the function to:
function(doc) {
if (("SAVE_TYPE_SUBMIT" in doc) && (doc.SAVE_TYPE_SUBMIT == "Submit")) {
emit(doc.LAST_UPDATE_BY, null);
}
}
and add ?include_docs=true when querying the view to add the original document bodies to the response.

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

Best Schema for a Data List in JSON for RestFul API to use in Angular

I've been wondering for some days what kind of scheme would be more appropriate to use a data list in json in a web application.
I'm developing a REST Web Application, and im using Angular for front end, i should order, filter and print these data list also in xml ...
For you what scheme is better and why?
1) {
"datas": [
{ "first":"","second":""},
{ "first":"","second":""},
{ "first":"","second":""}
]
}
2) {
"datas": [{
"data": { "first":"","second":""},
"data": { "first":"","second":""},
"data": { "first":"","second":""}
}]
}
3) [
{ "first":"","second":""},
{ "first":"","second":""},
{ "first":"","second":""}
]
Thanks so much.
The first and third notations are quite similar because the third notation is included in your first.
So the question is "Should I return my datas as an array or should I return an object with a property that contain the array ?
It will depend on either you want to have more information alongside your datas or not.
For exemple, if your API might return an error, you will want to manage it from the front end.
In case of error, the JSON will looks like this :
{
"datas": null,
"error": "An error occured because of some reasons..."
}
At the opposite, if everything goes well and your API actually return the results, it will looks like this :
{
"datas": [
{ "first":"","second":""},
{ "first":"","second":""},
{ "first":"","second":""}
],
"error": null
}
Then your front end can use the error property to manage errors sent from the API.
var result = getDatas(); // Load datas from the API
if(result.error){
// Handle the error, display a message to the user, ...
} else {
doSomething(result.datas); // Use your datas
}
If you don't need to have extra properties like error then you can stick with the third schema.
The second notation is invalid. The datas array will contain only one object which will have one property named data. In this case data is a property that is defined multiple times so the object in the array will contain only the last occurence:
var result = {
"datas": [{
"data": { "first":"a","second":"b"},
"data": { "first":"c","second":"d"},
"data": { "first":"e","second":"f"}
}]
}
console.log("Content of result.datas[0].data : ")
console.log(result.datas[0].data)
Obviously the first option would be easy to use. Once you will access datas it'll give you an array. Any operation (filter, sort, print) on that array will be easy in comparison to anything else. Everywhere you just need to pass datas not datas.data.

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.

Why does only one of these CouchDB views work?

I have some documents in CouchDB of type: "Person" and of type "Account".
For a straight list of all documents of these types I have created the following design document in _design/directory_views:
{
"_id": "_design/directory_views",
"_rev": "21-f5df9253504e66f28393f4bda360e110",
"views": {
"all_persons": {
"map": "(function(doc) { if (doc.type == \"Person\") { emit(null, { 'last_name': doc.last_name, 'first_name': doc.first_name }); } })"
},
"all_accounts": {
"map": "(function(doc) { if (doc.type == \"Account\") { emit(null, { 'username': doc.username }); } })"
}
}
}
This JSON validates on JSONLint and is accepted when saving the document in Futon's source view.
Futon lists directory_views/all_persons and directory_views/all_accounts as expected in the dropdown. all_persons creates the correct list of documents of type "Person", however all_accounts redirects back to the toplevel All Documents, and lists everything.
Why does all_persons work, but all_accounts fail?
PS. I've experienced this behavior on a number of design documents so far. This example http://kore-nordmann.de/blog/couchdb_a_use_case.html#a-practical-example shows two views in the same design document, so I don't think that you can only have one view per document.
Try accessing your view directly (ie. outside of Futon) to see if it behaves the same way.