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

В MongoDB mapreduce, как я могу сгладить объект значений?

Я пытаюсь использовать MongoDB для анализа файлов журнала Apache. Я создал коллекцию receipts из журналов доступа Apache. Вот краткое описание моих моделей:

db.receipts.findOne()
{
    "_id" : ObjectId("4e57908c7a044a30dc03a888"),
    "path" : "/videos/1/show_invisibles.m4v",
    "issued_at" : ISODate("2011-04-08T00:00:00Z"),
    "status" : "200"
}

Я написал функцию MapReduce, которая группирует все данные в поле даты issued_at. Он суммирует общее количество запросов и предоставляет разбивку количества запросов для каждого уникального пути. Вот пример того, как выглядит вывод:

db.daily_hits_by_path.findOne()
{
    "_id" : ISODate("2011-04-08T00:00:00Z"),
    "value" : {
        "count" : 6,
        "paths" : {
            "/videos/1/show_invisibles.m4v" : {
                "count" : 2
            },
            "/videos/1/show_invisibles.ogv" : {
                "count" : 3
            },
            "/videos/6/buffers_listed_and_hidden.ogv" : {
                "count" : 1
            }
        }
    }
}

Как я могу сделать вывод похожим на это:

{
    "_id" : ISODate("2011-04-08T00:00:00Z"),
    "count" : 6,
    "paths" : {
        "/videos/1/show_invisibles.m4v" : {
            "count" : 2
        },
        "/videos/1/show_invisibles.ogv" : {
            "count" : 3
        },
        "/videos/6/buffers_listed_and_hidden.ogv" : {
            "count" : 1
        }
    }
}
4b9b3361

Ответ 2

Взяв лучшее из предыдущих ответов:

db.items.find().forEach(function(item) {
    db.items.update({_id: item._id}, item.value);
});

От http://docs.mongodb.org/manual/core/update/#replace-existing-document-with-new-document
"Если аргумент update содержит только пары полей и значений, метод update() заменяет существующий документ документом в аргументе update, за исключением поля _id."

Поэтому вам не нужно ни $unset value, ни список каждого поля.

Ответ 3

AFAIK, по дизайну карта Mongo уменьшит выплевывает результаты в "кортежах значений", и я не видел ничего, что будет настраивать этот "выходной формат". Может быть использован метод finalize().

Вы можете попробовать выполнить пост-процесс, который изменит данные с помощью

results.find({}).forEach( function(result) {
  results.update({_id: result._id}, {count: result.value.count, paths: result.value.paths})
});

Да, это выглядит уродливо. Я знаю.

Ответ 4

Вы можете сделать код Дэн с помощью ссылки коллекции:

    function clean(collection) { 
      collection.find().forEach( function(result) {
      var value = result.value;
      delete value._id;     
      collection.update({_id: result._id}, value);     
      collection.update({_id: result.id}, {$unset: {value: 1}} ) } )};

Ответ 5

Аналогичный подход к @ljonas, но не требуется жестко кодировать поля документа:

db.results.find().forEach( function(result) {
    var value = result.value;
    delete value._id;
    db.results.update({_id: result._id}, value);
    db.results.update({_id: result.id}, {$unset: {value: 1}} )
} );

Ответ 6

Все предлагаемые решения далеки от оптимальных. Самое быстрое, что вы можете сделать до сих пор, это что-то вроде:

var flattenMRCollection=function(dbName,collectionName) {
    var collection=db.getSiblingDB(dbName)[collectionName];

    var i=0;
    var bulk=collection.initializeUnorderedBulkOp();
    collection.find({ value: { $exists: true } }).addOption(16).forEach(function(result) {
        print((++i));
        //collection.update({_id: result._id},result.value);

        bulk.find({_id: result._id}).replaceOne(result.value);

        if(i%1000==0)
        {
            print("Executing bulk...");
            bulk.execute();
            bulk=collection.initializeUnorderedBulkOp();
        }
    });
    bulk.execute();
};

Затем назовите его: flattenMRCollection("MyDB","MyMRCollection")

Это ПУТЬ быстрее, чем выполнение последовательных обновлений.

Ответ 7

Во время эксперимента с ответом Винсента я обнаружил пару проблем. В основном, если вы выполняете обновления в цикле foreach, это переместит документ в конец коллекции, и курсор снова достигнет этого документа (example). Это можно обойти, если используется $snapshot. Следовательно, я предоставляю пример Java ниже.

final List<WriteModel<Document>> bulkUpdate = new ArrayList<>();

// You should enable $snapshot if performing updates within foreach
collection.find(new Document().append("$query", new Document()).append("$snapshot", true)).forEach(new Block<Document>() {
    @Override
    public void apply(final Document document) {
        // Note that I used incrementing long values for '_id'. Change to String if
        // you used string '_id's
        long docId = document.getLong("_id");
        Document subDoc = (Document)document.get("value");
        WriteModel<Document> m = new ReplaceOneModel<>(new Document().append("_id", docId), subDoc);
        bulkUpdate.add(m);

        // If you used non-incrementing '_id's, then you need to use a final object with a counter.
        if(docId % 1000 == 0 && !bulkUpdate.isEmpty()) {
            collection.bulkWrite(bulkUpdate);
            bulkUpdate.removeAll(bulkUpdate);
        }
    }
});
// Fixing bug related to Vincent answer.
if(!bulkUpdate.isEmpty()) {
    collection.bulkWrite(bulkUpdate);
    bulkUpdate.removeAll(bulkUpdate);
}

Примечание. Этот фрагмент занимает в среднем 7,4 секунды, чтобы выполнить на моей машине 100 тыс. записей и 14 атрибутов (набор данных IMDB). Без пакетной обработки это занимает в среднем 25,2 секунды.