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

如何在MongoDB查询中传入特定数据?网站访问图表查询的TS实现需求

解决方案:参数化MongoDB查询 + TypeScript集成

我来帮你把固定日期的查询改成支持前端传参的版本,同时集成到TypeScript代码里,分步骤拆解:

一、先把固定查询改成参数化版本

针对三个图表场景,我们把查询中的硬编码日期替换为动态参数,同时通过日期计算确定精准的时间范围:

1. 选定日期按小时统计访问量

需要接收目标日期(如 YYYY-MM-DD 格式字符串),查询该日期00:00到次日00:00的记录,按小时分组统计:

// 参数:targetDate: string (格式 "YYYY-MM-DD")
const startOfDay = new Date(`${targetDate}T00:00:00Z`);
const endOfDay = new Date(startOfDay);
endOfDay.setDate(endOfDay.getDate() + 1);

db.visits.aggregate([
  {
    $match: {
      timestamp: {
        $gte: startOfDay,
        $lt: endOfDay
      }
    }
  },
  {
    $group: {
      _id: { $hour: "$timestamp" },
      count: { $sum: 1 }
    }
  },
  { $sort: { _id: 1 } }
]);

2. 选定月份按天统计访问量

需要接收目标月份(如 YYYY-MM 格式字符串),查询该月第一天00:00到次月第一天00:00的记录,按天分组:

// 参数:targetMonth: string (格式 "YYYY-MM")
const [year, month] = targetMonth.split("-").map(Number);
const startOfMonth = new Date(Date.UTC(year, month - 1, 1));
const endOfMonth = new Date(Date.UTC(year, month, 1));

db.visits.aggregate([
  {
    $match: {
      timestamp: {
        $gte: startOfMonth,
        $lt: endOfMonth
      }
    }
  },
  {
    $group: {
      _id: { $dayOfMonth: "$timestamp" },
      count: { $sum: 1 }
    }
  },
  { $sort: { _id: 1 } }
]);

3. 选定年份按月统计访问量

需要接收目标年份(如 YYYY 格式字符串),查询该年1月1日00:00到次年1月1日00:00的记录,按月分组:

// 参数:targetYear: string (格式 "YYYY")
const year = Number(targetYear);
const startOfYear = new Date(Date.UTC(year, 0, 1));
const endOfYear = new Date(Date.UTC(year + 1, 0, 1));

db.visits.aggregate([
  {
    $match: {
      timestamp: {
        $gte: startOfYear,
        $lt: endOfYear
      }
    }
  },
  {
    $group: {
      _id: { $month: "$timestamp" },
      count: { $sum: 1 }
    }
  },
  { $sort: { _id: 1 } }
]);

二、TypeScript代码集成

接下来把这些逻辑封装成类型安全的TS函数,同时处理前端传入的参数验证和格式转换:

1. 定义类型和工具函数

先明确参数类型,再加一个日期格式验证函数,避免无效参数导致的错误:

// 定义三种查询的参数类型
type HourlyQueryParams = { type: "hourly"; targetDate: string };
type DailyQueryParams = { type: "daily"; targetMonth: string };
type MonthlyQueryParams = { type: "monthly"; targetYear: string };
type VisitStatsQueryParams = HourlyQueryParams | DailyQueryParams | MonthlyQueryParams;

// 验证日期格式的工具函数
const isValidDateString = (str: string, format: "YYYY-MM-DD" | "YYYY-MM" | "YYYY"): boolean => {
  switch (format) {
    case "YYYY-MM-DD":
      return /^\d{4}-\d{2}-\d{2}$/.test(str) && !isNaN(new Date(str).getTime());
    case "YYYY-MM":
      return /^\d{4}-\d{2}$/.test(str) && !isNaN(new Date(`${str}-01`).getTime());
    case "YYYY":
      return /^\d{4}$/.test(str) && !isNaN(new Date(`${str}-01-01`).getTime());
    default:
      return false;
  }
};

2. 封装MongoDB查询函数

假设你用的是mongodb官方TS驱动,封装一个统一的查询函数:

import { MongoClient, Collection } from "mongodb";

// 全局存储集合实例(实际项目中建议用依赖注入)
let visitsCollection: Collection;

// 初始化MongoDB连接(示例)
const initMongo = async () => {
  const client = await MongoClient.connect("your-mongodb-uri");
  visitsCollection = client.db("your-db-name").collection("visits");
};

// 获取访问统计的核心函数
export const getVisitStats = async (params: VisitStatsQueryParams) => {
  // 先验证参数格式
  switch (params.type) {
    case "hourly":
      if (!isValidDateString(params.targetDate, "YYYY-MM-DD")) {
        throw new Error("无效的日期格式,请传入YYYY-MM-DD格式的字符串");
      }
      break;
    case "daily":
      if (!isValidDateString(params.targetMonth, "YYYY-MM")) {
        throw new Error("无效的月份格式,请传入YYYY-MM格式的字符串");
      }
      break;
    case "monthly":
      if (!isValidDateString(params.targetYear, "YYYY")) {
        throw new Error("无效的年份格式,请传入YYYY格式的字符串");
      }
      break;
  }

  let aggregatePipeline;

  // 根据参数类型构建对应的聚合管道
  switch (params.type) {
    case "hourly": {
      const startOfDay = new Date(`${params.targetDate}T00:00:00Z`);
      const endOfDay = new Date(startOfDay);
      endOfDay.setDate(endOfDay.getDate() + 1);
      aggregatePipeline = [
        { $match: { timestamp: { $gte: startOfDay, $lt: endOfDay } } },
        { $group: { _id: { $hour: "$timestamp" }, count: { $sum: 1 } } },
        { $sort: { _id: 1 } }
      ];
      break;
    }
    case "daily": {
      const [year, month] = params.targetMonth.split("-").map(Number);
      const startOfMonth = new Date(Date.UTC(year, month - 1, 1));
      const endOfMonth = new Date(Date.UTC(year, month, 1));
      aggregatePipeline = [
        { $match: { timestamp: { $gte: startOfMonth, $lt: endOfMonth } } },
        { $group: { _id: { $dayOfMonth: "$timestamp" }, count: { $sum: 1 } } },
        { $sort: { _id: 1 } }
      ];
      break;
    }
    case "monthly": {
      const year = Number(params.targetYear);
      const startOfYear = new Date(Date.UTC(year, 0, 1));
      const endOfYear = new Date(Date.UTC(year + 1, 0, 1));
      aggregatePipeline = [
        { $match: { timestamp: { $gte: startOfYear, $lt: endOfYear } } },
        { $group: { _id: { $month: "$timestamp" }, count: { $sum: 1 } } },
        { $sort: { _id: 1 } }
      ];
      break;
    }
  }

  // 执行聚合查询并返回结果
  return await visitsCollection.aggregate(aggregatePipeline).toArray();
};

3. 和图表端的交互

前端图表组件(比如ECharts、Chart.js)在用户选择日期/月份/年份后,将参数传给后端TS接口。这里用Express写个示例接口:

import express from "express";
const app = express();
app.use(express.json());

// 初始化Mongo连接
initMongo().then(() => console.log("MongoDB连接成功"));

app.post("/api/visit-stats", async (req, res) => {
  try {
    const params = req.body as VisitStatsQueryParams;
    const stats = await getVisitStats(params);
    res.json(stats);
  } catch (error) {
    res.status(400).json({ error: (error as Error).message });
  }
});

app.listen(3000, () => console.log("服务运行在3000端口"));

前端调用示例(React为例):

// 用户选择日期后触发查询
const fetchHourlyStats = async (targetDate: string) => {
  const response = await fetch("/api/visit-stats", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ type: "hourly", targetDate })
  });
  const stats = await response.json();
  // 更新图表数据
  setChartData(stats);
};

三、性能优化建议

  • 给timestamp字段创建单字段索引,加速时间范围查询:
    db.visits.createIndex({ timestamp: 1 });
    
  • 如果数据量极大,可以考虑预聚合(比如每天定时统计并存储结果),减少实时聚合的计算压力。

内容的提问来源于stack exchange,提问作者William.P

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 10:10:31