如何用Mongoose Schema存储HourTime参数,实现超市当前营业查询
基于Node.js/Express/Mongoose实现超市营业时间管理与实时营业查询
一、数据模型设计(Mongoose Schema)
先合理设计营业时间的存储结构,推荐按星期维度拆分,同时支持跨天营业(比如深夜关门的超市)。示例Schema如下:
const mongoose = require('mongoose'); const hourSchema = new mongoose.Schema({ day: { type: Number, required: true, min: 0, // 0=周日,1=周一...6=周六 max: 6 }, open: { type: String, required: true, match: /^([01]\d|2[0-3]):([0-5]\d)$/ // 验证24小时制格式,如"08:30" }, close: { type: String, required: true, match: /^([01]\d|2[0-3]):([0-5]\d)$/ } }); const supermarketSchema = new mongoose.Schema({ name: { type: String, required: true, trim: true }, hours: [hourSchema], // 存储一周7天的营业时间 timezone: { type: String, default: 'Asia/Shanghai', // 可选:存储时区,处理跨区域时间问题 required: true } }); module.exports = mongoose.model('Supermarket', supermarketSchema);
设计要点:
- 用数字0-6代表星期几,避免字符串歧义
- 营业时间用24小时制字符串存储,格式验证确保合法性
- 可选时区字段:如果超市分布在不同时区,必须存储时区信息,避免UTC时间偏差
二、实现"查询当前营业超市"的核心逻辑
核心是对比当前时间与超市的营业时间,重点处理跨天营业的场景(比如20:00开门,次日02:00关门)。
方法1:MongoDB聚合查询(推荐,数据库端处理更高效)
直接在数据库层面过滤,减少内存占用:
const Supermarket = require('./models/supermarket'); async function getOpenSupermarkets() { // 获取当前时间(以上海时区为例,若需多时区可引入时区转换库) const now = new Date(); const currentDay = now.getDay(); // 0=周日 const currentTime = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`; return await Supermarket.aggregate([ // 展开营业时间数组 { $unwind: '$hours' }, // 筛选当前星期的营业时间 { $match: { 'hours.day': currentDay } }, // 判断是否正在营业:分两种情况 { $match: { $expr: { $or: [ // 情况1:关门时间在当天(open <= 当前时间 <= close) { $and: [ { $gte: [currentTime, '$hours.open'] }, { $lte: [currentTime, '$hours.close'] } ]}, // 情况2:跨天营业(当前时间 >= open 或者 当前时间 <= close) { $and: [ { $gt: ['$hours.open', '$hours.close'] }, { $or: [ { $gte: [currentTime, '$hours.open'] }, { $lte: [currentTime, '$hours.close'] } ]} ]} ] } } }, // 合并回原文档结构(可选) { $group: { _id: '$_id', name: { $first: '$name' }, currentHours: { $first: '$hours' }, timezone: { $first: '$timezone' } }} ]); }
方法2:内存中过滤(适合小数据集)
如果超市数量不多,可以先查询所有超市,再在Node.js层面过滤:
async function getOpenSupermarkets() { const allSupermarkets = await Supermarket.find(); const now = new Date(); const currentDay = now.getDay(); const currentTime = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`; return allSupermarkets.filter(market => { // 找到当前星期的营业时间 const todayHours = market.hours.find(h => h.day === currentDay); if (!todayHours) return false; // 无当日营业时间,默认不营业 const { open, close } = todayHours; // 处理跨天情况 if (open > close) { return currentTime >= open || currentTime <= close; } else { return currentTime >= open && currentTime <= close; } }); }
三、Express接口开发
将查询逻辑封装为API接口:
const express = require('express'); const router = express.Router(); const getOpenSupermarkets = require('./services/supermarketService'); // 查询当前正在营业的超市 router.get('/open-now', async (req, res) => { try { const openMarkets = await getOpenSupermarkets(); res.status(200).json({ success: true, data: openMarkets }); } catch (err) { res.status(500).json({ success: false, message: '查询失败', error: err.message }); } }); module.exports = router;
四、最佳实践
- 时区处理优先:如果服务面向多区域用户,必须基于超市的时区计算当前时间,可使用
moment-timezone或date-fns-tz库转换时间,避免用本地服务器时区。 - 缓存优化:营业状态不会实时频繁变化,可以缓存查询结果(比如用Redis),设置5-10分钟过期时间,减少数据库查询压力。
- 数据验证:创建/更新超市营业时间时,验证时间格式、星期范围,确保数据合法性,可使用Mongoose的
validate钩子或Joi库。 - 支持特殊日期:如果需要处理节假日营业时间,可新增
holidayHours字段,查询时优先检查是否有当日的节假日营业时间。 - 索引优化:为
hours.day字段创建索引,提升查询效率:supermarketSchema.index({ 'hours.day': 1 });
内容的提问来源于stack exchange,提问作者Segev Solomon
相关产品推荐
相关产品推荐

