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.
Related
I'm using Swift here and confused about how to save the document ID into itself field. Here is an example :
I have a collection named "menu"
here we can focus on the one and only document in the "menu" collection which saved the name field "ayam goreng". How do i save the document ID "
7D3fuw3fri6oj287SySW" to the field named "menu_id" inside the document?
As shown in the documentation on adding a document:
In some cases, it can be useful to create a document reference with an auto-generated ID, then use the reference later. For this use case, you can call doc():
let newCityRef = db.collection("cities").document()
// later...
newCityRef.setData([
// ...
])
In your case, you can then get the document ID from the new ref with:
newCityRef.documentId
I am storing a simple list of id's as GUIDs in Realm, but would like the ability to delete an object at a particular index position.
So for example, I want to remove 04b8d81b9e614f1ebb6de41cb0e64432 at index position 1, how can this be achieved? Do I need to add a primary key, or is there a way to remove the item directly using the given index position?
Results<RecipeIds> <0x7fa844451800> (
[0] RecipeIds {
id = a1e28a5eef144922880945b5fcca6399;
},
[1] RecipeIds {
id = 04b8d81b9e614f1ebb6de41cb0e64432;
},
[2] RecipeIds {
id = cd0eead0dcc6403493c4f110667c34ad;
}
)
It seems like this should be a straightforward ask, but I can't find any documentation on it. Even a pointer in the right direction would do.
Results are auto-updating and you cannot directly modify them. You need to update/add/delete objects in your Realm to effect the state of your Results instance.
So you can simply grab the element you need from your Results instance, delete it from Realm and it will be removed from the Results as well.
Assuming the Results instance shown in your question is stored in a variable called recipes, you can do something like the following:
let recipeToDelete = recipes.filter("id == %#","04b8d81b9e614f1ebb6de41cb0e64432")
try! realm.write {
realm.delete(recipeToDelete)
}
That is meant to be read as a dual upsert operation, upsert the document then the array element.
So MongoDB is a denormalized store for me (we're event sourced) and one of the things I'm trying to deal with is the concurrent nature of that. The problem is this:
Events can come in out of order, so each update to the database need to be an upsert.
I need to be able to not only upsert the parent document but an element in an array property of that document.
For example:
If the document doesn't exist, create it. All events in this stream have the document's ID but only part of the information depending on the event.
If the document does exist, then update it. This is the easy part. The update command is just written as UpdateOneAsync and as an upsert.
If the event is actually to update a list, then that list element needs to be upserted. So if the document doesn't exist, it needs to be created and the list item will be upserted (resulting in an insert); if the document does exist, then we need to find the element and update it as an upsert, so if the element exists then it is updated otherwise it is inserted.
If at all possible, having it execute as a single atomic operation would be ideal, but if it can only be done in multiple steps, then so be it. I'm getting a number of mixed examples on the net due to the large change in the 2.x driver. Not sure what I'm looking for beyond the UpdateOneAsync. Currently using 2.4.x. Explained examples would be appreciated. TIA
Note:
Reiterating that this is a question regarding the MongoDB C# driver 2.4.x
Took some tinkering, but I got it.
var notificationData = new NotificationData
{
ReferenceId = e.ReferenceId,
NotificationId = e.NotificationId,
DeliveredDateUtc = e.SentDate.DateTime
};
var matchDocument = Builders<SurveyData>.Filter.Eq(s => s.SurveyId, e.EntityId);
// first upsert the document to make sure that you have a collection to write to
var surveyUpsert = new UpdateOneModel<SurveyData>(
matchDocument,
Builders<SurveyData>.Update
.SetOnInsert(f => f.SurveyId, e.EntityId)
.SetOnInsert(f => f.Notifications, new List<NotificationData>())){ IsUpsert = true};
// then push a new element if none of the existing elements match
var noMatchReferenceId = Builders<SurveyData>.Filter
.Not(Builders<SurveyData>.Filter.ElemMatch(s => s.Notifications, n => n.ReferenceId.Equals(e.ReferenceId)));
var insertNewNotification = new UpdateOneModel<SurveyData>(
matchDocument & noMatchReferenceId,
Builders<SurveyData>.Update
.Push(s => s.Notifications, notificationData));
// then update the element that does match the reference ID (if any)
var matchReferenceId = Builders<SurveyData>.Filter
.ElemMatch(s => s.Notifications, Builders<NotificationData>.Filter.Eq(n => n.ReferenceId, notificationData.ReferenceId));
var updateExistingNotification = new UpdateOneModel<SurveyData>(
matchDocument & matchReferenceId,
Builders<SurveyData>.Update
// apparently the mongo C# driver will convert any negative index into an index symbol ('$')
.Set(s => s.Notifications[-1].NotificationId, e.NotificationId)
.Set(s => s.Notifications[-1].DeliveredDateUtc, notificationData.DeliveredDateUtc));
// execute these as a batch and in order
var result = await _surveyRepository.DatabaseCollection
.BulkWriteAsync(
new []{ surveyUpsert, insertNewNotification, updateExistingNotification },
new BulkWriteOptions { IsOrdered = true })
.ConfigureAwait(false);
The post linked as being a dupe was absolutely helpful, but it was not the answer. There were a few things that needed to be discovered.
The 'second statement' in the linked example didn't work
correctly, at least when translated literally. To get it to work, I had to match on the
element and then invert the logic by wrapping it in the Not() filter.
In order to use 'this index' on the match, you have to use a
negative index on the array. As it turns out, the C# driver will
convert any negative index to the '$' character when the query is
rendered.
In order to ensure they are run in order, you must include bulk write
options with IsOrdered set to true.
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
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.