数组查询、计算命中次数并按照命中次数排序
可以通过 聚合管道(Aggregation Pipeline) 精确返回文档ID、命中的字段A和字段B的具体数据,并统计命中次数。以下是具体实现方法及示例:
1. 数据结构示例
假设集合 docs 中有如下文档:
{
"_id": ObjectId("5f9d1a9b8c7d6e3d48b4567a"),
"fieldA": ["苹果", "香蕉", "橘子"],
"fieldB": ["苹果", "西瓜", "葡萄"]
}
{
"_id": ObjectId("5f9d1a9b8c7d6e3d48b4567b"),
"fieldA": ["香蕉", "芒果"],
"fieldB": ["苹果", "橙子"]
}
2. 目标:搜索关键词“苹果”
- 期望结果:
- 返回文档ID。
- 返回
fieldA中匹配“苹果”的数组项。 - 返回
fieldB中匹配“苹果”的数组项。 - 统计
fieldA和fieldB中各自命中次数。
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 命中权重为 2,fieldB 权重为 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. 性能优化建议
-
索引优化:
为fieldA和fieldB创建 多键索引(Multikey Index),加速数组查询:db.docs.createIndex({ "fieldA": 1 }); db.docs.createIndex({ "fieldB": 1 }); -
分页查询:
结合$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实现自定义评分。 - 性能保障:为数组字段创建多键索引,优化查询速度。
此方案可直接用于你的场景,灵活支持后续权重计算需求!
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 程序员小航
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果