Split JSON API's object into chunks in express app - javascript

I want to separate my API's output into different pages. I want to call them like this: http://127.0.0.1:3000/api/articles/0/<API-TOKEN>
Which is returning the first page with 2-3, etc. articles.
Full code can be found here: https://github.com/DJviolin/horgalleryNode/blob/master/routes/api.js
I have dummy data JSON file:
[
{
"articles": [
{
"id": "0",
"url": "audrey-hepburn",
"title": "Audrey Hepburn",
"body": "Nothing is impossible, the word itself says 'I'm possible'!",
"category": "foo",
"tags": [ "foo" ]
},
{
"id": "1",
"url": "walt-disney",
"title": "Walt Disney",
"body": "You may not realize it when it happens, but a kick in the teeth may be the best thing in the world for you.",
"category": "foo",
"tags": [ "foo", "bar" ]
},
{
"id": "2",
"url": "unknown",
"title": "Unknown",
"body": "Even the greatest was once a beginner. Don't be afraid to take that first step.",
"category": "bar",
"tags": [ "foo", "bar", "baz" ]
},
{
"id": "3",
"url": "neale-donald-walsch",
"title": "Neale Donald Walsch",
"body": "You are afraid to die, and you're afraid to live. What a way to exist.",
"category": "bar",
"tags": [ "foo", "bar", "baz" ]
}
]
},
{
"users": [
{ "name": "Admin" },
{ "name": "User" }
]
}
]
Which is called into my API router this way:
function fsAsync(callback) {
fs.readFile(__dirname + '/../public/articles/data.json', 'utf8', function(err, data) {
if (err) {
return callback(err);
}
callback(null, JSON.parse(data));
});
};
I calling every articles like this at this route: http://127.0.0.1:3000/api/articles/<API-TOKEN>
router.get('/articles/:token', function(req, res) {
fsAsync(function(err, data) {
if (err) {
return res.send(err);
}
var articles = data[0].articles;
var q = articles.filter(function (article) {
// return article.id === req.params.id;
return article && apiToken === req.params.token;
});
res.json(q);
});
});
However I want to separate this API's output into different pages when I rendering this API route: http://127.0.0.1:3000/api/articles/0/<API-TOKEN>
I tried to implement the array.slice method described here: https://stackoverflow.com/a/8495740/1442219
How can it be achieved?
Thank You!
Update:
One thing could cause problem if I spliting a JSON object into chunks, than it will need to parse everything from that object first, than decide where to split? What if user visiting the last page at 567? This means the code first have to query through millions of millions of line to return the wanted page? What if it's a database, not a JSON with dummy data? In sort, what are the best practices to return a specific page for a blog from JSON / Mongodb / etc. source?
Update 2:
This returns the first two article from the object:
// http://127.0.0.1:3000/api/articles/c/<API-TOKEN>
router.get('/articles/c/:token', function(req, res) {
fsAsync(function(err, data) {
if (err) {
return res.send(err);
}
var articles = data[0].articles;
var count = 0;
var countMultiply = count * 2;
var a = countMultiply + 0;
var b = countMultiply + 2;
var c = articles.slice(a, b);
console.log(c);
var q = c.filter(function (article) {
// return article.id === req.params.id;
return article && apiToken === req.params.token;
});
res.json(q); // (0*2+0=0, 0*2+2=2), (1*2+0=2, 1*2+2=4), (2*2+0=4, 2*2+2=6), (3*2+0=6, 3*2+2=8)
});
});
How can I automate this able to use 0, 1, 2, 3 for page separators? So the first page at http://127.0.0.1:3000/api/articles/0/<API-TOKEN> URL to return articles (0,2) and the second page at http://127.0.0.1:3000/api/articles/1/<API-TOKEN> URL to return articles (2,4)?

Update 3:
Looks like it's working:
// http://127.0.0.1:3000/api/articles/page/0/<API-TOKEN>
router.get('/articles/page/:id/:token', function(req, res) {
fsAsync(function(err, data) {
if (err) {
return res.send(err);
}
var articles = data[0].articles.reverse();
var count = req.params.id; // Page number
var multiplier = 2; // Posts per page
var countResult = count * multiplier;
var a = countResult + 0;
var b = countResult + multiplier;
var c = articles.slice(a, b);
var pagesLength = articles.length / multiplier;
var pagesLengthCeil = Math.ceil(pagesLength); // Sum of all pages
console.log('pagesLengthCeil: ' + pagesLengthCeil);
console.log(c);
var q = c.filter(function (article) {
// return article.id === req.params.id;
return article && apiToken === req.params.token;
});
res.json(q); // (0*2+0=0, 0*2+2=2), (1*2+0=2, 1*2+2=4), (2*2+0=4, 2*2+2=6), (3*2+0=6, 3*2+2=8)
});
});
But I still don't know is it an efficient way to do this with huge JSON files or a database?

Related

How to check a propery of an object inside another object exist?

Using the following array with two objects that include another object inside I would like to know if there is a way to find which is the applicationID has applicantID equal with "5bd3480af09ddf0258c3a31d"
Array [
Object {
"__typename": "JobApplication",
"_id": "5be097753397465946e051fd",
"applicant": Object {
"__typename": "User",
"_id": "5bd83b9a62a9f33cf0f1033b",
},
},
Object {
"__typename": "JobApplication",
"_id": "5bdc7c8b3241cb5bc10ac694",
"applicant": Object {
"__typename": "User",
"_id": "5bd3480af09ddf0258c3a31d",
},
},
]
So in this case it has to return "5bdc7c8b3241cb5bc10ac694".
This are my two constants first will return the user id and second will return only the applications id.
const { _id } = this.props.getMe.me._id;
const applicationID = getJobApplicationsForThisJob.map(application => application._id);
I could check if the user id is in any of the objects like this
const applicantId = getJobApplicationsForThisJob.map(user => user.applicant._id);
const hasApplied = applicantId.includes(_id);
Thanks
You can use the .find method.
var item = arr.find(function(x) {
return x.applicant._id == find;
});
The find method returns the first found item or undefined if a record was not found.
var arr = [{
"__typename": "JobApplication",
"_id": "5be097753397465946e051fd",
"applicant": {
"__typename": "User",
"_id": "5bd83b9a62a9f33cf0f1033b",
}
}, {
"__typename": "JobApplication",
"_id": "5bdc7c8b3241cb5bc10ac694",
"applicant": {
"__typename": "User",
"_id": "5bd3480af09ddf0258c3a31d",
},
}];
var find = "5bd3480af09ddf0258c3a31d";
var item = arr.find(function(x) {
return x.applicant._id == find;
});
console.log(item != undefined ? item._id : "Not Found");
Since you are using Javascript, you would want to do something like my comment,
let _id = "5bd3480af09ddf0258c3a31d";
let list = []; //your list of objects.
let applicationResults = list.where( (app) => app.applicant._id = _id);
if (applicationResults.length == 0){
console.log("no applicantion with this applicant found.");
}else {
console.log("Found " + applicationResults.length + " Applications from this Applicant")
return applicationResults;
}

How to delete an item in a json file via http delete request in nodeJS?

So Basically, i have a JSON file which consists of user and group data. I want to delete a particular group. This is what my JSON file looks like:
authdata.json:
[{
"name": "Allan",
"role": ["Group Admin", "Super Admin"],
"group": ["Cool-Group", "ss"]
}, {
"name": "super",
"group": ["Nerd Group"],
"role": ["Super Admin"]
}, {
"name": "Terry",
"role": ["Group Admin"],
"group": ["Cool-Group"]
}, {
"name": "Kaitlyn",
"role": ["Group Admin"],
"group": ["Nerd-Group"]
}, {
"name": "Alex",
"role": ["Group Admin"],
"group": ["Cool-Group"]
}]
I'm just confused on how to handle a http delete request in nodeJS?
this how my angular component is sending the request to the server:
remove.component.ts:
RemoveGroup() {
this.httpService.delete < any > (this.apiURL + 'deletegroup', {
group: this.groups
}).subscribe(
data => {
if (data['success'] == true) {
alert(data.group + " is removed");
} else {
alert("No groups found");
}
},
(err: HttpErrorResponse) => {
console.log(err.message);
}
);
}
This is the server side on NodeJS (reading the json file, assigning the data to a variable, trying to delete the group (which is not working for me) and writting back to the JSON file):
deletegroup.js:
app.delete('/api/deletegroup', (req, res) => {
// localhost:3000/api/auth?username=Terry
var groups = req.body.group;
var userObj;
fs.readFile('authdata.json', 'utf8', function(err, data) {
if (err) {
console.log(err);
//Some error happended opening the file. No Success
res.send({
'group': '',
'success': false
});
} else {
userObj = JSON.parse(data);
for (let i = 0; i < userObj.length; i++) {
for (let j = 0; i < userObj.length; j++) {
if (userObj[i].group[j] == groups) {
userObj.splice(userObj.indexOf(groups), 1);
//find first instance of user name and success
}
}
}
var newdata = JSON.stringify(userObj);
fs.writeFile('authdata.json', newdata, 'utf-8', function(err) {
if (err) throw err;
//Send response that registration was successfull.
res.send({
'group': groups,
'success': true
});
});
//no username was found that matched
}
});
});
I assume that the problem is not with HTTP DELETE request. The concern is with how to remove a child node. See the below code snippet. You can pass the groups as an array to the deleteGroup function and see the result.
var data = [{
"name": "Allan",
"role": ["Group Admin", "Super Admin"],
"group": ["Cool-Group", "ss"]
}, {
"name": "Terry",
"role": ["Group Admin"],
"group": ["Cool-Group"]
}];
function deleteGroup(groupArray) {
groupArray.map((needle)=>{
data.map((userObj) => {
if(userObj.group) {
userObj.group.map((groupName, index)=>{
if (groupName == needle){
userObj.group.splice(index)
}
});
} else {
console.log("No group present for this user.")
}
});
});
return data
}
//uncomment below line & run in console to see the results
//console.log(deleteGroup(["Cool-Group"]))
Try out directly in Jsbin - https://jsbin.com/pejidag/1/edit?js,console
Happy coding!

Creating Tableau WDC from associative array

I am creating a Tableau Web Data Connector as described in their Getting Started guide HERE.
I have implemented a similar solution previous with data from a basic associative array, but I am having trouble with my current API call.
I make an API call to an external web service that returns a JSON similar to the one listed below (simplified version)(COMMENT simply added for clarity and not part of original code).
{
"status": true,
"employee": {
"id": 123
},
"company": {
"id": 123
},
"job": {
"id": 123,
"job_workflows": [{
"id": 1,
"name": "Start",
"action_value_entered": "Start"
}, {
"id": 2,
"name": "Date",
"action_value_entered": "2017-09-11"
}, {
"id": 3,
"name": "Crew",
"action_value_entered": "Crew 3"
},
**COMMENT** - 17 other unique fields - omitted for brevity
]
}
}
For my requirements, I need to create a new column for each of my JSON job_workflows to display the data in Tableau. I.e.
Column 1 Header = Start
Column 1 value = Start
Column 2 Header = Date Complete
Column 2 value = 2017-09-11
Etc.
My Tableau JavaScript file looks as below:
(function () {
var myConnector = tableau.makeConnector();
myConnector.init = function(initCallback) {
initCallback();
tableau.submit();
};
myConnector.getSchema = function (schemaCallback) {
var cols = [
{ id : "start", alias : "Start", dataType: tableau.dataTypeEnum.string },
{ id : "date", alias : "Date", dataType: tableau.dataTypeEnum.datetime },
{ id : "crew", alias : "Crew", dataType: tableau.dataTypeEnum.string }
];
var tableInfo = {
id : "myData",
alias : "My Data",
columns : cols
};
schemaCallback([tableInfo]);
};
myConnector.getData = function (table, doneCallback) {
$.getJSON("http://some/api/call/data.php", function(response) {
var resp = response; // Response is JSON as described above
var tableData = [];
// Iterate over the JSON object
for (var i = 0, len = feat.length; i < len; i++) {
// not working
tableData.push({
"start": resp.job.job_workflows[i].action_value_entered,
"date": resp.job.job_workflows[i].action_value_entered,
"crew": resp.job.job_workflows[i].action_value_entered
});
}
table.appendRows(tableData);
doneCallback();
});
};
tableau.registerConnector(myConnector);
})();
How do I iterate over the JSON job_workflow and assign each action_value_entered to be a id in my getSchema() function? My current version simply hangs and no values are returned. NO error or warnings thrown.

Recursive function in node.js using q library

I need to prepare JSON by fetching the data from MYSQL. I have data in MYSQl in tree structure. I am trying to make recursive function to prepare JSON to meet the requirement but getting errors, I have following two files
main.js
/* jshint node: true */
"use strict";
var db = require('./helpers/db');
var list_worker = require('./workers/list');
var Q = require("q");
module.exports = function (app) {
/**
* return the profiles list
*/
app.get('/api/lists/get_list_tree_by_user/:user_id', function (req, res) {
list_worker.fetch_list_tree(req.params.user_id, 0).done(function (out) {
console.log(out);
res.send(out);
});
});
};
list.js
/* jshint node: true */
"use strict";
var db = require('../helpers/db');
var Q = require("q");
var output = {
data: []
};
var fetch_list_tree = function (user_id, list_id) {
// prepare query to fetch lists assosiated with a user.
var query = "SELECT b.`id`, b.`name` FROM `lists_users` a JOIN `lists` b ON(a.`list_id` = b.`id`) WHERE a.`user_id` = " + user_id + " AND a.`user_role` = 'owner' AND b.`parent_id` = " + list_id + " AND b.`deleted` = 'N';";
return db.query(query).then(function (result) {
if (result.length > 0) {
var lists = result.map(function (list, index) {
output.data[index] = {
label: list.name,
data: {
id: list.id
}
};
return fetch_list_tree(user_id, list.id).then(function (leaf_childs) {
output.data[index].children = [];
output.data[index].children.push(leaf_childs);
return leaf_childs;
});
});
return Q.all(lists).then(function (data) {
return output;
}, function (err) {
throw err;
});
} else {
return [];
}
}, function (err) {
throw err;
});
};
module.exports = {
fetch_list_tree: fetch_list_tree
};
data in database I am having is
item 1
item 1.1
item 1.1.1
item 2
Output I want
{
"label": "item 1",
"data": {
"id": "1"
},
"children": [{
"label": "item 1.1",
"data": {
"id": "2"
},
"children": [{
"label": "item 1.1.1",
"data": {
"id": "3"
},
"children": []
}]
}]
}
I am getting the following error
TypeError: Converting circular structure to JSON
Try change this.fetch_list_tree to exports.fetch_list_tree.
this != module.exports on line 28 => undefined

Convert CSV file to JSON dictionary?

I need to convert a large CSV data set to JSON, however the output should be a JSON dictionary like this:
var products = {
"crystal": {
"description": "This is a crystal",
"price": "2.95"
},
"emerald": {
"description": "This is a emerald",
"price": "5.95"
}
};
This is what the CSV table would look like:
I am using a script referenced here to generate the JSON:
var csv = require('csv')
var fs = require('fs')
var f = fs.createReadStream('Fielding.csv')
var w = fs.createWriteStream('out.txt')
w.write('[');
csv()
.from.stream(f, {columns:true})
.transform(function(row, index) {
return (index === 0 ? '' : ',\n') + JSON.stringify(row);
})
.to.stream(w, {columns: true, end: false})
.on('end', function() {
w.write(']');
w.end();
});
However the output from that script is created in this format:
[
{
"name": "crystal",
"description": "This is a crystal",
"price": "2.95"
},
{
"name": "emerald",
"description": "This is a emerald",
"price": "5.95"
}
]
How would I modify the script to get my desired "dictionary" format?
All you need to do is loop over the array and use item.name as key for your dictionary object
var products ={};
data.forEach(function(item){
products[item.name] = item;
});
This will leave the name property in the item but that shouldn't be an issue
I found csv parser library most useful:
var csvText=`status,path,name,ext,checksum,size,document_service_id,document_service_path,message
success,./15-02-2017_17-11/d77c7886-ffe9-40f2-b2fe-e68410d07891//expE1.txt,expE1.txt,txt,38441337865069eabae7754b29bb43e1,414984,8269f7e3-3221-49bb-bb5a-5796cf208fd1,/neuroinftest/20170215/expE1.txt,
success,./15-02-2017_17-11/d77c7886-ffe9-40f2-b2fe-e68410d07891//expE10.txt,expE10.txt,txt,f27e46979035706eb0aaf58c26e09585,368573,2c94ed19-29c9-4660-83cf-c2148c3d6f61,/neuroinftest/20170215/expE10.txt,
success,./15-02-2017_17-11/d77c7886-ffe9-40f2-b2fe-e68410d07891//expE2.txt,expE2.txt,txt,e1040d9546423c823944120de0e5c46c,333308,b3898f5d-1058-4cf3-acf9-76759117b810,/neuroinftest/20170215/expE2.txt,
`
var csv = require('csv');
csv.parse(csvText, {columns: true}, function(err, data){
console.log(JSON.stringify(data, null, 2));
});
In variable csvText I have my comma-separated file, with the first line serving as a header. I use the parse function and I'm passing the {columns: true} to indicated that the first line has the headers. Second parameter in the callback function (data) has the object with keys being the headers and the values being the corresponding csv cells. I use JSON.stringify to print it nicely and the result object looks like this (it puts it into an array):
[
{
"status": "success",
"path": "./15-02-2017_17-11/d77c7886-ffe9-40f2-b2fe-e68410d07891//expE1.txt",
"name": "expE1.txt",
"ext": "txt",
"checksum": "38441337865069eabae7754b29bb43e1",
"size": "414984",
"document_service_id": "8269f7e3-3221-49bb-bb5a-5796cf208fd1",
"document_service_path": "/neuroinftest/20170215/expE1.txt",
"message": ""
},
{
"status": "success",
"path": "./15-02-2017_17-11/d77c7886-ffe9-40f2-b2fe-e68410d07891//expE10.txt",
"name": "expE10.txt",
"ext": "txt",
"checksum": "f27e46979035706eb0aaf58c26e09585",
"size": "368573",
"document_service_id": "2c94ed19-29c9-4660-83cf-c2148c3d6f61",
"document_service_path": "/neuroinftest/20170215/expE10.txt",
"message": ""
},
{
"status": "success",
"path": "./15-02-2017_17-11/d77c7886-ffe9-40f2-b2fe-e68410d07891//expE2.txt",
"name": "expE2.txt",
"ext": "txt",
"checksum": "e1040d9546423c823944120de0e5c46c",
"size": "333308",
"document_service_id": "b3898f5d-1058-4cf3-acf9-76759117b810",
"document_service_path": "/neuroinftest/20170215/expE2.txt",
"message": ""
}
]
UPD: This array can easily be turned into the object you need with reduce:
var res_obj = data.reduce(function(acc, cur, i) {
acc[cur.name] = cur;
return acc;
}, {});
In my case I use the name property as a key. Make sure it's unique.
I think something like this would work :
var products_arr = [{"name":"crystal","description":"This is a crystal","price":"2.95"},
{"name":"emerald","description":"This is a emerald","price":"5.95"}]
var products = {};
for (var i = 0, l = products_arr.length ; i < l ; ++i) {
var x = products_arr[i];
var name = x.name
delete x.name; // deletes name property from JSON object
products[name] = x;
}
This will output :
{
"crystal": {
"description": "This is a crystal",
"price": "2.95"
},
"emerald": {
"description": "This is a emerald",
"price": "5.95"
}
}
If you would like to modify your specific code, you could change the line
return (index === 0 ? '' : ',\n') + JSON.stringify(row);
to
var clonedRow = JSON.parse(JSON.stringify(row));
var key = clonedRow['name'];
delete clonedRow['name'];
var newRow = {};
newRow[key] = clonedRow;
return (index === 0 ? '' : ',\n') + JSON.stringify(newRow);
This creates a new object for each row, modifying the structure according to your requirement.
Your best bet is to use PapaParse, a powerful csv parser/dumper. It supports streams, various string encodings, header row, and it's fast.

Categories