인덱싱 된 열의 MongoDB 선택 수 (고유 x)-대용량 데이터 세트에 대한 고유 한 결과 계산


82

여러 기사와 예제를 살펴 봤지만 MongoDB에서이 SQL 쿼리를 수행하는 효율적인 방법을 아직 찾지 못했습니다. 서류)

첫번째 시도

(예를 들어 거의 중복되는 질문에서 -Mongo는 SQL의 SELECT DISTINCT에 해당합니까? )

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

내 데이터 세트가 거대하기 때문에 분명히이 오류가 발생했습니다.

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

두 번째 시도

나는 그룹을 시도하고하기로 결정했다

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

하지만 대신이 오류 메시지가 나타납니다.

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

세 번째 시도

나는 아직 시도하지 않았지만 관련된 몇 가지 제안이 있습니다. mapReduce

예 :

또한

GitHub에서 .distinct메서드를 수정하여 개수 만 반환해야한다고 언급 하는 풀 요청이있는 것 같지만 여전히 열려 있습니다. https://github.com/mongodb/mongo/pull/34

하지만이 시점에서 저는 여기서 질문 할 가치가 있다고 생각했습니다.이 주제에 대한 최신 정보는 무엇입니까? 고유 카운트를 위해 SQL 또는 다른 NoSQL DB로 이동해야합니까? 아니면 효율적인 방법이 있습니까?

최신 정보:

MongoDB 공식 문서에 대한이 의견은 고무적이지 않습니다. 정확합니까?

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

업데이트 2 :

새로운 Aggregation Framework가 위의 의견에 답하는 것 같습니다 ... (MongoDB 2.1 / 2.2 이상, 개발 미리보기 가능, 프로덕션 용이 아님)

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


이 작업을 자주 수행해야하며 그렇지 않으면 성능이 그다지 중요하지 않을 것입니다. 이 경우 큰 컬렉션에 대해 구별을 시도하는 대신 새 문서를 삽입 할 때 업데이트되는 별도의 컬렉션에 고유 한 값을 저장합니다. 아니면 MongoDb의 사용을 재평가하고 아마도 다른 것으로 이동할 것입니다. 아시다시피 MongoDb는 현재 수행하려는 작업에 적합하지 않습니다.
Tim Gautier

@TimGautier 감사합니다. 두려웠습니다. 이러한 모든 값을 삽입하는 데 몇 시간이 걸렸고 이전에 생각 했어야했습니다. :) 이제 그 통계를 위해 MySQL에 삽입하는 데 시간을 할애 할 것 같습니다 ...
Eran 메단

기본적으로 집계 데이터의 델타 인덱싱을 에뮬레이트하는 증분 MR을 수행 할 수도 있습니다. 사용하는 것에 대한 결과가 필요한시기에 따라 다릅니다. 나는 MySQL이 아마도 많은 IO를 얻을 것이라고 상상할 수 있고 이것을 수행하지 않은 것 (인덱스에서 인라인으로 10 만 개의 문서를 구별하는 작은 서버를 죽일 수 있음)을 상상할 수 있지만 여전히 이런 종류의 쿼리에 더 유연하다고 생각합니다. .
Sammaye

나는 몽고가 이런 일을 잘하지 않는다는 데 동의하지 않습니다. Mongo가 뛰어난 점이라면 이런 종류입니다.
superluminary

1
불행히도 중재자가 중복 질문에 게시 한 내 답변을 삭제했습니다. 나는 거기에서 그것을 삭제할 수 없으며 여기에 다시 게시하면 링크 : stackoverflow.com/a/33418582/226895
전문가

답변:


75

1)이를 수행하는 가장 쉬운 방법은 집계 프레임 워크를 사용하는 것입니다. 두 개의 "$ group"명령이 필요합니다. 첫 번째는 고유 한 값으로 그룹화되고 두 번째는 모든 고유 한 값을 계산합니다.

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

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

2) Map / Reduce로이 작업을 수행하려면 할 수 있습니다. 이것은 또한 2 단계 프로세스입니다. 첫 번째 단계에서는 키에 대한 모든 고유 값 목록이있는 새 컬렉션을 만듭니다. 두 번째에서는 새 컬렉션에 대해 count ()를 수행합니다.

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

16MB 문서 크기 제한을 초과 할 가능성이 있으므로 맵 / 축소 인라인의 결과를 반환 할 수 없습니다. 당신은 수있는 컬렉션의 계산을 저장 한 다음 (계산) 컬렉션의 크기, 또는 맵리 듀스의 반환 값에서 결과의 수를 얻을 수있다 ().


5
Mongo 2.2 RC0을 다운로드하고 첫 번째 제안을 사용했으며 작동합니다! 빠르고! 고맙습니다 (10gen 잘했습니다 ...) 여기에 요점을 만들었습니다 (단축 집계 명령을 사용하여 한 줄에 입력). gist.github.com/3241616
Eran Medan

@EranMedan하지만 경고해야합니다. 2.2 rc0은 아직 전체 배포를위한 준비가되지 않았기 때문에 집계 프레임 워크를 제안하지 않았습니다. 뼈대.
Sammaye

@Sammaye 예, 알고 있습니다. 아직 프로덕션으로 이동하지 않을 것입니다. 내부 통계를 위해 데이터를 SQL로 이동하는 것을 피하고 싶었습니다 (그리고 호기심을 해소)
Eran Medan

Mongo가 : this.plugins.X-Powered-By.string을 허용하지 않는 이유는 무엇입니까? 이것을 어떻게 피할 수 있습니까?
EarlyPoster

이 답변이 샤드 환경에서 신뢰할 수 있는지 궁금합니다. 내가 이해하는 바와 같이, 샤드는 각각 자체 집계를 수행 한 다음 결과가 집계 될 결과를 반환합니다. 그래서이 시나리오에서는 $group몽고에게 다시 전달되기 전에 두 번째 문 에서 고유 한 값이 손실 되었기 때문에 중복이 존재할 기회 가 없을까요?
Verran 2015-04-02

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

바로 결과 :

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

1
그래, 그게 낫다. 그러나 그것은 윌리엄이 이미 제공 한 것과 같은 대답이 아닙니까?
JohnnyHK 2013 년

2
비슷하지만 한 줄에 있다는 사실이 마음에 듭니다. 그래도 오류가 발생했습니다. "Undefined의 '0'속성을 읽을 수 없습니다."마지막 줄을 제거하면 아름답게 작동합니다.
Nico

그리고 정말 거대한 데이터베이스에 대해 이야기한다면 {allowDiskUse : true}를 잊지 마세요. db.myCollection.aggregate ([{$ group ..}, {$ group :}], {allowDiskUse : true}). result [ 0] .count;
hi_artem

3

다음 솔루션이 저에게 효과적이었습니다.

db.test.distinct ( 'user'); [ "alex", "England", "France", "Australia"]

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.