Querying items between a range of dates using Mongoose - javascript

I'm trying to get my query working correctly. It's supposed to return 5 items which have a utcTime that falls between two dates. I've verified that documents in my mongoDB do have correct dates and should be returned. This query works if I remove the .where line. In fact it works if I remove the gte and lt lines. The date gets parsed correctly so I'm not sure why this query is failing me.
I've also tried the alternative which is to put a filter in the find function like this
{
"utcTime": {
"$gte": new Date(req.params.startdate),
"$lt": new Date(req.params.enddate)
}
}
But that doesn't work either.
// Get the top 5 items sorted in decreasing order by karma in between
// the specified dates
listItemRoute.route('/top5/:startdate/:enddate').get((req, res) => {
console.log(req.params.startdate) //2020-05-13T07:00:00Z
console.log(req.params.enddate) //2020-05-20T07:00:00Z
ListItem
.find()
.limit(5)
.where('utcTime').gte( new Date(req.params.startdate) ).lt( new Date(req.params.enddate) )
.sort({karma: -1})
.exec((error, data) => {
if (error) {
return next(error)
} else {
res.json(data)
}
})
})
Here's my model
// Define collection and schema
let ListItemSchema = new Schema({
name: {
type: String,
required: true
},
karma: {
type: Number
},
utcTime: {
type: Date
}
})
Everything looks correct and I've tried many other stackoverflow suggestions to no avail. Any help would be awesome.
Edit: example of DB data
{
"name": "TITLE BLAH",
"karma": 11502,
"utcTime": "2020-05-17T09:50:21Z"
}

Related

Mongoose - Deleting documents is unresponsive

I'm trying to use Mongoose (MongoDB JS library) to create a basic database, but I can't figure out how to delete the documents / items, I'm not sure what the technical term for them is.
Everything seems to work fine, when I use Item.findById(result[i].id), it returns a valid id of the item, but when I use Item.findByIdAndDelete(result[i].id), the function doesn't seem to start at all.
This is a snippet the code that I have: (Sorry in advance for bad indentation)
const testSchema = new schema({
item: {
type: String,
required: true
},
detail: {
type: String,
required: true
},
quantity: {
type: String,
required: true
}
})
const Item = mongoose.model("testitems", testSchema)
Item.find()
.then((result) => {
for (i in result) {
Item.findByIdAndDelete(result[i].id), function(err, result) {
if (err) {
console.log(err)
}
else {
console.log("Deleted " + result)
}
}
}
mongoose.connection.close()
})
.catch((err) => {
console.log(err)
})
I'm not sure what I'm doing wrong, and I haven't been able to find anything on the internet.
Any help is appreciated, thanks.
_id is a special field on MongoDB documents that by default is the type ObjectId. Mongoose creates this field for you automatically. So a sample document in your testitems collection might look like:
{
_id: ObjectId("..."),
item: "xxx",
detail: "yyy",
quantity: "zzz"
}
However, you retrieve this value with id. The reason you get a value back even though the field is called _id is because Mongoose creates a virtual getter for id:
Mongoose assigns each of your schemas an id virtual getter by default which returns the document's _id field cast to a string, or in the case of ObjectIds, its hexString. If you don't want an id getter added to your schema, you may disable it by passing this option at schema construction time.
The key takeaway is that when you get this value with id it is a string, not an ObjectId. Because the types don't match, MongoDB will not delete anything.
To make sure the values and types match, you should use result[i]._id.

Mongo findById() only works sometimes even when passed a valid ID

I am having a strange issue querying a Mongo DB collection. I am using findById() to get a single item that works sometimes and not others.
I have checked the id being passed to the server route and in all cases, they match perfectly with the targeted document in the collection.
Here is the basic code:
router.get("/:postId", async (req, res) => {
console.log('id : ', req.params.postId)
console.log('type: ', typeof(req.params.postId)) // id is a string
try {
const post = await Post.findById(req.params.postId).exec();
console.log('post :', post) // sometimes null
res.json(post);
} catch (err) {
res.json({ message: err });
}
});
In the above route, only certain posts will be found while others come back null. This happens regardless of whether the id passed is correct and the document exists with the exact id.
If anyone has any ideas about what could be going wrong here I'd much appreciate the help!
EDIT
I have done some more debugging and think it is something to do with the Schema for the Post model.
For example, this object will be found:
{
"tags": ["foo"],
"_id": "8394839483fhg020834903",
"title": "bar",
"content": "baz",
"isPrivate": true,
}
But this one will not because of the missing isPrivate property.
{
"tags": [],
"_id": "5e0fdc631ef5c46b285a4734",
"title": "New post",
"content": "Some content here",
}
I have tested this across multiple queries and it appears to the root of the problem.
I have tried adding
isPrivate: {
required: false
}
To the Schema but it doesn't seem to solve the issue.
Here is the full Schema
const postSchema = mongoose.Schema({
title: {
type: String,
required: true
},
content: {
type: String,
required: true
},
tags: [{ type: String }],
date: {
type: Date,
default: Date.now
},
isPrivate: {
type: Boolean
required: false
}
});
I'm not a Mongo/Mongoose expert, so any guidance would be much appreciated.
If post id match with any record it return data, otherwise it will return null. You should handle the exception
router.get("/:postId", async (req, res) => {
try {
const post = await Post.findById(req.params.postId).exec();
if(post) {
return res.json(post);
}
res.json({ message:'No Post found' });
} catch (err) {
res.json({ message: err });
}
});
You can manually check is record exists against a post id. You can use MongoDB Compass for gui browse the record
I believe the issue might be with your _id as per mongo standard _id should be a String is of 12 bytes or a string of 24 hex characters.
We can check if the _id is valid using mongoose.isValidObjectId()
I did run this check on your objects that you posted and indeed 1 is invalid while other is valid
const mongoose = require('mongoose');
console.log(`is '8394839483fhg020834903' valid - ${mongoose.isValidObjectId('8394839483fhg020834903')}`);
console.log(`is '5e0fdc631ef5c46b285a4734' valid - ${mongoose.isValidObjectId('5e0fdc631ef5c46b285a4734')}`);
It gives me
You will have to check what is modifying your ID's in the code, you can upload your schema to get a better understanding as well.

Meteor MongoDB Setting field of array per document

I have a Documents in a Collection that have a field that is an Array (foo). This is an Array of other subdocuments. I want to set the same field (bar) for each subdocument in each document to the same value. This value comes from a checkbox.
So..my client-side code is something like
'click #checkAll'(e, template) {
const target = e.target;
const checked = $(target).prop('checked');
//Call Server Method to update list of Docs
const docIds = getIds();
Meteor.call('updateAllSubDocs', docIds, checked);
}
I tried using https://docs.mongodb.com/manual/reference/operator/update/positional-all/#positional-update-all
And came up with the following for my Server helper method.
'updateAllSubDocs'(ids, checked) {
Items.update({ _id: { $in: ids } }, { $set: { "foo.$[].bar": bar } },
{ multi: true }, function (err, result) {
if (err) {
throw new Meteor.Error('error updating');
}
});
}
But that throws an error 'foo.$[].bar is not allowed by the Schema'. Any ideas?
I'm using SimpleSchema for both the parent and subdocument
Thanks!
Try passing an option to bypass Simple Schema. It might be lacking support for this (somewhat) newer Mongo feature.
bypassCollection2
Example:
Items.update({ _id: { $in: ids } }, { $set: { "foo.$[].bar": bar } },
{ multi: true, bypassCollection2: true }, function (err, result) {
if (err) {
throw new Meteor.Error('error updating');
}
});
Old answer:
Since you say you need to make a unique update for each document it sounds like bulk updating is the way to go in this case. Here's an example of how to do this in Meteor.
if (docsToUpdate.length < 1) return
const bulk = MyCollection.rawCollection().initializeUnorderedBulkOp()
for (const myDoc of docsToUpdate) {
bulk.find({ _id: myDoc._id }).updateOne({ $set: update })
}
Promise.await(bulk.execute()) // or use regular await if you want...
Note we exit the function early if there's no docs because bulk.execute() throws an exception if there's no operations to process.
If your data have different data in the $set for each entry on array, I think you need a loop in server side.
Mongo has Bulk operations, but I don't know if you can call them using Collection.rawCollection().XXXXX
I've used rawCollection() to access aggregate and it works fine to me. Maybe work with bulk operations.

How to delete an item in an array using Mongoose/Node.js

So I've read and tried to implement the other solutions for this. I'm just trying to get some insight into why it's not working for me. This is my first project with back end work. I'm working my way through a course but wanted to try something on my own to make the concepts stick.
Here is my Schema
//ANIMAL
var animalSchema = new mongoose.Schema({
image: String,
name: String,
variety: String,
DOB: Date,
logs: [{
amount: Number,
notes: String,
dateMilked: Date
}],
created: { type: Date, default: Date.now }
});
So far I've managed to get all of my RESTful routes for animals and logs working except to delete a log.
This is what I have, but it isn't deleting anything - it also doesn't throw any errors.
app.delete("/animals/:id/logs/:id", function(req, res) {
Animal.findOneAndUpdate({ 'logs._id': req.params.id }, {
$pull: {
"logs": { "_id": req.body.id }
}
}, { safe: true, multi: true },
function(err, foundAnimal) {
if (err) {
console.log(err);
res.redirect("/");
}
else {
res.redirect("/animals/" + foundAnimal._id + "/logs");
}
});
});
Any help or insight would be great!! I'm looking to learn!
I think the problem is req.body.id probably doesn't have any value, because you are sending both ids in the path.
And if you call them the same app.delete("/animals/:id/logs/:id", I'm pretty sure req.params.id is going to have one of two values, but you are not going to be able to get the other one.
You should call them with different names, like this:
app.delete("/animals/:animalId/logs/:logId",
and then you can access to both variables without any collision:
req.params.animalId
req.params.logId
Hope it helps

Mongoose find inside nested schema

In mongoose we are deeply searching inside a nested schema, without much success. Every time we run this function we always get an empty array returned.
function findAlarms(lastUpdate = new Date(0), record = Record) {
// For docs on find http://mongoosejs.com/docs/queries.html
return record
.find({
// Date due must be less than "now"
'documents.alarm.date_due': {
$lte: Date.now(),
},
// Must be greater than the last update and less than "now"
'documents.alarm.date_reminder.reminder': {
$gte: lastUpdate,
$lte: Date.now(),
},
})
.populate('documents')
.exec();
}
Our schemas, greatly summarized, look like this:
const RecordSchema = new mongoose.Schema({
documents: [
{
type: Schema.Types.ObjectId,
ref: 'Document',
},
],
});
And our documents schema, similarly summarized looks like this:
const DocumentSchema = new mongoose.Schema({
alarm: {
date_due: { type: Date },
date_reminder: [
{
reminder: { type: Date },
},
],
},
});
This search returns no matching elements, even though we know there are documents that match. If we modify our findAlarms method to use the documents schema:
function findAlarms(lastUpdate = new Date(0), document = Document) {
// For docs on find http://mongoosejs.com/docs/queries.html
return document
.find({
// Date due must be less than "now"
'alarm.date_due': {
$lte: Date.now(),
},
// Must be greater than the last update and less than "now"
'alarm.date_reminder.reminder': {
$gte: lastUpdate,
$lte: Date.now(),
},
})
.exec();
}
It will return all of our matching documents. However, having records is essential for our needs. Now, I could use a hack and then find records using the array of document._ids that return.
Nonetheless, I would love to know if there's an approach where we can find using the records directly, since adding that extra step feels really hacky, and this operation runs every 5 minutes so I'd love to be more efficient wherever posible.

Categories