I have a mongoDB database set up with a express server.
I try to access a object which is inside an array in a document.
I have this route :
app.get("/api/" + BLOGS_COLLECTION + "/:blogId" + "/post" + "/:postId", function (req, res) {
db.collection(BLOGS_COLLECTION).aggregate([
{ $match: { _id: req.params.postId } }, {
$project: {
posts: {
$filter: {
input: "$posts",
as: "posts",
cond: { $eq: ["$$posts._id", req.params.postId] }
}
}
}
}].toArray((err, doc) => {
console.log(doc)
if (err) {
handleError(res, err.message, "Failed to get post");
} else {
res.status(200).send(doc);
}
}));
});
But it returns an error :
[{(intermediate value)},{(intermediate value)}].toArray is not a function
If I do not use the toArray but a plain function, it can't construct to json to send it to the front.
I do not use Mongoose.
If the code above can't work, how can I query only the object I want inside the array ?
P.S: this is how my database is made :
_id:60336bcc961785586471938b
title:"<p>ttttttttttttttttttttttttttt</p>"
description:"<p>tttttttttttttttttttttttttt</p>"
authorId:"60336bb5961785586471938a"
createDate:2021-02-22T08:31:08.889+00:00
posts:
0:
_id:"53394732-d60b-c869-1fed-1fb82c03780f"
title:"<p>iiiiiiiiiiiiiiiiiiiiiiiiiiiii</p>"
content:"<p>iiiiiiiiiiiiiiiiiiii</p>"
date:"2021-02-22T08:31:14.911Z"
You need to call toArray on the cursor reference you get back from the aggregation and not on the aggregation array:
db.collection(BLOGS_COLLECTION).aggregate([
{ $match: { _id: req.params.postId } },
{
$project: {
posts: {
$filter: {
input: "$posts",
as: "posts",
cond: { $eq: ["$$posts._id", req.params.postId] }
}
}
}
}
],
(err, cursor) => {
if (err) {
handleError(res, err.message, "Failed to get post");
} else {
cursor.toArray((error, documents) => {
if (error) { return handleError(res, error.message, "Failed to get post"); }
console.log(documents)
res.status(200).send(documents);
});
}
});
MongoDB Aggregation Pipeline
Your stages between square prackets should look like this:
[
{ $match: { _id: ObjectId( req.params.blogId) } },
{
$project: {
posts: {
$filter: {
input: "$posts",
as: "posts",
cond: { $eq: ["$$posts._id", ObjectId(req.params.postId)] }
}
}
}
},
{$unwind : "$posts" },
{$replaceRoot :{ newRoot:"$posts"}
]
After filtering and projecting, unwind array objects and then simply replace the root. And of course, follow the instructions from the previous answer about the usage of toArray on aggregate cursor result.
Related
I have a mongoDB collection which I use with a mongoose Schema :
const balanceSchema = new mongoose.Schema({
userId: {
type: mongoose.Schema.Types.ObjectId, ref: 'user'
},
incomes: { Number },
fees: { Number },
},
{ strict: false })
I use the strict mode to false, so I can push any 'key' I want with its value.
I would like to delete just one of the "incomes" category, but I can't specify the line because there is no 'defined key'.
Here is an exemple of the data inside :
{
"_id": {
"$oid": "60c763df3d260204865d2069"
},
"incomes": {
"income1": 1300,
"anyKeyNameIWant": 400
},
"fees": {
"charge1": 29,
"charge2": 29,
"chargetest": 29,
"charge7": 29
},
"__v": 0,
}
I tried this, but no success :
module.exports.deleteOneBalance = (req, res) => {
let data = req.body
if (!ObjectID.isValid(req.params.id))
return res.status(400).send('ID unknown : ' + req.params.id);
BalanceModel.update(
{ _id: req.params.id },
{
$unset: { "incomes.salairetest": "400" }
}), (err, docs) => {
if (!err) res.send('Deleted. ' + data)
else console.log('Error : ' + err)
}
}
Any idea ?
There are several ways to delete fields with dynamic field names.
One solution is this one:
var unset = {};
unset["incomes." + "anyKeyNameIWant"] = null;
db.balanceModel.updateOne({ _id: req.params.id }, { $unset: unset })
Or you can use an aggregation pipelinie like this:
db.balanceModel.updateOne(
{ _id: req.params.id },
[
{ $set: { incomes: { $objectToArray: "$incomes" } } },
{ $set: { incomes: { $filter: { input: "$incomes", cond: { $ne: ["$$this.k", "anyKeyNameIWant"] } } } } },
{ $set: { incomes: { $arrayToObject: "$incomes" } } }
]
)
If you want to remove/unset specific value/(s) from the documents then you have to provide the complete path of that key.
Let's take an example if you want to remove anyKeyNameIWant then your path will be incomes.anyKeyNameIWant and the update query will be like this
db.sample.update(
{
_id: ObjectId("60c763df3d260204865d2069")},
{
$unset: {"incomes.anyKeyNameIWant":""}
})
In your code, you are passing an object having the key incomes in $unset which will remove the complete incomes key from the document
Here is the link to the official document in case you want more details $unset
I want to return one index's object of the array,
but when I query, It returns to me that all of the documents.
This is my Schema(userTb)
const userTbSchema = new Schema({
_id: mongoose.Schema.Types.ObjectId,
userId: String,
folders: [
{
folderTitle: String,
}
]
}
and this is the result of the query of my Schema(userTb).
{
"_id": "5fc4c13f32ab3174acb08540",
"userId": "go05111",
"folders": [
{
"_id": "5fb7b0473fddab615456b166",
"folderTitle": "first-go"
},
{
"_id": "5fb7b0473fddab615456b16b",
"folderTitle": "second-go"
}
]
}
I want to get the only { "folderTitle" : "first-go" } folder's object, like...
{
"_id": "5fb7b0473fddab615456b166",
"folderTitle": "first-go"
}
so I query like this
router.get('/folder/:folderId', (req, res, next) => {
UserTb.find({ folders : { "$elemMatch" : { _id : req.params.folderId} } })
.exec()
.then(docs => {
res.status(200).json({
docs
});
})
.catch(err => {
res.status(500).json({
error: err
});
});
});
but the result is nothing changed.
I tried a few different ways, but it didn't work out.
how can I fix it?
please help me...
Try this (live version):
UserTb.aggregate({
$match: {
"folders._id": req.params.folderId }
},
{
$project: {
folders: {
$filter: {
input: "$folders",
as: "f",
cond: {
$eq: [
"$$f.folderTitle",
"first-go"
]
}
}
},
_id: 0
}
})
It will retrieve folders:[{...}] this will be easy to tackle using JS, and quicker.
Mechanism
Match only documents containing _id:folderId
project only the inner document
Consider the query in Mongoose :
let StudentCodes = .... // getting this from somewhere
await Students.aggregate(
[
{
$project: {
StudentCODE: "$StudentCODE",
StudName: "$StudName",
StudProfileDesc: "$StudProfileDesc",
IsReviewed: {
$cond: [{ $eq: [StudentCodes, "$StudentCODE"] }, 1, 0]
}
}
}
],
function(err, results) {
if (err) {
console.log(err);
}
console.log(results);
return res.status(200).json(results);
}
);
How can We project IsReviewed as true or false if the property StudentCODE exists in the array StudentCodes ?
Try as below, you can use $in in $cond to do that :
let StudentCodes = .... // getting this from somewhere
await Students.aggregate(
[
{
$project: {
StudentCODE: "$StudentCODE",
StudName: "$StudName",
StudProfileDesc: "$StudProfileDesc",
IsReviewed: {
$cond: [{ $in: ["$StudentCODE", StudentCodes] }, true, false]
}
}
}
],
function (err, results) {
if (err) {
console.log(err);
}
console.log(results);
return res.status(200).json(results);
}
);
This question already has answers here:
How do you use a variable in a regular expression?
(27 answers)
Closed 4 years ago.
how to use search_key variable as field value instead of static value BRO
if(search_key!=''){
dbo.collection("assets").aggregate([
{
"$match": { $and: [ { status: 1 }, { $or: [ { maker_name : /^.*BRO.*$/i }, { serial_number : /^.*BRO.*$/i } ] } ] }
},
{
$lookup:
{
from: 'asset_type',
localField: 'asset_type',
foreignField: 'asset_type_id',
as: 'asset_type_details'
}
}
]).sort({_id:-1}).toArray(function(err, result) {
if (err) throw err;
res.status(200).json({'return_data': result });
db.close();
});
}
Try this way
{ serial_number: { $regex: `${search_key}.*`, $options: 'i' } }
Use this same thing for maker_name also.
This is works for me.
We need to pass a string to the function. so we can simply change that by passing query string.
if(search_key!=''){
let query_string = "/^.*" + search_key + ".*$/i" // we are creating the query string here
dbo.collection("assets").aggregate([
{
"$match": { $and: [ { status: 1 }, { $or: [ { maker_name : query_string }, { serial_number : query_string } ] } ] }
},
{
$lookup:
{
from: 'asset_type',
localField: 'asset_type',
foreignField: 'asset_type_id',
as: 'asset_type_details'
}
}
]).sort({_id:-1}).toArray(function(err, result) {
if (err) throw err;
res.status(200).json({'return_data': result });
db.close();
});
}
try like this :
if(search_key!=''){
dbo.collection("assets").aggregate([
{
"$match": { $and: [ { status: 1 },
{ $or: [
{ maker_name : '/^.*'+ search_key +'*$/i' },
{ serial_number : '/^.*'+ search_key +'*$/i' }
]
}
]
}
},
{
$lookup:
{
from: 'asset_type',
localField: 'asset_type',
foreignField: 'asset_type_id',
as: 'asset_type_details'
}
}
]).sort({_id:-1}).toArray(function(err, result) {
if (err) throw err;
res.status(200).json({'return_data': result });
db.close();
});
}
This could be a dumb question, but I'm desperate already! I need to do this query:
db.clients.aggregate(
{
$group: {
_id: '$enterprise',
lodging_days: { $sum: '$lodging_days' }
}
},
{
$sort : {
lodging_days: -1
}
})
And, if I copy this on the mongo bash, I returned this: Bash Return
(Sorry, I can't upload images yet)
JUST LIKE I WANT!
But, when I put the query on node:
router.get('/query', function(req, res){
var db = req.db;
var clients=db.get('clients');
clients.aggregate(
{
$group: {
_id: '$enterprise',
lodging_days: { $sum: '$lodging_days' }
}
},
{
$sort: {
'lodging_days': -1
}
},
function(e, data){
res.json(data);
}
);
});
This "ignore" the $sort and return me this: Interface Return
Now, my question are... Why!? And what can I make to fix it?
Your need to wrap your pipeline into array.
router.get('/query', function(req, res){
var db = req.db;
var clients=db.get('clients');
clients.aggregate([
{
$group: {
_id: '$enterprise',
lodging_days: { $sum: '$lodging_days' }
}
},
{
$sort: {
'lodging_days': -1
}
}],
function(e, data){
res.json(data);
}
);
});