2011-08-31 44 views
32

Tôi đang cố gắng sử dụng MongoDB để phân tích các tệp nhật ký Apache. Tôi đã tạo một bộ sưu tập receipts từ nhật ký truy cập Apache. Dưới đây là một bản tóm tắt tóm lược về những gì mô hình của tôi trông giống như:Trong Mapouce MongoDB, làm thế nào tôi có thể làm phẳng đối tượng giá trị?

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

Tôi đã viết một MapReduce function rằng nhóm tất cả các dữ liệu bằng cách lĩnh vực issued_at ngày. Nó tóm tắt tổng số yêu cầu và cung cấp phân tích về số lượng yêu cầu cho từng đường dẫn duy nhất. Dưới đây là một ví dụ về những gì đầu ra trông giống như:

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

Làm thế nào tôi có thể làm cho giao diện đầu ra như thế này thay vì:

{ 
    "_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 
     } 
    } 
} 

Trả lời

12

Hiện không thể, nhưng tôi khuyên bạn nên bỏ phiếu cho trường hợp này: https://jira.mongodb.org/browse/SERVER-2517.

+0

Đây là câu trả lời đúng, vì vậy hãy bỏ phiếu cho trường hợp này và sau đó sử dụng http://stackoverflow.com/a/18124090/1402121 làm giải pháp của bạn – dmo

4

AFAIK, do thiết kế đồ Mongo của giảm sẽ nhổ kết quả ra trong "các bộ giá trị "và tôi chưa thấy bất cứ điều gì sẽ cấu hình" định dạng đầu ra "đó. Có thể sử dụng phương thức finalize().

Bạn có thể thử chạy một hậu quá trình đó sẽ định hình lại dữ liệu sử dụng

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

Yep, trông xấu xí. Tôi biết.

+0

không có cách nào để sửa đổi trực tiếp đối tượng/tài liệu 'result'? –

3

Một cách tiếp cận tương tự như của @ljonas nhưng không cần phải hardcode lĩnh vực tài liệu:

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

Bạn có thể làm mã của Dan với một tài liệu tham khảo bộ sưu tập:

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

Lấy tốt nhất từ ​​câu trả lời trước và nhận xét:

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

Từ http://docs.mongodb.org/manual/core/update/#replace-existing-document-with-new-document
"Nếu đối số update chỉ chứa cặp trường và giá trị, phương thức update() sẽ thay thế tài liệu hiện có bằng tài liệu trong đối số update, ngoại trừ trường _id."

Vì vậy, bạn không cần phải $unset value cũng như không liệt kê từng trường.

Từ https://docs.mongodb.com/manual/core/read-isolation-consistency-recency/#cursor-snapshot "Con trỏ MongoDB có thể trả lại cùng một tài liệu nhiều lần trong một số trường hợp. ... sử dụng chỉ mục duy nhất trên trường này hoặc các trường này để truy vấn trả về từng tài liệu không quá một lần. hint() để buộc truy vấn sử dụng chỉ mục đó một cách rõ ràng. "

+0

Điều này dẫn đến các điều kiện chủng tộc nghiêm trọng. –

+0

@DerekBrown, cảm ơn, tôi đã khắc phục nó ngay bây giờ –

+0

không có bạn đã không ... –

3

Tất cả các giải pháp được đề xuất đều tối ưu. Nhanh nhất bạn có thể làm cho đến nay là một cái gì đó như:

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

Sau đó gọi nó là: flattenMRCollection("MyDB","MyMRCollection")

Đây là cách nhanh hơn so với thực hiện cập nhật liên tục.

+3

NB: đây là phiên bản mới trong MongoDB 2.6 – Vincent

0

Trong khi thử nghiệm với câu trả lời của Vincent, tôi đã tìm thấy một vài vấn đề.Về cơ bản, nếu bạn thực hiện cập nhật trong vòng lặp foreach, thao tác này sẽ di chuyển tài liệu đến cuối bộ sưu tập và con trỏ sẽ tiếp cận lại tài liệu đó (example). Điều này có thể bị phá vỡ nếu sử dụng $snapshot. Do đó, tôi đang cung cấp một ví dụ Java bên dưới.

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

Lưu ý: Đoạn mã này mất trung bình 7,4 giây để thực thi trên máy với 100k bản ghi và 14 thuộc tính (bộ dữ liệu IMDB). Nếu không có hàng loạt, sẽ mất trung bình 25,2 giây.

Các vấn đề liên quan