Positional operator not working in MongoDB with array elements FastAPI - python

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": "a.s#ug.bilkent.edu.tr",
"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
}
]
})
Mongo Playground

Related

check if a property contains a specific value in a document with pymongo

I have a collection of documents that looks like this
{
"_id": "4",
"contacts": [
{
"email": "mail#mail.com",
"name": "A1",
"phone": "00",
"crashNotificationEnabled": false,
"locationShared": true,
"creationDate": ISODate("2020-10-19T15:19:04.498Z")
},
{
"email": "mail#mail.com",
"name": "AG2",
"phone": "00",
"crashNotificationEnabled": false,
"locationShared": false,
"creationDate": ISODate("2020-10-19T15:19:04.498Z")
}
],
"creationDate": ISODate("2020-10-19T15:19:04.498Z"),
"_class": ".model.UserContacts"
}
And i would like to iterate through all documents to check if either crashNotificationEnabled or locationShared is true and add +1 to a counter if its the case, im quite new to python and mongosql so i actually have a hard time trying to do that, i tried a lot of things but there is my last try :
def users_with_guardian_angel(mongoclient):
try:
mydb = mongoclient["main"]
userContacts = mydb["userContacts"]
users = userContacts.find()
for user in users:
result = userContacts.find_one({contacts : { $in: [true]}})
if result:
count_users = count_users + 1
print(f"{count_users} have at least one notificiation enabled")
But the result variable stays empty all the time, so if somebody could help me to accomplish what i want to do and tell what i did wrong here ?
Thanks !
Here's one way you could do it by letting the MongoDB server do all the work.
N.B.: This doesn't consider the possibility of multiple entries of the same user.
db.userContacts.aggregate([
{
"$unwind": "$contacts"
},
{
"$match": {
"$expr": {
"$or": [
"$contacts.crashNotificationEnabled",
"$contacts.locationShared"
]
}
}
},
{
"$count": "userCountWithNotificationsEnabled"
}
])
Try it on mongoplayground.net.
Example output:
[
{
"userCountWithNotificationsEnabled": 436
}
]

How can I find a specific key from a python dict and then get a value from that key in Python

I have a python dictionary that looks something like this:
[
{
"timestamp": 1621559698154,
"user": {
"uri": "spotify:user:xxxxxxxxxxxxxxxxxxxx",
"name": "Panda",
"imageUrl": "https://i.scdn.co/image/ab67757000003b82b54c68ed19f1047912529ef4"
},
"track": {
"uri": "spotify:track:6SJSOont5dooK2IXQoolNQ",
"name": "Dirty",
"imageUrl": "http://i.scdn.co/image/ab67616d0000b273a36e3d46e406deebdd5eafb0",
"album": {
"uri": "spotify:album:0NMpswZbEcswI3OIe6ml3Y",
"name": "Dirty (Live)"
},
"artist": {
"uri": "spotify:artist:4ZgQDCtRqZlhLswVS6MHN4",
"name": "grandson"
},
"context": {
"uri": "spotify:artist:4ZgQDCtRqZlhLswVS6MHN4",
"name": "grandson",
"index": 0
}
}
},
{
"timestamp": 1621816159299,
"user": {
"uri": "spotify:user:xxxxxxxxxxxxxxxxxxxxxxxx",
"name": "maja",
"imageUrl": "https://i.scdn.co/image/ab67757000003b8286459151d5426f5a9e77cfee"
},
"track": {
"uri": "spotify:track:172rW45GEnGoJUuWfm1drt",
"name": "Your Best American Girl",
"imageUrl": "http://i.scdn.co/image/ab67616d0000b27351630f0f26aff5bbf9e10835",
"album": {
"uri": "spotify:album:16i5KnBjWgUtwOO7sVMnJB",
"name": "Puberty 2"
},
"artist": {
"uri": "spotify:artist:2uYWxilOVlUdk4oV9DvwqK",
"name": "Mitski"
},
"context": {
"uri": "spotify:playlist:0tol7yRYYfiPJ17BuJQKu2",
"name": "I Bet on Losing Dogs",
"index": 0
}
}
}
]
How can I get, for example, the group of values for user.name "Panda" and then get that specific "track" list? I can't parse through the list by index because the list order changes randomly.
If you are only looking for "Panda", then you can just loop over the list, check whether the name is "Panda", and then retrieve the track list accordingly.
Otherwise, that would be inefficient if you want to do that for many different users. I would first make a dict that maps user to its index in the list, and then use that for each user (I am assuming that the list does not get modified while you execute the code, although it can be modified between executions.)
user_to_id = {data[i]['user']['name']: i for i in range(len(data))} # {'Panda': 0, 'maja': 1}
def get_track(user):
return data[user_to_id[user]]['track']
print(get_track('maja'))
print(get_track('Panda'))
where data is the list you provided.
Or, perhaps just make a dictionary of tracks directly:
tracks = {item['user']['name']: item['track'] for item in data}
print(tracks['Panda'])
If you want to get list of tracks for user Panda:
tracks = [entry['track'] for entry in data if entry['user']['name'] == 'Panda']

How can I access and count a value of an attribute of Json file in Python?

I have this json file. It's a report of threat intelligence platform.
{
"data": {
"attributes": {
"authentihash": "a077f952798eb3bc0730c7c4774da7636326cf4b524ed6571b7eaf3d43f0bd9b",
"creation_date": 1387937380,
"crowdsourced_yara_results": [
{
"author": "Florian Roth",
"description": "Malware InstallRex / AntiFW",
"rule_name": "PUP_InstallRex_AntiFWb",
"ruleset_id": "000ca30c43",
"ruleset_name": "crime_antifw_installrex",
"source": "https://github.com/Neo23x0/signature-base"
}
],
"first_submission_date": 1389124248,
"last_analysis_date": 1603898773,
"last_analysis_results": {
"ALYac": {
"category": "undetected",
"engine_name": "ALYac",
"engine_update": "20201028",
"engine_version": "1.1.1.5",
"method": "blacklist",
"result": null
},
"APEX": {
"category": "undetected",
"engine_name": "APEX",
"engine_update": "20201028",
"engine_version": "6.90",
"method": "blacklist",
"result": null
},
"AVG": {
"category": "malicious",
"engine_name": "AVG",
"engine_update": "20201028",
"engine_version": "18.4.3895.0",
"method": "blacklist",
"result": "FileRepMetagen [Malware]"
},
I would count how many antimalware detected the malware as "maliciuous". So
i can access and print the whole list of "last_analysis_results", but i can't access to "category" of each antimalware.
I try with:
for elem in data['data']['attributes']['last_analysis_results']:
but then? thank you :)
Use .values() to get the dictionary values. Then you can access the category element of each dictionary.
sum(r['category'] == 'malicious' for r in data['data']['attributes']['last_analysis_results'].values())
Booleans are treated as 1 and 0 when used arithmetically, so summing the comparisons counts the number of times it's true.

Getting Deeper Level JSON Values in Python

I have a Python script that make an API call to retrieve data from Zendesk. (Using Python 3.x) The JSON object has a structure like this:
{
"id": 35436,
"url": "https://company.zendesk.com/api/v2/tickets/35436.json",
"external_id": "ahg35h3jh",
"created_at": "2009-07-20T22:55:29Z",
"updated_at": "2011-05-05T10:38:52Z",
"type": "incident",
"subject": "Help, my printer is on fire!",
"raw_subject": "{{dc.printer_on_fire}}",
"description": "The fire is very colorful.",
"priority": "high",
"status": "open",
"recipient": "support#company.com",
"requester_id": 20978392,
"submitter_id": 76872,
"assignee_id": 235323,
"organization_id": 509974,
"group_id": 98738,
"collaborator_ids": [35334, 234],
"forum_topic_id": 72648221,
"problem_id": 9873764,
"has_incidents": false,
"due_at": null,
"tags": ["enterprise", "other_tag"],
"via": {
"channel": "web"
},
"custom_fields": [
{
"id": 27642,
"value": "745"
},
{
"id": 27648,
"value": "yes"
}
],
"satisfaction_rating": {
"id": 1234,
"score": "good",
"comment": "Great support!"
},
"sharing_agreement_ids": [84432]
}
Where I am running into issues is in the "custom_fields" section specifically. I have a particular custom field inside of each ticket I need the value for, and I only want that particular value.
To spare you too many specifics of the Python code, I am reading through each value below for each ticket and adding it to an output variable before writing that output variable to a .csv. Here is the particular place the breakage is occuring:
output += str(ticket['custom_fields'][id:23825198]).replace(',', '')+','
All the replace nonsense is to make sure that since it is going into a comma delimited file, any commas inside of the values are removed. Anyway, here is the error I am getting:
output += str(ticket['custom_fields'][id:int(23825198)]).replace(',', '')+','
TypeError: slice indices must be integers or None or have an __index__ method
As you can see I have tried a couple different variations of this to try and resolve the issue, and have yet to find a fix. I could use some help!
Thanks...
Are you using json.loads()? If so you can then get the keys, and do an if statement against the keys. An example on how to get the keys and their respective values is shown below.
import json
some_json = """{
"id": 35436,
"url": "https://company.zendesk.com/api/v2/tickets/35436.json",
"external_id": "ahg35h3jh",
"created_at": "2009-07-20T22:55:29Z",
"updated_at": "2011-05-05T10:38:52Z",
"type": "incident",
"subject": "Help, my printer is on fire!",
"raw_subject": "{{dc.printer_on_fire}}",
"description": "The fire is very colorful.",
"priority": "high",
"status": "open",
"recipient": "support#company.com",
"requester_id": 20978392,
"submitter_id": 76872,
"assignee_id": 235323,
"organization_id": 509974,
"group_id": 98738,
"collaborator_ids": [35334, 234],
"forum_topic_id": 72648221,
"problem_id": 9873764,
"has_incidents": false,
"due_at": null,
"tags": ["enterprise", "other_tag"],
"via": {
"channel": "web"
},
"custom_fields": [
{
"sid": 27642,
"value": "745"
},
{
"id": 27648,
"value": "yes"
}
],
"satisfaction_rating": {
"id": 1234,
"score": "good",
"comment": "Great support!"
},
"sharing_agreement_ids": [84432]
}"""
# load the json object
zenJSONObj = json.loads(some_json)
# Shows a list of all custom fields
print("All the custom field data")
print(zenJSONObj['custom_fields'])
print("----")
# Tells you all the keys in the custom_fields
print("How keys and the values")
for custom_field in zenJSONObj['custom_fields']:
print("----")
for key in custom_field.keys():
print("key:",key," value: ",custom_field[key])
You can then modify the JSON object by doing something like
print(zenJSONObj['custom_fields'][0])
zenJSONObj['custom_fields'][0]['value'] = 'something new'
print(zenJSONObj['custom_fields'][0])
Then re-encode it using the following:
newJSONObject = json.dumps(zenJSONObj, sort_keys=True, indent=4)
I hope this is of some help.

How to Convert Json Value of Http Post Parameter to Python Dict in Django?

I am using Django to receive and process push notifications from the foursquare real-time api. Each checkin is pushed as a POST request to my server containing a single parameter named checkin. I am trying to grab the value of the checkin parameter and convert it to a python dict. However, calling json.loads always results in the following error:
NameError: name 'true' is not defined
I know the json is valid, so I must be doing something wrong.
The code is:
import json
def push(request):
if request.is_secure():
checkin_json = request.POST['checkin']
checkin = json.load(request.POST)
The body of the post request is:
"checkin =
{
"id": "4e6fe1404b90c00032eeac34",
"createdAt": 1315955008,
"type": "checkin",
"timeZone": "America/New_York",
"user": {
"id": "1",
"firstName": "Jimmy",
"lastName": "Foursquare",
"photo": "https://foursquare.com/img/blank_boy.png",
"gender": "male",
"homeCity": "New York, NY",
"relationship": "self"
},
"venue": {
"id": "4ab7e57cf964a5205f7b20e3",
"name": "foursquare HQ",
"contact": {
"twitter": "foursquare"
},
"location": {
"address": "East Village",
"lat": 40.72809214560253,
"lng": -73.99112284183502,
"city": "New York",
"state": "NY",
"postalCode": "10003",
"country": "USA"
},
"categories": [
{
"id": "4bf58dd8d48988d125941735",
"name": "Tech Startup",
"pluralName": "Tech Startups",
"shortName": "Tech Startup",
"icon": "https://foursquare.com/img/categories/building/default.png",
"parents": [
"Professional & Other Places",
"Offices"
],
"primary": true
}
],
"verified": true,
"stats": {
"checkinsCount": 7313,
"usersCount": 565,
"tipCount": 128
},
"url": "http://foursquare.com"
}
}"
Try json.loads(checkin_json) instead of json.load(request.POST). Notice the extra 's'.
change checkin = json.load(request.POST) to checkin = json.loads(checkin_json)
On python, boolean values are Capitalized (first letter is uppercase): True/False.
Check this.
EDIT:
Pay attentiot at this lines:
"primary": true
}
],
"verified": true,
Both "true" values are lowercase and need to be capitalized

Categories

Resources