可以通过 聚合管道(Aggregation Pipeline) 精确返回文档ID、命中的字段A和字段B的具体数据,并统计命中次数。以下是具体实现方法及示例:


1. 数据结构示例

假设集合 docs 中有如下文档:

{
  "_id": ObjectId("5f9d1a9b8c7d6e3d48b4567a"),
  "fieldA": ["苹果", "香蕉", "橘子"],
  "fieldB": ["苹果", "西瓜", "葡萄"]
}
{
  "_id": ObjectId("5f9d1a9b8c7d6e3d48b4567b"),
  "fieldA": ["香蕉", "芒果"],
  "fieldB": ["苹果", "橙子"]
}

2. 目标:搜索关键词“苹果”

  • 期望结果
    • 返回文档ID。
    • 返回 fieldA 中匹配“苹果”的数组项。
    • 返回 fieldB 中匹配“苹果”的数组项。
    • 统计 fieldAfieldB 中各自命中次数。

3. 实现步骤

步骤1:匹配包含关键词的文档

db.docs.aggregate([
  // 筛选 fieldA 或 fieldB 包含“苹果”的文档
  {
    $match: {
      $or: [
        { "fieldA": "苹果" },
        { "fieldB": "苹果" }
      ]
    }
  }
]);

步骤2:提取命中的具体数据

使用 $filter 过滤出匹配的数组元素:

db.docs.aggregate([
  {
    $match: { ... }  // 同上
  },
  {
    $project: {
      _id: 1,
      // 提取 fieldA 中匹配“苹果”的元素
      matchedA: {
        $filter: {
          input: "$fieldA",
          as: "item",
          cond: { $eq: ["$$item", "苹果"] }
        }
      },
      // 提取 fieldB 中匹配“苹果”的元素
      matchedB: {
        $filter: {
          input: "$fieldB",
          as: "item",
          cond: { $eq: ["$$item", "苹果"] }
        }
      }
    }
  }
]);

步骤3:统计命中次数

添加 $size 计算命中次数:

db.docs.aggregate([
  { $match: { ... } },  // 同上
  {
    $project: {
      _id: 1,
      matchedA: { ... },  // 同上
      matchedB: { ... },  // 同上
      // 统计命中次数
      countA: { $size: { $ifNull: ["$matchedA", []] } },
      countB: { $size: { $ifNull: ["$matchedB", []] } },
      // 总命中次数(用于权重计算)
      totalHits: {
        $add: [
          { $size: { $ifNull: ["$matchedA", []] } },
          { $size: { $ifNull: ["$matchedB", []] } }
        ]
      }
    }
  }
]);

最终输出示例

{
  "_id": ObjectId("5f9d1a9b8c7d6e3d48b4567a"),
  "matchedA": ["苹果"],
  "matchedB": ["苹果"],
  "countA": 1,
  "countB": 1,
  "totalHits": 2
}
{
  "_id": ObjectId("5f9d1a9b8c7d6e3d48b4567b"),
  "matchedA": [],
  "matchedB": ["苹果"],
  "countA": 0,
  "countB": 1,
  "totalHits": 1
}

4. 权重计算扩展

场景:不同字段权重不同

假设 fieldA 命中权重为 2fieldB 权重为 1,计算加权总分:

db.docs.aggregate([
  { $match: { ... } },  // 同上
  {
    $project: {
      _id: 1,
      matchedA: { ... },  // 同上
      matchedB: { ... },  // 同上
      // 加权总分
      weightedScore: {
        $add: [
          { $multiply: [{ $size: "$matchedA" }, 2] },  // fieldA权重x2
          { $multiply: [{ $size: "$matchedB" }, 1] }   // fieldB权重x1
        ]
      }
    }
  },
  // 按权重排序(可选)
  { $sort: { weightedScore: -1 } }
]);

5. 性能优化建议

  1. 索引优化
    fieldAfieldB 创建 多键索引(Multikey Index),加速数组查询:

    db.docs.createIndex({ "fieldA": 1 });
    db.docs.createIndex({ "fieldB": 1 });
    
  2. 分页查询
    结合 $skip$limit 实现分页:

    db.docs.aggregate([
      { $match: { ... } },
      { $sort: { totalHits: -1 } },
      { $skip: 20 },
      { $limit: 10 }
    ]);
    

6. 替代方案:使用 $reduce 统计命中

若需更复杂的命中逻辑(如模糊匹配),可用 $reduce 遍历数组:

db.docs.aggregate([
  {
    $project: {
      _id: 1,
      // 统计 fieldA 中匹配“苹果”的次数
      countA: {
        $reduce: {
          input: "$fieldA",
          initialValue: 0,
          in: {
            $add: [
              "$$value",
              { $cond: [{ $eq: ["$$this", "苹果"] }, 1, 0] }
            ]
          }
        }
      }
    }
  }
]);

总结

  • 精准命中提取:使用 $filter 返回匹配的数组元素。
  • 统计命中次数:结合 $size$reduce 计算次数。
  • 权重计算:通过 $multiply$add 实现自定义评分。
  • 性能保障:为数组字段创建多键索引,优化查询速度。

此方案可直接用于你的场景,灵活支持后续权重计算需求!