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 have a schema like this
const someSchema = new mongoose.Schema (
{
updatedAt: { type: string }
}, { timestamps: { currentTime: () => moment().format("MM/DD/YY, h:mm") } }
);
I want to query this collection to find the documents updated within a date range for example:
12/05/20 to 12/31/20, hours and minutes don't matter. My startDate and endDate is in the YYYY-MM-DD format. I have tried to find
updatedAt: {
$gte: new Date(startDate),
$lte: new Date(endDate)
}
or
updatedAt: {
$gte: startDate,
$lte: endDate
}
but they are not working. Thank you for your time!
First try to alter moment().format("MM/DD/YY, h:mm")
to moment().format("MM/DD/YY, HH:MM:SS")
then
updatedAt: { $gte: new Date(startDate).setHours(0,0,0,0)
$lte: new Date(endDate).setHours(0,0,0,0)},
You are declaring moment().format as a datetime but when you create Date() object you are passing only the date!
When you are practicing date manipulation better use milliseconds and UTC notation. It is something universal to all type of databases. Avoid to use varchar type to store date as strings.
Give it a try!
I have a creation date attribute of a document which has a format like this: "05/03/2020" of type String.
I must extract all the documents from my system having a creation date before "05/03/2020".
I tried with:
db.MyCollection.find ({DateC: {$lte: "05/03/2020"}})
but it still returns 0.
Is there a way to compare if a date format String is before another date?
Thank you
You can use $dateFromString operator to convert $DateC to ISODate,
Make sure your input should be in ISODate format,
db.MyCollection.find([
{
$expr: {
$lte: [
{
$dateFromString: {
dateString: "$DateC",
format: "%d/%m/%Y"
}
},
ISODate("2020-03-05T00:00:00.000Z")
]
}
}
])
Playground
I am not sure with your date format, I have predicted as d/m/y, you can manage it your self if its different.
I'm a bit new to Vue and was wondering how to get the the past month date: I have this code:
import SomeTable from "./table/SomeTable";
export default {
name: "Cabinets",
components: {SomeTable},
data() {
return {
defaultColumns: [
'id',
'serialNumber'
],
defaultStartDate: new Date(),
defaultEndDate: new Date('2019-10-07')
}
},
props: {
startDate: {type: Date, required: false},
endDate: {type: Date, required: false},
}
}
</script>
And then I put defaultStartDate and defaultEndDate in the SomeTable element as follows:
<some-table :start-date="defaultStartDate" :end-date="defaultEndDate" :default-columns="defaultColumns"></some-table>
Which then returns the correct startDate of today and also the set one. But when i try for instance to do something like this:
defaultEndDate: new Date().getFullYear() + '-' + new Date().getMonth() + '-' + new Date().getDate()
My local environment turns blank and get all sorts of errors. I think this is due to the fact I can't use Javascript in that place in Vue? But again I'm really not yet sure how Vue works and couldn't find much on it by googling. So how could I do this using Javascript or maybe even if Vue has a neat trick for it?
EDIT:
the errors i'm getting are of the form:
Error in data(): "TypeError: Date().getFullYear is not a function"
But then with all the javascript functions i used inside Vue. And also
Invalid prop: type check failed for prop "endDate". Expected Date, got String with value "2019-9-5".
In your codes, endDate: {type: Date, required: false}, means endDate should be in Date type.
So you need to convert calculated value into Date like below:
defaultEndDate: new Date(`${(new Date()).getFullYear()}-${(new Date()).getMonth()}-${(new Date()).getDate()}`)
EDIT:
And you need to think of the month is January(0). By using the above method, you will get error when it is on January.
I think It could be better to use computed value like below;
computed: {
defaultEndDate() {
const now = new Date();
now.setMonth(now.getMonth() - 1);
return new Date(now.toJSON().slice(0, 10));
}
}
I've been searching around for some examples for this but none of them seem to work. I'm trying to return only unique values from one field where the conditions from another field are true. Here is what my collection looks like:
Records
{
_id: "XuZcMm2MGCr7CshB5"<br>
clientAvailability: "Direct Solutions<br>
"enhancementId: "D08F817L"<br>
enhancementName: "Allow TN Transfer"<br>
follow: false<br>
impactedProducts: Array[4]<br>
internal: false<br>
like: false<br>
releaseDate: Wed Aug 27 2014 00:00:00 GMT-0500 (CDT)<br>
requestingMso: "Internal"<br>
requestingProduct: "Voice Services"<br>
required: false<br>
}
I tried to limit my query to only documents where the 'releaseDate' is in 2014 and return all of the unique values of 'clientAvailability' for those records. Why isn't this working? I'm a noob, so I''m sure I'm missing something simple. Thanks in advance!
return Records.distinct('clientAvailability', { $and: [{releaseDate: {$gte: new Date('Jan 1, 2014'), $lte: new Date('Nov 26,2014')}}]}
);
distinct is an async function so you need to provide a callback to receive the results. You're currently just returning the Query object it represents. You also don't need to use the $and operator.
Records.distinct('clientAvailability',
{releaseDate: {$gte: new Date('Jan 1, 2014'), $lte: new Date('Nov 26,2014')}},
function (err, result) {
// result contains the array of unique values
}
);