거대한 (1 억 레코드)에서 무작위 레코드를 얻으려고합니다 mongodb
.
가장 빠르고 효율적인 방법은 무엇입니까? 데이터가 이미 있으며 임의의 숫자를 생성하고 임의의 행을 얻을 수있는 필드가 없습니다.
어떤 제안?
답변
MongoDB 3.2 릴리스부터는 $sample
집계 파이프 라인 연산자를 사용하여 콜렉션에서 N 개의 임의 문서를 얻을 수 있습니다 .
// Get one random document from the mycoll collection.
db.mycoll.aggregate([{ $sample: { size: 1 } }])
필터링 된 컬렉션의 하위 집합에서 임의의 문서를 선택 $match
하려면 파이프 라인 앞에 스테이지를 추가하십시오 .
// Get one random document matching {a: 10} from the mycoll collection.
db.mycoll.aggregate([
{ $match: { a: 10 } },
{ $sample: { size: 1 } }
])
주석에서 언급했듯이 size
1보다 크면 반환 된 문서 샘플에 중복이있을 수 있습니다.
답변
모든 레코드 수를 계산하고 0과 수 사이의 난수를 생성 한 후 다음을 수행하십시오.
db.yourCollection.find().limit(-1).skip(yourRandomNumber).next()
답변
MongoDB 3.2 업데이트
3.2 소개 된 $ sample 집계 파이프 라인에 을 .
좋은 것도 있습니다 블로그 게시물도 있습니다실습에 관한 .
이전 버전의 경우 (이전 답변)
이것은 실제로 기능 요청이었습니다. http://jira.mongodb.org/browse/SERVER-533 “Wo n’t fix”아래에 제출되었습니다.
요리 책에는 컬렉션에서 임의의 문서를 선택하기위한 매우 좋은 레시피가 있습니다. http://cookbook.mongodb.org/patterns/random-attribute/
레시피를 역설하려면 문서에 임의의 숫자를 할당하십시오.
db.docs.save( { key : 1, ..., random : Math.random() } )
그런 다음 임의의 문서를 선택하십시오.
rand = Math.random()
result = db.docs.findOne( { key : 2, random : { $gte : rand } } )
if ( result == null ) {
result = db.docs.findOne( { key : 2, random : { $lte : rand } } )
}
모두 쿼리 $gte
및 $lte
필요한 것은 임의의 숫자 가까운과 문서를 찾을 수rand
.
물론 임의의 필드를 색인화하고 싶을 것입니다.
db.docs.ensureIndex( { key : 1, random :1 } )
인덱스에 대해 이미 쿼리하는 경우 인덱스를 삭제 random: 1
하고 추가 한 후 다시 추가하십시오.
답변
MongoDB의 지리 공간 색인 기능을 사용하여 ‘가장 가까운’문서를 임의의 숫자로 선택할 수 있습니다.
먼저 컬렉션에서 지형 공간 색인 생성을 활성화합니다.
db.docs.ensureIndex( { random_point: '2d' } )
X 축에서 임의의 점으로 문서 묶음을 만들려면 :
for ( i = 0; i < 10; ++i ) {
db.docs.insert( { key: i, random_point: [Math.random(), 0] } );
}
그런 다음 컬렉션에서 임의의 문서를 다음과 같이 얻을 수 있습니다.
db.docs.findOne( { random_point : { $near : [Math.random(), 0] } } )
또는 임의의 지점에 가장 가까운 여러 문서를 검색 할 수 있습니다.
db.docs.find( { random_point : { $near : [Math.random(), 0] } } ).limit( 4 )
여기에는 쿼리가 하나만 필요하고 null 검사가 필요하지 않으며 코드는 깨끗하고 단순하며 유연합니다. 지오 포인트의 Y 축을 사용하여 쿼리에 두 번째 임의성 차원을 추가 할 수도 있습니다.
답변
다음 레시피는 몽고 요리 책 솔루션보다 약간 느리지 만 (모든 문서에 임의의 키를 추가하십시오) 더 균등하게 분산 된 임의의 문서를 반환합니다. skip( random )
솔루션 보다 약간 덜 고르지 만 문서가 제거되는 경우 훨씬 빠르고 안전합니다.
function draw(collection, query) {
// query: mongodb query object (optional)
var query = query || { };
query['random'] = { $lte: Math.random() };
var cur = collection.find(query).sort({ rand: -1 });
if (! cur.hasNext()) {
delete query.random;
cur = collection.find(query).sort({ rand: -1 });
}
var doc = cur.next();
doc.random = Math.random();
collection.update({ _id: doc._id }, doc);
return doc;
}
또한 문서에 임의의 “무작위”필드를 추가해야하므로 문서를 만들 때 반드시 추가해야합니다. Geoffrey가 표시 한대로 컬렉션을 초기화해야 할 수도 있습니다.
function addRandom(collection) {
collection.find().forEach(function (obj) {
obj.random = Math.random();
collection.save(obj);
});
}
db.eval(addRandom, db.things);
벤치 마크 결과
이 방법은 skip()
(ceejayoz의) 방법 Michael이보고 한 “요리 책”방법보다 더 균일 한 임의의 문서를 생성합니다.
1,000,000 개의 요소가있는 컬렉션의 경우 :
-
이 방법은 내 컴퓨터에서 1 밀리 초도 채 걸리지 않습니다
-
이
skip()
방법은 평균 180ms 소요
요리 책 방법은 임의의 숫자가 선호하지 않기 때문에 많은 수의 문서를 선택하지 않습니다.
-
이 방법은 시간이 지남에 따라 모든 요소를 고르게 선택합니다.
-
내 벤치 마크에서는 요리 책 방법보다 30 % 느 렸습니다.
-
무작위성은 100 % 완벽하지는 않지만 매우 좋습니다 (필요한 경우 개선 될 수 있음)
이 레시피는 완벽하지 않습니다. 완벽한 솔루션은 다른 사람들이 언급했듯이 기본 제공 기능입니다.
그러나 많은 목적을 위해 좋은 절충안이되어야합니다.
답변
다음은 약간의 수학 및 논리에 ObjectId
대한 기본값을 사용하는 방법 _id
입니다.
// Get the "min" and "max" timestamp values from the _id in the collection and the
// diff between.
// 4-bytes from a hex string is 8 characters
var min = parseInt(db.collection.find()
.sort({ "_id": 1 }).limit(1).toArray()[0]._id.str.substr(0,8),16)*1000,
max = parseInt(db.collection.find()
.sort({ "_id": -1 })limit(1).toArray()[0]._id.str.substr(0,8),16)*1000,
diff = max - min;
// Get a random value from diff and divide/multiply be 1000 for The "_id" precision:
var random = Math.floor(Math.floor(Math.random(diff)*diff)/1000)*1000;
// Use "random" in the range and pad the hex string to a valid ObjectId
var _id = new ObjectId(((min + random)/1000).toString(16) + "0000000000000000")
// Then query for the single document:
var randomDoc = db.collection.find({ "_id": { "$gte": _id } })
.sort({ "_id": 1 }).limit(1).toArray()[0];
이것이 쉘 표현의 일반적인 논리이며 쉽게 적용 할 수 있습니다.
그래서 포인트에서 :
-
컬렉션에서 최소 및 최대 기본 키 값 찾기
-
해당 문서의 타임 스탬프 사이에있는 임의의 숫자를 생성하십시오.
-
최소값에 난수를 추가하고 해당 값보다 크거나 같은 첫 번째 문서를 찾으십시오.
이것은 “16 진”의 타임 스탬프 값에서 “패딩”을 사용하여 ObjectId
우리가 찾고있는 것이므로 유효한 값 을 형성합니다 . 정수를 _id
값 으로 사용하는 것은 본질적으로 간단하지만 포인트의 기본 아이디어는 동일합니다.
답변
Python에서 pymongo를 사용하는 경우 :
import random
def get_random_doc():
count = collection.count()
return collection.find()[random.randrange(count)]
![](http://daplus.net/wp-content/uploads/2023/04/coupang_part-e1630022808943-2.png)