You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MongoDB聚合查询验证营业时间失效问题排查求助

问题:MongoDB营业时间验证查询失效

数据模型

用户使用的Mongoose Schema如下:

const registerSchema = new Schema(
  {
    UserName: {
      type: String,
      requires: true,
    },
    Contact: {
      type: String,
      requires: false,
    },
    Email: {
      type: String,
      requires: false,
    },
    Password: {
      type: String,
      requires: true,
    },
    RoleCode: {
      type: String,
      enum: ["001", "111", "222"], // 001-user, 111-manager, 222-admin
      requires: true,
    },
    openingHours: [
      {
        day: {
          type: Number,
        },
        startTime: {
          type: Number,
        },
        endTime: {
          type: Number,
        },
      },
    ],
  }, { timestamps: true });

当前使用的聚合查询

用户编写的用于验证预订时间是否在营业时间内的聚合代码:

const inOpeningHours = await Register.aggregate([
  {
    $match: {
      _id: managerId,
    },
  },
  {
    $unwind: "$openingHours", // Unwind the openingHours array
  },
  {
    $match: {
      $or: [
        {
          $and: [
            {
              "openingHours.day": {
                $eq: reservationData.startTime.getDay(),
              },
            },
            {
              "openingHours.startTime": {
                $gt: reservationData.startTime.getHours(),
              },
            },
          ],
        },
        {
          $and: [
            {
              "openingHours.day": {
                $eq: reservationData.endTime.getDay(),
              },
            },
            {
              "openingHours.endTime": {
                $lt: reservationData.endTime.getHours(),
              },
            },
          ],
        },
      ],
    },
  },
]);

问题原因

  1. 条件运算符完全写反:
    验证逻辑应该是「预订开始时间不早于营业开始时间,且预订结束时间不晚于营业结束时间」,但原查询用$gt(营业开始时间 > 预订开始小时)、$lt(营业结束时间 < 预订结束小时),这会导致只有当预订时间完全在营业时间之外时才会匹配,完全违背需求。
  2. 逻辑运算符错误:
    原查询使用$or,意味着只要满足其中一个条件就会返回结果,但我们需要的是整个预订时段都在营业时间内,应该使用$and来确保所有条件同时满足。
  3. 不必要的$unwind:
    使用$unwind会展开数组,增加查询复杂度,直接用$elemMatch可以更精准高效地匹配数组中符合条件的元素。

修正方案

情况1:预订时间在同一天

如果用户的预订不会跨天,可以直接用find配合$elemMatch完成验证,无需聚合:

const startDay = reservationData.startTime.getDay();
const startHour = reservationData.startTime.getHours();
const endHour = reservationData.endTime.getHours();

const valid = await Register.exists({
  _id: managerId,
  openingHours: {
    $elemMatch: {
      day: startDay,
      startTime: { $lte: startHour },
      endTime: { $gte: endHour }
    }
  }
});

// 如果valid为true,说明预订时间在营业时间内

情况2:预订时间跨天

如果允许预订跨天(比如从周一晚到周二凌晨),需要分别验证两天的时段是否符合:

const startDay = reservationData.startTime.getDay();
const endDay = reservationData.endTime.getDay();
const startHour = reservationData.startTime.getHours();
const endHour = reservationData.endTime.getHours();

let valid = false;

if (startDay === endDay) {
  // 同一天验证逻辑
  valid = await Register.exists({
    _id: managerId,
    openingHours: {
      $elemMatch: {
        day: startDay,
        startTime: { $lte: startHour },
        endTime: { $gte: endHour }
      }
    }
  });
} else {
  // 跨天验证:开始日的营业结束时间 >= 预订开始小时,且结束日的营业开始时间 <= 预订结束小时
  valid = await Register.exists({
    _id: managerId,
    $and: [
      {
        openingHours: {
          $elemMatch: {
            day: startDay,
            endTime: { $gte: startHour }
          }
        }
      },
      {
        openingHours: {
          $elemMatch: {
            day: endDay,
            startTime: { $lte: endHour }
          }
        }
      }
    ]
  });
}

说明

使用exists方法可以直接判断是否存在符合条件的文档,比聚合更简洁高效。如果需要获取具体的营业时间数据,可以把exists换成findOne。

内容的提问来源于stack exchange,提问作者Ranjit Sapkota

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 23:05:00