I want to get nested JSON from SQLite database with NodeJS.
I did
app.get("/api/orders", (req, res, next) => {
var sql = `SELECT json_object('number', o.number
,'orderType', o.orderType
,'paymentType', o.paymentType
,'isPaid', o.isPaid
,'isReady', o.isReady
,'isProgress', o.isProgress
,'isCanceled', o.isCanceled
,'isDelivered', o.isDelivered
,'createdAt', o.createdAt
,'updatedAt', o.updatedAt
,'orderItems',
(SELECT json_group_array(json_object('id', ot.itemID,
'name', ot.itemName,
'quantity', ot.quantity))
FROM orderItems AS ot WHERE ot.orderID = o.number)) AS 'order'
FROM orders AS o`;
db.all(sql, [], function (err, result) {
if (err) {
res.status(400).json({"error" : err.message});
return;
}
res.send(result)
});
});
but I get this
[
{
"order": "{\"number\":1,\"orderType\":\"Take out\",\"paymentType\":\"At counter\",\"isPaid\":0,\"isReady\":0,\"isProgress\":0,\"isCanceled\":1,\"isDelivered\":0,\"createdAt\":\"10/25/2021 15:34:48\",\"updatedAt\":\"10/25/2021 17:19:2\",\"orderItems\":[{\"id\":2,\"name\":\"Chicken Wings\",\"quantity\":1},{\"id\":6,\"name\":\"Saltado Shaken Fries\",\"quantity\":1},{\"id\":7,\"name\":\"Steam rice\",\"quantity\":1}]}"
},
{
"order": "{\"number\":2,\"orderType\":\"Dine in\",\"paymentType\":\"Pay here\",\"isPaid\":1,\"isReady\":0,\"isProgress\":1,\"isCanceled\":0,\"isDelivered\":0,\"createdAt\":\"10/25/2021 15:35:59\",\"updatedAt\":\"10/25/2021 15:35:59\",\"orderItems\":[{\"id\":1,\"name\":\"Fam's Salad\",\"quantity\":2},{\"id\":2,\"name\":\"Chicken Wings\",\"quantity\":2},{\"id\":8,\"name\":\"Vietnamese Coffee\",\"quantity\":1}]}"
},
{
"order": "{\"number\":3,\"orderType\":\"Dine in\",\"paymentType\":\"At counter\",\"isPaid\":0,\"isReady\":0,\"isProgress\":1,\"isCanceled\":0,\"isDelivered\":0,\"createdAt\":\"10/25/2021 15:44:48\",\"updatedAt\":\"10/25/2021 15:44:48\",\"orderItems\":[{\"id\":8,\"name\":\"Vietnamese Coffee\",\"quantity\":1},{\"id\":15,\"name\":\"Soft Drink: Coke\",\"quantity\":1}]}"
}
]
It returns weird format keys like \"number\", \"orderType\"
Is there any way that I can get a nicer JSON in this case?
Thank you
Related
I'm trying to get a document with specific student number returned to me with postman. Currently it does not return anything if the student number's type is Int, but if I change it to String, it works as intended. I've only recently started as you probably can see, so please go easy on me
Here is some code
app.get("/students/:snumber", function (req, res, next) {
console.log('student number =', req.params.snumber)
next() },
function (req, res, next) {
var query = { student_number: req.params.snumber };
MongoClient.connect('mongodb://localhost:27017/uni', function (err, client) {
if (err) throw err
var db = client.db('uni')
db.collection('students').find(query).toArray(function (err, result) {
if (err) throw err
console.log(result)
res.send(result)
})
})
});
{
"student_number": 1,
"first_name": "Jeanette",
"last_name": "Penddreth",
"email": "jpenddreth0#census.gov",
"gender": "Female"
}
Image showing some documents where 1 and 3 are as String & 2 and 4 as Int
URL parameters will always be set as string therefore snumber needs to be converted to an integer i.e.
const query = { student_number: parseInt(req.params.snumber, 10) }
Converting value using Number(v) or parseInt(v, 10) would be enought.
Also, if you want to match both cases, you can wrap it inside $or, like:
const query = { $or: [
{ student_number: req.params.snumber },
{ student_number: Number(req.params.snumber) },
] }
I have a timeout with my application-level join between two documents : Thread and Message.
I am trying to get all the Messages of my Thread with this code:
router.get('/:themeId/threads/:threadId/messages', function(req, res, next) {
Thread.findById(req.params.threadId, function(err, thread) {
if (err) return next(err);
Message.find({ _id: { $in: thread.messages } }), function(err, message) {
if (err) return next(err);
res.json(message);
}
});
});
Unfortunately, I got a timeout with my request which I tested Postman.
I had this message when I tested it:
Could not get any response
There was an error connecting to
http://localhost:3000/api/themes/5b1bb59d4210c50cf798da57/threads/5b1bb5e84210c50cf798da59/messages.
Also I checked that thread.messages is an array with another request:
GET http://localhost:3000/api/themes/5b1bb59d4210c50cf798da57/threads/5b1bb5e84210c50cf798da59 :
The result is below and from my Threaddocument :
{
"numberOfViews": 0,
"numberOfComments": 0,
"numberOfLikes": 0,
"numberOfThanks": 0,
"messages": [
"5b1bb5ad4210c50cf798da58",
"5b1bb6464210c50cf798da5a"
],
"_id": "5b1bb5e84210c50cf798da59",
"theme": "5b1bb59d4210c50cf798da57",
"title": "Title azerty",
"createdAt": "2018-06-09T11:11:36.358Z",
"updatedAt": "2018-06-09T11:13:41.062Z",
"__v": 1
}
From my understanding, my request should do a find Message in my array of Thread.messages...
But it seems I miss something.
Any idea to solve my timeout request?
Thanks a lot for your help.
Couple of suggestions to try out:
Make sure thread.messages is not an empty array. You can do this by asserting that its length is greater than 0.
In the clause Message.find({ _id: { $in: thread.messages } }), cast the string values to objectids by before sending it to the find clause. So you can do
var myobj1 = [];
thread.messages.forEach(element => {myobj1.push(Mongoose.Types.ObjectId(element)})
I found a solution with the populate of Mongoose and theses sources :
http://mongoosejs.com/docs/populate.html
https://alexanderzeitler.com/articles/mongoose-referencing-schema-in-properties-and-arrays/
Below the modified code:
router.get('/:themeId/threads/:threadId/messages', function(req, res, next) {
Thread.findById(req.params.threadId, function(err, thread) {
if (err) return next(err);
Message.find({_id: { $in : thread.messages } } )
.populate('messages')
.exec(function(error, messages) {
res.json(messages);
});
});
});
I have a collection of fixtures that 'belong' to a competitor and look something like this:
{
"_id": {
"$oid": "59dbdf6dbe628df3a80419bc"
},
"timeOfEntrance": "1507581805813",
"timeOfFinish": null,
"competitor": {
"$oid": "59db5a3f3d6119e69911a61a"
},
"__v": 0
}
My goal is to update only the document's timeOfFinish by sending a PUT request with competitor's ID as a param in the url and timestamp in the body. However I'm struggling to compose the update query.
The following is what I have currently, it never finds the right match and to my surprise it's always updating the wrong document.
fixtureController.put = (req, res) => {
const competitorId = req.params.id;
const timeOfFinish = req.body.timeOfFinish;
Fixture.findOne({'competitor.$oid': competitorId}, (err, fixture) => {
fixture.set({ timeOfFinish });
fixture.save()
.then(updatedFixture => {
return res.status(200).json({
success: true,
fixture: updatedFixture
})
})
.catch(err => {
return res.status(500).json({
message: err
});
});
});
};
Bit of a beginner in the MongoDB field, will appreciate your comments and solutions.
Turns out there was no need to specify the exact field in the match parameter. Mongoose matches by the id field automatically.
Fixture.findOne({'competitor': competitorId}, (err, fixture) => {
...
});
I am building a server-less app in AWS with Lambda and Node.js. I currently have a MongoDB out at mLab. I am attempting to get the "latest" record based on an ISODate string. Using either findOne() or find w/limit 1 it returns the same record everytime (not the latest one).
I have 2 records in my test table that look like:
{ "field1": "myField", "versionTimestamp": "2017-06-13T18:33:06.223Z" }
{ "field1": "myField", "versionTimestamp": "2017-12-13T18:33:06.223Z" }
No matter what I do it always returns the one from the 6th
col.findOne(q, { "sort": ['versionTimestamp', 'desc'] }, function (err, doc) {
db.close();
if (err) {
sendErrorResponse("500", "Unable to query DB", err);
}
else {
if (doc) {
console.log(doc);
callback(null, doc.invoiceDocument);
}
else {
sendErrorResponse("404", "Record Not Found", "No records found that match those parameters.");
}
}
});
Or with limit 1
col.find(q, { "limit": 1, "sort": ['versionTimestamp', 'desc'] }).toArray(function (err, docs) {
db.close();
if (err) {
sendErrorResponse("500", "Unable to query DB", err);
}
else {
if (docs) {
console.log(docs[0]);
callback(null, docs[0].invoiceDocument);
}
else {
sendErrorResponse("404", "Record Not Found", "No records found that match those parameters.");
}
}
});
Asya found it! It was a malformed array in sort option:
sort takes an array of sort preferences, default being 'asc'. I'm
guessing you want another set of array brackets: [ [ 'field', 'desc']
] – Asya Kamsky yesterday
I have two MongoDB models "Users" and "Roles". Each user can have multiple roles and each role can be assigned to many users. To keep the relation simple I would like to store the reference between both models only in the "Users" model which is already working as expected. But when I'm loading all roles at once with .find({}), I would also like to know how many users are assigned to these roles (to check if a role can be modified).
I'm using Node.js + ExpressJS and mongoose. This is what I already have:
var userSchema = new Schema({
username: String,
...
roles : [ {
type : Number,
ref : 'Role'
} ]
});
var roleSchema = new Schema({
_id : Number,
name : String
});
--------------------------------------------------
function getRoles(request, response) {
Role.find({}, function(err, roles) {
....
response.send(roles);
});
}
But now I wonder how I would achive the count query per role and still be able to send the result in one response.
I would like to avoid to send a single request per role and try to do the count within the getRoles() function. In a relational database I would do something like this:
select r.*,
(select count(*) from user2role u2r where u2r.role_id = r.role_id)
from role r;
But what's the MongoDB equivalent?
Any help and hints will be appreciated.
Thanks, Gerry
To achieve the user count query per role with the given schema, you could either use the count() method as follows:
function getUsersCount(roleName, req, res) {
Role.findOne({"name": roleName}, function(err, role) {
var roleId = role._id;
// var countQuery = User.where({ "role": roleId }).count();
// User.count({ "roles": roleId }).count(callback)
// User.count({ "roles": roleId }, callback)
User.where({ "roles": roleId }).count(function (err, count) {
if (err) return handleError(err);
console.log("There are %d users with the role %d", count, roleName);
res.send(count);
})
});
}
or you can use the aggregate() method like:
// Find the user count for a given role
function getUsersCount(roleName, req, res) {
Role.findOne({"name": roleName}, function(err, role) {
var roleId = role._id;
var pipeline = [
{
"$match": { "roles": roleId } /* filter the documents that only have the roleId in the role array */
},
{ "$unwind": "$roles" },
{
"$match": { "roles": roleId }
},
{
"$group": {
"_id": null, "count": { "$sum": 1 }
}
},
{
"$project": {
"_id": 0, "count": 1
}
}
];
Users.aggregate(pipeline, function (err, result) {
if (err) return handleError(err);
console.log(result); // [ { count: 55 } ] for example
res.send(result);
});
// Or use the aggregation pipeline builder.
Users.aggregate()
.match({ "roles": roleId })
.unwind("roles")
.match({ "roles": roleId })
.group({ "_id": null, "count": { "$sum": 1 } })
.select("-id count")
.exec(function (err, result) {
if (err) return handleError(err);
console.log(result); // [ { count: 55 } ] for example
res.send(result);
});
});
}
-- EDIT --
One approach that you could take is to use the lean() method to get pure JavaScript objects that you can manipulate to add the extra userCount field for each role. With lean() it's possible because documents returned from queries with the lean option enabled are plain javascript objects', not MongooseDocuments. Thus with the roles returned from the find() cursor, you can convert them into plain JS objects
that you can iterate over using the native JavaScript map() method, get the number of users per role using another query that takes into consideration the count() method explained above and return the modified object.
The following demonstrates how the function is implemented:
function getRoles(request, response) {
Role.find({}).lean().exec(function(err, roles) {
var result = roles.map(function(r){
var obj = {},
countQuery = User.where({ "roles": r._id }).count();
obj["userCount"] = countQuery;
obj["_id"] = r._id;
obj["name"] = r.name;
return obj;
});
response.send(result);
});
}
As per your above explanation about the schema design, i can assume your schema is as below :
Roles collection :
{
role_id :
name :
}
User collection :
{
user :
role_id : [ ]
....
}
you can use aggregation to achieve this :
db.users.aggregate([{$unwind: "$role_id"},
{$group: {_id: "$role_id", count : {"$sum":1}} },
]);