programing

MongoDB mapreduce에서 values 객체를 평탄화하려면 어떻게 해야 합니까?

padding 2023. 7. 2. 19:05
반응형

MongoDB mapreduce에서 values 객체를 평탄화하려면 어떻게 해야 합니까?

MongoDB를 사용하여 Apache 로그 파일을 분석하려고 합니다.다음을 만들었습니다.receiptsApache 액세스 로그에서 수집합니다.다음은 제 모델의 개요입니다.

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

현재는 불가능하지만, 저는 이 경우에 투표할 것을 제안합니다: https://jira.mongodb.org/browse/SERVER-2517 .

이전 답변과 의견을 최대한 활용합니다.

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

http://docs.mongodb.org/manual/core/update/ #에서 기존 버전을 새 버전으로 교체
"만약에update인수에는 필드 및 값 쌍만 포함됩니다.update()메소드는 기존 문서를 의 문서로 대체합니다.update인수, 제외_id"현장."

그래서 당신은 둘 다 할 필요가 없습니다.$unset value각 필드를 나열할 수 없습니다.

https://docs.mongodb.com/manual/core/read-isolation-consistency-recency/ #cisco-messages에서 "MongoDB 커서는 일부 상황에서 동일한 문서를 두 번 이상 반환할 수 있습니다. ...쿼리가 각 문서를 두 번 이상 반환하지 않도록 이 필드 또는 이 필드에 고유 색인을 사용합니다.hint()를 사용하여 쿼리가 해당 인덱스를 사용하도록 명시적으로 강제합니다."

AFAIK, 설계상 Mongo의 맵 축소는 "값 튜플"로 결과를 뱉을 것이고 저는 그 "출력 형식"을 구성할 어떤 것도 보지 못했습니다.final() 방법을 사용할 수 있습니다.

다음을 사용하여 데이터를 재구성하는 사후 프로세스를 실행할 수 있습니다.

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

네, 보기 흉해요.알고있어요.

수집 참조를 사용하여 Dan의 코드를 수행할 수 있습니다.

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

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

제안된 모든 솔루션은 최적의 솔루션과는 거리가 먼 솔루션입니다.지금까지 할 수 있는 가장 빠른 방법은 다음과 같습니다.

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")

이것은 순차적 업데이트보다 훨씬 빠릅니다.

빈센트의 대답을 실험하던 중 몇 가지 문제점을 발견했습니다.기본적으로 각 루프 내에서 업데이트를 수행하면 문서가 컬렉션의 끝으로 이동하고 커서가 해당 문서에 다시 도달합니다().$snapshot을 사용하면 이 문제를 피할 수 있습니다.따라서 아래에 자바의 예를 제공합니다.

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's answer.
if(!bulkUpdate.isEmpty()) {
    collection.bulkWrite(bulkUpdate);
    bulkUpdate.removeAll(bulkUpdate);
}

참고: 이 스니펫은 100k 레코드와 14개의 속성(IMDB 데이터 세트)을 가진 내 컴퓨터에서 실행하는 데 평균 7.4초가 걸립니다.배치하지 않으면 평균 25.2초가 소요됩니다.

언급URL : https://stackoverflow.com/questions/7257989/in-mongodb-mapreduce-how-can-i-flatten-the-values-object

반응형