$elemmatch not working in MongoDB - mongodb

I am trying to filter mongo data by using the below query in mongodb version 2.6.1 but getting error.
MongoDB version 2.4.6 (Working):
> db.BC_1839.find({data: {$elemMatch:{$where : "this.First_name.toLowerCase().indexOf('kim') ==0"}}});
output:
{
"_id" : ObjectId("53719a9d5b9e5c8c110001b9"),
"data" : [
{
"First_name" : "Kimberely",
"Last_name" : "Weyman",
"Company_name" : "Scientific Agrcltl Svc Inc",
"Address" : "7721 Harrison St",
"City" : "Kingsway West",
"State" : "NS",
"Post" : "2208",
"Phone1" : "02-7091-8948",
"Phone2" : "0441-151-810",
"Email" : "kweyman#weyman.com.au",
"Web" : "http://www.scientificagrcltlsvcinc.com.au",
"active" : "true"
}
],
"history" : [
{
"timestamp" : "2014-05-13 06:07:55",
"event": "creation",
"createdby" : "Srikesh Infotech",
"creation_data" : [
{
"crm_base_contact_id" : "1839",
"crm_imported_files_id" : "1464"
}
]
},
{
"timestamp" : "2014-05-13 06:09:05",
"event" : "Task",
"createdby" : "Srikesh Infotech",
"Task_data" : [
{
"Campaign ID" : "193",
"Campagin Name" : "Test Campa1"
}
]
}
],
"ref" : [
{ "crm_base_contact_id" : "1839", "crm_imported_files_id" : "1464" }
]
}
MongoDB version 2.6.1(Not Working):
> db.BC_1839.find({data: {$elemMatch:{$where : "this.First_name.toLowerCase().indexOf('kim') ==0"}}});
output:
error: {
"$err" : "Can't canonicalize query: BadValue $elemMatch cannot contain $
where expression",
"code" : 17287
}
Same query executes in mongodb version 2.4.6 but not in mongodb version 2.6.1 Why???

It shouldn't have worked in earlier versions at all, as at the very least you have modified the scoping of this to now refer to "data" as a top level element. In short, this is no longer allowed and you really should not be using JavaScript methods unless you absolutely have to. Even then, there is probably still a better way in most cases.
But in fact this is an un-necessary use of JavaScript matching as it is not required when there are other operators existing that will do this.
You should be using a $regex form instead:
db.docs.find({ "data.First_name": /^kim/i })
Or anywhere within the field, remove the caret ^:
db.docs.find({ "data.First_name": /kim/i })
Which is pretty much as inefficient as JavaScript execution but not as much as there is not the overhead of processing through that interpreter engine. And of course it works everywhere.
Also think about what a query relying on JavaScript to resolve is actually doing:
Invokes a JavaScript interpreter instance
Converts BSON document types per document to JavaScript types
Evaluates JavaScript code in the interpreter per document
Casts JavaScript true|false back as a result per document
Considering that $regex ( but with a case insensitive match which is not optimal ) is doing the same operations but using the "pcre" C library natively without conversion and recasting per document, then it is clearly the sane choice of the two.

Related

PyMongo delete document by type

Cashbook Collection
{ "_id" : ObjectId("1"), "DR" : "Bank", "CR" : "Roger", "Amount" : "100.00" }
{ "_id" : ObjectId("2"), "DR" : "Bank", "CR" : "Amy", "Amount" : 999.99 }
...
CB = conn['Cashbook']
def CB_del_mult(search,value):
query = {search:value}
CB.delete.many(query)
CB.delete_many('Amount'," { $type : 'string' }")
According to the MongoDO Docs, I can query by type.
I am attempting to remove all documents from Cashbook collection depending on whether 'Amount' is a string value. In this example, the 1st entry will be completely removed as Amount is “100.00” (not 100.00)
The above code raises no errors, however is not deleting the string values.
Something like CB.delete_many('Amount',{$type : 'string'} ) raises an invalid syntax error
Many thanks
JS and Python syntaxes are different and you need to use the correct one for the language you are working with.
Use pymongo documentation to find out the proper method signatures, options etc. to use with pymongo. E.g. https://pymongo.readthedocs.io/en/stable/api/pymongo/collection.html for delete_many.
CB.delete_many({'Amount': {'$type': 'string'}})

Why $lt in MongoDB doesn´t work in this query?

I am developing a web platform for people who is learning languages, I have a collection for users like that:
{
"_id" : ObjectId("54a15e7e3453b5741a6c7be0"),
"name" : "Miguel Lopez",
"lng" : [
{
"ES" : 5
},
{
"EN" : 4
},
{
"IT" : 3
}
],
"email" : "miguel#gmail.com",
"password" : "123456"
}
In "lng" field I save the languages that this user is learning, each language have a level between 1 and 5. To get the list of users who are learning "ES" and have level 5, I use this query:
db.users.find({lng : {ES:5}})
And it´s working perfectly, but when I want the get users who have a specific language (for example "EN") with a level less than 5, I use this query:
db.users.find({lng : {EN:{$lt: 5}}})
But this query doesn´t work, and I dont know the reason, when I try to execute this query in Robomongo I get this message: "Script executed succesfully, but there is no results to show"
You need to use $elemMatch or dot notation
db.users.find({lng : {$elemMatch: {EN:{$lt: 5}}}})
db.users.find({ "lng.EN" : { "$lt" : 5 } })

$Where gives error

I have a collection containing data:
{
"_id" : ObjectId("51dfb7abe4b02f15ee93a7c7"),
"date_created" : "2013-7-12 13:25:5",
"referrer_id" : 13,
"role_name" : "Physician",
"status_id" : "1",
}
I am sending the query:
cmd {
"mapreduce" : "doctor" ,
"map" : "function map(){emit(this._id,this);}" ,
"reduce" : "function reduce(key,values){return values;}" ,
"verbose" : true ,
"out" : { "merge" : "map_reduce"} ,
"query" : { "$where" : "this.demographics.first_name=='makdoctest'"}
}
I am getting error as:
"errmsg" : "exception: count failed in DBDirectClient: 10071 error on invocation of $where function:\nJS Error: TypeError: this.demographics has no properties nofile_a:0"
As Sammaye says in a comment:
It means that somewhere in one of your documents demographics is null or does not exist, you need to do a null check first, but more importantly why are you dong this in a $where?
I would go even further that that, and I wouldn't even use the Map/Reduce mechanism here. It slow, can't use indexes and can not run in parallel with others.
You would be much better off using the Aggregation Framework where you can do something like:
db.doctor.aggregate( [
{ $match: { "demographics.first_name" : 'makdoctest' } },
{ $group: …
You didn't specify the final goal here, but once you do I can update the answer.

mongo query fetching embedded document

based on this :
db.room.save({name:'public',msg:[{to:'Jo',msg:'whats up'},{to:'Jo',msg:'dude?'}]})
db.room.find()
> { "_id" : ObjectId("4f86282a87850ac4bfe9a664"), "name" : "public", "msg" : [ { "to" : "Jo", "msg" : "whats up" }, { "to" : "Jo", "msg" : "dude?" } ] }
I would like to get the following result:
db.room.find(????)
> {"name" : "public", "to" : "Jo", "msg" : "whats up"}
> {"name" : "public", "to" : "Jo", "msg" : "dude?"}
What query should i use in order to achieve this ?
The short answer is that it is not possible to retrieve individual embedded documents. Only top-level documents are returned.
There is a feature request for this functionality. "Support for positional ($) operator in fields to return specifier" - https://jira.mongodb.org/browse/SERVER-828
This feature request is slated for version 2.1.1, but this is subject to change.
A similar question was asked on Google Groups a while back, "Retrieving DBObject from an array of DBObjects" - http://groups.google.com/group/mongodb-user/browse_thread/thread/7d2c8919dd6c4dd8 the question is on the Java Driver specifically, but the answer is still applicable.
If you want to modify embedded documents, you can do so as described in the Google Groups response. Additionally, the new Aggregation Framework contains an $unwind method, which you might want to experiment with. If you would like to print out only the embedded documents, you will have to do it via your application, or consider changing your document structure such that the documents that you want to display are top-level.
Hope this helps!

Get nested fields with MongoDB shell

I've "users" collection with a "watchlists" field, which have many inner fields too, one of that is "arrangeable_values" (the second field within "watchlists").
I need to find for each user in "users" collection, each "arrangeable_values" within "watchlists".
How can I do that with mongodb shell ?
Here is an example of data model :
> db.users.findOne({'nickname': 'superj'})
{
"_id" : ObjectId("4f6c42f6018a590001000001"),
"nickname" : "superj",
"provider" : "github",
"user_hash" : null,
"watchlists" : [
{
"_id" : ObjectId("4f6c42f7018a590001000002"),
"arrangeable_values" : {
"description" : "My introduction presentation to node.js along with sample code at various stages of building a simple RESTful web service with journey, cradle, winston, optimist, and http-console.",
"tag" : "",
"html_url" : "https://github.com/indexzero/nodejs-intro"
},
"avatar_url" : "https://secure.gravatar.com/avatar/d43e8ea63b61e7669ded5b9d3c2e980f?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
"created_at" : ISODate("2011-02-01T10:20:29Z"),
"description" : "My introduction presentation to node.js along with sample code at various stages of building a simple RESTful web service with journey, cradle, winston, optimist, and http-console.",
"fork_" : false,
"forks" : 13,
"html_url" : "https://github.com/indexzero/nodejs-intro",
"pushed_at" : ISODate("2011-09-12T17:54:58Z"),
"searchable_values" : [
"description:my",
"description:introduction",
"description:presentation",
"html_url:indexzero",
"html_url:nodejs",
"html_url:intro"
],
"tags_array" : [ ],
"watchers" : 75
},
{
"_id" : ObjectId("4f6c42f7018a590001000003"),
"arrangeable_values" : {
"description" : "A Backbone alternative idea",
"tag" : "",
"html_url" : "https://github.com/maccman/spine.todos"
},
"avatar_url" : "https://secure.gravatar.com/avatar/baf018e2cc4616e4776d323215c7136c?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
"created_at" : ISODate("2011-03-18T11:03:42Z"),
"description" : "A Backbone alternative idea",
"fork_" : false,
"forks" : 31,
"html_url" : "https://github.com/maccman/spine.todos",
"pushed_at" : ISODate("2011-11-20T22:59:45Z"),
"searchable_values" : [
"description:a",
"description:backbone",
"description:alternative",
"description:idea",
"html_url:https",
"html_url:github",
"html_url:com",
"html_url:maccman",
"html_url:spine",
"html_url:todos"
],
"tags_array" : [ ],
"watchers" : 139
}
]
}
For the document above, the following find() query would extract both the "nickname" of the document, and its associated "arrangeable_values" (where the document is in the users collection):
db.users.find({}, { "nickname" : 1, "watchlists.arrangeable_values" : 1 })
The result you get for your single document example would be:
{ "_id" : ObjectId("4f6c42f6018a590001000001"), "nickname" : "superj",
"watchlists" : [
{ "arrangeable_values" : { "description" : "My introduction presentation to node.js along with sample code at various stages of building a simple RESTful web service with journey, cradle, winston, optimist, and http-console.", "tag" : "", "html_url" : "https://github.com/indexzero/nodejs-intro" } },
{ "arrangeable_values" : { "description" : "A Backbone alternative idea", "tag" : "", "html_url" : "https://github.com/maccman/spine.todos" } }
] }
MongoDB queries return entire documents. You are looking for a field inside an array inside of the document and this will break the find().
The problem here is that any basic find() query, will return all matching documents. The find() does have the option to only return specific fields. But that will not work with your array of sub-objects. You could returns watchlists, but not watchlist entries that match.
As it stands you have two options:
Write some client-side code that loops through the documents and does the filtering. Remember that the shell is effectively a javascript driver, so you can write code in there.
Use the new aggregation framework. This will have a learning curve, but it can effectively extract the sub-items you're looking for.