MongoDB RecursiveFind с проекцией

#mongodb

#mongodb

Вопрос:

У меня есть рекурсивная структура документа mongo, которая представляет собой иерархию «событий»:

 {   
    "request": "open|casetype", 
    "datetime": ISODate("2013-10-10T23:06:37.000Z"),
    "type": "pageload",
    "totaltime": 4000,
    "serverTime": 3000,
    "clientTime": 1000,
    "cpuTime" : "2000",
    "externalTime" : "500",
    "dbTime": "500", 

    "events":[
        {

            "type": "interaction",  
            "label": "interaction 1 - load",
            "time": "2000",
            "events" :[{
                "type": "method",  
                "label": "test method",
                "time": "1000",
                "events" :[
                    { "type": "dbquery", "sql": "select * from x", "time":"100"},
                    { "type": "connector", "connectorName": "Google", "time":"200"},
                    {
                        "type":"method",
                        "label":"another method",
                        "time":800,
                        "events":[
                            { "type": "dbquery", "sql": "select * from y", "time":"500"}
                        ]
                    }

                ]
            } ]
        }
    ]
}
 

Я хочу иметь возможность запускать запросы, которые находят и группируют различные типы событий. Например. Найти среднее время для данного dbquery или соединителя. Группируйте соединители по имени соединителя и подсчитывайте количество и т. Д.

Используя следующее (которое я нашел в StackOverflow) Я могу найти полные документы с типом =»X» в любом месте моего документа:

 db.pageloads.find(
  function () {
    var findKey = "type",
        findVal = "dbquery";

    function inspectObj(doc) {
      return Object.keys(doc).some(function(key) {
        if ( typeof(doc[key]) == "object" ) {
          return inspectObj(doc[key]);
        } else {
          return ( key == findKey amp;amp; doc[key] == findVal );
        }
      });
    }
    return inspectObj(this);
  }
)
 

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

Ответ №1:

В качестве варианта вы можете использовать пользовательскую функцию запроса.

Это пример нахождения среднего времени. Поместите приведенный ниже код в query.js файл:

 (function() {
    var cursor = db.pageloads.find(),
        result = [],
        findKey = 'type',
        findVal = 'dbquery',
        findParam = 'time';

    function average(data) {
        return sum(data) / data.length;
    }

    function sum(data) {
        var sum = 0;
        for (var i = 0; i < data.length; i  ) {
            sum  = parseInt(data[i], 10);
        }

        return sum;
    }

    while (cursor.hasNext()) {
        function filter(current) {
            for (var item in current) {
                if (typeof(current[item].events) === 'object') {
                    var next = current[item].events;
                    delete current[item].events;
                }
                if (typeof(current[item][findKey]) !== 'undefined' amp;amp; current[item][findKey] === findVal) {
                    result.push(current[item][findParam]);
                }
            }
            if (typeof(next) !== 'undefined') {
                filter(next);
            };
        }
        filter(cursor.next().events);
    };

    return average(result); // You can use sum or other function
})();
 

и запустите:

 mongo your_database < query.js --quiet
 

Вы можете настроить логику агрегирования, изменив параметры и внутренние подфункции.

Конечно, это не чистое решение, но это лучше, чем ничего.