MongoDB多条件数组过滤与按fieldName聚合查询需求(Scala/Java)
MongoDB多条件过滤聚合解决方案
现有集合结构
[ { "_id": ObjectId("6451529ad254d565bd12156b"), "contentType": "Json", "fieldName": "xxx", "method": "GET", "value": "Car" }, { "_id": ObjectId("6451529ad254d565bd12156d"), "contentType": "Json", "fieldName": "xxx", "method": "GET", "value": "Plane" }, { "_id": ObjectId("6451529ad254d565bd121570"), "contentType": "Json", "fieldName": "yyy", "method": "GET", "value": "Smile" }, { "_id": ObjectId("6451529ad254d565bd12156b"), "contentType": "Empty", "fieldName": "zzz", "method": "POST", "value": "Train" }, { "_id": ObjectId("6451529ad254d5651112156b"), "contentType": "Empty", "fieldName": "zzz", "method": "POST", "value": "Player" }, { "_id": ObjectId("6451529ad254d565bd12156d"), "contentType": "Empty", "fieldName": "zzz", "method": "POST", "value": "Van" }, { "_id": ObjectId("6451529ad254d565bd121570"), "contentType": "Json", "fieldName": "name4", "method": "GET", "value": "Van" } ]
需求说明
传入包含多个条件对象的数组,每个条件由method、contentType、fieldName三个字段的组合构成。需要:
- 过滤出符合任一条件组合的文档
- 按
method+contentType+fieldName的组合分组,将每组的value字段聚合为数组 - 返回结构与请求条件一致的结果,新增聚合后的
values数组字段
请求示例
[ { "method": "GET", "contentType": "Json", "fieldName": "xxx" }, { "method": "POST", "contentType": "Empty", "fieldName": "zzz" } ]
期望结果示例
[ { "method": "GET", "contentType": "Json", "fieldName": "xxx", "values": ["Car", "Plane"] }, { "method": "POST", "contentType": "Empty", "fieldName": "zzz", "values": ["Player", "Train", "Van"] } ]
无效聚合语句说明
之前尝试的聚合语句存在两个核心问题:
- 仅能匹配单一条件,无法处理多条件数组的场景
- 分组逻辑错误,未按三个字段的组合分组,也没有正确收集
value字段
db.collection.aggregate([ {$match: {"$and": [{method: "GET"}, {contentType: 'Json'}, {fieldName: "xxx"}]}}, {$group: {values: "$fieldName"}} ])
Scala 解决方案(基于Mongo Scala Driver)
import org.mongodb.scala._ import org.mongodb.scala.model.Aggregates._ import org.mongodb.scala.model.Filters._ import org.mongodb.scala.model.Projections._ import scala.concurrent.Await import scala.concurrent.duration._ // 请求条件数据类 case class Condition(method: String, contentType: String, fieldName: String) // 返回结果数据类 case class Result(method: String, contentType: String, fieldName: String, values: List[String]) object MongoAggregationDemo extends App { // 初始化Mongo客户端 val mongoClient = MongoClient("mongodb://localhost:27017") val db = mongoClient.getDatabase("your_db_name") val collection = db.getCollection("your_collection_name") // 模拟传入的请求条件 val requestConditions = List( Condition("GET", "Json", "xxx"), Condition("POST", "Empty", "zzz") ) // 构建多条件匹配的$or过滤器 val matchFilter = or( requestConditions.map(cond => and( equal("method", cond.method), equal("contentType", cond.contentType), equal("fieldName", cond.fieldName) )) ) // 构建聚合管道 val pipeline = Seq( match(matchFilter), // 按三个字段组合分组,收集对应value到数组 group( Document("method" -> "$method", "contentType" -> "$contentType", "fieldName" -> "$fieldName"), push("values", "$value") ), // 投影成目标结构,展开分组的_id字段并移除原_id project(Document( "method" -> "$_id.method", "contentType" -> "$_id.contentType", "fieldName" -> "$_id.fieldName", "values" -> 1, "_id" -> 0 )) ) // 执行聚合并转换结果 val result = Await.result( collection.aggregate(pipeline).toFuture(), 10.seconds ).map(doc => Result( doc.getString("method"), doc.getString("contentType"), doc.getString("fieldName"), doc.getList("values", classOf[String]).toList )) // 输出结果 result.foreach(println) // 关闭客户端 mongoClient.close() }
Java 解决方案(基于Mongo Java Driver)
import com.mongodb.client.MongoClients; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; import com.mongodb.client.model.Aggregates; import com.mongodb.client.model.Filters; import com.mongodb.client.model.Projections; import org.bson.Document; import java.util.ArrayList; import java.util.List; import static com.mongodb.client.model.Accumulators.push; public class MongoAggregationDemo { // 请求条件类 static class Condition { String method; String contentType; String fieldName; Condition(String method, String contentType, String fieldName) { this.method = method; this.contentType = contentType; this.fieldName = fieldName; } } // 返回结果类 static class Result { String method; String contentType; String fieldName; List<String> values; Result(String method, String contentType, String fieldName, List<String> values) { this.method = method; this.contentType = contentType; this.fieldName = fieldName; this.values = values; } @Override public String toString() { return "Result{" + "method='" + method + '\'' + ", contentType='" + contentType + '\'' + ", fieldName='" + fieldName + '\'' + ", values=" + values + '}'; } } public static void main(String[] args) { // 初始化Mongo客户端(try-with-resources自动关闭) try (var mongoClient = MongoClients.create("mongodb://localhost:27017")) { MongoDatabase db = mongoClient.getDatabase("your_db_name"); MongoCollection<Document> collection = db.getCollection("your_collection_name"); // 模拟传入的请求条件 List<Condition> requestConditions = List.of( new Condition("GET", "Json", "xxx"), new Condition("POST", "Empty", "zzz") ); // 构建多条件匹配的$or过滤器 List<Document> orFilters = new ArrayList<>(); for (Condition cond : requestConditions) { orFilters.add(Filters.and( Filters.eq("method", cond.method), Filters.eq("contentType", cond.contentType), Filters.eq("fieldName", cond.fieldName) )); } Document matchStage = Aggregates.match(Filters.or(orFilters)); // 分组阶段:按三个字段组合分组,收集value数组 Document groupStage = Aggregates.group( new Document("method", "$method") .append("contentType", "$contentType") .append("fieldName", "$fieldName"), push("values", "$value") ); // 投影阶段:调整结构,展开分组的_id字段并移除原_id Document projectStage = Aggregates.project(Projections.fields( Projections.computed("method", "$_id.method"), Projections.computed("contentType", "$_id.contentType"), Projections.computed("fieldName", "$_id.fieldName"), Projections.include("values"), Projections.excludeId() )); // 执行聚合并转换结果 List<Result> results = new ArrayList<>(); for (Document doc : collection.aggregate(List.of(matchStage, groupStage, projectStage))) { results.add(new Result( doc.getString("method"), doc.getString("contentType"), doc.getString("fieldName"), doc.getList("values", String.class) )); } // 输出结果 results.forEach(System.out::println); } } }
内容的提问来源于stack exchange,提问作者VitalyT
相关产品推荐
相关产品推荐

