mongoengine查询嵌入式文档列表中的重复项



我正在用mongoengine制作一个python应用程序,其中我有一个由n个用户组成的mongodb数据库,每个用户保存n条日常记录。我有一个每个用户n个新记录的列表,我想添加到我的数据库中

在向用户添加新记录之前,我想检查用户是否已经存在某个日期的记录

我在文档中发现的是迭代列表中的每个嵌入文档以检查重复字段,但这是一个O(n^2(算法,300条记录需要5秒,太长了。下面是代码的缩写版本

肯定有更好的查询方式吧?我尝试访问user.records.date之类的内容,但这会抛出一个未找到的

import mongoengine
#snippet here is abbreviated and does not run
# xone of interest in conditional_insert(), line 16
class EmbeddedRecord(mongoengine.EmbeddedDocument):
date = mongoengine.DateField(required = True)
#contents = ...
class User(mongoengine.Document):
#meta{}
#account details
records = mongoengine.EmbeddedDocumentListField(EmbeddedRecord)

def conditional_insert(user, new_record):
# the docs tell me to iterate tthrough every record in the user
# there has to be a better way
for r in user.records:
if str(new_record.date) == str(r.date): #i had to do that in my program 
#because python kep converting datetime obj to str
return
# if record of duplicate date not found, insert new record
save_record(user, new_record)
def save_record(): pass

if __name__ == "__main__":
lst_to_insert = [] # list of (user, record_to_insert)
for object in lst_to_insert: #O(n)
conditional_insert(object[0],object[1]) #O(n)
#and I have n lst_to_insert so in reality I'm currently at O(n^3)

大家好(以及10年后可能会搜索同一问题的未来我(

我使用搜索树的思想优化了代码。我没有把所有记录放在用户的单个列表中,而是按年度和月度进行了分解

class EmbeddedRecord(mongoengine.EmbeddedDocument):
date = mongoengine.DateField(required = True)
#contents = ...
class Year(mongoengine.EmbeddedDocument):
monthly_records = mongoengine.EmbeddedDocumentListField(Month)
class Month(mongoengine.EmbeddedDocument):
daily_records = mongoengine.EmbeddedDocumentListField(EmbeddedRecord)
class User(mongoengine.Document):
#meta{}
#account details
yearly_records = mongoengine.EmbeddedDocumentListField(Year)

因为它是mongodb,我以后可以划分几十年,甚至几个世纪,但到那时我认为这段代码与无关

然后,我将数据分组,按月份插入到单独的panda数据帧中,并分别提供每个数据帧。因此,数据流看起来像:

0) get monthly df    
1) loop through years until we get the right one (lets say 10 steps, i dont think my program will live that long)
2) loop through months until we get the right one (12 steps)
3) for each record in df loop through each daily record in month to check for duplicates

使用检查插入的算法仍然是O(n^2(,但由于最后一步最多有31条记录,因此代码要快得多。我测试了2000条重复记录,它在不到一秒钟的时间内运行(实际上没有计时,但只要它感觉很快,在我的用例中就没那么重要了(

Mongo无法方便地为您提供合适的索引,这非常令人难过。

您经常迭代user.records。如果你能负担得起为300个用户分配内存的费用,只需迭代一次并将它们放入set中,

  1. 提供O(1(恒定时间查找,并且
  2. 提供RAM速度而非网络延迟

保存用户时,还要注意是否使用cache.add((user_id, str(new_record.date)))

编辑

如果你负担不起所有这些CCD_ 4元组的内存,那么关系数据库JOIN是公平的,这只是已排序记录的核心外合并。

我在使用sqlalchemy命中本地sqlite(内存或文件备份(,或较重的数据库,如Postgres或MariaDB。

请记住,关系数据库提供了良好的ACID保证,但你在为这些担保买单。在此应用程序中听起来你不需要这样的属性。像/usr/bin/sort这样简单的东西可以做核心之外的事情将用户的所有当前他的历史记录旁边的记录,让您适当地过滤它们。Sleepycat不是RDBMS,但它的B树确实提供了外部排序,足以解决手头的问题。(是的,一个可以与sleycat进行交易,但是这个问题只需要一些简单的报告。(

坐下来看看。在没有针对特定工作负载的分析数据的情况下,很难判断是否有额外的复杂性这是值得的。识别真正的内存或CPU瓶颈,专注于此。


您不一定需要排序,因为哈希就足够了,只要有足够的核心。将这些元组发送到redis缓存,并使其成为他的问题把它们存放在某个地方。

最新更新