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


Tôi đã đi qua một số bài báo và ví dụ, và vẫn chưa tìm thấy một 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 CHỌN SQL?)

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

Rõ ràng là tôi nhận được lỗi này vì số 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ử và 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 vài gợi ý 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 chữa .distinct phương pháp đề cập đến nó chỉ nên trả về một số, nhưng nó vẫn mở: https://github.com/mongodb/mongo/pull/34

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

Cập nhật: 

Nhận xét này về tài liệu chính thức của MongoDB không đáng khích lệ, điều này có chính xác không?

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

Update2:

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

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


76
2017-08-02 17:17


gốc


Tôi cho rằng bạn cần phải làm điều này thường xuyên hoặc hiệu suất sẽ không có vấn đề gì nhiều. 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 làm một khác biệt trên một bộ sưu tập lớn. Hoặc là tôi sẽ đánh giá lại việc sử dụng MongoDb của tôi và có thể chuyển sang thứ khác. Như bạn thấy, MongoDb hiện tại không giỏi với những gì bạn đang cố gắng làm. - Tim Gautier
@TimGautier cảm ơn, tôi đã sợ như vậy, phải mất nhiều giờ để chèn tất cả những giá trị đó, và tôi nên nghĩ về điều đó trước :) Tôi nghĩ rằng tôi sẽ dành thời gian để chèn nó vào MySQL cho những số liệu thống kê ... - Eran Medan
Bạn cũng có thể làm MR tăng dần về cơ bản mô phỏng lập chỉ mục delta của dữ liệu tổng hợp. Tôi có nghĩa là nó phụ thuộc vào khi 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 phải từ việc này (tôi có thể giết một máy chủ nhỏ với 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 truy vấn cho loại công cụ này . - Sammaye
Tại sao downvote? - Eran Medan
Rất tiếc, 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 - expert


Các câu trả lời:


1) Cách dễ nhất để thực hiện điều này là thông qua khung tổng hợp. Điều này có hai lệnh "$ nhóm": nhóm đầu tiên theo các giá trị riêng biệt, giá trị thứ hai tính tất cả các giá trị khác 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 Map / Reduce bạn có thể. Đây cũng là một quá trình hai giai đoạn: trong giai đoạn đầu tiên, chúng tôi xây dựng một bộ sưu tập mới với một danh sách tất cả các giá trị khác biệt cho khóa. Trong lần thứ hai, chúng ta sẽ đế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ả lại kết quả của nội tuyến bản đồ / giảm, vì điều đó sẽ có khả năng vượt quá giới hạn kích thước tài liệu 16MB. Bạn có thể lưu tính toán trong một bộ sưu tập và sau đó đếm () kích thước của bộ sưu tập, hoặc bạn có thể nhận được số lượng kết quả từ giá trị trả về của mapReduce ().


68
2017-08-02 22:39



Tôi đã tải xuống Mongo 2.2 RC0 và sử dụng đề xuất thứ nhất của bạn và nó hoạt động! va nhanh nhẹn! cảm ơn bạn (cũng được thực hiện 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 Tôi nên cảnh báo bạn mặc dù, 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 ý đến điều này, tôi sẽ chờ 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ó, cảm ơn tôi đã biết về nó, sẽ không đi vào sản xuất, tôi cần điều đó cho các thống kê nội bộ và muốn tránh chuyển dữ liệu sang SQL nếu có thể (và làm dịu sự 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 nếu câu trả lời này là đáng tin cậy cho một môi trường sharded. Khi tôi hiểu nó, mỗi mảnh sẽ tự tạo ra tập hợp của riêng mình và sau đó trả về kết quả mà tại đó kết quả sẽ được tổng hợp. Vì vậy, trong trường hợp này, chúng tôi 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 lần thứ hai $group tuyên bố trước khi được trả lại cho mongos? - Verran


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;

37
2018-03-04 21:32



@ JohnnyHK - bạn nói đúng và tôi đã sửa nó - Stackee007
Đúng vậy, tốt hơn. Nhưng đó không phải là câu trả lời tương tự mà William đã cung cấp? - JohnnyHK
Tương tự, nhưng tôi thích thực tế là nó trên một dòng. Tôi đã nhận một lỗi mặc dù: "Không thể đọc thuộc tính '0' của undefined" Hủy bỏ dòng cuối cùng và nó hoạt động đẹp. - Nico
Nó làm việc cho tôi - shenyan
và nếu chúng ta nói về cơ sở dữ liệu khổng lồ, đừng quên {allowDiskUse: true}, db.myCollection.aggregate ([{$ group ..}, {$ group:}], {allowDiskUse: true}). 0] .count; - hi_artem


Giải pháp sau đây đã làm việc cho tôi

db.test.distinct ('user');   ["alex", "Anh", "Pháp", "Úc"]

chiều dài db.countries.distinct ('quốc gia').   4


3
2018-05-25 06:06