Số lượng chọn MongoDB (x riêng biệt) trên một cột được lập chỉ mục - đếm các kết quả duy nhất cho các tập dữ liệu lớn


82

Tôi đã xem qua một số bài báo và ví dụ nhưng vẫn chưa tìm ra cách hiệu quả để thực hiện truy vấn SQL này trong MongoDB (nơi có hàng triệu hàng các tài liệu)

Nỗ lực đầu tiên

(ví dụ: từ câu hỏi gần như trùng lặp này - Mongo tương đương với SELECT DISTINCT của SQL? )

db.myCollection.distinct("myIndexedNonUniqueField").length

Rõ ràng là tôi gặp lỗi này vì tập dữ liệu của tôi rất lớn

Thu Aug 02 12:55:24 uncaught exception: distinct failed: {
        "errmsg" : "exception: distinct too big, 16mb cap",
        "code" : 10044,
        "ok" : 0
}

Nỗ lực thứ hai

Tôi quyết định thử làm một nhóm

db.myCollection.group({key: {myIndexedNonUniqueField: 1},
                initial: {count: 0}, 
                 reduce: function (obj, prev) { prev.count++;} } );

Nhưng thay vào đó, tôi nhận được thông báo lỗi này:

exception: group() can't handle more than 20000 unique keys

Nỗ lực thứ ba

Tôi chưa thử nhưng có một số đề xuất liên quan đến mapReduce

ví dụ

Cũng thế

Có vẻ như có một yêu cầu kéo trên GitHub sửa .distinctphương thức đề cập rằng nó chỉ trả về một số lượng, nhưng nó vẫn đang mở: https://github.com/mongodb/mongo/pull/34

Nhưng tại thời điểm này, tôi nghĩ rằng điều đáng hỏi ở đây là gì mới nhất về chủ đề này? Tôi có nên chuyển sang SQL hoặc một DB NoSQL khác để có số lượng riêng biệt không? hoặc là có một cách hiệu quả?

Cập nhật:

Nhận xét này trên tài liệu chính thức của MongoDB không được khuyến khích, điều này có chính xác không?

http://www.mongodb.org/display/DOCS/Aggregation#comment-430445808

Cập nhật2:

Có vẻ như Khung tổng hợp mới trả lời nhận xét trên ... (MongoDB 2.1 / 2.2 trở lên, có sẵn bản xem trước phát triển, không dành cho sản xuất)

http://docs.mongodb.org/manual/application/aggregation/


Tôi cho rằng bạn cần phải làm điều này thường xuyên nếu không hiệu suất sẽ không quan trọng lắm. Trong trường hợp đó, tôi sẽ lưu trữ các giá trị riêng biệt trong một bộ sưu tập riêng biệt được cập nhật khi bạn chèn một tài liệu mới thay vì cố gắng tạo một sự khác biệt trên một bộ sưu tập lớn như vậy. Đó là hoặc tôi sẽ đánh giá lại việc sử dụng MongoDb của mình và có thể chuyển sang thứ khác. Như bạn thấy, MongoDb hiện không giỏi những gì bạn đang cố gắng thực hiện.
Tim Gautier

@TimGautier cảm ơn, tôi e rằng phải mất hàng giờ để chèn tất cả các giá trị đó, và tôi nên nghĩ đến điều đó trước đó :) Tôi nghĩ tôi sẽ dành thời gian bây giờ để chèn nó vào MySQL cho những thống kê đó ...
Eran Medan

Bạn cũng có thể thực hiện MR gia tăng về cơ bản mô phỏng lập chỉ mục delta của dữ liệu tổng hợp. Ý tôi là nó phụ thuộc vào thời điểm bạn cần kết quả như những gì bạn sử dụng. Tôi có thể tưởng tượng rằng MySQL sẽ nhận được rất nhiều IO và những gì không làm được điều này (tôi có thể giết một máy chủ nhỏ với việc phân biệt chỉ 100k tài liệu nội tuyến trên một chỉ mục) nhưng tôi cho rằng nó linh hoạt hơn trong việc truy vấn loại nội dung này vẫn .
Sammaye

Tôi không đồng ý rằng mongo không giỏi trong việc này. Loại này nếu thứ mà Mongo vượt trội.
superluminary

1
Thật không may, người kiểm duyệt đã xóa câu trả lời của tôi mà tôi cũng đã đăng trên câu hỏi trùng lặp. Tôi không thể xóa nó ở đó và đăng lại ở đây, do đó liên kết: stackoverflow.com/a/33418582/226895
chuyên gia

Câu trả lời:


75

1) Cách dễ nhất để làm điều này là thông qua khung tổng hợp. Điều này sử dụng hai lệnh "$ group": lệnh đầu tiên nhóm theo các giá trị riêng biệt, lệnh thứ hai đếm tất cả các giá trị riêng biệt

pipeline = [ 
    { $group: { _id: "$myIndexedNonUniqueField"}  },
    { $group: { _id: 1, count: { $sum: 1 } } }
];

//
// Run the aggregation command
//
R = db.runCommand( 
    {
    "aggregate": "myCollection" , 
    "pipeline": pipeline
    }
);
printjson(R);

2) Nếu bạn muốn làm điều này với Bản đồ / Thu nhỏ, bạn có thể. Đây cũng là một quá trình gồm hai giai đoạn: trong giai đoạn đầu, chúng tôi xây dựng một tập hợp mới với danh sách mọi giá trị riêng biệt cho khóa. Trong lần thứ hai, chúng tôi thực hiện đếm () trên bộ sưu tập mới.

var SOURCE = db.myCollection;
var DEST = db.distinct
DEST.drop();


map = function() {
  emit( this.myIndexedNonUniqueField , {count: 1});
}

reduce = function(key, values) {
  var count = 0;

  values.forEach(function(v) {
    count += v['count'];        // count each distinct value for lagniappe
  });

  return {count: count};
};

//
// run map/reduce
//
res = SOURCE.mapReduce( map, reduce, 
    { out: 'distinct', 
     verbose: true
    }
    );

print( "distinct count= " + res.counts.output );
print( "distinct count=", DEST.count() );

Lưu ý rằng bạn không thể trả về kết quả của bản đồ / thu nhỏ nội tuyến, vì điều đó có thể sẽ vượt quá giới hạn kích thước tài liệu 16MB. Bạn có thể lưu phép tính trong một tập hợp và sau đó đếm () kích thước của tập hợp hoặc bạn có thể lấy số lượng kết quả từ giá trị trả về của mapReduce ().


5
Tôi đã tải xuống Mongo 2.2 RC0 và sử dụng gợi ý đầu tiên của bạn, và nó hoạt động! va nhanh nhẹn! cảm ơn bạn (cũng done 10gen ...) Tạo ra một ý chính ở đây (sử dụng lệnh tổng hợp phím tắt và đặt nó trong một dòng) gist.github.com/3241616
Eran Medan

@EranMedan Tuy nhiên, tôi nên cảnh báo với bạn rằng, tôi không đề xuất khung tổng hợp vì 2.2 rc0 vẫn chưa thực sự sẵn sàng để triển khai đầy đủ, chỉ cần lưu ý điều gì đó, tôi sẽ đợi cho đến khi bản phát hành đầy đủ 2.2 trước khi đề xuất triển khai tập hợp khuôn khổ.
Sammaye

@Sammaye có, nhờ Tôi nhận thức được nó, sẽ không đi vào sản xuất nêu ra, tôi cần có cho các thống kê nội bộ và muốn tránh di chuyển dữ liệu vào SQL nếu có thể (và làm dịu cơn tò mò của tôi)
Eran Medan

Tại sao Mongo không chấp nhận: this.plugins.X-Powered-By.string? Làm thế nào tôi sẽ thoát khỏi điều này?
EarlyPoster

Tôi tự hỏi liệu câu trả lời này có đáng tin cậy cho một môi trường bị phân đoạn hay không. Theo tôi hiểu, mỗi phân đoạn sẽ thực hiện tổng hợp riêng của chúng và sau đó trả về kết quả nơi kết quả sau đó sẽ được tổng hợp. Vì vậy, trong trường hợp này, chúng ta sẽ không có cơ hội để các bản sao tồn tại vì các giá trị khác biệt đã bị mất trong $groupcâu lệnh thứ hai trước khi được chuyển trở lại mongos?
Verran

37
db.myCollection.aggregate( 
   {$group : {_id : "$myIndexedNonUniqueField"} }, 
   {$group: {_id:1, count: {$sum : 1 }}});

thẳng đến kết quả:

db.myCollection.aggregate( 
   {$group : {_id : "$myIndexedNonUniqueField"} }, 
   {$group: {_id:1, count: {$sum : 1 }}})
   .result[0].count;

1
Đúng vậy, tốt hơn. Nhưng đó không phải là câu trả lời giống như William đã cung cấp?
JohnnyHK

2
Tương tự, nhưng tôi thích thực tế là nó nằm trên một dòng. Tuy nhiên, tôi đã gặp lỗi: "Không thể đọc thuộc tính '0' của undefined" Hãy xóa dòng cuối cùng và nó hoạt động rất đẹp.
Nico

và nếu chúng ta nói về cơ sở dữ liệu khổng lồ thực sự, đừng quên {allowDiskUse: true} vậy, db.myCollection.aggregate ([{$ group ..}, {$ group:}], {allowDiskUse: true}). result [ 0] .count;
hi_artem 19/03/18

3

Giải pháp sau phù hợp với tôi

db.test.distinct ('người dùng'); ["alex", "Anh", "Pháp", "Úc"]

db.countries.distinct ('country'). length 4

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.