如何针对含不同类型嵌套对象的Vehicle集合进行筛选
问题描述
MongoDB的Vehicle集合里存着Suv和Truck两种类型的文档,对应的C#模型定义如下:
public abstract class BaseCar { public Guid Id { get; set; } public string Title { get; set; } // 原代码中Title为Guid类型应为笔误,此处修正为string public int YearOfProduction { get; set; } // 其他属性 } public class Suv : BaseCar { // 其他属性 public List<Engine> Engines { get; set; } } public class Truck : BaseCar { // 其他属性 public List<TruckPart> Parts { get; set; } } public class TruckPart { // 其他属性 public List<Engine> Engines { get; set; } } public class Engine { // 其他属性 public int HorsePower { get; set; } }
现在需要按YearOfProduction(生产年份)和HorsePower(马力)筛选集合,获取所有符合条件的车辆ID。除了分别查询两种类型文档再合并结果的方式外,还有其他实现方案吗?
当前初步实现代码如下:
var suvCollection = database.GetCollection<Suv>("vehicles"); var truckCollection = database.GetCollection<Truck>("vehicles"); // 分别为SUV和Truck编写筛选逻辑,然后合并ID列表
解决方案
当然有更高效的实现方式,不用分开查询两种类型,下面给出两种可行方案:
1. 使用BaseCar集合配合LINQ查询
直接获取BaseCar类型的集合,利用MongoDB LINQ的多态查询能力,一次查询即可筛选出符合条件的文档并提取ID:
var vehicleCollection = database.GetCollection<BaseCar>("vehicles"); // 示例筛选条件:2020年生产、马力不低于300 var targetYear = 2020; var minHorsePower = 300; var matchingIds = vehicleCollection.AsQueryable() .Where(car => car.YearOfProduction == targetYear && ( // SUV的引擎中至少有一个符合马力要求 car is Suv suv && suv.Engines.Any(e => e.HorsePower >= minHorsePower) || // Truck的任意零件下的引擎中至少有一个符合马力要求 car is Truck truck && truck.Parts.Any(p => p.Engines.Any(e => e.HorsePower >= minHorsePower)) )) .Select(car => car.Id) .ToList();
这种方式依赖MongoDB对继承类型的支持——驱动默认会在文档中存储_t字段标记具体子类,LINQ会自动转换为对应的MongoDB查询语句,一次请求就能获取所有符合条件的ID。
2. 使用BsonDocument构建原生查询
如果需要更灵活的控制,也可以直接编写原生MongoDB查询语句,通过$or条件同时匹配两种类型的文档:
var vehicleCollection = database.GetCollection<BsonDocument>("vehicles"); var filter = Builders<BsonDocument>.Filter.And( Builders<BsonDocument>.Filter.Eq("YearOfProduction", 2020), Builders<BsonDocument>.Filter.Or( // 匹配SUV类型,且存在引擎马力>=300 Builders<BsonDocument>.Filter.And( Builders<BsonDocument>.Filter.Eq("_t", "Suv"), Builders<BsonDocument>.Filter.ElemMatch("Engines", Builders<BsonDocument>.Filter.Gte("HorsePower", 300)) ), // 匹配Truck类型,且存在零件下的引擎马力>=300 Builders<BsonDocument>.Filter.And( Builders<BsonDocument>.Filter.Eq("_t", "Truck"), Builders<BsonDocument>.Filter.ElemMatch("Parts", Builders<BsonDocument>.Filter.ElemMatch("Engines", Builders<BsonDocument>.Filter.Gte("HorsePower", 300))) ) ) ); var matchingIds = vehicleCollection.Find(filter) .Project(Builders<BsonDocument>.Projection.Include("Id")) // 仅返回Id字段,减少数据传输 .ToList() .Select(doc => doc.GetValue("Id").AsGuid) .ToList();
这种方式不依赖实体类,直接操作BsonDocument,适合复杂查询场景,同样只需要一次数据库查询。
注意事项
- 确保MongoDB文档中存在
_t字段(MongoDB驱动默认会为继承类型添加该字段,标记具体子类),否则无法区分Suv和Truck。 - 可以为
YearOfProduction、_t以及嵌套的HorsePower字段创建索引,提升查询性能。
内容的提问来源于stack exchange,提问作者Adam Wróbel
相关产品推荐
相关产品推荐

