Mongoid查询:获取缺少至少一类必填文档的用户
问题分析与解决方案
你的代码存在两个核心问题,导致查询结果不符合预期:
- 错误的筛选条件:
$or数组中的第二个条件{ "$and" => required_categories.map { |cat| { :"documents.category" => cat } } }是在筛选拥有所有必填分类文档的用户,这和你的需求完全相反,必须移除。 - 无文档用户的判断逻辑错误:因为你用的是
User has_many Document的引用关联(而非嵌入关联),User集合的文档中并不存在documents字段,所以原代码中通过$size判断数组长度的方式根本无法定位到无文档的用户。
下面提供两种可行的修正方案:
方案一:分查询合并(简洁易读)
def self.without_all_required_documents required_cats = required_categories # 1. 找出有文档但缺少至少一个必填分类的用户ID missing_required_user_ids = Document.collection.aggregate([ { "$match" => { "user_id" => { "$ne" => nil }, "category" => { "$in" => required_cats } } }, { "$group" => { "_id" => "$user_id", "existing_categories" => { "$addToSet" => "$category" } } }, # 直接比较现有必填分类数量和总必填数量,判断是否缺失 { "$match" => { "$expr" => { "$lt" => [ { "$size" => "$existing_categories" }, required_cats.size ] } } }, { "$project" => { "_id" => 1 } } ]).pluck("_id") # 2. 找出完全没有任何文档的用户(排除所有有文档的用户ID) no_documents_condition = { id: { "$nin" => Document.distinct(:user_id) } } # 合并两个条件 where(:"$or" => [ { id: { "$in" => missing_required_user_ids } }, no_documents_condition ]) end
方案二:单聚合查询(性能更优)
通过MongoDB的$lookup关联用户与文档,一次性完成筛选,减少数据库交互次数:
def self.without_all_required_documents required_cats = required_categories target_user_ids = collection.aggregate([ # 关联用户的所有文档 { "$lookup" => { from: "documents", localField: "_id", foreignField: "user_id", as: "user_documents" } }, # 计算关键指标:是否有文档、已拥有的必填分类集合 { "$project" => { "_id" => 1, "has_documents" => { "$gt" => [ { "$size" => "$user_documents" }, 0 ] }, "existing_required_cats" => { "$setIntersection" => [ required_cats, { "$map" => { input: "$user_documents", as: "doc", in: "$$doc.category" } } ] } } }, # 筛选目标用户:无文档 或 缺少必填分类 { "$match" => { "$or" => [ { "has_documents" => false }, { "$expr" => { "$lt" => [ { "$size" => "$existing_required_cats" }, required_cats.size ] } } ] } }, { "$project" => { "_id" => 1 } } ]).pluck("_id") where(id: target_user_ids) end
说明
- 两种方案都能准确筛选出两类目标用户:完全无文档的用户、有文档但缺少至少一个必填分类的用户。
- 方案一逻辑拆分清晰,适合调试;方案二用单次聚合完成,性能更优,适合数据量较大的场景。
内容的提问来源于stack exchange,提问作者Samy Kacimi
相关产品推荐
相关产品推荐

