I'm attempting to find() in one collection and concatenate that value to the correct object returned by subsequent find(). However I cannot guarantee the value retrieved in the first find() will match the index . How do I ensure the first value (Boolean) is attached to the correct instance of the second find()?
What I have so far is to use the indexes of the first condition but it may not match if an instance has been removed
My Model:
let Instance= new Schema({
imgName: String,
offer: String,
brand: String,
desc: String,
keywords: Array,
loc: String,
location: {
type: {
type: String, // Don't do `{ location: { type: String } }`
enum: ['Point'], // 'location.type' must be 'Point'
required: true
},
coordinates: {
type: [Number],
required: true
}
},
categories: Array,
userId: String,
active: Boolean,
stockPic: String,
startTime: Number,
endTime: Number,
range: Boolean
});
mongoose.model('Beams2', Instance);
let LikesSchema = new Schema({
userId: String,
likeId: String,
categories: Array,
public: Boolean
});
mongoose.model('Likes', LikesSchema);
//My query:
exports.findAllLikes = function(req, res){
Likes.find({'userId': req.body.userId}, function(err, results) {
let likeIds = results.map((currentValue, index, array) => {
return currentValue.likeId;
});
let statusArr = results.map((currentValue, index, array) => {
return currentValue.public;
});
Instances.find({'_id': { $in: likeIds }}, function(err, favs){
if (err) return console.log(err);
let newArr = [];
favs.forEach(function(element, i) {
//console.log(statusArr[i]);
let post = element.toObject();
post.public = statusArr[i]; //need to guarantee correct value
newArr.push(post);
console.log(post);
});
return res.send(newArr);
});
});
};
I think, this is how you could achieve what you are looking for:
exports.findAllLikes = function(req, res) {
Likes.find({
'userId': req.body.userId
}, (err, results) => {
if (err) {
console.log('Error while fetching likes: ', err);
return res.status(500).json({
Error: 'Error while fetching likes.'
});
}
const likeIds = results.map(({
likeId
}) => likeId);
Instances.find({
'_id': {
$in: likeIds
}
}, (err, favs) => {
if (err) {
console.log('Error while fetching likes: ', err);
return res.status(500).json({
Error: 'Error while fetching Instances.'
});
}
let newArr = [];
favs.forEach((elem) => {
let post = elem.toObject();
const likeDoc = results.find(({
likeId
}) => likeId === post._id.toString());
post.public = (likeDoc && likeDoc.public) ? likeDoc.public : '';
newArr.push(post);
});
console.log(post);
return res.status(200).send(newArr);
});
});
};
You shouldn't ever play with indexes for mapping stuff, until and unless you are sure that you have same length arrays.
Hope this helps :)
Related
i am working on a mern project where i need to create an agenda that contains an attribute as array of appointments types.
in the nodejs api i am declaring a var typeRefs = [];
if a type is present in the types model i insert its ref in the previous array else i create the type and insert the new type ref in the previous array, finally i create the agenda doc based on the typeRefs array, but the array is empty outside the map function scope , inside the map function scope i can see the array values changing.
//agenda model
const agendaSchema = mongoose.Schema(
{
name: String,
types: [{ type: mongoose.Schema.Types.ObjectId, ref: "Type" }],
establishment: {
type: mongoose.Schema.Types.ObjectId,
ref: "Establishment",
},
template: { type: mongoose.Schema.Types.ObjectId, ref: "Template" },
isActive: { type: Boolean, default: true },
},
{ timestamps: true }
);
var Agenda = mongoose.model("Agenda", agendaSchema);
export default Agenda;
// types model
import mongoose from "mongoose";
const typeSchema = mongoose.Schema(
{
name: String,
duration: Number,
color: String,
online: { type: Boolean, default: true },
establishment: {
type: mongoose.Schema.Types.ObjectId,
ref: "Establishment",
},
},
{ timestamps: true }
);
var Type = mongoose.model("Type", typeSchema);
export default Type;
// api function for agenda creation
export const add = async (req, res) => {
var data = req.body;
try {
var typesRefs = [];
data.types.map((type) => {
Type.find({ name: type.text.toUpperCase() }, (err, res) => {
if (res.length === 0) {
const newType = new Type({
name: type.text.toUpperCase(),
establishment: data.establishment,
});
newType.save();
typesRefs = [...typesRefs, newType._id];
} else {
typesRefs = [...typesRefs, type._id];
}
});
});
console.log(typesRefs);
await Agenda.create({ ...data, types: typesRefs });
res.status(200).json({ message: "Agenda created successfully" });
} catch (error) {
console.log(error);
res.status(401).json({ message: "An error occured !" });
}
};
the trick is to use a for loop instead of map function.
export const add = async (req, res) => {
var data = req.body;
var typeRefs = [];
try {
for (let i = 0; i < data.types.length; i++) {
const typeExist = await Type.find({
name: data.types[i].text.toUpperCase(),
});
if (typeExist.length === 0) {
const newType = await Type.create({
name: data.types[i].text.toUpperCase(),
establishment: data.establishment,
});
typeRefs = [...typeRefs, newType._id];
} else {
typeRefs = [...typeRefs, data.types[i]._id];
}
}
console.log(typeRefs);
await Agenda.create({ ...data, types: typeRefs });
res.status(200).json({ message: "Agenda created successfully" });
} catch (error) {
console.log(error);
res.status(401).json({ message: "An error occured !" });
}
};
I've two endpoints: one that delete product document and one that retrieve the document.
After I delete the document throught by Id, the GET api call return me already the document even if it's deleted and It's not present on MongoDb.
Response of DELETE call returns { deletedCount: 1 }
Here code of GET product:
exports.getSingleProduct = (req, res, next) => {
let id = req.params.id;
Products.findById(id).populate({ path: 'internalColor' }).then(result => {
if(result && result.visible == true) {
res.status(200).json(result)
} else {
res.status(404).json({
message: 'product_not_found',
id: id
})
}
}).catch(err => {
res.status(404).json({
message: 'error_operation: ' + err,
id: id
})
});
}
Here code of DELETE product:
exports.deleteDefinallySingleProduct = (req, res, next) => {
let id = req.params.id;
console.log(id)
Products.deleteOne({ id: id }).then(result => {
if(result) {
res.status(200).json({
message: 'deleted_product'
});
}
}).catch(err => {
res.status(404).json({
message: 'error_operation: ' + err
})
})
}
Products Model
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const options = {
timestamps: true
}
const productSchema = new Schema({
name: {
type: String,
required: true
},
description: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
externalUrl: {
type: String,
required: true
},
imgUrl: {
type: String,
required: true
},
brand: {
type: String,
required: true
},
visible: {
type: Boolean,
required: true
}
}, options);
const Product = mongoose.model('Products', productSchema);
module.exports = Product;
I think the error that you are facing is caused by a typo in your code.
exports.deleteDefinallySingleProduct = (req, res, next) => {
...
Products.deleteOne({ id: id }).then(result => {
if(result) {
// this code will run always
console.log(result) // will show { n: 0, ok: 1, deletedCount: 0 },
// That is, this section of code will run always despite of delete count being 0 or more due to the request to be excecuted successfully.
...
}
The correct implementation is here:
exports.deleteDefinallySingleProduct = (req, res, next) => {
...
Products.deleteOne({ _id: id }).then(result => {
...
}
This is because by default mongooose use _id representing the document id, unless create a custom id in the schema which you didn't do.
I am having a bit of an issue with Mongoose/MongoDB this afternoon. I have a situation where I need to return all items from a collection, and doing so means that I do not pass in any search params to mongoose.find().
This is the controller that handles the get all request:
exports.get_all_posts = async (req, res, next) => {
const { params } = req;
const { sortby } = params;
//Sortby param takes two arguments for now: most_recent, oldest
try {
const getAllPosts = await BlogPost.find({}, { _id: 0 });
console.log(getAllPosts);
if (!getAllPosts) throw new Error('Could not get blog posts.');
res.json({
posts: date_.sort(getAllPosts, sortby)
});
} catch (error) {
next(error);
}
};
This is particularly where I think the issue is coming from:
const getAllPosts = await BlogPost.find({}, { _id: 0 });
I am passing an empty search parameter and then removing the _id so that it doesn't throw an error telling me that I need to provide the _id.
However I still need to be able to pull in all of the posts. My items from this collection return as normal, just without their _id's.
Here is my model for the blog posts:
const mongoose = require('mongoose');
const BlogPostSchema = new mongoose.Schema({
date: {
type: Date,
required: true
},
title: {
type: String,
required: true
},
author: {
type: String,
required: true
},
likes: {
type: Number,
required: false
},
post_body: {
type: String,
required: true
},
description: {
type: String,
required: true
},
tags: [
{
type: String,
required: false
}
],
featuredImage: {
type: String,
required: false
},
draft: {
type: Boolean,
required: true
}
});
module.exports = mongoose.model('BlogPost', BlogPostSchema);
One thing to note is that I have not defined an _id. Mongoose automatically adds in the _id field before saving a schema, so I think it is okay without it, as it has been in the past.
Thanks in advance for reading and any input!
Just as Joe has commented, { _id: 0 } as the second parameter is making your query not return the _id field.
Also as he said, there should be no problem whatsoever with using find({}).
Since other than what has already been stated, I couldn't figure out any mistake in the code snippets you provided, I guess this error could be coming from somewhere else in your project.
exports.get_all_posts = async (req, res, next) => { const { params } = req; const { sortby } = params;
try { const getAllPosts = await BlogPost.find({}); console.log(getAllPosts); if (!getAllPosts) throw new Error('Could not get blog posts.'); res.json({ posts: date_.sort(getAllPosts, sortby) }); } catch (error) { next(error); } };
no need to {_id:0} in the find() method because this method retrieve all the documents in the db collection
I need to filter data between two dates, a start and an end, to display in front only events that occurred between the selected dates ...
Product Controller method:
async show(req, res){
const { product_id, dt_ini, dt_fin } = req.params;
let dtIni = new Date(dt_ini);
let dtFin = new Date(dt_fin);
dtIni = dtIni.toISOString();
dtFin = dtFin.toISOString();
let product = await Product.findOne({ product_id });
if(product){
product = await product.populate('event').execPopulate();
await product.find({"event.timestamp": {'$gte': dtIni,'$lt': dtFin}});
}else{
return res.status(404).json({ error: `Product not found.`});
}
return res.json(product);
}
Model Event :
const mongoose = require('mongoose');
const EventSchema = new mongoose.Schema({
product_id: String,
timestamp: Date,
created_by: String,
description: String,
sale_price: Number,
list_price: Number,
has_discount: Boolean,
url: String,
warehouse: String,
page_type: String,
showcase:{
name: String,
position : Number
},
});
module.exports = mongoose.model('Event', EventSchema);
Model Product :
const mongoose = require('mongoose');
const ProductSchema = new mongoose.Schema({
product_id: Number,
product_sku: String,
product_name: String,
product_brand: String,
product_category: String,
event: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Event'
}
],
});
module.exports = mongoose.model('Product', ProductSchema);
After MongoDB populate() I am receiving this error:
(node:3496) UnhandledPromiseRejectionWarning: TypeError: product.find is not a function
What am I missing?
You can do a conditional populate:
async show(req, res){
try {
const { product_id, dt_ini, dt_fin } = req.params;
if(!product_id || !dt_ini || !dt_fin)
throw "You must supply a product id with start and end dates!";
const existingProduct = await Product
.findOne({ product_id })
.populate("event", null, { timestamp: { $gte: new Date(dt_ini), $lte: new Date(dt_fin) } })
if(!existingProduct)
throw "Unable to locate any product events in that date range.";
res.status(200).json({ product: existingProduct });
} catch(e) {
res.status(404).json({ error: e.toString() });
}
}
Or, you can do an aggregated query:
Here's a simplified working playground: https://mongoplayground.net/p/YGiywDCkR-2
async show(req, res){
try {
const { product_id, dt_ini, dt_fin } = req.params;
if(!product_id || !dt_ini || !dt_fin)
throw "You must supply a product id with start and end dates!";
const existingProduct = await Product.aggregate([
// match against the product id
{ $match: { product_id } },
// lookup (populate) the "event" field with event data as "events"
{
$lookup: {
from: "events",
localField: "event",
foreignField: "_id",
as: "events"
}
},
// spread out the populated "events" field
{ $unwind: "$events"},
// filter the "events.timestamp" with dates
{
$match: {
"events.timestamp": {
"$gte": new Date(dt_ini),
"$lte": new Date(dt_fin)
}
}
}
]);
if(existingProduct.length === 0)
throw "Unable to locate any product events in that date range.";
res.status(200).json({ product: existingProduct });
} catch(e) {
res.status(404).json({ error: e.toString() });
}
}
Although, be careful about using $lookup as it can be quite expensive.
how can I get specify email and its value only, from JSON array result which should be like=>only( email: abc#gmail.com)
here is my code:
MongoClient.connect(url, function(err, db) {
if (err) throw err;
var dbo = db.db("Scream_It");
var query = { bid_location : 'abbottabad' };
dbo.collection("bid_placement").find(query).sort({bid_amount:-1}).limit(1).toArray(function(err, result){
if (err) throw err;
console.log(result);
// console.log(JSON.stringify(result));
var string = JSON.stringify(result);
console.log(string);
var objectValue = JSON.parse(string);
// console.log(objectValue);
console.log(objectValue.email);
this is the result which i am getting in console
[ { _id: 5a9f8849fc49ca1ff4aee3dc,
email: 'abc#gmail.com',
bid_amount: '200',
bid_time: '22:22:22:22',
bid_location: 'abbottabad',
bid_status: 'false' } ]
This is a simple JavaScript:
var res = [
{ _id: '5a9f8849fc49ca1ff4aee3dc',
email: 'abc#gmail.com',
bid_amount: '200',
bid_time: '22:22:22:22',
bid_location: 'abbottabad',
bid_status: 'false' },
{ _id: '5a9f8849fc49ca1ff4aee3dd',
email: 'abcd#gmail.com',
bid_amount: '200',
bid_time: '22:22:22:22',
bid_location: 'abbottabad',
bid_status: 'false' },
{ _id: '5a9f8849fc49ca1ff4aee3de',
email: 'abcde#gmail.com',
bid_amount: '200',
bid_time: '22:22:22:22',
bid_location: 'abbottabad',
bid_status: 'false' }
];
var finalRes = res.map(({email}) => ({email}));
console.log(finalRes);
You can use reduce or map on your array:
Using reduce
reducedResults = result.reduce((accumulator, current) => {
accumulator.push({ email: current.email });
return accumulator;
}, [])
Using map
mappedResults = result.map((user) => {
return { email: user.email };
})
You could use select method from mongoose api. Basically, you can control with it what will result object contains of its properties. So, your code could look like this:
MongoClient.connect(url, function(err, db) {
if (err) throw err;
var dbo = db.db("Scream_It");
var query = { bid_location : 'abbottabad' };
dbo.collection("bid_placement").find(query).select({email: 1, _id: 0}).sort({bid_amount:-1}).limit(1).toArray(function(err, result){
if (err) throw err;
console.log(result);
// console.log(JSON.stringify(result));
var string = JSON.stringify(result);
console.log(string);
var objectValue = JSON.parse(string);
// console.log(objectValue);
console.log(objectValue.email);
You should get something like this:
[ { email: 'abc#gmail.com'} ]
If you need _id, use this in select {email: 1, _id: 1}