如何用C# MongoDB Driver查询content任意位置含指定值的文档?
问题描述
我用C#编写了ASP.NET Core 9.0 Web API,使用MongoDB Driver 3.1版本查询MongoDB。数据库里有个Foo集合,存储多源数据,Schema如下:
_id: objectId, sourceName: string, content: document
content字段的结构完全不固定,比如:
{ "_id": ObjectId('66c63a62a9fa7c172aea24f8'), "sourceName": "Foo-1", "content": { "opName": "Django", "opId": 3, "accounts": ["sample1", "joe@google.com"] } }, { "_id": ObjectId('66c63a62a9fa7c172aea24f9'), "sourceName": "Foo-2", "content": { "magazine": "Foo for beginners", "author": "Joe Blogs" } }
需求是查找所有content中任意位置包含指定术语(比如"Joe")的文档,不管匹配的是哪个键,只要值里有这个术语就行。我尝试过用正则表达式查询,但没得到结果:
// request.Term = "Joe" // _foo = mongoDbService.Database?.GetCollection<Foo>("foo"); var regex = new Regex(Regex.Escape(request.Term), RegexOptions.IgnoreCase); var filter = Builders<Foo>.Filter.Regex(r => r.Content, new BsonRegularExpression(regex)); await _foo.Find(filter).ToListAsync()
Foo的C#类定义:
[BsonIgnoreExtraElements] public class Foo { [BsonId] [BsonRepresentation(BsonType.ObjectId)] public string Id { get; set; } [BsonElement("sourceName")] public string Source { get; set; } [BsonElement("content")] public BsonDocument Content { get; set; } }
请问怎么查询content字段,判断其任意键的值是否包含、以指定词开头或结尾?
解决方案
为什么之前的方法无效
直接对Content字段用Regex过滤,MongoDB会尝试匹配整个content文档的BSON序列化字符串,而不是遍历里面的每个值,所以无法命中嵌套的字段或数组元素。
方法1:使用$expr+$objectToArray遍历所有值
利用MongoDB的聚合表达式,把content转换成键值对数组,然后遍历所有值进行正则匹配:
var term = request.Term; var regex = new BsonRegularExpression(Regex.Escape(term), "i"); // "i"表示忽略大小写 var filter = Builders<Foo>.Filter.Expr( BsonDocument.Parse($"{{ $anyElementTrue: {{ $map: {{ input: {{ $objectToArray: '$content' }}, as: 'kv', in: {{ $regexMatch: {{ input: '$$kv.v', regex: {regex.ToBsonDocument()} }} }} }} }} }}") ); var result = await _foo.Find(filter).ToListAsync();
解释:
$objectToArray: '$content':把content文档转成[{k: "key1", v: "value1"}, ...]的数组$map:遍历每个键值对,用$regexMatch检查值是否匹配正则$anyElementTrue:只要有一个匹配结果为true,就返回该文档
方法2:创建文本索引(推荐,性能更优)
如果频繁做这类模糊搜索,建议给content字段创建通配符文本索引:
// 在MongoDB Shell中执行 db.foo.createIndex({"content.$**": "text"})
之后在C#中可以用文本搜索过滤:
var filter = Builders<Foo>.Filter.Text(request.Term, new TextSearchOptions { CaseSensitive = false }); var result = await _foo.Find(filter).ToListAsync();
注意:
- 文本索引会自动处理数组元素(比如
accounts数组里的字符串) - 支持模糊匹配,但默认是按词匹配,如果需要前缀匹配可以调整索引参数
- 性能比聚合表达式好,适合大规模数据
针对开头/结尾匹配的调整
如果需要精确的"以指定词开头"或"结尾",修改正则表达式即可:
- 开头匹配:
new BsonRegularExpression($"^{Regex.Escape(term)}", "i") - 结尾匹配:
new BsonRegularExpression($"{Regex.Escape(term)}$", "i")
内容的提问来源于stack exchange,提问作者Leigh.D
相关产品推荐
相关产品推荐

