Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

String field value length in mongoDB

The data type of the field is String. I would like to fetch the data where character length of field name is greater than 40.

I tried these queries but returning error. 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 } 

this is working in 2.4.9 But my version is 2.6.5

like image 478
SURYA GOKARAJU Avatar asked Apr 11 '15 12:04

SURYA GOKARAJU


People also ask

How do I find the length of a string in MongoDB?

As for the logical condition, there are String Aggregation Operators that you can use $strLenCP operator to check the length of the string. If the length is $gt a specified value, then this is a true match and the document is "kept".

How can check field value in MongoDB?

to do a text search on all fields, you first must create a text index on all fields. as the mongodb documentation indicates, "To allow for text search on all fields with string content, use the wildcard specifier ($**) to index all fields that contain string content."

What is string in MongoDB?

string. A string of terms that MongoDB parses and uses to query the text index. MongoDB performs a logical OR search of the terms unless specified as a phrase. See Behavior for more information on the field.

How do I search for text in a field in MongoDB?

In MongoDB, we can perform text search using text index and $text operator. Text index: MongoDB proved text indexes that are used to find the specified text from the string content. Text indexes should be either a string or an array of string elements.


2 Answers

For MongoDB 3.6 and newer:

The $expr operator allows the use of aggregation expressions within the query language, thus you can leverage the use of $strLenCP operator to check the length of the string as follows:

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

For MongoDB 3.4 and newer:

You can also use the aggregation framework with the $redact pipeline operator that allows you to proccess the logical condition with the $cond operator and uses the special operations $$KEEP to "keep" the document where the logical condition is true or $$PRUNE to "remove" the document where the condition was false.

This operation is similar to having a $project pipeline that selects the fields in the collection and creates a new field that holds the result from the logical condition query and then a subsequent $match, except that $redact uses a single pipeline stage which is more efficient.

As for the logical condition, there are String Aggregation Operators that you can use $strLenCP operator to check the length of the string. If the length is $gt a specified value, then this is a true match and the document is "kept". Otherwise it is "pruned" and discarded.


Consider running the following aggregate operation which demonstrates the above concept:

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

If using $where, try your query without the enclosing brackets:

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

A better query would be to to check for the field's existence and then check the length:

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

or:

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

MongoDB evaluates non-$where query operations before $where expressions and non-$where query statements may use an index. A much better performance is to store the length of the string as another field and then you can index or search on it; applying $where will be much slower compared to that. It's recommended to use JavaScript expressions and the $where operator as a last resort when you can't structure the data in any other way, or when you are dealing with a small subset of data.


A different and faster approach that avoids the use of the $where operator is the $regex operator. Consider the following pattern which searches for

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

Note - From the docs:

If an index exists for the field, then MongoDB matches the regular expression against the values in the index, which can be faster than a collection scan. Further optimization can occur if the regular expression is a “prefix expression”, which means that all potential matches start with the same string. This allows MongoDB to construct a “range” from that prefix and only match against those values from the index that fall within that range.

A regular expression is a “prefix expression” if it starts with a caret (^) or a left anchor (\A), followed by a string of simple symbols. For example, the regex /^abc.*/ will be optimized by matching only against the values from the index that start with abc.

Additionally, while /^a/, /^a.*/, and /^a.*$/ match equivalent strings, they have different performance characteristics. All of these expressions use an index if an appropriate index exists; however, /^a.*/, and /^a.*$/ are slower. /^a/ can stop scanning after matching the prefix.

like image 121
chridam Avatar answered Oct 13 '22 07:10

chridam


Queries with $where and $expr are slow if there are too many documents.

Using $regex is much faster than $where, $expr.

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

This query is the same meaning with

db.usercollection.find({    "$where": "this.name && this.name.length >= 40", })  or  db.usercollection.find({      "name": { "$exists": true },     "$expr": { "$gte": [ { "$strLenCP": "$name" }, 40 ] }  }) 

I tested each queries for my collection.

# find $where: 10529.359ms $expr: 5305.801ms $regex: 2516.124ms  # count $where: 10872.006ms $expr: 2630.155ms $regex: 158.066ms 
like image 20
Fumiya Karasawa Avatar answered Oct 13 '22 09:10

Fumiya Karasawa