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

MongoDB 3.6+PHP环境下文档分组及特定文档查询求助

解决MongoDB 3.6中查找使用量最少且时间戳最早的DID文档问题

针对你遇到的$group操作不符合预期的问题,我来梳理下正确的聚合思路,结合PHP新版mongodb库给出可直接运行的方案,同时优化1000万条数据的查询性能。

核心思路拆解

要实现"did使用量最少且时间戳最早"的查询,需要分三步完成:

  1. 按did分组,统计每个did的使用次数,同时锁定该did下时间戳最早的文档
  2. 对分组结果按「使用量升序、时间戳升序」排序,确保使用量最少的排在最前,相同使用量的则时间最早的优先
  3. 取排序后的第一个结果,关联原集合拿到完整文档数据

正确的聚合管道

下面是经过优化的聚合管道,适合1000万条数据的场景:

[
  // 先按did分组,再按timestamp升序排序——这一步是关键!确保每个did下的第一个文档是时间最早的
  {
    $sort: {
      did: 1,
      timestamp: 1
    }
  },
  // 分组统计使用量,同时记录最早的时间戳和文档ID
  {
    $group: {
      _id: "$did",
      usageCount: { $sum: 1 },
      earliestTs: { $first: "$timestamp" },
      earliestDocId: { $first: "$_id" }
    }
  },
  // 按使用量从小到大排序,相同使用量的按时间戳从小到大排序
  {
    $sort: {
      usageCount: 1,
      earliestTs: 1
    }
  },
  // 只取第一个结果,就是我们要找的目标
  {
    $limit: 1
  },
  // 关联原集合获取完整的文档内容(如果只需要did、次数和时间戳,可以跳过这两步)
  {
    $lookup: {
      from: "your_collection_name", // 替换成你的集合名称
      localField: "earliestDocId",
      foreignField: "_id",
      as: "targetDocument"
    }
  },
  {
    $unwind: "$targetDocument"
  },
  // 整理输出字段,按需调整
  {
    $project: {
      _id: 0,
      did: "$_id",
      usageCount: 1,
      earliestTimestamp: "$earliestTs",
      fullDocument: "$targetDocument"
    }
  }
]

PHP代码实现(新版mongodb库)

这里用官方的mongodb/mongodb composer包来执行聚合查询,代码如下:

<?php
require 'vendor/autoload.php';

// 连接MongoDB
$client = new MongoDB\Client("mongodb://localhost:27017");
// 替换成你的数据库和集合名称
$collection = $client->your_database_name->your_collection_name;

// 定义聚合管道
$pipeline = [
    [
        '$sort' => [
            'did' => 1,
            'timestamp' => 1
        ]
    ],
    [
        '$group' => [
            '_id' => '$did',
            'usageCount' => ['$sum' => 1],
            'earliestTs' => ['$first' => '$timestamp'],
            'earliestDocId' => ['$first' => '$_id']
        ]
    ],
    [
        '$sort' => [
            'usageCount' => 1,
            'earliestTs' => 1
        ]
    ],
    [
        '$limit' => 1
    ],
    [
        '$lookup' => [
            'from' => 'your_collection_name', // 必须和上面的集合名一致
            'localField' => 'earliestDocId',
            'foreignField' => '_id',
            'as' => 'targetDocument'
        ]
    ],
    [
        '$unwind' => '$targetDocument'
    ],
    [
        '$project' => [
            '_id' => 0,
            'did' => '$_id',
            'usageCount' => 1,
            'earliestTimestamp' => '$earliestTs',
            fullDocument => '$targetDocument'
        ]
    ]
];

// 执行聚合查询
$cursor = $collection->aggregate($pipeline);
$result = $cursor->toArray();

// 处理结果
if (!empty($result)) {
    $target = $result[0];
    echo "✅ 找到目标文档:\n";
    echo "DID: {$target['did']}\n";
    echo "使用次数: {$target['usageCount']}\n";
    echo "最早时间戳: {$target['earliestTimestamp']}\n";
    echo "完整文档详情:\n" . print_r($target['fullDocument'], true);
} else {
    echo "❌ 未找到符合条件的文档";
}
?>

性能优化建议

针对1000万条数据的大集合,一定要创建复合索引来加速查询:

// 在MongoDB shell中执行,创建did和timestamp的复合索引
db.your_collection_name.createIndex({did: 1, timestamp: 1})

这个索引会让第一步的$sort阶段直接使用索引排序,避免全表扫描,大幅提升速度。

为什么你的$group没达到预期?

大概率是这两个原因:

  1. 没有提前排序:如果在$group之前不对did和timestamp排序,$group里的$first拿到的文档是随机的,无法保证是时间最早的
  2. 排序逻辑错误:分组后没有同时按「使用量升序+时间戳升序」排序,导致使用量最少的did可能被排在后面,或者相同使用量的did没有按时间戳筛选最早的

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 07:05:01