Используя PyMongo, мне нужно получить поля другой коллекции
Мне нужно построить запрос, используя PyMongo, который получает данные из двух связанных коллекций в базе данных MongoDB.
Коллекция X имеет поля UserId, Name и EmailId:
[
{
"UserId" : "941AB",
"Name" : "Alex Andresson",
"EmailId" : "alex@example.com"
},
{
"UserId" : "768CD",
"Name" : "Bryan Barnes",
"EmailId" : "bryan@example.com"
}
]
Коллекция Y имеет поля UserId1, UserID2 и Rating:
[
{
"UserId1" : "941AB",
"UserId2" : "768CD",
"Rating" : 0.8
}
]
Мне нужно напечатать имя и идентификатор электронной почты UserId1 и UserId2 и рейтинг, что-то вроде этого:
[
{
"UserId1" : "941AB",
"UserName1" : "Alex Andresson"
"UserEmail1" : "alex@example.com",
"UserId2" : "768CD",
"UserName2" : "Bryan Barnes"
"UserEmail2" : "bryan@example.com",
"Rating": 0.8
}
]
Это означает, что мне нужно получать данные из коллекции Y, а также X. Я сейчас работаю с PyMongo, и мне не удалось найти его решение. Может кто-нибудь даже дать мне псевдокод об этой концепции или подход, как двигаться дальше с этим.
1 ответ
Вам нужно выполнить соединение вручную или использовать библиотеку, которая сделает это за вас - возможно, http://mongoengine.org/.
В основном вам нужно найти интересующие вас рейтинги, а затем найти пользователей, связанных с этими рейтингами.
Пример:
#!/usr/bin/env python3
import pymongo
from random import randrange
client = pymongo.MongoClient()
db = client['test']
# clean collections
db['users'].drop()
db['ratings'].drop()
# insert data
user_count = 100
rating_count = 20
db['users'].insert_many([
{'UserId': i, 'Name': 'John', 'EmailId': i}
for i in range(user_count)])
db['ratings'].insert_many([
{'UserId1': randrange(user_count), 'UserId2': randrange(user_count), 'Rating': i}
for i in range(rating_count)])
# don't forget the indexes
db['users'].create_index('UserId')
# but it would be better if we used _id as the UserId
# if you want to make queries based on Rating value, then add also this index:
db['ratings'].create_index('Rating')
# now print ratings with users that have value 10+
# simple approach:
ratings = db['ratings'].find({'Rating': {'$gte': 10}})
for rating in ratings:
u1 = db['users'].find_one({'UserId': rating['UserId1']})
u2 = db['users'].find_one({'UserId': rating['UserId2']})
print('Rating between {} (UserId {:2}) and {} (UserId {:2}) is {:2}'.format(
u1['Name'], u1['UserId'], u2['Name'], u2['UserId'], rating['Rating']))
print('---')
# optimized approach:
ratings = list(db['ratings'].find({'Rating': {'$gte': 10}}))
user_ids = {r['UserId1'] for r in ratings}
user_ids |= {r['UserId2'] for r in ratings}
users = db['users'].find({'UserId': {'$in': list(user_ids)}})
users_by_id = {u['UserId']: u for u in users}
for rating in ratings:
u1 = users_by_id.get(rating['UserId1'])
u2 = users_by_id.get(rating['UserId2'])
print('Rating between {} (UserId {:2}) and {} (UserId {:2}) is {:2}'.format(
u1['Name'], u1['UserId'], u2['Name'], u2['UserId'], rating['Rating']))
Обратите внимание, что первый подход вызывает один find
для рейтингов и двух find
с рейтингом, но второй подход требует только три find
в общей сложности. Это может привести к огромной разнице в производительности, если вы обращаетесь к MongoDB по сети.
Я рекомендую использовать _id
вместо UserId
если возможно для коллекции пользователей.
Конечно, этот конкретный вариант использования будет гораздо проще с базой данных SQL. Если вы используете MongoDB для повышения производительности и у вас гораздо больше операций чтения, чем записи, рассмотрите возможность кэширования связанных имен пользователей в рейтинговый документ.