帮我解决这个问题并改进我的脚本。这是一篇很长的帖子,请耐心等待。我正在编写一个小型网络应用程序,帮助用户维护他们感兴趣的产品的心愿单。我使用MongoDB来存储数据,使用Python Tornado作为服务器。
数据库中有两个集合,一个用于存储产品详细信息,另一个用于维护用户详细信息。每当用户添加新产品链接时,该URL将被添加到产品集合中,其对象ID将被添加到用户的文档中。它实际上是一个列表,它将被添加到其中。
因此,产品集合中的典型文档将是:
{
_id: ObjectId("53a2bfcfa7603606c2765342"),
name: "Nexus 7 from Google (7-Inch, 16 GB, Black)",
url: "http://www.amazon.com/Nexus-Google-7-Inch-Black-Tablet/dp/B00DVFLJDS/",
base_price: 222,
current_price: 213.96,
}
用户的用户将是:
{
_id: ObjectId("539c4adea760360886d7ef02"),
email_id: "johnappleseed@apple.com",
tracked_products: [
ObjectId("53a2bfcfa7603606c2765342"),
ObjectId("53a2d2ada7603606c2765344"),
ObjectId("53a2d294a7603606c2765343")
]
}
现在我已经编写了一个每天运行一次的cron作业并更新了current_price
。如果current_price
低于base_price
,我必须向用户发送提醒。这对我来说太棘手了。即使产品价格发生任何变化,我也希望发送电子邮件。
一种简单但效率低下的方法,检查价格,如果价格有变化,则在产品文档中添加新值。可能是alert: yes
然后循环遍历所有用户,遍历跟踪产品的每个对象ID,如果与这些对象ID匹配的任何文档的警报字段为是,则将用户电子邮件添加到电子邮件队列。另一个重置警报的cron工作。代码将是
email_id_queue = []
for product in product.collection:
# price changed? if yes, then update
# add/update alert: yes
# ...
product.collection.save(product)
for user in users.collection:
for product_id in user['tracked_products']:
product = products_collection.find({'_id': product_id})
if product.get('alert', None):
email_id_queue.append(user['email_id'])
break
# send emails to all of those who are in email_id_queue
我发现上面的实现效率不高,我相信可以有很多方法来改进它。现在我有以下解决方案,每当我找到价格发生变化的产品时,我会将其添加到单独的列表中。当我遍历用户时,我将检查它是否有任何这些对象ID不是。
email_id_queue = []
price_changed_products = set()
for product in product.collection:
# price changed? if yes, then update
# ...
# ...
price_changed_products.add(product['_id'])
product.collection.save(product)
for user in users.collection:
if set(user['tracked_products']) & price_changed_products:
email_id_queue.append(user[email_id])
# send emails to all of those who are in email_id_queue
reddit上的用户建议我另类。我将维护另一个集合,可能被称为trackers
。此集合将包含product_ids以及跟踪它的用户文档的对象ID。可能是这样的:
{
_id: ObjectId("77a2bfcfa7603606c2765399"),
product_id: ObjectId("53a2bfcfa7603606c2765342"),
subscribers: ['user1@email.com', 'user2@email.com', 'user3@email.com']
}
现在每当价格发生变化时,我都会将该产品的订阅者列表添加到电子邮件队列中。这不需要任何额外的循环。每当我检查新价格时(循环的早期),我都可以这样做。
但是,如果我还要发送其他通知,例如Chrome提醒,iOS推送等,那么上面的工作就不会发生。所以我想让我们跟踪user_id
而不是他们的电子邮件。类似的东西:
{
_id: ObjectId("77a2bfcfa7603606c2765399"),
product_id: ObjectId("53a2bfcfa7603606c2765342"),
subscribers: [ObjectId("12a2bfcfa7603606c2765399"), ObjectId("14a2bfcfa7603606c2765399"), ObjectId("56a2bfcfa7603606c2765399")]
}
然后再次使用用户的对象ID查询数据库并发送通知。最后它将是:
user_ids_queue = set()
for product in product.collection:
# price changed? if yes, then update
# ...
# ...
# find the subscribers:
product_document = db.trackers.find_one({'product_id': product['_id']})
user_ids_queue.add(product_document['trackers'])
product.collection.save(product)
# now send notifications to each of those users:
for user_id in user_ids_queue:
user_document = db.users.find_one({'_id': user_id})
# now send notifications:
add_to_email_queue(user_document['email'])
add_to_chrome_queue(user_document['chrome_id'])
add_to_ios_queue(user_document['ios_push_id'])
# etc
但我可以做得更好吗?任何帮助表示赞赏!谢谢!