mongoDB中的字符串字段值长度


74

该字段的数据类型为字符串。我想获取字段名称的字符长度大于40的数据。

我尝试了这些查询,但返回错误。1。

db.usercollection.find(
{$where: "(this.name.length > 40)"}
).limit(2);

output :error: {
    "$err" : "TypeError: Cannot read property 'length' of undefined near '40)' ",
    "code" : 16722
}

这在2.4.9中工作,但我的版本是2.6.5


1
您是否尝试过gt而不是>
gpullen 2015年

Answers:


160

对于MongoDB 3.6及更高版本:

$expr运营商将允许查询语言中使用聚合表达式,从而可以利用使用的$strLenCP运营商来检查字符串的长度如下:

db.usercollection.find({ 
    "name": { "$exists": true },
    "$expr": { "$gt": [ { "$strLenCP": "$name" }, 40 ] } 
})

对于MongoDB 3.4及更高版本:

您还可以将聚合框架与$redact管道运算符一起使用,该运算符允许您通过运算符处理逻辑条件,$cond并使用特殊操作$$KEEP来“保留”逻辑条件为真的文档,或$$PRUNE“删除”条件为真的文档。假。

这个操作类似于具有$project管道,其选择所述集合中的字段,并创建保持从逻辑条件的查询结果的新字段,然后随后的$match,所不同的是$redact使用一个单一的流水线级,其是更有效的。

至于逻辑条件,可以使用“字符串聚合运算$strLenCP符”来检查字符串的长度。如果长度是$gt指定值,则这是真正的匹配,并且文档被“保留”。否则,将其“修剪”并丢弃。


考虑运行以下汇总操作,以证明上述概念:

db.usercollection.aggregate([
    { "$match": { "name": { "$exists": true } } },
    {
        "$redact": {
            "$cond": [
                { "$gt": [ { "$strLenCP": "$name" }, 40] },
                "$$KEEP",
                "$$PRUNE"
            ]
        }
    },
    { "$limit": 2 }
])

如果使用$where,请尝试使用不带括号的查询:

db.usercollection.find({$where: "this.name.length > 40"}).limit(2);

更好的查询是检查该字段的存在,然后检查长度:

db.usercollection.find({name: {$type: 2}, $where: "this.name.length > 40"}).limit(2); 

要么:

db.usercollection.find({name: {$exists: true}, $where: "this.name.length > 
40"}).limit(2); 

MongoDB$where$where表达式和非$where查询语句可以使用索引之前先评估非查询操作。更好的性能是将字符串的长度存储为另一个字段,然后可以对其进行索引或搜索。$where相比之下,申请会慢很多。$where当您无法以任何其他方式构造数据或处理少量数据时,建议使用JavaScript表达式和运算符作为最后的选择。


避免使用$where操作员的另一种更快的方法是$regex操作员。考虑以下搜索模式

db.usercollection.find({"name": {"$type": 2, "$regex": /^.{41,}$/}}).limit(2); 

注意-从文档中

如果该字段存在索引,则MongoDB将正则表达式与索引中的值进行匹配,这可能比集合扫描更快。如果正则表达式是“前缀表达式”,则可能会发生进一步的优化,这意味着所有潜在的匹配都以相同的字符串开头。这允许MongoDB从该前缀构造一个“范围”,并且仅与索引中属于该范围的那些值匹配。

如果正则表达式以尖号(^)或左锚(\A)开头,然后是一串简单符号,则为“前缀表达式” 。例如,/^abc.*/将仅通过匹配索引中以开头的值来优化正则表达式abc

此外,虽然/^a/, /^a.*/,/^a.*$/匹配等效字符串,但它们具有不同的性能特征。如果存在适当的索引,则所有这些表达式都使用索引;但是, /^a.*//^a.*$/较慢。/^a/匹配前缀后可以停止扫描。


所有3个查询都工作正常。但是第一个是接受最多15。即“ this.name.length> 15”。如果我们给出16或以上,则给出相同的错误。
SURYA GOKARAJU 2015年

如果我们要对内部文档(例如profile.name)执行相同的操作,那么可以请您提出一个语法。
SURYA GOKARAJU 2015年

1
对于嵌入式文档字段,请尝试db.usercollection.find({"profile.name": {$type: 2}, $where: "this.profile.name.length > 40"}).limit(2);
chridam

2
工作完美,但我想知道,还有其他方法可以做到这一点。像Mongo Waydb.collection.find({'country.length':{$gt:20}})这样的东西。
rummykhan

1
@chridam的答案是完美的!但只是想强调一个事实,即错误更多是与检查记录中字段的存在有关,而不是与方括号的存在/不相关。即使有/没有括号,它对我也不起作用。这样, db.usercollection.find({name: {$exists: true}, $where: "this.name.length > 40"}).limit(2); 是正确的查询,并且将始终有效。
Yahya

8

如果文档过多,使用$where和查询$expr会很慢。

使用$regex远快于$where$expr

db.usercollection.find({ 
  "name": /^[\s\S]{40,}$/, // name.length >= 40
})

or 

db.usercollection.find({ 
  "name": { "$regex": "^[\s\S]{40,}$" }, // name.length >= 40
})

此查询的含义与

db.usercollection.find({ 
  "$where": "this.name && this.name.length >= 40",
})

or

db.usercollection.find({ 
    "name": { "$exists": true },
    "$expr": { "$gte": [ { "$strLenCP": "$name" }, 40 ] } 
})

我测试了每个查询的集合。

# find
$where: 10529.359ms
$expr: 5305.801ms
$regex: 2516.124ms

# count
$where: 10872.006ms
$expr: 2630.155ms
$regex: 158.066ms

6

这是mongodb中可以实现的一种方法。

db.usercollection.find({ $where: 'this.name.length < 4' })

2
由于此查询使用JavaScript表达式,因此无法使用mongodb索引,因此速度较慢。
shahin mahmud

$where可能也不允许在免费的MongoDB SaaS层上使用(例如,不在Atlas 512MB计划中)。
Dan Dascalescu

2

我也有类似的情况,但是在我的情况下,字符串不是1级属性。它在对象内部。在这里,我找不到合适的答案。因此,我想与大家分享我的解决方案(希望这将对遇到类似问题的所有人有所帮助)。

Parent Collection 

{
"Child":
{
"name":"Random Name",
"Age:"09"
}
}

例如:如果我们只需要获得孩子名字长度超过10个字符的集合。

 db.getCollection('Parent').find({$where: function() { 
for (var field in this.Child.name) { 
    if (this.Child.name.length > 10) 
        return true;

}
}})

0

该查询将提供字段值和长度:

db.usercollection.aggregate([
{
    $project: {
        "name": 1,
        "length": { $strLenCP: "$name" }
    }} ])
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.