简体   繁体   中英

Mongodb/mongoose query for completely overlapping dates (that could span multiple documents)

I'm having some issues designing a query that deals with overlapping dates.

So here's the scenario, I can't reveal too much about the actual project but here is a similar example. Lets say I have a FleaMarket . It has a bunch of data about itself such as name , location , etc.

So a FleaMarket would have many Stalls , that are available to be booked for a portion of the year (as short as 2 days, as long as all year sort of thing). So the FleaMarket needs to specify when in a year it will be open. Most scenarios would either be open all year, or all summer/fall, but it could possible be broken down further (because seasons determine pricing). Each FleaMarket would define their Seasons which would include a startDate and endDate (including year).

Here's an ERD to model this example:

在此处输入图片说明

When a user attempts to book a Stall , they have already selected a FleaMarket (although ideally it would be nice to search based on availability in the future). It's really easy to tell if a Stall is already booked for the requested dates:

bookings = await Booking.find({
  startDate: { $lt: <requested end date> },
  endDate: { $gt: <requested start date> },
  fleaMarketId: <flea market id>,
}).select('stallId');
bookedIds = bookings.map(b => b.stallId);

stalls = await Stall.find({
  fleaMarketId: <flea marked id>,
  _id: { $nin: bookedIds }
});

The issue I'm having is determining if a Stall is available for the specified Season . The problem comes that 2 seasons could be sequential, so you could make a booking that spans 2 seasons.

I originally tried a query like so:

seasons = await Season.find({
  fleaMarketId: <flea market id>,
  startDate: { $lt: <requested end date> },
  endDate: {$gt: <requested start date> }
});

And then programatically checked if any returned seasons were sequential, and plucked the available stalls from that that existed in all seasons. But unfortunately I just realized this won't work if the requested date only partially overlaps with a season (ex: requested Jan 1 2020 - Jan 10 2020 , but the season is defined as Jan 2 2020 - May 1 2020 )

Is there a way I can handle checking for completely overlapping dates that could possible overlap with multiple documents? I was thinking about calculating and storing the current and future available season dates (stored as total ranges) denormalized on the Stall .

At this point I'm almost thinking I need to restructure the schema quite a bit. Any recommendations? I know this seems very relational, but pretty much everywhere else in the application doesn't really do much with the relationships. It's just this search that is quite problematic.

Update :

I just had the thought of maybe creating some sort of Calendar Document that can store a centralized list of availability for a FleaMarket , that would do a rolling update to only store future and present data, and slowly wiping away historical data, or maybe archiving it in a different format. Perhaps this will solve my issue, I will be discussing it with my team soon.

So as I said in an update in my post, I came up with the idea to create a rolling calendar.

For anyone who is interested, here's what I got:

I created an Availability collection, that contains documents like the following:

{
  marketId: ObjectId('5dd705c0eeeaf900450e7009'),
  stallId: ObjectId('5dde9fc3bf30e500280f80ce'),
  availableDates: [
    {
      date: '2020-01-01T00:00:00.000Z',
      price: 30.0,
      seasonId: '5dd708e7534f3700a9cad0e7',
    },
    {
      date: '2020-01-02T00:00:00.000Z',
      price: 30.0,
      seasonId: '5dd708e7534f3700a9cad0e7',
    },
    {
      date: '2020-01-03T00:00:00.000Z',
      price: 35.0,
      seasonId: '5dd708e7534f3700a9cad0e8',
    }
  ],
  bookedDuring: [
    '2020-01-01T00:00:00.000Z'
    '2020-01-02T00:00:00.000Z'
  ]
}

Then handling updates to this collection:

Seasons

  • when creating, $push new dates onto each stall (and delete dates from the past)
  • When updating, remove the old dates, and add on the new ones (or calculate difference, either works depending on the integrity of this data)
  • When deleting, remove dates

Stalls

  • When creating, insert records for associated seasons
  • When deleting, delete records from availability collection

Bookings

  • When creating, add dates to bookedDuring
  • When updating, add or remove dates from bookedDuring

Then to find available stalls for a market, you can query where { marketId: /* market's ID */, availableDates.date: { $all: [/* each desired day */], bookedDuring: { $nin: [/* same dates */ ] } }} and then pluck out the stallId

And to find markets that have available, do { availableDates.dates: { $all: [/* each desired day */], bookedDuring: { $nin: [/* same dates */ ] } }} select distinct marketIds

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM