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
Related
Here is a part of an application that creates a file tree data structure from JSON input. However, there are one or more bugs in this code. It seems that for some data sets this code creates an incorrect tree structure.
'use strict';
function FileTreeNode(nodeId, name, type) {
const children = [];
this.nodeId = nodeId;
this.name = name;
this.type = type;
this.parentNode = null;
this.setParent = function(parentNode) {
this.parentNode = parentNode;
};
this.addChild = function(node){
if (this.type !== 'DIRECTORY') {
throw "Cannot add child node to a non-directory node";
}
else{
children.push(node);
node.setParent(this);
}
};
this.getChildren = function() {
return children;
};
};
function FileTree() {
this.nodes = [];
this.getRootNodes = function() {
const result = [];
for (let i = 0; i < this.nodes.length; i++) {
if (!this.nodes[i].parentNode) {
result.push(this.nodes[i]);
}
}
return result;
};
this.findNodeById = function(nodeId) {
for (let i = 0; i < this.nodes.length; i++) {
if (this.nodes[i].nodeId === nodeId) {
return this.nodes[i];
}
}
return null;
};
this.createNode = function(nodeId, name, type, parentNode) {
const node = new FileTreeNode(nodeId, name, type);
if (parentNode != null) {
parentNode.addChild(node);
}
this.nodes.push(node);
}
};
export function createFileTree(input) {
const fileTree = new FileTree();
const nodenotFound = (inputNodeId,input)=>{
let result = input.filter((item) => item.id == inputNodeId);
// const fileTree2 = new FileTree();
let node=fileTree.createNode(result[0].id, result[0].name, result[0].type,fileTree.findNodeById(result[0].parentId));
return node;
}
for (const inputNode of input) {
var parentNode = null;
if(inputNode.parentId != undefined){
if(fileTree.findNodeById(inputNode.parentId)!=null){
parentNode = fileTree.findNodeById(inputNode.parentId)
}
else{
parentNode= nodenotFound(inputNode.parentId,input)
}
}
else{
parentNode=null
}
fileTree.createNode(inputNode.id, inputNode.name, inputNode.type, parentNode);
}
return fileTree;
}
and my test file is as follows
'use strict';
import { createFileTree } from '../src/app'
const getDataset = file =>
require(`../src/dataset/${file}`)
describe('fileTree', function () {
var traverseTreeAndFindNode = function(inputNode, nodes) {
if (!nodes) {
return undefined;
}
for (var i = 0; i < nodes.length; i++) {
var node = nodes[i];
if (node.nodeId === inputNode.id) {
return node;
}
var nodeFoundAtChildren = traverseTreeAndFindNode(inputNode, node.getChildren());
if (nodeFoundAtChildren) {
return nodeFoundAtChildren;
}
}
return undefined;
};
function testTreeNode (inputNode, foundNode) {
it('tree node ' + inputNode.id + ' should have correct data', function() {
expect(foundNode.nodeId).toEqual(inputNode.id);
expect(foundNode.name).toEqual(inputNode.name);
expect(foundNode.type).toEqual(inputNode.type);
});
it('tree node ' + inputNode.id + ' should have correct parent', function () {
if (inputNode.parentId) {
expect(foundNode.parentNode).not.toBeNull();
expect(foundNode.parentNode.nodeId).toEqual(inputNode.parentId);
} else {
expect(foundNode.parentNode).toBeNull();
}
});
}
function testTreeContentsWithDataSet(dataSet) {
describe('created from ' + dataSet + ' dataSet', function() {
var inputData = getDataset(dataSet);
var fileTree = createFileTree(inputData);
for (var i = 0; i < inputData.length; i++) {
var inputNode = inputData[i];
var foundNode = traverseTreeAndFindNode(inputNode, fileTree.getRootNodes());
testTreeNode(inputNode, foundNode);
}
it('should contain all nodes from dataset', function () {
for (var i = 0; i < inputData.length; i++) {
expect(traverseTreeAndFindNode(inputData[i], fileTree.getRootNodes())).toBeDefined();
}
});
});
}
testTreeContentsWithDataSet('simple-data.json');
testTreeContentsWithDataSet('data-for-bug.json');
});
There are two JSON files used in the testing.
1.simple-data.json
[
{
"id": 877234010,
"name": "project",
"type": "DIRECTORY"
},
{
"id": 877234002,
"name": "src",
"type": "DIRECTORY",
"parentId": 877234010
},
{
"id": 877234003,
"name": "app",
"type": "DIRECTORY",
"parentId": 877234002
},
{
"id": 877234004,
"name": "app.js",
"type": "FILE",
"parentId": 877234003
}
]
the second one is data-for-bug.json
[
{
"id": 7832454551,
"name": "usr",
"type": "DIRECTORY"
},
{
"id": 7832454554,
"name": "applications",
"type": "DIRECTORY",
"parentId": 7832454553
},
{
"id": 7832454555,
"name": "mimeinfo.cache",
"type": "FILE",
"parentId": 7832454554
},
{
"id": 7832454553,
"name": "share",
"type": "DIRECTORY",
"parentId": 7832454552
},
{
"id": 7832454552,
"name": "local",
"type": "DIRECTORY",
"parentId": 7832454551
}
]
The result of the test run is
Using Jasmine version: 3.5.0
Started
............F...F...
Failures:
1) fileTree created from data-for-bug.json dataSet tree node 7832454554 should have correct parent
Message:
Expected null not to be null.
Stack:
Error: Expected null not to be null.
at <Jasmine>
at UserContext.<anonymous> (C:\Users\hp\Downloads\devskiller-code-PTQG-EEWG-46UW-S1F\test\/app.spec.js:39:42)
at <Jasmine>
Message:
TypeError: Cannot read property 'nodeId' of null
Stack:
at <Jasmine>
at UserContext.<anonymous> (C:\Users\hp\Downloads\devskiller-code-PTQG-EEWG-46UW-S1F\test\/app.spec.js:40:37)
at <Jasmine>
at processImmediate (internal/timers.js:464:21)
2) fileTree created from data-for-bug.json dataSet tree node 7832454553 should have correct parent
Message:
Expected null not to be null.
Stack:
Error: Expected null not to be null.
at <Jasmine>
at UserContext.<anonymous> (C:\Users\hp\Downloads\devskiller-code-PTQG-EEWG-46UW-S1F\test\/app.spec.js:39:42)
at <Jasmine>
Message:
TypeError: Cannot read property 'nodeId' of null
Stack:
at <Jasmine>
at UserContext.<anonymous> (C:\Users\hp\Downloads\devskiller-code-PTQG-EEWG-46UW-S1F\test\/app.spec.js:40:37)
at <Jasmine>
at processImmediate (internal/timers.js:464:21)
20 specs, 2 failures
Finished in 0.117 seconds
I couldn't notice what is wrong with the code.pls help
I am destructing the data from the function
let { destructValue } = require("./destructValue.js");
const fs = require("fs");
const path = require("path");
function retrunValues(data) {
fs.readdir("./userData", (err, files) => {
if (err) throw console.log(err.message);
else {
files.forEach(async (file) => {
fs.readFile(`./userData/${file}`, (err, data) => {
destructValue(data);
let jsonObject = {};
if (destructValue(data).length !== 0) {
jsonObject = {
name: data.name,
value: [...destructValue(data)],
};
console.log(jsonObject);
}
});
});
}
});
}
so after doing console.log(jsonObject) I am getting this values
{
name: "Tomas",
value:[{
age: "21",
address: "New York"
}]
}
{
name: "Jerry",
value:[{
age: "22",
address: "Tokyo"
}]
}
this output look fine to me as this was the expected output so I tried to call jsonObject into new schema like this
let { destructValue } = require("./destructValue.js");
const fs = require("fs");
const path = require("path");
function retrunValues(data) {
fs.readdir("./userData", (err, files) => {
if (err) throw console.log(err.message);
else {
files.forEach(async (file) => {
fs.readFile(`./userData/${file}`, (err, data) => {
destructValue(data);
let jsonObject = {},
testingObject = {};
if (destructValue(data).length !== 0) {
jsonObject = {
name: data.name,
value: [...destructValue(data)],
};
}
testingObject = {
default: "default value",
got: jsonObject,
};
console.log(testingObject);
});
});
}
});
}
so after doing console.log(testingObject) I am getting this value
{ default: 'defaultObject'}
{ default: 'defaultObject'}
{ default: 'defaultObject',
name: "Tomas",
value:[{
age: "21",
address: "New York"
}]
}
{ default: 'defaultObject'}
{ default: 'defaultObject'}
{ default: 'defaultObject',
name: "Jerry",
value:[{
age: "22",
address: "Tokyo"
}]
}
As I was not expecting this output as my expected output was like this
[
{
"default": "defaultObject",
"got": [
{
"name": "Tomas",
"value": [{ "age": "21", "address": "New York" }]
},
{
"name": "Jerry",
"value": [{ "age": "22", "address": "Tokyo" }]
}
]
}
]
but I am not getting the expected value and I don't know where it went wrong how can I achieve my expected value
destructValue.js
const {age,address}=require(valueGot.js);
function destructValue(data){
const destructSchema=[];
for(const value of data){
switch(value.userDetails){
case "age":
destructSchema.push(age(data));
break;
case "address":
destructSchema.push(address(data));
break;
}
}
return destructSchema;
}
module.exports={destructValue}
Try the below. Save your results into an array and when you have finished going through the files, you output your results.
Edit: I added a filter for the results, but haven't tested. It should only add destructed data that hasn't been added before.
let { destructValue } = require("./destructValue.js");
const fs = require("fs");
const path = require("path");
function filterAgainstExisting(existing, check) {
return check.reduce((carry, value, index, array) => {
//Check if value exists - not generic, catered to your needs
if (existing[value.name] && existing[value.name].age == value.value[0].age && existing[value.name].address == value.value[0].address) {
return carry;
}
existing[value.name] = value;
carry.push(value);
return carry;
}, []);
}
function retrunValues(data) {
fs.readdir("./userData", (err, files) => {
if (err) throw console.log(err.message);
else {
let jsonArray = [];
let jsonObject = {};
files.forEach(async (file) => {
fs.readFile(`./userData/${file}`, (err, data) => {
let destructedData = destructValue(data);
if (destructedData.length !== 0) {
jsonArray.push({
name: data.name,
value: [...this.filterAgainstExisting(jsonObject, destructedData)],
});
}
});
});
const testingObject = [{
default: "defaultObject",
got: jsonArray,
}];
console.log(testingObject);
}
});
}
Try changing requrie
let {destructValue} = requrie("./destructeValue.js");
to require
let {destructValue} = require("./destructeValue.js");
I have a json-server with the following files:
db.json:
var faker = require('faker');
module.exports = function () {
var data = {
slack_users_list: slack_users_list(),
slack_users_info: slack_users_info()
}
return data;
}
function slack_users_list() {
const data = {
members: []
}
for (var i = 0; i < 10; i++) {
data.members.push({
id: "user_id_" + i
})
}
return data;
}
function slack_users_info() {
const data = {
user: []
}
for (var i = 0; i < 10; i++) {
data.user.push({
id: "user_id_" + i,
name: faker.internet.userName(),
team_id: faker.datatype.string(),
profile: {
title: faker.name.title(),
first_name: faker.name.firstName(),
last_name: faker.name.lastName(),
title: faker.name.title(),
email: faker.internet.email(),
phone: faker.phone.phoneNumber(),
skype: faker.internet.userName(),
},
})
}
return data;
}
And server.js:
const jsonServer = require('json-server')
const server = jsonServer.create()
const router = jsonServer.router(require('./db.js')())
const middlewares = jsonServer.defaults()
server.use(middlewares)
server.use(jsonServer.bodyParser)
// Wrap response depending on the object being called.
router.render = (req, res) => {
if (req.url.startsWith('/slack_users_list')) {
res.jsonp(res.locals.data)
} else if (req.url.startsWith('/slack_users_info')) {
param_user = req.query.user
if (param_user == null) {
res.jsonp({
"ok": false,
"error": "user_not_found"
})
return
}
user_list = res.locals.data.user
user_info = {}
for (var i = 0; i < user_list.length; i++) {
if (user_list[i].id == param_user) {
user_info = user_list[i]
}
}
res.jsonp({ user: user_info })
} else {
res.jsonp(res.locals.data)
}
}
server.use(router)
// Run server on indicated port.
server.listen(port = 3000, () => {
console.log('JSON Server is running')
console.log('http://localhost:' + port)
})
So when I call http://localhost:3000/slack_users_list I get:
{
"members": [
{
"id": "user_id_0"
},
{
"id": "user_id_1"
},
{
"id": "user_id_2"
},
{
"id": "user_id_3"
},
{
"id": "user_id_4"
},
{
"id": "user_id_5"
},
{
"id": "user_id_6"
},
{
"id": "user_id_7"
},
{
"id": "user_id_8"
},
{
"id": "user_id_9"
}
]
}
But if I want to apply pagination to it (e.g. http://localhost:3000/slack_users_list?_page=1&_limit=2), I get all the same objects, I assume because the pagination is getting applied to the first line of objects and not to each of the element inside members that get generated in db.json.
How can I modify this to apply the pagination on the sub-object members?
Thanks!
You can do the pagination right before sending the response.
// !!!
// This is just for demonstration.
// consider this req as the actual request.(req, res)
const req = {
params: { _page: '2', _limit:'3'}
}
// consider this as actual slack_users_list.members
const members = [
{
"id": "user_id_0"
},
{
"id": "user_id_1"
},
{
"id": "user_id_2"
},
{
"id": "user_id_3"
},
{
"id": "user_id_4"
},
{
"id": "user_id_5"
},
{
"id": "user_id_6"
},
{
"id": "user_id_7"
},
{
"id": "user_id_8"
},
{
"id": "user_id_9"
}
]
const page=Number(req.params._page)
const limit=Number(req.params._limit)
// paginating
const limited = members.slice((page - 1) * limit, page * limit)
console.log(limited)
// // send paginated response
//res.json(limited)
I am using DataTables with ajax and I am trying to dynamically generate a columns list as in the example here: https://datatables.net/examples/ajax/orthogonal-data.html (only in my case I have display and order for all items).
I tried with this approach, but this doesn't work and I am guessing push my sub values wrong.
Input (ajax result):
[{
"itemId": {
"order": "BG007002",
"display": "BG007002"
},
"builtDate": {
"order": "2000-03-01",
"display": "01.03.2000"
},
"openedDate": {
"order": "2005-07-09",
"display": "09.07.2005"
},
"buildingSize": {
"order": 15000,
"display": "15.000"
}
}, // ...
Expected output:
[
{ data: {
_: "itemId.display",
sort: "itemId.order"
}
},
{ data: {
_: "builtDate.display",
sort: "builtDate.order"
}
},
{ data: {
_: "openedDate.display",
sort: "openedDate.order"
}
},
{ data: {
_: "buildingSize.display",
sort: "buildingSize.order"
}
}
]
My approach:
var reportColsShort = $('#reportColsShort').text().slice(0,-1);
var aoCols = [];
var colss = reportColsShort.split(',');
for (var i = 0; i < reportColsShort.split(',').length; i++) {
var aoColss = {};
aoColss['data']['_'] = colss[i].display;
aoColss['data']['sort'] = colss[i].order;
aoCols.push(aoColss); // expected output
}
Error:
Cannot set property '_' of undefined.
Update:
Here is one more reference to what I am trying to achieve:
https://datatables.net/reference/option/columns.render#Examples
You can take the ajax result and then use a map see docs to structure your data like this:
const data = [{
"itemId": {
"order": "BG007002",
"display": "BG007002"
},
"builtDate": {
"order": "2000-03-01",
"display": "01.03.2000"
},
"openedDate": {
"order": "2005-07-09",
"display": "09.07.2005"
},
"buildingSize": {
"order": 15000,
"display": "15.000"
}
}];
const mapResult = data.map((elem) => {
let array = [];
Object.keys(elem).forEach((key) => {
const temp = {
data: {
_: elem[key].display,
sort: elem[key].order
}
}
array.push(temp);
});
return array;
});
console.log(mapResult);
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?