Positional operator not working in MongoDB with array elements FastAPI
Question:
I have a document that looks like this:
{
"_id": "cc3a8d7f-5962-47e9-a3eb-09b0a57c9fdb",
"isDeleted": false,
"user": {
"timestamp": "2023-02-12",
"name": "john",
"surname": "doe",
"email": "[email protected]",
"phone": "+012345678912",
"age": 25,
"gender": "female",
"nationality": "smth",
"universityMajor": "ENGINEERING",
"preferences": null,
"highPrivacy": false,
},
"postings": [
{
"id": "f61b103d-8118-4054-8b24-b26e2f4febc4",
"isDeleted": false,
"timestamp": "2023-02-12",
"houseType": "apartment",
"totalNumOfRoommates": 5,
"location": {
"neighborhood": "Oran",
"district": "Çankaya",
"city": "Adana"
},
"startDate": "2022-11-10",
"endDate": "2022-11-15",
"postingType": "House Sharer",
"title": "House sharer post 1",
"description": "This is house sharer post 1",
"price": 2500,
"houseSize": "2 + 0"
},
{
"id": "b7d34113-1b13-4265-ba9b-766accecd267",
"isDeleted": false,
"timestamp": "2023-02-12",
"houseType": "apartment",
"totalNumOfRoommates": 5,
"location": {
"neighborhood": "Dikmen",
"district": "Çankaya",
"city": "Adana"
},
"startDate": "2022-09-13",
"endDate": "2023-12-24",
"postingType": "House Seeker",
"startPrice": 2002,
"endPrice": 2500
}
],
}
Each posting object has an ID. I am trying to "delete" (setting the property isDeleted to True, rather than actual deletion) the post whose ID is specified in the code below:
@router.delete('/{id}', response_description='Deletes a single posting')
async def deletePost(id: str):
update_result = await dbConnection.update_one({"postings.id": id, "postings.isDeleted" : False},
{"$set" : {"postings.$.isDeleted" : True} })
if update_result.modified_count == 1:
return Response(status_code=status.HTTP_204_NO_CONTENT)
else:
raise HTTPException(status_code=404, detail=f"Post {id} not found or has already been deleted")
The issue is that the first document (the one with ID f61b103d-8118-4054-8b24-b26e2f4febc4) is being "deleted" even when I supply the ID b7d34113-1b13-4265-ba9b-766accecd267 to the function. If I hit the endpoint again with the same ID, it "deletes" the array elements in order regardless of which ID I supply. Even though I am using the positional operator to set the specific element’s property isDeleted to True.
What exactly could be the problem here?
Here is a link with the earlier setup in Mongo Playground: https://mongoplayground.net/p/03HSkwDUPUE
P.S although @ray’s answer does work in MongoPlayground, I had to change a couple of things in the query with FastAPI, for anyone interested, the working query is below:
update_result = await dbConnection.update_one(
{"postings.id": id},
{
"$set": {
"postings.$[p].isDeleted": True
}
},
upsert=True,
array_filters=[
{
"p.id": id,
"p.isDeleted": False
}]
)
Answers:
Your query now is kind of like searching documents through 2 criteria in a "or" behaviour.
"postings.id": "b7d34113-1b13-4265-ba9b-766accecd267"
– find document with any postings element with id b7d34113-1b13-4265-ba9b-766accecd267
"postings.isDeleted": false
– find document with any postings element with deleted is false
Note the "any". That means the 2 criteria are not required to be happened on the same array element. So, that is kind of "or" behaviour.
You can use arrayFilters
to achieve what you want.
db.collection.update({},
{
"$set": {
"postings.$[p].isDeleted": true
}
},
{
arrayFilters: [
{
"p.id": "b7d34113-1b13-4265-ba9b-766accecd267",
"p.isDeleted": false
}
]
})
I have a document that looks like this:
{
"_id": "cc3a8d7f-5962-47e9-a3eb-09b0a57c9fdb",
"isDeleted": false,
"user": {
"timestamp": "2023-02-12",
"name": "john",
"surname": "doe",
"email": "[email protected]",
"phone": "+012345678912",
"age": 25,
"gender": "female",
"nationality": "smth",
"universityMajor": "ENGINEERING",
"preferences": null,
"highPrivacy": false,
},
"postings": [
{
"id": "f61b103d-8118-4054-8b24-b26e2f4febc4",
"isDeleted": false,
"timestamp": "2023-02-12",
"houseType": "apartment",
"totalNumOfRoommates": 5,
"location": {
"neighborhood": "Oran",
"district": "Çankaya",
"city": "Adana"
},
"startDate": "2022-11-10",
"endDate": "2022-11-15",
"postingType": "House Sharer",
"title": "House sharer post 1",
"description": "This is house sharer post 1",
"price": 2500,
"houseSize": "2 + 0"
},
{
"id": "b7d34113-1b13-4265-ba9b-766accecd267",
"isDeleted": false,
"timestamp": "2023-02-12",
"houseType": "apartment",
"totalNumOfRoommates": 5,
"location": {
"neighborhood": "Dikmen",
"district": "Çankaya",
"city": "Adana"
},
"startDate": "2022-09-13",
"endDate": "2023-12-24",
"postingType": "House Seeker",
"startPrice": 2002,
"endPrice": 2500
}
],
}
Each posting object has an ID. I am trying to "delete" (setting the property isDeleted to True, rather than actual deletion) the post whose ID is specified in the code below:
@router.delete('/{id}', response_description='Deletes a single posting')
async def deletePost(id: str):
update_result = await dbConnection.update_one({"postings.id": id, "postings.isDeleted" : False},
{"$set" : {"postings.$.isDeleted" : True} })
if update_result.modified_count == 1:
return Response(status_code=status.HTTP_204_NO_CONTENT)
else:
raise HTTPException(status_code=404, detail=f"Post {id} not found or has already been deleted")
The issue is that the first document (the one with ID f61b103d-8118-4054-8b24-b26e2f4febc4) is being "deleted" even when I supply the ID b7d34113-1b13-4265-ba9b-766accecd267 to the function. If I hit the endpoint again with the same ID, it "deletes" the array elements in order regardless of which ID I supply. Even though I am using the positional operator to set the specific element’s property isDeleted to True.
What exactly could be the problem here?
Here is a link with the earlier setup in Mongo Playground: https://mongoplayground.net/p/03HSkwDUPUE
P.S although @ray’s answer does work in MongoPlayground, I had to change a couple of things in the query with FastAPI, for anyone interested, the working query is below:
update_result = await dbConnection.update_one(
{"postings.id": id},
{
"$set": {
"postings.$[p].isDeleted": True
}
},
upsert=True,
array_filters=[
{
"p.id": id,
"p.isDeleted": False
}]
)
Your query now is kind of like searching documents through 2 criteria in a "or" behaviour.
"postings.id": "b7d34113-1b13-4265-ba9b-766accecd267"
– find document with any postings element with id b7d34113-1b13-4265-ba9b-766accecd267"postings.isDeleted": false
– find document with any postings element with deleted is false
Note the "any". That means the 2 criteria are not required to be happened on the same array element. So, that is kind of "or" behaviour.
You can use arrayFilters
to achieve what you want.
db.collection.update({},
{
"$set": {
"postings.$[p].isDeleted": true
}
},
{
arrayFilters: [
{
"p.id": "b7d34113-1b13-4265-ba9b-766accecd267",
"p.isDeleted": false
}
]
})