如何以1000块的形式阅读集合?

时间:2012-03-20 12:27:13

标签: python mongodb pymongo

我需要在Python代码中读取MongoDB的整个集合(集合名称为“test”)。我试过像

    self.__connection__ = Connection('localhost',27017)
    dbh = self.__connection__['test_db']            
    collection = dbh['test']

如何通过1000读取块中的集合(以避免内存溢出,因为集合可能非常大)?

5 个答案:

答案 0 :(得分:6)

我同意雷蒙的意见,但是你提到了1000批,他的回答并没有真正涵盖。您可以在光标上设置批量大小:

cursor.batch_size(1000);

您也可以跳过记录,例如:

cursor.skip(4000);

这是你要找的吗?这实际上是一种分页模式。但是,如果您只是想避免内存耗尽,那么您实际上并不需要设置批量大小或跳过。

答案 1 :(得分:5)

使用游标。游标有一个" batchSize"变量,用于控制在执行查询后每批实际发送到客户端的文档数。你不必触摸这个设置,因为默认是好的,如果调用" getmore"大多数驱动程序都隐藏了命令。我不熟悉pymongo,但它的工作原理如下:

cursor = db.col.find() // Get everything!

while(cursor.hasNext()) {
    /* This will use the documents already fetched and if it runs out of documents in it's local batch it will fetch another X of them from the server (where X is batchSize). */
    document = cursor.next();

    // Do your magic here
}

答案 2 :(得分:2)

在@Rafael Valero的启发下+修复了他代码中的最后一个小块错误,并使其更加通用,我创建了生成器函数来遍历带有查询和投影的mongo集合:

def iterate_by_chunks(collection, chunksize=1, start_from=0, query={}, projection={}):
   chunks = range(start_from, collection.find(query).count(), int(chunksize))
   num_chunks = len(chunks)
   for i in range(1,num_chunks+1):
      if i < num_chunks:
          yield collection.find(query, projection=projection)[chunks[i-1]:chunks[i]]
      else:
          yield collection.find(query, projection=projection)[chunks[i-1]:chunks.stop]

例如,您首先创建一个这样的迭代器:

mess_chunk_iter = iterate_by_chunks(db_local.conversation_messages, 200, 0, query={}, projection=projection)

然后按块进行迭代:

chunk_n=0
total_docs=0
for docs in mess_chunk_iter:
   chunk_n=chunk_n+1        
   chunk_len = 0
   for d in docs:
      chunk_len=chunk_len+1
      total_docs=total_docs+1
   print(f'chunk #: {chunk_n}, chunk_len: {chunk_len}')
print("total docs iterated: ", total_docs)

chunk #: 1, chunk_len: 400
chunk #: 2, chunk_len: 400
chunk #: 3, chunk_len: 400
chunk #: 4, chunk_len: 400
chunk #: 5, chunk_len: 400
chunk #: 6, chunk_len: 400
chunk #: 7, chunk_len: 281
total docs iterated:  2681

答案 3 :(得分:1)

这是一种通用解决方案,可以批量迭代任何迭代器或生成器:

def _as_batch(cursor, batch_size=50):
    # iterate over something (pymongo cursor, generator, ...) by batch. 
    # Note: the last batch may contain less than batch_size elements.
    batch = []
    try:
        while True:
            for _ in range(batch_size):
                batch.append(next(cursor))
            yield batch
            batch = []
    except StopIteration as e:
        if len(batch):
            yield batch

只要cursor定义了方法__next__(即我们可以使用next(cursor)),此方法就起作用。因此,我们可以在原始光标或转换后的记录上使用它。

示例

简单用法:

for batch in db['coll_name'].find():
    # do stuff

更复杂的用法(例如,用于批量更新):

def update_func(doc):
    # dummy transform function
    doc['y'] = doc['x'] + 1
    return doc

query = (update_func(doc) for doc in db['coll_name'].find())
for batch in _as_batch(query):
    # do stuff

重新实现count()函数:

sum(map(len, _as_batch( db['coll_name'].find() )))

答案 4 :(得分:0)

使用Pymongo创建当前在Python 2中的初始连接:

host = 'localhost'
port = 27017
db_name = 'test_db'
collection_name = 'test'

使用MongoClient进行连接

# Connect to MongoDB
client = MongoClient(host=host, port=port)
# Make a query to the specific DB and Collection
dbh = client[dbname]
collection = dbh[collection_name]

所以从这里得到正确答案。 我想通过使用块来读取(在这种情况下,大小为1000)。

chunksize = 1000

例如,我们可以决定我们想要多少块大小(chunksize)。

# Some variables to create the chunks
skips_variable = range(0, db_aux[collection].find(query).count(), int(chunksize))
if len(skips_variable)<=1:
    skips_variable = [0,len(skips_variable)]

然后我们可以检索每个块。

for i in range(1,len(skips_variable)):

    # Expand the cursor and retrieve data 

    data_from_chunk = dbh[collection_name].find(query)[skips_variable[i-1]:skips_variable[i]]))

本案例中的查询为query = {}

Here我使用类似的想法从MongoDB创建数据帧。 Here我使用类似的东西以块的形式写入MongoDB。

我希望它有所帮助。