Табулирование глубоко вложенной коллекции MongoDB с использованием PyMongo

#python #mongodb #pandas #pymongo

#python #mongodb #панды #pymongo

Вопрос:

Я запрашиваю коллекцию, используя pymongo:

 import pymongo

client = pymongo.MongoClient('0.0.0.0', 27017)
db = client.documents
collection = db.collections
test_data = collection.find_one({'metadata.encodingStage.terms.data.line.data.account.shortDescription': {'$exists': True}}, 
{'metadata.encodingStage.terms.data.line.data.account.shortDescription': 1})
  

Я использую find_one здесь для иллюстрации, но на практике это find запрос по всей коллекции.

Это дает следующий результат:

 {'_id': ObjectId('5a2fb9371de46756df51f37b'),
 'metadata': {'encodingStage': {'terms': {'data':
    {'line': [{'data': {'account': {'shortDescription': ['123456']}}},
              {'data': {'account': {'shortDescription': ['7890123']}}}]}}}}}
  

Однако я хотел бы, чтобы данные были в табличном формате, в соответствии с SQL или Pandas:

                                _id    shortDescription
-------------------------------------------------------
ObjectId('5a2fb9371de46756df51f37b')            123456
ObjectId('5a2fb9371de46756df51f37b')           7890123
  

Я понимаю, как это сделать на Python, просматривая результаты, но для эффективности вычислений я бы хотел, чтобы больше табулирования происходило в Mongo.

Есть ли простой способ использовать pymongo для вывода результатов в виде {'_id': 'XXX', 'shortDescription': 'XXX') пар, которые могут быть эффективно сведены в таблицу?

Отменить агрегацию?

Я попытался сделать это как $unwind агрегацию:

 unwind = collection.aggregate([{'$unwind': '$metadata.encodingStage.terms.data.line.data.account.shortDescription'}])
  

…но это не возвращает никаких данных.

Ответ №1:

Решение

Первая проблема с моей логикой заключалась в том, что line был массивом, поэтому его нужно было сначала размотать.

Объединение этого с двумя $project шагами и финалом $unwind в конечном массиве выравнивает данные, чтобы получить (_id, shortDescription) пары, которые можно быстро преобразовать в фрейм данных pandas:

 db.collection.aggregate([
                           {"$project": {"line": "$metadata.encodingStage.terms.data.line"}},
                           {"$unwind": "$line"},
                           {"$project": {"shortDescription": "$line.data.account.shortDescription"}},
                           {"$unwind": "$shortDescription"}
                        ])
  

Вывод:

 [{'_id': ObjectId('xxxxxxxxxxxxxxx123'), 'shortDescription': '12340000'},
 {'_id': ObjectId('xxxxxxxxxxxxxxx123'), 'shortDescription': '43210000'},
 {'_id': ObjectId('yyyyyyyyyyyyyyy789'), 'shortDescription': '56780000'},
 {'_id': ObjectId('yyyyyyyyyyyyyyy789'), 'shortDescription': '78920000'},
 {'_id': ObjectId('yyyyyyyyyyyyyyy789'), 'shortDescription': '55550000'}]
  

Которая может быть загружена в pandas без дополнительных преобразований:

 import pandas as pd
results = db.collection.aggregate([
                           {"$project": {"line": "$metadata.encodingStage.terms.data.line"}},
                           {"$unwind": "$line"},
                           {"$project": {"shortDescription": "$line.data.account.shortDescription"}},
                           {"$unwind": "$shortDescription"}
                        ])
df = pd.DataFrame([item for item in results])
  

Вывод:

 print(df)

                  _id shortDescription
0  xxxxxxxxxxxxxxx123         12340000
1  xxxxxxxxxxxxxxx123         43210000
2  yyyyyyyyyyyyyyy789         56780000
3  yyyyyyyyyyyyyyy789         78920000
4  yyyyyyyyyyyyyyy789         55550000