Есть ли способ спроецировать тип поля

Предположим, у нас есть что-то вроде следующего документа, но мы хотим вернуть только поля, содержащие числовую информацию:

{
    "_id" : ObjectId("52fac254f40ff600c10e56d4"),
    "name" : "Mikey",
    "list" : [ 1, 2, 3, 4, 5 ],
    "people" : [ "Fred", "Barney", "Wilma", "Betty" ],
    "status" : false,
    "created" : ISODate("2014-02-12T00:37:40.534Z"),
    "views" : 5
}

Теперь я знаю, что мы можем запрашивать поля, соответствующие определенному типу, используя Оператор $ type. Но я еще не нашел способ $ проецировать это как значение поля. Итак, если мы посмотрим на документ в «развернутом» виде, вы увидите следующее:

{
    "_id" : ObjectId("52fac254f40ff600c10e56d4"),
    "name" : 2,
    "list" : 16,
    "people" : 2
    "status" : 8,
    "created" : 9,
    "views" : 16
}

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

{
    "_id" : ObjectId("52fac254f40ff600c10e56d4"),
    "list" : [ 1, 2, 3, 4, 5 ],
    "views" : 5
}

Есть ли у кого-нибудь подход, чтобы справиться с этим.


person Neil Lunn    schedule 12.02.2014    source источник


Ответы (1)


Есть несколько проблем, которые делают это непрактичным:

  1. Поскольку запрос является параметром, отличным от возможности выполнять проекцию, это невозможно только с помощью одного запроса, поскольку результаты запроса не могут повлиять на проекцию.
  2. Поскольку в структуре агрегации нет возможности перебирать поля и проверять тип, это тоже не вариант.

При этом есть немного странный способ использования Map-Reduce, который действительно дает аналогичные ответы, хотя в выводе в стиле Map-Reduce это не очень хорошо:

map = function() {
    function isNumber(n) {
      return !isNaN(parseFloat(n)) && isFinite(n);
    }

    var numerics = [];
    for(var fn in this) {
        if (isNumber(this[fn])) {
            numerics.push({f: fn, v: this[fn]});
        }
        if (Array.isArray(this[fn])) {
            // example ... more complex logic needed
            if(isNumber(this[fn][0])) {
                numerics.push({f: fn, v: this[fn]});
            }
        }
    }
    emit(this._id, { n: numerics });
};

reduce = function(key, values) {
  return values;  
};

Это не все, но результаты похожи на то, что вы хотели:

"_id" : ObjectId("52fac254f40ff600c10e56d4"),
 "value" : {
         "n" : [
                 {
                         "f" : "list",
                         "v" : [
                                 1,
                                 2,
                                 3,
                                 4,
                                 5
                         ]
                 },
                 {
                         "f" : "views",
                         "v" : 5
                 }
         ]
 }

Карта просто просматривает каждое свойство и решает, похоже ли оно на число ... и если да, добавляя в массив, который будет сохранен как объект, чтобы механизм уменьшения карты не подавлялся выводом массива. В примере кода я сохранил простоту - вы наверняка могли бы улучшить логику проверки чисел и массивов. :)

Конечно, это не похоже на find или агрегацию, но, поскольку MongoDB не был разработан с учетом этого, это может потребоваться, если вам действительно нужна эта функциональность.

person WiredPrairie    schedule 12.02.2014
comment
да. Я тоже так думал. Это произошло из-за другого вопроса, который в основном хотел фильтровать поля таким образом. Может быть, оператор $ type в агрегации был бы хорош, во многом аналогично операторам даты, но я действительно не уверен в широкой полезности такой вещи. - person Neil Lunn; 12.02.2014