Im building an app where i need to add messages as sub documents in a chat room. i need to return the data of the the sub document as soon as i insert it to the existing room. this is my code
Room.findOne({ roomname: data.room }, (err, room) => {
room.messages.push({ username: data.username, message: data.message });
room.save((err, room) => {
socket.broadcast.to(data.room).emit("new message", {
username: room.messages.slice(-1).name,
message: room.messages.slice(-1).message,
createdat: room.messages.slice(-1).createdat
});
});
});
So this the code i have used to retrieve last sub document. Is there any other way to achieve this?
Use .findOneAndUpdate() with $push instead. That way you only touch the database "once", and it also avoids the problem that something else possibly modifies the document and you end up overwriting that. This avoids the danger of the .find() then .save() pattern:
Room.findOneAndUpdate(
{ roomname: data.room },
{ "$push": {
"messages": { username: data.username, message: data.message }
}},
{ "new": true },
(err, room) => {
let message = room.messages.slice(-1)[0];
socket.broadcast.to(data.room).emit(
"new message",
Object.keys(message).filter(k => k !== "_id")
.reduce((acc,curr) => Object.assign(acc,{ [curr]: message[curr] }),{})
);
}
);
Also, just .slice(-1)[0] to get the whole object and just return it, or filter out the unwanted keys just as shown above.
Or even simply just return all the fields from the array element and $slice the array to return from the query from the server:
Room.findOneAndUpdate(
{ roomname: data.room },
{ "$push": {
"messages": { username: data.username, message: data.message }
}},
{ "new": true, "fields": { "messages": { "$slice": -1 } } },
(err, room) => {
socket.broadcast.to(data.room).emit(
"new message", room.messages[0]
);
}
);
room.messages.slice(-1).name
This is incorrect, demo below
[1,2,3].slice(-1)
// returns [3], not 3
You still have to use index
room.messages.slice(-1)[0].name
// or simply
room.messages[room.messages.length - 1].name
Related
I have Mongoose CastError issue. I made a nodeJs API. At the specific route, it returns data appended with some other data. I saw many fixes available here but my scenario is different.
Here is my model and the problem occurs at fields property.
const deviceSchema = new Schema({
device_id: { type: String, required: true },
user_id: { type: Schema.Types.ObjectId, ref: 'User', require: true },
location_latitude: { type: String, default: '0' },
location_longitude: { type: String, default: '0' },
fields: [{ type: String }],
field_id: { type: Schema.Types.ObjectId, ref: 'Field', required: true },
timestamp: {
type: Date,
default: Date.now,
},
});
and my controller is
exports.getAllDevices = async (req, res) => {
try {
let devices = await Device.find({})
.sort({
timestamp: 'desc',
})
.populate('user_id', ['name']);
// Let us get the last value of each field
for (let i = 0; i < devices.length; i++) {
for (let j = 0; j < devices[i].fields.length; j++) {
if (devices[i].fields[j] !== null && devices[i].fields[j] !== '') {
await influx
.query(
`select last(${devices[i].fields[j]}), ${devices[i].fields[j]} from mqtt_consumer where topic = '${devices[i].device_id}'`
)
.then((results) => {
************** Problem occurs here **************
if (results.length > 0) {
devices[i].fields[j] = {
name: devices[i].fields[j],
last: results[0].last,
};
} else {
devices[i].fields[j] = {
name: devices[i].fields[j],
last: 0,
};
}
************** Problem occurs here **************
});
}
}
}
// Return the results
res.status(200).json({
status: 'Success',
length: devices.length,
data: devices,
});
} catch (err) {
console.log(err);
res.status(500).json({
error: err,
});
}
};
It actually gets data from InfluxDB and appends it to fields property which was fetched from MongoDB as mentioned in my model. But it refused to append and CastError occurs.
After addition, it will look like this
I can't resolve this error after trying so many fixes. I don't know where I'm wrong. Please suggest to me some solution for this.
I can see you are not using devices variable as Mongoose Document. devices is an array of Documents.
I would like to suggest you to use lean() function to convert from Document to plain JavaScript object like
let devices = await Device.find({})
.sort({
timestamp: 'desc',
})
.populate('user_id', ['name'])
.lean();
I have an embedded NeDB database with numerous entries with multiple fields and I'm looking to only get the dates from every entry and store it into an array. I'm using NeDB, NodeJS and express.
The dataset looks like so:
{"goalName":"swim 5km","details":"I want to swim 5km","date":"2021-05-15","completed":false,"author":"somename","_id":"BMnvTm54rNbwc9D4"}
{"goalName":"swim 5km","details":" I want to swim another 5km","date":"2021-03-14","completed":false,"author":"somename","_id":"EwEicEYZAfFxY9Z6"}
{"goalName":"20 pushups","details":"I want to complete 20 full pushups","date":"2021-05-14","completed":false,"author":"anthername","_id":"rP7q6L8jnwGyAgGD"}
I'm only interested in the dates where the author is somename,
I can retrieve these documents using:
getEntriesByUser(userName) {
return new Promise((resolve, reject) => {
this.db.find({ 'author': userName }, function (err, entries) {
if (err) {
reject(err);
} else {
resolve(entries);
console.log('getEntriesByUser returns: ', entries);
}
})
})
}
which then returns the documents where the username = someusername, but i'm only interested in the dates. Preferably storing them to an array with a result like so:
[2021-05-15, 2021-03-14, 2021-05-14]
How would I got about doing this?
Thanks for your help!
You can use the optional second projection parameter of the find() and findOne() methods to include or omit properties of the returned records. see: NeDB#projections.
db.find({ author: userName }, { date: 1, _id: 0 }, function (err, docs) {...});
const
Datastore = Nedb,
db = new Datastore(),
data = [
{ goalName: "swim 5km", details: "I want to swim 5km", date: "2021-05-15", completed: false, author: "somename" },
{ goalName: "swim 5km", details: " I want to swim another 5km", date: "2021-03-14", completed: false, author: "somename" },
{ goalName: "20 pushups", details: "I want to complete 20 full pushups", date: "2021-05-14", completed: false, author: "anthername" },
];
for (const datum of data) {
db.insert(datum);
}
function getEntriesByUser(userName) {
return new Promise((resolve, reject) => {
db.find({ author: userName }, { date: 1, _id: 0 }, function (err, entries) {
if (err) {
reject(err);
} else {
resolve(entries);
console.log('getEntriesByUser returns: ', entries);
}
})
})
}
getEntriesByUser('somename').then((entries) => {
console.log('Mapped return value: ', entries.map(({ date }) => date));
});
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/nedb/1.8.0/nedb.min.js"></script>
This is the User schema in mongoose:
var userSchema = new mongoose.Schema({
email: {
type: String,
unique: true,
required: true,
},
name: {
type: String,
required: true,
},
Addtasks: [
{
topic: String,
words: Number,
keywords: String,
website: String,
otherdetails: String,
exampleRadios: String,
deadline: Date,
Date: String,
fileName: String,
Bigpaths: [],
},
],
});
module.exports = mongoose.model('User', userSchema);
I want to use/access the Bigpaths array, which is defined inside the Addtasks array, which is defined in User. Data is already are there in mongoDB, which I have inserted via UI page. I am trying the following code but I am getting this error in console:
data.Addtasks[Object.keys(data.Addtasks).length - 2].Bigpaths.forEach(
(element) => {
// ...
}
)
as
TypeError: Cannot read property 'Bigpaths' of undefined
at \Desktop\grumpytext\routes\index.js:99:71
Code:
const { files } = req;
User.findOne({ email: req.user.email }, function (error, data) {
if (error) {
console.log('Three');
} else if (data) {
if (Object.keys(data.Addtasks).length > 1) {
data.Addtasks[Object.keys(data.Addtasks).length - 2].Bigpaths.forEach(
(element) => {
files.forEach((currentElement) => {
if (element.name == currentElement.filename) {
files.pull(currentElement.filename);
}
});
}
);
}
}
});
How to resolve this error or how to access all the elements of Bigpaths array so that I can iterate it with forEach loop?
I'm not sure here, but I think you need to populate Addtasks prior to manipulating it:
const files = req.files;
User.findOne({email:req.user.email}).populate('Addtasks').exec((error, data) => {
if (error) {
console.log("Three");
}
else
{
if(data)
{
if(Object.keys(data.Addtasks).length > 1)
{
console.log("Addtasks count: " + Object.keys(data.Addtasks).length);
data.Addtasks[Object.keys(data.Addtasks).length - 2].Bigpaths.forEach(element => {
files.forEach(currentElement => {
if(element.name == currentElement.filename)
{
files.pull(currentElement.filename);
}
})
});
}
}
}
});
Please notice the log console.log("Addtasks count: " + Object.keys(data.Addtasks).length); - in case the solution does not work, I advise to add some prints, especially to check if the count of elements is as expected or properties within an object are fine.
I need to remove a specific object from my mongoDB array.
Should remove the info above inside the red cube but (0: Object)
I tried the way I show below but didn't work.
And I need to remove the entire object but can't pass the values directly in the query so I need to grab the info from mongoDB and remove them.
router.post("/deleteArquive/:id", ensureAuthenticated, (req, res) => {
var id = mongoose.Types.ObjectId(req.params.id);
House.update(
{ "expensesHouse._id": id },
{
$pull: {
expensesHouse: {
status: "expensesHouse.status",
_id: "expensesHouse._id",
expenseType: "expensesHouse.expenseType"
}
}
}
).then(house => {
if (house.userID !== req.user.id) {
res.redirect("/houses/houses");
} else {
req.flash("success_msg", "House removed!");
res.redirect("/houses/houses");
}
});
});
If I understand the requirements correctly, this should do the job:
House.update(
{ "expensesHouse._id": id },
{
$pull: {
expensesHouse: {
_id: id
}
}
}
)
This is my db schema
let couponId = Schema({
RestaurantId: { type: String },
RestaurantName: { type: String },
RestaurantLocation: { type: String },
AssignedTo: Schema.Types.Mixed,
CouponValue: { type: [String] }
});
I want to update the AssignedTo field with a value of array of objects with a dynamic key and a value. I am performing this query
CouponId.findOne({
"RestaurantId": resId
}, (err, restaurant) => {
value.push({
[userNumber]: restaurant.CouponValue[0]
});
console.log(value);
restaurant.update({
"RestaurantId": resId
}, {
$set: {
"AssignedTo": value
}
}, function(err) {
if (err) {
console.log(err);
} else {
console.log("updated");
}
});
});
The query, when executed, is giving the result of updated in console but its not getting updated in db. If this query is converted to MongoShell query and executed, it gives the result and collection is getting updated, where mongoShell query i am running is
db.couponids.update({"RestaurantId" : "1234"},{$set:{"AssignedTo":[{"1234":"2345"}]}});
Where am i going wrong?
restaurant is the output from the first collection and doesn't have any update function in it... So, You need to keep the same collection name from which you have done findOne
CouponId.update({
"RestaurantId": resId
}, {
$set: {
"AssignedTo": value
}
}, function(err) {
if (err) {
console.log(err);
} else {
console.log("updated");
}
});