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

基于Mongoose、Express、Node.js实现国家城市分组站点地图及统计

解决站点地图风格的国家-城市列表展示问题

嘿,这个需求我之前帮朋友做过类似的,其实核心是用好Mongoose的聚合查询,你可能没意识到这一点。我来帮你拆解下你忽略的关键点,再给你一步步的实现方案:

你可能忽略的核心关键点

  • Mongoose聚合管道的强大能力:你可能一开始想着用find()或者distinct()来获取唯一的国家和城市,但这两个方法没法同时完成「分组+统计数量+构建层级结构」的需求,而聚合管道(Aggregation Pipeline)可以一次性搞定这些。
  • 数据层级的嵌套分组逻辑:要先按「国家+城市」分组统计数量,再把相同国家的城市数据聚合到一起,这个嵌套分组的逻辑是实现需求的核心,普通查询很难做到。
  • 空值过滤与数据清洗:如果数据库里存在country或city为空的记录,会导致你的站点地图出现无效分组,所以必须提前过滤掉这些脏数据。
  • 性能优化的索引设置:当数据量变大时,聚合查询可能会变慢,给country和city字段加上复合索引能大幅提升查询效率。

具体实现步骤

1. 编写Mongoose聚合查询代码

假设你的数据模型是Listing,包含city和country字段,我们用聚合管道来获取需要的结构:

const Listing = require('./models/Listing'); // 引入你的模型

async function getSiteMapData() {
  try {
    const result = await Listing.aggregate([
      // 第一步:过滤掉country或city为空的脏数据
      {
        $match: {
          country: { $ne: null, $ne: "" },
          city: { $ne: null, $ne: "" }
        }
      },
      // 第二步:按国家和城市分组,统计每个城市的列表数量
      {
        $group: {
          _id: { country: "$country", city: "$city" },
          listingCount: { $sum: 1 }
        }
      },
      // 第三步:按国家分组,把下属城市和统计数整理成数组
      {
        $group: {
          _id: "$_id.country",
          cities: {
            $push: {
              name: "$_id.city",
              count: "$listingCount"
            }
          }
        }
      },
      // 可选:对国家名称按字母排序,让展示更规整
      { $sort: { _id: 1 } }
    ]);
    return result;
  } catch (err) {
    console.error('获取站点地图数据失败:', err);
    throw err;
  }
}

2. 在Express路由中处理请求

把聚合查询的结果传给前端模板:

const express = require('express');
const router = express.Router();

router.get('/sitemap', async (req, res) => {
  try {
    const siteMapData = await getSiteMapData();
    // 这里假设你用EJS模板,也可以换成React/Vue等前端框架需要的JSON格式
    res.render('sitemap', { countries: siteMapData });
  } catch (err) {
    res.status(500).send('服务器获取站点地图数据失败');
  }
});

module.exports = router;

3. 前端模板展示(以EJS为例)

把数据渲染成站点地图风格的结构:

<!DOCTYPE html>
<html>
<head>
  <title>站点地图</title>
  <style>
    .country { margin: 20px 0; padding: 10px; border-bottom: 1px solid #eee; }
    .city-list { list-style-type: disc; margin-left: 20px; }
  </style>
</head>
<body>
  <h1>目录站点地图</h1>
  <% countries.forEach(country => { %>
    <div class="country">
      <h2><%= country._id %></h2>
      <ul class="city-list">
        <% country.cities.forEach(city => { %>
          <li><%= city.name %> - <%= city.count %> 条列表</li>
        <% }) %>
      </ul>
    </div>
  <% }) %>
</body>
</html>

4. 添加索引优化性能

在你的Listing模型里添加复合索引,提升聚合查询的速度:

const mongoose = require('mongoose');
const ListingSchema = new mongoose.Schema({
  // 你的其他字段...
  city: String,
  country: String
});

// 给country和city添加复合索引
ListingSchema.index({ country: 1, city: 1 });

module.exports = mongoose.model('Listing', ListingSchema);

额外说明

  • 如果你的国家或城市名称有大小写不一致的问题(比如"USA"和"usa"),可以在聚合的$match之前加一个$project步骤统一大小写,或者在数据入库时就做标准化处理。
  • 如果需要支持分页(比如数据量极大时),可以在聚合管道中加入$skip和$limit步骤,但要注意排序的稳定性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 08:00:14