2010-08-27 16 views
16

Tôi có một số tài liệu 25k (4 GB trong dữ liệu thô) của dữ liệu mà tôi muốn thực hiện một vài thao tác javascript để giúp người dùng cuối của tôi dễ tiếp cận hơn (R), và muốn sắp xếp "kiểm soát phiên bản" những thay đổi này bằng cách thêm bộ sưu tập mới cho mỗi thay đổi, nhưng tôi không thể tìm ra cách map/reduce mà không có reduce. Tôi muốn có một bản đồ tài liệu một-một, tôi bắt đầu với 25.356 tài liệu trong số collection_1 và tôi muốn kết thúc với 25.356 tài liệu trong số collection_2.mongoDB map/reduce trừ bớt số

tôi có thể hack nó với điều này:

var reducer = function(key, value_array) { 
    return {key: value_array[0]} 
} 

Và sau đó gọi nó thích:

db.flat_1.mapReduce(mapper, reducer, {keeptemp: true, out: 'flat_2'}) 

(mapper của tôi chỉ các cuộc gọi phát ra một lần, với một chuỗi như là đối số đầu tiên và văn bản cuối cùng thứ hai. Đó là tập hợp các đối số thứ hai mà tôi thực sự muốn.)

Nhưng điều đó có vẻ khó xử và tôi không biết tại sao nó hoạt động, kể từ cuộc gọi emit của tôi di tích trong bản đồ của tôi không tương đương với đối số trả về của số reducer của tôi. Ngoài ra, tôi kết thúc bằng một tài liệu như

{ 
    "_id": "0xWH4T3V3R", 
    "value": { 
     "key": { 
      "finally": ["here"], 
      "thisIsWhatIWanted": ["Yes!"] 
     } 
    } 
} 

có vẻ không cần thiết.

Ngoài ra, con trỏ thực hiện chèn riêng không phải là một phần mười nhanh như mapReduce. Tôi không biết MongoDB đủ tốt để đánh giá nó, nhưng tôi đoán nó sẽ chậm hơn khoảng 50x. Có cách nào để chạy qua một con trỏ song song không? Tôi không quan tâm liệu các tài liệu trong số collection_2 của tôi có theo thứ tự khác với các tài liệu trong số collection_1 hay không.

+0

Lý do nó hoạt động là bởi vì phát và giảm tốc của bạn gọi * là * giống nhau. Vì bạn sử dụng giá trị [0] làm đầu ra của bộ giảm tốc của bạn nên nó phải giống chính xác vì bạn chưa thay đổi nó (nó chỉ truyền qua bộ giảm tốc của bạn). – null

Trả lời

3

Nhưng điều đó dường như vụng về và tôi không biết tại sao nó thậm chí làm việc, kể từ emit đối số cuộc gọi của tôi trong mapper của tôi không phải là tương đương với sự trở lại đối số của tôi reducer.

Chúng tương đương nhau. Hàm reduce nhận một mảng giá trị T và phải trả lại một giá trị trong cùng định dạng T. Định dạng của T được xác định bởi chức năng bản đồ của bạn. Hàm reduce của bạn chỉ trả về mục đầu tiên trong mảng giá trị, sẽ luôn là loại T. Đó là lý do tại sao nó hoạt động :)

Dường như bạn đang đi đúng hướng. Tôi đã thực hiện một số thử nghiệm và có vẻ như bạn không thể thực hiện một hàm db.collection.save() từ chức năng bản đồ, nhưng bạn có thể thực hiện việc này từ chức năng giảm. Chức năng bản đồ của bạn chỉ cần xây dựng định dạng tài liệu mà bạn cần:

function map() { 
    emit(this._id, { _id: this.id, heading: this.title, body: this.content }); 
} 

Chức năng bản đồ sẽ sử dụng lại ID của tài liệu gốc. Điều này sẽ ngăn chặn mọi bước giảm lại, vì không có giá trị nào sẽ chia sẻ cùng một khóa.

Hàm giảm có thể đơn giản trả về null. Nhưng ngoài ra, bạn có thể ghi giá trị vào một bộ sưu tập riêng biệt.

function reduce(key, values) { 
    db.result.save(values[0]); 

    return null; 
} 

Bây giờ db.result phải chứa tài liệu được chuyển đổi mà không có thêm bất kỳ tiếng ồn bản đồ nào khác mà bạn có trong bộ sưu tập tạm thời. Tôi đã không thực sự thử nghiệm điều này trên một lượng lớn dữ liệu, nhưng cách tiếp cận này nên tận dụng lợi thế của việc thực hiện song song các chức năng giảm bản đồ.

+2

Bằng cách này đã 523 và kết thúc với một bộ sưu tập chính xác như tôi muốn nó, trong khi cách hackish tôi mô tả trong câu hỏi mất 319s. Thật không may là tôi không thể chỉ gọi 'db.coll.mapReduce (myMapperFunc, null, {' out ':' output '}) '. Tôi nghĩ rằng giảm có thể lưu trữ hàng loạt/chèn toàn bộ các mục; Tôi nghĩ rằng nút cổ chai ở đây là 'save()' được gọi trong mỗi lần giảm. – chbrown

+1

@chbrown: Có, 'save()' được thực hiện hai lần cho mỗi tài liệu; tiêu chuẩn giảm-tiết kiệm cho bộ sưu tập tạm thời, và rõ ràng tiết kiệm cho một bộ sưu tập riêng biệt. Chỉ cần tò mò, là giải pháp này thực sự nhanh hơn so với sử dụng một con trỏ duy nhất? –

+0

Xin chào tất cả, chúng tôi có một vấn đề tương tự để xử lý các tập dữ liệu lớn và kể từ khi ghép nối mảng và rtruning tài liệu lớn trong giảm không làm việc, chúng tôi đã theo các appaorach được đề cập ở trên tiết kiệm tài liệu trong bộ sưu tập riêng biệt và trở về null từ giảm. Nó hoạt động tốt nhưng db bị treo cổ khi chúng ta thực hiện bất kỳ thao tác nào khác trong khi chạy mapreduce. là có bất kỳ thẩm định tốt hơn cho cùng một. – MRK

6

Khi sử dụng bản đồ/giảm bạn sẽ luôn luôn kết thúc với

{ "value" : { <reduced data> } } 

Để loại bỏ các value chính mà bạn sẽ phải sử dụng một chức năng finalize.

Dưới đây là cách đơn giản nhất bạn có thể làm để sao chép dữ liệu từ một bộ sưu tập khác:

map = function() { emit(this._id, this); } 
reduce = function(key, values) { return values[0]; } 
finalize = function(key, value) { db.collection_2.insert(value); } 

Sau đó, khi bạn sẽ chạy như bình thường:

db.collection_1.mapReduce(map, reduce, { finalize: finalize }); 
+4

"Chức năng hoàn thiện không nên truy cập cơ sở dữ liệu vì bất kỳ lý do gì." - [Tài liệu MongoDB chính thức] (http://docs.mongodb.org/manual/reference/command/mapReduce/#mapreduce-finalize-cmd) – bloudermilk

+0

Đúng .. nhưng dù sao cũng hữu ích để có thể làm như vậy. –

+0

Đây là một nút cổ chai hiệu suất hoàn chỉnh và chống lại Map-Reduce !!! Làm ơn, đừng làm thế. –

0

tôi phải đối mặt với tình huống tương tự. Tôi đã có thể thực hiện điều này thông qua truy vấn Mongo và chiếu. thấy Mongo Query

1

Khi bạn có quyền truy cập vào các vỏ Mongo, nó chấp nhận một số lệnh Javascript và sau đó nó đơn giản hơn:

map = function(item){ 
     db.result.insert(item); 
} 

db.collection.find().forEach(map); 
Các vấn đề liên quan