I'm using mongoose 4.7.x and mongodb 3.2. I want to search all date without time. E.g update all obj with this date 2016-12-14.
In my database I have multiple datetime like this :
2016-12-14 00:00:00.000Z
2016-12-14 00:00:00.000Z
2016-12-14 01:00:00.000Z
2016-12-14 01:00:00.000Z
2016-12-14 02:00:00.000Z
2016-12-14 02:00:00.000Z
I tried this :
var query = {date: new Date('2016-12-14')};
var doc = {name: 'TEST'};
var options = {multi: true};
Model.update(query, doc, options, function(err, result){
console.log('All data updated')
});
But this will just update all fields with time 00:00:00 because new Date() will returns the datetime.
How can I do to search all fields with a certain date without time ? I can change the model if it's necessary.
You can do it in between a range of dates. Use $gte for the lower range, and $lt for the higher one (which is the next day) to keep it from selecting the next day at 00:00:00 hours.
var query = {
date: {
$gte: new Date('2016-12-14'),
$lt: new Date('2016-12-15')
}
};
var doc = {name: 'TEST'};
var options = {multi: true};
Model.update(query, doc, options, function(err, result){
console.log('All data updated')
});
All documents between 2016-12-14 00:00:00.000Z and 2016-12-14 23:59:59.999Z will be updated.
Related
Building a rental listing application using MERN stack. My Listing model is below:
const listingShcema = new mongoose.Schema(
{
hostedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
},
title: {
type: String,
required: true,
},
description: {
type: String,
required: true,
},
numberOfGuests: {
type: String,
required: true,
},
numberOfRooms: {
type: String,
required: true,
},
numberOfBeds: {
type: String,
required: true,
},
numberOfBaths: {
type: String,
required: true,
},
price: {
type: String,
requried: true,
},
location: {
streetAddress: { type: String },
city: { type: String },
state: { type: String },
postalCode: { type: String },
},
bookedDates: [
{
startDate: Date,
endDate: Date,
},
],
imgUrls: [
{
type: String,
},
],
amenities: [
{
type: String,
},
],
},
{ timestamps: true }
);
Now it is fairly easy to run queries on everything given by the users search query except for the dates they want to rent out. The listing model keeps track of all bookedDates. I want to be able search the mongodb for Listings that do not have bookedDates that match the dates supplied by the users search query (showing available listings to the user). I can't think of a way to do this?? I figured it is easy keeping track of only the booked dates instead of taking away booked dates from an array of all available dates.
Doing this directly in the DB is kind of awkward, especially if you're only storing the startDate and endDate for each booking. For example, if someone books a listing from the 1st to the 5th - if another user is searching for the same listing from the 3rd to the 7th, it doesn't match the booking saved, but the listing wouldn't still be counted as available.
I'd suggest taking another look at your model and perhaps even separating out the booked dates to their own documents.
But, keeping with what you have, assuming you're not booking too far in the future, it might be worth storing the bookedDates as a flat array. So if we have a listing booked from the 1st to the 3rd, and the 6th to the 8th, your array would look like this:
bookedDates: [
'2021-01-01',
'2021-01-02',
'2021-01-03',
'2021-01-06',
'2021-01-07',
'2021-01-08'
]
Then, if someone searches for the listing between the 2nd and 4th, you'd again break down those dates into a flat array, and then you should be able to use the $nin operator (https://docs.mongodb.com/manual/reference/operator/query/nin/):
const desiredDates = [
'2021-01-02',
'2021-01-03',
'2021-01-04'
]
Listing.find({ bookedDates: { $nin: desiredDates } })
To quote the relevant part of the page:
If the field holds an array, then the $nin operator selects the documents whose field holds an array with no element equal to a value in the specified array (e.g. , , etc.).
This is obviously going to work best if you have another way to filter out the majority of your listings, so your not doing an array-array check for every listing in your database.
You'll also have to keep bookedDates up-to-date by removing past dates.
Another option is just to query your listings and do the date filtering at the application level, in which case, you can probably keep the startDate and endDate format that you have.
Update for flattening dates
Something like this should work. I just brute force it - people are generally only going to book a listing for a few days mostly, so your loop is going to be quite small. There are some checks in there if it's for one day, and to make sure the start is before the end, but that's about it.
As a method, you can call it whenever you want, and it'll split two dates into a flattened string array in yyyy-mm-dd format
function getFlattenedDatesAr(inputStart, inputEnd) {
// convert to dates and make sure that start is before end
let startDate = new Date(inputStart)
let endDate = new Date(inputEnd)
if(startDate > endDate) {
let temp = startDate;
startDate = endDate;
endDate = temp;
}
// get our dates in yyyy-mm-dd format
const startDateStr = startDate.toISOString().substr(0, 10)
const endDateStr = endDate.toISOString().substr(0, 10)
// check if they've only booked for one day
if(startDateStr === endDateStr) {
return [startDateStr];
return;
}
// fill our our dates array
const bookedDates = [startDateStr]
let currDate = startDate;
while(true) {
// NOTE: setDate returns a timestamp, not a Date
const nextDateTS = currDate.setDate(currDate.getDate() + 1);
// get our date string and add it to our bookedDates array
currDate = new Date(nextDateTS)
const currDateStr = currDate.toISOString().substr(0, 10);
bookedDates.push(currDateStr);
// if our string matches our end date, we're done
if(currDateStr === endDateStr) {
break
}
}
return bookedDates
}
// assume these are the dates sent, in yyyy-mm-dd format
let inputStart = '2021-01-01'
let inputEnd = '2021-01-05'
const datesAr = getFlattenedDatesAr(inputStart, inputEnd)
console.log(datesAr);
I am using .aggregate and $match to filter dates and then $sample them. Right now I get an empty array. I am using the following code:
const result = await Event.aggregate([
{
$match: { event_added: condition },
},
{ $sample: { size: 10 } },
]);
When I use .find in the code below it works perfectly, and I get all records satisfying the condition.
const result = await Event.find({ event_added: condition });
The condition object is the following:
var condition = { $gt: start, $lt: end };
For Model.find() operations, mongoose can infer the data type based on your schema and converts the types in your query object accordingly.
So if start and end are not Date objects, mongoose will try to cast the values to Date, as specified in your Event schema.
Aggregation objects are more complex and mongoose can not automatically do type casting. If your values are not Date objects you'll have to convert them first before passing them to Model.aggregate()
Generally this should work
const condition = { $gt: new Date(start), $lt: new Date(end) }
I can not query the collection filtering by a date
The following code is that I have so far
//The following code shows how my schema is:
date: {type: Date, required: true}
//This is a date from a collection in MongoDB:
date: 2019-09-06T16:48:14.000+00:00
//This is how I saved the date in the MongoDB:
"2019/09/09 08:55:15"
//And this is how I perform the query in NodeJS:
let year = req.query.year;
let month = req.query.month;
let day = req.query.day;
let startDate = new Date(year, month, day);
let endDate = new Date(year, month, day);
// find documents in MongoDB
let query = SALES.find({ date: { $gte: startDate, $lte: endDate }});
// execute the query at a later time
query.exec(function (err, item) { // item is a dictionary
if (err) return handleError(err); // throws an error if any
if (item === null || item.length === 0) {
res.json({
status: 'empty',
});
}
else {
res.json({
salesRecord: item
});
}
});
I read that is easy to get, but I am not able to do it. Any help is welcome 🙏
I have not error on the query, simply I get the response as empty.
The expected results is to get the dates from the specified date
Short answer
Date you saved("2019/09/09 08:55:15") is being treated as a string.
Try this:
db.mycollection.find({
"date" : {"$gte": new Date()}
})
or
db.mycollection.find({
"date" : {"$gte": ISODate(new Date())} // current date
});
Description
Mongodb shell provides various methods to return the date.It can be a string or as a Date object:
Date() method which returns the current date as a string.
new Date() constructor which returns a Date object using the ISODate() wrapper.
ISODate() constructor which returns a Date object using the ISODate() wrapper.
I have left my code for over a year and have ran NPM install and since it obviously has changed :)
My Query
var mongoose = require('mongoose'),
Clubs = mongoose.model('Clubs');
getNorsemanClubs: function(passData){
return new Promise(function (resolve) {
Clubs.find({norseman:true}, 'name teamID date norseman eleven').sort({ eleven : 'asc'})
.then(function(clubs){
console.info(clubs);
passData.clubs = clubs;
resolve(passData);
});
})
}
console.info(clubs); Output
Here you can see that it is returning the model rather than the results of the clubs.
My model
var mongoose = require('mongoose')
, Schema = mongoose.Schema;
/**
* User Schema
*/
var ClubsScheme = new Schema({
name: String,
teamID: { type: String, default: null },
date: { type: Date, default: Date.now},
norseman: {type: Boolean, default: false},
eleven: Number
})
mongoose.model('Clubs', ClubsScheme);
How can I get it return a list of the clubs?
The clubs table is full of match data. But I used to get a return from 'name teamID date norseman eleven' now I just get a return of :\
**Using exec()
You need to execute your find mongoose query method, by trailing your query with exec() method so that it return a Promise with your list values resolved, so change the following line:
Clubs.find({norseman:true}, 'name teamID date norseman eleven').sort({ eleven : 'asc'})
to:
Clubs.find({norseman:true}, 'name teamID date norseman eleven').sort({ eleven : 'asc'}).exec()
I'm trying to achieve the result:
Get tweets made by user which emails starts with 'a'.
var Tweet = mongoose.model('Tweets', new mongoose.Schema({
user_id : String,
text : String,
date : { type: Date, default: Date.now }
})
);
var User = mongoose.model('Users', new mongoose.Schema({
name : String,
email : String,
date : { type: Date, default: Date.now }
})
);
The queries look like:
app.get('/mongodb/tweets/users/get', function(req, res, next) {
var time = process.hrtime();
User.find({email : /^a/}, { _id:1 }, function(err, users) {
var dataArr = [];
for(o in users) { dataArr.push(users[o]._id); }
Tweet.find({user_id : { $in : dataArr }}, function(err, tweets) {
var diff = process.hrtime(time);
res.send({ seconds : diff[0], nanoseconds : diff[1], result: tweets.length});
});
});
});
I'm aware that I'm doing something wrong because the performance of those queries are kind of bad in comparison to the normal MySQL query.
Tweets : {"seconds":0,"nanoseconds":904058152,"result":4396}
Tweets (MySQL) : {"seconds":0,"nanoseconds":455872373,"result":4368}
I've also tried to use the populate() method but this occurred in even further lost in performance.
Any suggestions how to handle that in order to make it work faster? I'm looking for clean code that won't require me to do workaround (object to array conversion). What would be the solution to this kind of problem with some elegant and most correct approach?
#kevin
Thanks for the index tip, it helped with performance a lot and made it:
{"seconds":0,"nanoseconds":412133579,"result":4396}
Try adding an index to the user_id field. This will improve performance on collections with large numbers of records.