Подтвердить что ты не робот

$ развернуть объект в структуре агрегации

В структуре агрегации MongoDB я надеялся использовать оператор $unwind для объекта (т.е. коллекцию JSON). Не похоже, что это возможно, есть ли обходной путь? Планируется ли это реализовать?

Например, возьмите коллекцию статей из документации агрегации . Предположим, что существует дополнительное поле "рейтинги", которое представляет собой карту пользователя → рейтинг. Не могли бы вы рассчитать средний рейтинг для каждого пользователя?

Кроме этого, я вполне доволен структурой агрегации.

Обновление: здесь упрощенная версия коллекции JSON для каждого запроса. Я храню геномные данные. Я не могу сделать генотипы массивом, потому что наиболее распространенный поиск - получить генотип для случайного человека.

variants: [

    {
        name: 'variant1', 
        genotypes: {

            person1: 2,
            person2: 5,
            person3: 7,

        }
    }, 

    {
        name: 'variant2', 
        genotypes: {

            person1: 3,
            person2: 3,
            person3: 2,

        }
    }

]
4b9b3361

Ответ 1

Невозможно выполнить тип вычислений, который вы описываете с помощью структуры агрегации, и не, потому что для не-массивов нет метода $unwind. Даже если объекты person: value были документами в массиве, $unwind не помогло бы.

Функциональность "group by" (будь то в MongoDB или в любой реляционной базе данных) выполняется по значению поля или столбца. Мы группируем по значению поля и sum/average/etc в зависимости от значения другого поля.

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

{ title : title of article", ...
  ratings: [
         { voter: "user1", score: 5 },
         { voter: "user2", score: 8 },
         { voter: "user3", score: 7 }
  ]
}

Теперь вы можете агрегировать это с помощью:

[ {$unwind: "$ratings"},
  {$group : {_id : "$ratings.voter", averageScore: {$avg:"$ratings.score"} } } 
]

Но этот пример, структурированный, как вы его описываете, будет выглядеть так:

{ title : title of article", ...
  ratings: {
         user1: 5,
         user2: 8,
         user3: 7
  }
}

или даже это:

{ title : title of article", ...
  ratings: [
         { user1: 5 },
         { user2: 8 },
         { user3: 7 }
  ]
}

Даже если бы вы могли $unwind, здесь нечего собирать. Если вы не знаете полный список всех возможных ключей (пользователей), вы не можете много сделать с этим. [*]

Аналогичная реляционная схема БД к тому, что у вас есть:

CREATE TABLE T (
   user1: integer,
   user2: integer,
   user3: integer
   ...
);

Это не то, что было бы сделано, вместо этого мы сделали бы это:

CREATE TABLE T (
   username: varchar(32),
   score: integer
);

и теперь мы объединяем SQL:

select username, avg(score) from T group by username;

Существует запрос расширения для MongoDB, который может позволить вам сделать это в структуре агрегации в будущем - возможность проецировать значения на клавиши для наоборот. Между тем всегда есть карта/сокращение.

[*] Существует сложный способ сделать это, если вы знаете все уникальные ключи (вы можете найти все уникальные ключи со способом, похожим на this), но если вы знаете все ключи, которые вы можете, просто запустите последовательность запросов формы db.articles.find({"ratings.user1":{$exists:true}},{_id:0,"ratings.user1":1}) для каждого пользователяX, который вернет все свои рейтинги, и вы можете суммировать и усреднять их достаточно просто, а не делать очень сложную проекцию, структура агрегации будет требуется.

Ответ 3

Это старый вопрос, но я столкнулся с проблемой информации через проб и ошибок, которые люди могут найти полезными.

На самом деле можно отключить фиктивное значение, обманув парсер таким образом:

db.Opportunity.aggregate(
  { $project: {
        Field1: 1, Field2: 1, Field3: 1,
        DummyUnwindField: { $ifNull: [null, [1.0]] }
    }
  },
  { $unwind: "$DummyUnwindField" }
);

Это приведет к получению 1 строки на документ независимо от того, существует ли это значение. Вы можете быть в состоянии возиться с этим, чтобы генерировать результаты, которые вы хотите. Я надеялся объединить это с несколькими $unwinds (вроде emit() в map/reduce), но, увы, последние $unwind выигрывают или объединяются как пересечение, а не объединение, что делает невозможным достижение результатов я искал. Я с грустью разочарован функциональностью общей структуры, поскольку она не подходит для одного варианта использования, на который я надеялся использовать его (и кажется странным, что многие вопросы по StackOverflow в этой области задают) - упорядочение результатов на основе соответствия ставка. Улучшение плохой карты снижает производительность, что сделало бы эту функцию ненужной.

Ответ 4

Это то, что я нашел и расширил.

Позволяет создавать экспериментальную базу данных в mongo

db.copyDatabase('livedb' , 'experimentdb')

Теперь используйте экспериментdb и преобразуйте массив в объект в вашем эксперименте.

db.getCollection('experimentcollection').find({}).forEach(function(e){
    if(e.store){
        e.ratings = [e.ratings]; //Objects name to be converted to array eg:ratings
        db.experimentcollection.save(e);
    }
})

Некорректный js-код для преобразования json в плоский объект

var flatArray = [];

var data = db.experimentcollection.find().toArray();

for (var index = 0; index < data.length; index++) {

  var flatObject = {};

  for (var prop in data[index]) {

    var value = data[index][prop];

    if (Array.isArray(value) && prop === 'ratings') {
      for (var i = 0; i < value.length; i++) {
        for (var inProp in value[i]) {
          flatObject[inProp] = value[i][inProp];
        }
      }
    }else{
        flatObject[prop] = value;
    }
  }
  flatArray.push(flatObject);
}

printjson(flatArray);