Вопрос по mongodb, aggregation-framework – $ раскрутить объект в структуре агрегации

28

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

Например, взять коллекцию статей из агрегациидокументация , Предположим, есть дополнительное поле «Рейтинги». это карта от пользователя - & gt; рейтинг. Не могли бы вы рассчитать средний рейтинг для каждого пользователя?

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

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

variants: [

    {
        name: 'variant1', 
        genotypes: {

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

        }
    }, 

    {
        name: 'variant2', 
        genotypes: {

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

        }
    }

]
Опубликуйте свою коллекцию JSON. Sergio Tulentsev
Учитывая, что это ваша коллекция (?), Для какой агрегации вы хотите выполнить, для чего вам нужно $ unwind? Asya Kamsky
это безопасное предположение, что число "человек" в генотипах субдокумент переменный? Я не понимаю, почему вы не можете сделать генотипы массивом, кстати. Asya Kamsky
В настоящее время$unwind Оператор работает только с массивами. Пожалуйста, будьте очень конкретны в отношении того, что вы пытаетесь сделать, может быть другой способ достижения того же результата. Tyler Brock
Почему использование массива в качестве типа данных для генотипов мешает вам выбрать случайного человека из него? Tyler Brock

Ваш Ответ

4   ответа
0

Это то, что я нашел & amp; продлен.

Lets create experimental database in mongo

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

Now Use experimentdb & convert Array to object in your experimentcollection

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);
    }
})

Some nerdy js code to convert json to flat object

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);
6

вы можете преобразовать объект в массив, используя $ objectToArray

Увидеть: https://docs.mongodb.com/manual/reference/operator/aggregation/objectToArray/

Эта функциональность начиная с 3.6
@chridam я обновил пост
29

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

& Quot; Сгруппировать по & quot; функциональность (будь то в MongoDB или в любой реляционной базе данных) выполняется для значения поля или столбца. Мы группируем по значению поля и сумме / среднему значению / и т. Д. На основе значения другого поля.

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

{ 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, который может позволить вам сделать это в структуре агрегации в будущем - возможность проецировать значения в ключи и наоборот. Между тем, всегда есть карта / уменьшить.

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

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceededjira.mongodb.org/browse/SERVER-5947Error: User Rate Limit ExceedednotError: User Rate Limit Exceeded
0

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

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

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

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

Похожие вопросы