2015-05-25 4 views
3

В MongoDb моего сайта хранится отдельный документ для каждого пользователя. Каждый пользователь будет отвечать на несколько анкетных анкет во время своего визита. Формы хранятся в массиве, но поскольку документы не пересекаются, достаточно одного, единственного документа. Для анализа я хочу составить плоскую таблицу всех ответов по всем формам.Как объединить гетерогенный массив в один документ в MongoDb?

Рассмотрим следующую структуру данных:

{ 
    "USER_SESSION_ID": 456, 
    "forms": [ 
     { 
      "age": 21, 
      "gender": "m" 
     }, 
     { 
      "job": "Student", 
      "years_on_job": "12" 
     }, 
     { 
      "Hobby": "Hiking", 
      "Twitter": "@my_account" 
     } 
    ] 
}, 
{ 
    "USER_SESSION_ID": 678, 
    "forms": [ 
     { 
      "age": 46, 
      "gender": "f" 
     }, 
     { 
      "job": "Bodyguard", 
      "years_on_job": "2" 
     }, 
     { 
      "Hobby": "Skiing", 
      "Twitter": "@bodyguard" 
     } 
    ] 
} 

форм-документы все выглядят по-разному и не имеют конфликтующие поля, поэтому я хотел бы объединить их, получая табличный, плоская структура, как это:

{ 'USER_SESSION_ID': 456, 'age': 21, 'gender': 'm', 'job': 'Student', ... 'Twitter': '@my_account' } 
{ 'USER_SESSION_ID': 678, 'age': 46, 'gender': 'f', 'job': 'Bodyguard', ... 'Twitter': '@bodyguard' } 

Использование Python, это общая ежу понятно, глядя, как это:

for session in sessions:   # Iterate all docs 
    for form in session['forms']: # Iterate all children 
     session.update(form)  # Integrate to parent doc 
    del session['forms']   # Remove nested child 

В MongoDb я нахожу это довольно трудным для достижения. Я пытаюсь использовать агрегатный трубопровод, который, как я полагаю, должен быть подходящим для этого.

До сих пор я помог себе отвинтив мою структуру данных, как это:

db.sessions.aggregate(
    { 
     '$unwind': '$forms' 
    }, 
    { 
     '$project': { 
      'USER_SESSION_ID': true, 
      'forms': true 
     } 
    }, 
    { 
     '$group': { 
      '_id': '$USER_SESSION_ID', 
      'forms': <magic?!> 
     } 
    } 
) 

В разматывания этапа я создаю документ с данными родителя для каждого ребенка. Это должно быть примерно эквивалентно циклу double-for в моем коде python. Однако то, что я чувствую, что я концептуально отсутствует, является аккумулятором «Слияние» при группировке. В python это делается с dict.update(), в underscore.js это будет _.extend(destination, *sources).

Как достичь этого в MongoDB?

ответ

1

Я играл с совокупный конвейер на века, пока я не дал команде mapReduce попробовать. Это то, что я придумал:

db.sessions.mapReduce(
    function() { 
     var merged = {}; 
     this.forms.forEach(function (form) { 
      for(var key in form) { 
       merged[key] = form[key]; 
      } 
     }); 
     emit(this.USER_SESSION_ID, merged); 
    }, 
    function() {}, 
    { 
     "out": {"inline": true} 
    } 
) 

Стадия отображения сочетает в себе элементы, так как нет ни одного $ оператора слияния доступен в качестве шага агрегации трубопровода. Требуется пустая функция reduce. out либо записывает в другую коллекцию, либо просто возвращает результат (inline, что я здесь делаю).

Он очень похож на метод, который показал chridam в его ответе, но на самом деле использует проекцию. Его версия намного ближе к тому, как работает мой код на Python, но для того, что я пытаюсь сделать, это хорошо и не меняет исходный набор. Обратите внимание, что код python делает это, но не чередование коллекции ввода весьма полезно!

1

Попробуйте следующее, которая использует вложенные forEach() вызов методов на find() курсора перебрать результат курсора и получить ключи объектов для элементов в пределах forms массива с помощью Object.keys():

db.sessions.find().forEach(function (doc){ 
    doc.forms.forEach(function (e){ 
     var keys = Object.keys(e); 
     keys.forEach(function(key){ doc[key] = e[key] }); 
    }); 
    delete doc.forms; 
    db.sessions.save(doc); 
}); 
+0

Спасибо за ваш ответ! Мне удалось исправить мою проблему, используя функцию mapReduce, которая позволяет продолжить обработку! Мне нравится, как близко к моему коду Python, но я не знал, что это возможно! Большое спасибо! – cessor

+0

@cessor Не беспокойтесь, рад, что вы нашли решение. – chridam

Смежные вопросы