Search for ObjectId of a document: pymongo - mongodb

I want to access a document in collection by 'name' attribute for getting its ObjectId so that i can insert that unique objectid to other document for reference.
cursorObject = db.collectionIngredient.find({'name': 'sugar'})
I want _id field of cursorObject.
cursorObject.'_id' or cursorObject._id not working.
I have tried __getitem__, __getattribute__ and so much internet surfing but couldn't able to find a way.
Please help

First, as #jjmartinez pointed out, find returns a cursor, which you need to iterate over, in order to get hold of the documents returned by your query. The _id field belongs to the documents, not the cursor.
I'm guessing that in your case the name is unique, so you can avoid cursor/iterating if you use find_one instead of find. Then you get the document directly.
Then, to access the _id, you just need a standard dict-item access:
id = doc['_id']
So we get:
ingredient = db.collectionIngredient.find_one({'name': 'sugar'})
if ingredient is not None:
id = ingredient['_id']
else:
id = None

When you do cursorObject = db.collectionIngredient.find({'name': 'sugar'}) you have a collection of documents, not a single element. So you need to explore all the collection. You need to iterate inside the cursor:
try:
cursorObject = db.collectionIngredient.find({'name': 'sugar'})
except:
print "Unexpected error:", sys.exc_info()[0]
for doc in cursorObject:
print doc
Here you have the Pymongo Tutorial

Related

Deleting a Reference in a MongoEngine ListField?

I have Mongoengine ORM that maps the following relationship, (simplified):
class UserInfo(mg.Document):
username = mg.StringField()
allcharacters = mg.ListField(mg.ReferenceField("Character"))
pass
class Character(mg.Document):
charid = mg.IntField()
pass
A User refers to an array of references to Character's. The problem is that when I access the User's allcharacter array and delete a reference:
for x in db.UserInfo.objects(username = session["user"])[0].allcharacters:
if x.charid == someint:
x.delete()
The document in question gets deleted but the reference in the array remains, pointing to a document that no longer exists.
I tried to
allcharacters = mg.ListField(
mg.ReferenceField("Character"), reverse_delete_rule=mg.CASCADE)
but that seems to do nothing.
How do I delete the entry in the ListField of an ListField(ReferenceField) in Mongoengine?
To guide future generations:
In order to delete the reference element from an array once the actual reference has been deleted, requires using the $pull operation.
char = db.Character.objects()[0]
objid = char.pk
char.delete()
user = db.UserInfo.objects().update_one(pull__allcharacters=objid)
Get a variable that points to the specific reference you want to delete. Use the .pk attribute to get it's ObjectId. You can now delete the document. To delete the reference in the array within the document, it's a pull query such that if the objid is in the array of allcharacters
May the future generation find this a solace.

How to pass variable between two queries in MongoDB?

I want to put the query result from one collection in a variable and use it as input for query in another collection. The queries look like this as follows:
Query 1:
var ID=db.User.findOne({Name:"Ivan"}, {ID: 1});
db.Artists.find({"Listeners.ID":ID});
Query 2:
var Friends=db.Users.find({Friends:x});
//Users.Friends is an array of interger identifier for User
db. Artists.find({"Listeners.ID":{$in:Friends}});
But they all don't work. How to write the right one?
The query db.User.findOne({Name:"Ivan"}, {ID: 1}); does not return a single value, it returns the document, reduced to the field you requested. What you get is an object, with two fields: _id (because you didn't explicitly exclude it) and ID (when it exists in the document). Your var ID looks like this:
{
_id:ObjectId(<long hex string>),
ID:<value>
}
So when you want to query by the ID value, you need to specify it:
db.Artists.find({"Listeners.ID":ID.ID});
Regarding your second query: when you use find instead of findOne you get a cursor object which can then be used to retrieve the individual documents using cursor.next() or cursor.toArray().

Update an array using Jongo

I have a mongodb collection of the form
{
"_id":"id",
"userEmail":"userEmailFromCustomerCollection",
"customerFavs":[
"www.xyz.com",
"www.xyz.com",
"www.xyz.com"
]
}
I need to add an element to the customers favs array using Jongo , I am using the following code snippet to do so .
String query = "{userEmail:'"+emailId+"'}";
customerFavCollection.update(query).with("{$addToSet:{customerFavs:#}}", favUrl);
My problem , is that I need to upsert the document if the document does not
exist already , how can I do so using Jongo, I know an easier option would be to retrieve the document by Id and if it does not exist then insert the document using save() , but I am trying to avoid the extra retrieve.
You can add upsert() on the query.
customerFavCollection.update("userEmail:#", emailId)
.with("{$addToSet:{customerFavs:#}}", favUrl)
.upsert();

Multi level MongoDB object querying by key

If you only know the key name (say "nickname"), but not the exact path to that key in the object.
e.g. nickname may be at the first level like:
{"nickname":"Howie"}
or at the second level:
{"user":{"nickname":"Howie"}}
Is it possible to query for nickname equal "Howie" that would return both documents?
Unfortunately there is no wild card that allows you to search for a field at any level in the db. If the position is not relevant and you can modify the document structure you have 2 choices here. You can store your document as
{ fieldname:"nickname", value : "Howie" }
{ fieldname:"user/nickname", value: "Howie" }
You can then query using
db.so.find({fieldname:/nickname/, value:"Howie"})
Alternatively you can store as
db.so.insert({value:"Howie", fieldpath:["nickname"]})
db.so.insert({value:"Howie", fieldpath:["user", "nickname"]})
The advantage with the second approach is that you can now index {fieldpath:1, value:1} and a query on it such as
db.so.find({fieldpath:"nickname", value:"Howie"})
will be an indexed query.

How does Mongoengine decide if 2 EmbeddedDocuments are equal or not?

I have the following Mongoengine document:
class MyEmbed(EmbeddedDocument):
embedField = StringField(primary_key=True)
varField = StringField()
class TestDoc(Document):
myField = StringField()
embed_list = ListField(EmbeddedDocumentField(MyEmbed))
So I keep a list of embedded documents, to which I wish to add new documents if they don't exist already. The problem is that when I use the atomic update operator add_to_set things don't turn out the way I want them to.
This is what I am trying to do:
embed1 = models.MyEmbed(embedField="F1")
parent = models.TestDoc(myField="ParentField")
embed_list = []
embed_list.append(embed1)
parent.embed_list = embed_list
parent.save()
embed2 = models.MyEmbed(embedField="F1", varField="varField")
TestDoc.objects(id=parent.id).update_one(add_to_set__embed_list=embed2)
The problem is that after doing this, I have in the DB a list of embedded documents with 2 elements. And what I want is to decide upon one field (embedField in this case) whether 2 EmbeddedDocuments are equal or not, and not by taking into account all the properties. My questions are:
What are the default criteria according to which Mongoengine decides whether 2 EmbeddedDocuments are equal or not?
How can I redefine the function that makes Mongoengine decide when 2 EmbeddedDocuments are equal or not?
Thanks!
The actual checking is done inside MongoDB and not mongoengine.
The object sent to mongodb should be the same, but this is where it gets tricky as with BSON order is important and in python with dictionaries its not. When converting to send to mongodb mongoengine just passes a dictionary. This is a bug - so I've added #296 and will fix for 0.8
See https://github.com/MongoEngine/mongoengine/blob/master/mongoengine/document.py#L51 and https://github.com/MongoEngine/mongoengine/blob/master/mongoengine/base/document.py#L52:
def __eq__(self, other):
if isinstance(other, self.__class__):
return self._data == other._data
return False
It compare dicts of Embedded documents data. So you can override this method.
If you look at Document update that calls QuerySet update (find add_to_set and addToSet) you can find that mongoengine doesnt't check exists document in list and just call mongo $addToSet operation: https://github.com/MongoEngine/mongoengine/blob/master/mongoengine/queryset/transform.py#L156.
In your code you have document MyEmbed(embedField="F1") and try add another document MyEmbed(embedField="F1", varField="varField") so logic right: it add new document. If you try next code:
embed1 = models.MyEmbed(embedField="F1")
parent = models.TestDoc(myField="ParentField")
embed_list = []
embed_list.append(embed1)
parent.embed_list = embed_list
parent.save()
embed2 = models.MyEmbed(embedField="F1", varField="varField")
TestDoc.objects(id=parent.id).update_one(add_to_set__embed_list=embed2)
embed3 = models.MyEmbed(embedField="F1")
TestDoc.objects(id=parent.id).update_one(add_to_set__embed_list=embed3)
embed4 = models.MyEmbed(embedField="F1", varField="varField")
TestDoc.objects(id=parent.id).update_one(add_to_set__embed_list=embed4)
you can find that parent contains only embed1 and embed2.
So, to resolve you problem you can override __eq__ method and check document in list, but you must find another solution for update document list, because it have direct call of mongo method.