MongoDB से रैंडम रिकॉर्ड


336

मैं एक विशाल (100 मिलियन रिकॉर्ड) से एक यादृच्छिक रिकॉर्ड प्राप्त करना चाहता हूँ mongodb

ऐसा करने का सबसे तेज़ और सबसे कुशल तरीका क्या है? डेटा पहले से ही है और कोई क्षेत्र नहीं है जिसमें मैं एक यादृच्छिक संख्या उत्पन्न कर सकता हूं और एक यादृच्छिक पंक्ति प्राप्त कर सकता हूं।

कोई सुझाव?


2
यह एसओ प्रश्न भी देखें जिसका शीर्षक है "मूंगो में बेतरतीब ढंग से सेट किए गए परिणाम का आदेश देना" । परिणाम सेट को बेतरतीब ढंग से ऑर्डर करने के बारे में सोचना इस सवाल का एक सामान्य संस्करण है - अधिक शक्तिशाली और अधिक उपयोगी।
डेविड जे।

11
यह सवाल खटकता रहता है। नवीनतम जानकारी संभवतः MongoDB टिकट ट्रैकर में एक संग्रह से यादृच्छिक आइटम प्राप्त करने के लिए सुविधा अनुरोध पर मिल सकती है । यदि मूल रूप से लागू किया जाता है, तो यह संभवतः सबसे कुशल विकल्प होगा। (यदि आप सुविधा चाहते हैं, तो इसे वोट करें।)
डेविड जे।

क्या यह एक शार्पड कलेक्शन है?
डायलन टोंग

3
सही उत्तर नीचे दिए गए @ जॉनीएचके द्वारा दिया गया है: db.mycoll.aggregate ({$ नमूना: {size: 1}})
फ्लोरियन

क्या किसी को पता है कि यह पहले रिकॉर्ड लेने की तुलना में कितना धीमा है? मैं बहस कर रहा हूं कि क्या यह कुछ करने के लिए एक यादृच्छिक नमूना लेने के लायक है, बस इसे क्रम में कर रहा है।
डेविड काँग

जवाबों:


248

MongoDB के 3.2 रिलीज के साथ शुरू, आप $sampleएकत्रीकरण पाइपलाइन ऑपरेटर का उपयोग कर एक संग्रह से एन यादृच्छिक डॉक्स प्राप्त कर सकते हैं :

// 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 } }
])

जैसा कि टिप्पणियों में कहा गया है, जब size1 से अधिक है, तो लौटे दस्तावेज़ नमूने में डुप्लिकेट हो सकते हैं।


12
यह एक अच्छा तरीका है, लेकिन याद रखें कि यह गारंटी नहीं देता है कि नमूने में एक ही वस्तु की कोई प्रतियां नहीं हैं।
मथेयस अराउजो

10
@MatheusAraujo जो अगर आप एक रिकॉर्ड लेकिन फिर भी अच्छी बात चाहते हैं तो कोई बात नहीं होगी
Toby

3
पांडित्य नहीं है, लेकिन सवाल एक MongoDB संस्करण को निर्दिष्ट नहीं करता है, इसलिए मुझे लगता है कि सबसे हाल का संस्करण उचित है।
डालनमिलर

2
@Nepoxx शामिल प्रसंस्करण के बारे में डॉक्स देखें ।
जॉनीएचके

2
@brycejl अगर $ सैंपल चरण में किसी भी मेलिंग डॉक्यूमेंट का चयन नहीं किया गया तो कुछ भी मेल न खाने का घातक दोष होगा।
जॉनीएचके

115

सभी रिकॉर्ड की गिनती करें, 0 और गिनती के बीच एक यादृच्छिक संख्या उत्पन्न करें, और फिर करें:

db.yourCollection.find().limit(-1).skip(yourRandomNumber).next()

139
दुर्भाग्य से स्किप () बल्कि अक्षम है क्योंकि इसमें कई दस्तावेजों को स्कैन करना है। इसके अलावा, एक दौड़ की स्थिति है यदि पंक्तियों को गिनती प्राप्त करने और क्वेरी चलाने के बीच हटा दिया जाता है।
mstearn

6
ध्यान दें कि यादृच्छिक संख्या 0 और गणना (अनन्य) के बीच होनी चाहिए। यानी, यदि आपके पास 10 आइटम हैं, तो यादृच्छिक संख्या 0 और 9 के बीच होनी चाहिए। अन्यथा कर्सर अंतिम आइटम को छोड़ने की कोशिश कर सकता है, और कुछ भी वापस नहीं किया जाएगा।
मैट

4
धन्यवाद, मेरे उद्देश्यों के लिए पूरी तरह से काम किया। @mstearn, दक्षता और दौड़ की स्थिति दोनों पर आपकी टिप्पणियां मान्य हैं, लेकिन ऐसे संग्रह के लिए जहां न तो मायने रखता है (संग्रह में एक बार सर्वर-साइड बैच निकालने जहां रिकॉर्ड हटाए नहीं जाते हैं), यह हैकी (IMO) से बहुत बेहतर है मोंगो रसोई की किताब में समाधान।
माइकल मौसा

4
-1 की सीमा क्या है?
मंकीबोनी

@MonkeyBonkey docs.mongodb.org/meta-driver/latest/legacy/… "यदि नंबरटॉरनेट 0 है, तो db डिफ़ॉल्ट रिटर्न आकार का उपयोग करेगा। यदि संख्या नकारात्मक है, तो डेटाबेस उस नंबर को लौटा देगा और कर्सर को बंद कर देगा। "
सियजयोज

86

MongoDB 3.2 के लिए अद्यतन

3.2 ने एकत्रीकरण पाइपलाइन के लिए $ नमूना पेश किया ।

इसे व्यवहार में लाने के लिए एक अच्छी ब्लॉग पोस्ट भी है ।

पुराने संस्करणों के लिए (पिछला उत्तर)

यह वास्तव में एक फीचर अनुरोध था: http://jira.mongodb.org/browse/SERVER-533 लेकिन इसे "W'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करें, और इसे फिर से जोड़ें।


7
और यहां संग्रह में हर दस्तावेज़ में यादृच्छिक क्षेत्र को जोड़ने का एक सरल तरीका है। function setRandom () {db.topics.find ()। forEach (फ़ंक्शन (obj) {obj.random = Math.random (); db.topics.save (obj);}); } db.eval (सेट-रैंडम);
ज्योफ्री

8
यह किसी दस्तावेज़ को यादृच्छिक रूप से चुनता है, लेकिन यदि आप इसे एक से अधिक बार करते हैं, तो लुकअप स्वतंत्र नहीं हैं। रैंडम चांस डिक्टेट करने की तुलना में आपको एक ही डॉक्यूमेंट दो बार मिलने की संभावना है।
कमी

12
ऐसा लगता है कि परिपत्र हैशिंग के खराब कार्यान्वयन की तरह है। यह कमी से भी बदतर है कहते हैं: यहां तक ​​कि एक लगाव पक्षपाती है क्योंकि यादृच्छिक संख्या समान रूप से वितरित नहीं हैं। इसे ठीक से करने के लिए, आपको दस्तावेज़ के प्रति 10 यादृच्छिक संख्याओं के सेट की आवश्यकता होगी। आप जितने अधिक रैंडम नंबर प्रति दस्तावेज़ का उपयोग करते हैं, उतना ही अधिक आउटपुट वितरण समान हो जाता है।
थॉमस

4
MongoDB JIRA टिकट अभी भी जीवित है: jira.mongodb.org/browse/SERVER-533 टिप्पणी करें और वोट दें यदि आप सुविधा चाहते हैं।
डेविड जे।

1
उल्लिखित कैविएट के प्रकार पर ध्यान दें। यह कम मात्रा में दस्तावेजों के साथ कुशलता से काम नहीं करता है। 3 और 63 की यादृच्छिक कुंजी के साथ दो आइटम दिए गए। दस्तावेज़ # 63 अधिक बार चुना जाएगा जहां $gteपहले है। वैकल्पिक समाधान stackoverflow.com/a/9499484/79201 इस मामले में बेहतर काम करेगा।
रयान शूमाकर

56

आप यादृच्छिक संख्या के लिए दस्तावेजों को 'निकटतम' चुनने के लिए MongoDB के भू-स्थानिक अनुक्रमण सुविधा का भी उपयोग कर सकते हैं।

सबसे पहले, एक संग्रह पर भू-स्थानिक अनुक्रमण सक्षम करें:

db.docs.ensureIndex( { random_point: '2d' } )

एक्स-अक्ष पर यादृच्छिक बिंदुओं के साथ दस्तावेजों का एक गुच्छा बनाने के लिए:

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 )

इसके लिए केवल एक क्वेरी और शून्य चेक की आवश्यकता होती है, साथ ही कोड साफ, सरल और लचीला होता है। आप अपनी क्वेरी में दूसरा यादृच्छिकता आयाम जोड़ने के लिए भू-स्थान के Y- अक्ष का उपयोग भी कर सकते हैं।


8
मुझे यह जवाब पसंद है, इसका सबसे कुशल एक मैंने देखा है कि सर्वर साइड के बारे में गड़बड़ करने की आवश्यकता नहीं है।
टोनी मिलियन

4
यह उन दस्तावेजों के प्रति भी पक्षपाती है जो अपने आसपास के क्षेत्र में कुछ बिंदु रखते हैं।
थॉमस

6
यह सच है, और साथ ही अन्य समस्याएं भी हैं: दस्तावेजों को उनके यादृच्छिक कुंजियों पर दृढ़ता से सहसंबद्ध किया जाता है, इसलिए यह अत्यधिक अनुमानित है कि यदि आप कई दस्तावेज़ों का चयन करते हैं तो दस्तावेज़ एक समूह के रूप में वापस आ जाएंगे। इसके अलावा, सीमा (0 और 1) के करीब दस्तावेजों को चुने जाने की संभावना कम है। उत्तरार्द्ध को गोलाकार जियोमिंग का उपयोग करके हल किया जा सकता है, जो किनारों पर घूमता है। हालाँकि, आपको इस उत्तर को कुकबुक रेसिपी के बेहतर संस्करण के रूप में देखना चाहिए, न कि एक सही यादृच्छिक चयन तंत्र के रूप में। यह अधिकांश प्रयोजनों के लिए पर्याप्त यादृच्छिक है।
निको दे पोएल

@NicodePoel, मुझे आपका जवाब और साथ ही आपकी टिप्पणी पसंद है! और मेरे पास आपके लिए कुछ प्रश्न हैं: 1- आप कैसे जानते हैं कि अंक 0 और 1 के करीब बिंदुओं को चुना जाना कम है, क्या यह कुछ गणितीय आधार पर आधारित है ?, 2- क्या आप गोलाकार जियोमिंग पर अधिक विस्तार कर सकते हैं? यह यादृच्छिक चयन को बेहतर कैसे करेगा, और इसे MongoDB में कैसे करें? ... सराहना की!
अक्टूबर

अपने विचार की सराहना करें। अंत में, मेरे पास एक बेहतरीन कोड है जो कि सीपीयू और रैम के अनुकूल है! धन्यवाद
Qais Bsharat

21

निम्नलिखित नुस्खा मोंगो कुकबुक समाधान की तुलना में थोड़ा धीमा है (हर दस्तावेज़ पर एक यादृच्छिक कुंजी जोड़ें), लेकिन अधिक समान रूप से वितरित यादृच्छिक दस्तावेज़ लौटाता है। यह 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;
}

आपको अपने दस्तावेज़ों में एक यादृच्छिक "यादृच्छिक" फ़ील्ड जोड़ने की भी आवश्यकता होती है, ताकि आप इसे बनाते समय इसे जोड़ना न भूलें: आपको अपना संग्रह आरंभ करने की आवश्यकता हो सकती है जैसा कि ज्योफ्री द्वारा दिखाया गया है

function addRandom(collection) { 
    collection.find().forEach(function (obj) {
        obj.random = Math.random();
        collection.save(obj);
    }); 
} 
db.eval(addRandom, db.things);

बेंचमार्क परिणाम

यह विधि की तुलना में बहुत तेज है skip() विधि (ceejayoz) की और माइकल द्वारा रिपोर्ट की गई "रसोई की किताब" विधि की तुलना में अधिक समान रूप से यादृच्छिक दस्तावेज उत्पन्न करती है:

1,000,000 तत्वों के संग्रह के लिए:

  • यह विधि मेरी मशीन पर एक मिलीसेकंड से कम है

  • skip()विधि औसतन 180 एमएस लेता है

रसोई की किताब विधि बड़ी संख्या में दस्तावेजों को कभी नहीं लेने का कारण बनेगी क्योंकि उनका यादृच्छिक संख्या उनके पक्ष में नहीं है।

  • यह विधि समय के साथ सभी तत्वों को समान रूप से चुनेगी।

  • मेरे बेंचमार्क में कुकबुक विधि की तुलना में यह केवल 30% धीमा था।

  • यादृच्छिकता 100% सही नहीं है, लेकिन यह बहुत अच्छा है (और यदि आवश्यक हो तो इसमें सुधार किया जा सकता है)

यह नुस्खा सही नहीं है - सही समाधान एक अंतर्निहित सुविधा होगी जैसा कि अन्य ने नोट किया है।
हालाँकि यह कई उद्देश्यों के लिए एक अच्छा समझौता होना चाहिए।


10

यहाँ एक तरह से डिफ़ॉल्ट 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];

यह शेल प्रतिनिधित्व में सामान्य तर्क है और आसानी से अनुकूलनीय है।

तो अंक में:

  • संग्रह में न्यूनतम और अधिकतम प्राथमिक मान खोजें

  • एक यादृच्छिक संख्या उत्पन्न करें जो उन दस्तावेजों के टाइमस्टैम्प के बीच आती है।

  • यादृच्छिक संख्या को न्यूनतम मान में जोड़ें और पहला दस्तावेज़ ढूंढें जो उस मूल्य से अधिक या उसके बराबर है।

यह "हेक्स" में टाइमस्टैम्प मूल्य से "पैडिंग" का उपयोग करता है ताकि एक वैध ObjectIdमूल्य बनाया जा सके जो हम देख रहे हैं। पूर्णांक को _idमान के रूप में उपयोग करना अनिवार्य रूप से सरल है लेकिन अंकों में समान मूल विचार है।


मेरे पास 300 000 000 लाइनों का संग्रह है। यह एकमात्र समाधान है जो काम करता है और यह काफी तेज है।
निकोस

8

अजगर का उपयोग करते हुए पायथन में:

import random

def get_random_doc():
    count = collection.count()
    return collection.find()[random.randrange(count)]

5
आंतरिक रूप से ध्यान देने योग्य, यह अन्य उत्तरों की तरह ही, स्किप और सीमा का उपयोग करेगा।
जॉनीएचके

आपका उत्तर सही है। हालांकि, कृपया बदलें count()साथ estimated_document_count()के रूप में count()Mongdo v 4.2 के पश्चात् में मान्य नहीं है।
user3848207

8

अब आप समुच्चय का उपयोग कर सकते हैं। उदाहरण:

db.users.aggregate(
   [ { $sample: { size: 3 } } ]
)

डॉक्टर को देखें


3
नोट: $ नमूना एक ही दस्तावेज़ को एक से अधिक बार प्राप्त कर सकता है
समन शफिघ

6

अगर वहाँ कोई डेटा नहीं है तो यह कठिन है। _id क्षेत्र क्या हैं? क्या वे मोंगोडब ऑब्जेक्ट आईडी हैं? यदि ऐसा है, तो आप उच्चतम और निम्नतम मान प्राप्त कर सकते हैं:

lowest = db.coll.find().sort({_id:1}).limit(1).next()._id;
highest = db.coll.find().sort({_id:-1}).limit(1).next()._id;

यदि आप मान लेते हैं कि आईडी समान रूप से वितरित की गई है (लेकिन वे नहीं हैं, लेकिन कम से कम यह एक शुरुआत है):

unsigned long long L = first_8_bytes_of(lowest)
unsigned long long H = first_8_bytes_of(highest)

V = (H - L) * random_from_0_to_1();
N = L + V;
oid = N concat random_4_bytes();

randomobj = db.coll.find({_id:{$gte:oid}}).limit(1);

1
किसी भी विचार कि PHP में कैसा लगेगा? या कम से कम आपने किस भाषा का प्रयोग किया है? क्या यह पायथन है?
मार्सिन

6

पायथन (पाइमोंगो) का उपयोग करते हुए, कुल कार्य भी कार्य करता है।

collection.aggregate([{'$sample': {'size': sample_size }}])

यह दृष्टिकोण यादृच्छिक संख्या (जैसे collection.find ([random_int]) के लिए क्वेरी चलाने की तुलना में बहुत तेज़ है। यह विशेष रूप से बड़े संग्रह के लिए मामला है।


5

आप एक यादृच्छिक टाइमस्टैम्प चुन सकते हैं और पहले ऑब्जेक्ट की खोज कर सकते हैं जो बाद में बनाया गया था। यह केवल एक ही दस्तावेज को स्कैन करेगा, हालांकि यह जरूरी नहीं कि आपको एक समान वितरण प्रदान करे।

var randRec = function() {
    // replace with your collection
    var coll = db.collection
    // get unixtime of first and last record
    var min = coll.find().sort({_id: 1}).limit(1)[0]._id.getTimestamp() - 0;
    var max = coll.find().sort({_id: -1}).limit(1)[0]._id.getTimestamp() - 0;

    // allow to pass additional query params
    return function(query) {
        if (typeof query === 'undefined') query = {}
        var randTime = Math.round(Math.random() * (max - min)) + min;
        var hexSeconds = Math.floor(randTime / 1000).toString(16);
        var id = ObjectId(hexSeconds + "0000000000000000");
        query._id = {$gte: id}
        return coll.find(query).limit(1)
    };
}();

सुपरलाइन डेटाबेस वृद्धि के लिए रैंडम डेट को तिरछा करना आसान होगा।
मार्टिन नोवाक

यह यहाँ बहुत बड़े संग्रह के लिए सबसे अच्छा तरीका है, यह हे (1), unline छोड़ () या गिनती () अन्य समाधान में इस्तेमाल किया पर काम करता है
marmor

4

Php पर मेरा समाधान:

/**
 * Get random docs from Mongo
 * @param $collection
 * @param $where
 * @param $fields
 * @param $limit
 * @author happy-code
 * @url happy-code.com
 */
private function _mongodb_get_random (MongoCollection $collection, $where = array(), $fields = array(), $limit = false) {

    // Total docs
    $count = $collection->find($where, $fields)->count();

    if (!$limit) {
        // Get all docs
        $limit = $count;
    }

    $data = array();
    for( $i = 0; $i < $limit; $i++ ) {

        // Skip documents
        $skip = rand(0, ($count-1) );
        if ($skip !== 0) {
            $doc = $collection->find($where, $fields)->skip($skip)->limit(1)->getNext();
        } else {
            $doc = $collection->find($where, $fields)->limit(1)->getNext();
        }

        if (is_array($doc)) {
            // Catch document
            $data[ $doc['_id']->{'$id'} ] = $doc;
            // Ignore current document when making the next iteration
            $where['_id']['$nin'][] = $doc['_id'];
        }

        // Every iteration catch document and decrease in the total number of document
        $count--;

    }

    return $data;
}

3

डुप्लिकेट के बिना यादृच्छिक डॉक्स की निर्धारित संख्या प्राप्त करने के लिए:

  1. पहले सभी आईडी प्राप्त करें
  2. दस्तावेजों का आकार प्राप्त करें
  3. लूप रैंडम इंडेक्स प्राप्त करना और डुप्लिकेट को छोड़ना

    number_of_docs=7
    db.collection('preguntas').find({},{_id:1}).toArray(function(err, arr) {
    count=arr.length
    idsram=[]
    rans=[]
    while(number_of_docs!=0){
        var R = Math.floor(Math.random() * count);
        if (rans.indexOf(R) > -1) {
         continue
          } else {           
                   ans.push(R)
                   idsram.push(arr[R]._id)
                   number_of_docs--
                    }
        }
    db.collection('preguntas').find({}).toArray(function(err1, doc1) {
                    if (err1) { console.log(err1); return;  }
                   res.send(doc1)
                });
            });

2

मैं मानचित्र / कम का उपयोग करने का सुझाव दूंगा, जहां आप मानचित्र फ़ंक्शन का उपयोग केवल तभी उत्सर्जन करने के लिए करते हैं जब एक यादृच्छिक मान किसी दिए गए प्रायिकता से ऊपर होता है।

function mapf() {
    if(Math.random() <= probability) {
    emit(1, this);
    }
}

function reducef(key,values) {
    return {"documents": values};
}

res = db.questions.mapReduce(mapf, reducef, {"out": {"inline": 1}, "scope": { "probability": 0.5}});
printjson(res.results);

उपर्युक्त फ़ंक्शन कम हो जाता है क्योंकि मानचित्र फ़ंक्शन से केवल एक कुंजी ('1') उत्सर्जित होती है।

"संभावना" के मान को "गुंजाइश" में परिभाषित किया गया है, जब नक्शा खींचना (...)

इस तरह के MapReduce का उपयोग करना भी एक शार्प डीबी पर प्रयोग करने योग्य होना चाहिए।

यदि आप db से बिल्कुल सही m दस्तावेज़ों का चयन करना चाहते हैं, तो आप इसे इस तरह से कर सकते हैं:

function mapf() {
    if(countSubset == 0) return;
    var prob = countSubset / countTotal;
    if(Math.random() <= prob) {
        emit(1, {"documents": [this]}); 
        countSubset--;
    }
    countTotal--;
}

function reducef(key,values) {
    var newArray = new Array();
for(var i=0; i < values.length; i++) {
    newArray = newArray.concat(values[i].documents);
}

return {"documents": newArray};
}

res = db.questions.mapReduce(mapf, reducef, {"out": {"inline": 1}, "scope": {"countTotal": 4, "countSubset": 2}})
printjson(res.results);

जहाँ "countTotal" (m) db में दस्तावेज़ों की संख्या है, और "countSubset" (n) पुनः प्राप्त करने के लिए दस्तावेज़ों की संख्या है।

यह दृष्टिकोण शार्क डेटाबेस पर कुछ समस्याएं दे सकता है।


4
1 तत्व वापस करने के लिए एक पूर्ण संग्रह स्कैन कर रहा है ... इसे करने के लिए कम से कम कुशल तकनीक होनी चाहिए।
थॉमस

1
चाल यह है, कि यह यादृच्छिक तत्वों की एक मनमानी संख्या को वापस करने के लिए एक सामान्य समाधान है - जिस स्थिति में यह> 2 यादृच्छिक तत्वों को प्राप्त करने पर अन्य समाधानों की तुलना में तेज़ होगा।
torbenl

2

आप यादृच्छिक _id चुन सकते हैं और संबंधित वस्तु वापस कर सकते हैं:

 db.collection.count( function(err, count){
        db.collection.distinct( "_id" , function( err, result) {
            if (err)
                res.send(err)
            var randomId = result[Math.floor(Math.random() * (count-1))]
            db.collection.findOne( { _id: randomId } , function( err, result) {
                if (err)
                    res.send(err)
                console.log(result)
            })
        })
    })

यहाँ आपको संग्रह में यादृच्छिक संख्याओं के भंडारण पर स्थान खर्च करने की आवश्यकता नहीं है।


1

मैं प्रत्येक वस्तु में एक यादृच्छिक int क्षेत्र जोड़ने का सुझाव दूंगा। तब आप सिर्फ एक कर सकते हैं

findOne({random_field: {$gte: rand()}}) 

एक यादृच्छिक दस्तावेज़ लेने के लिए। बस सुनिश्चित करें कि आप सुनिश्चित करेंइंडेक्स ({random_field: 1})


2
यदि आपके संग्रह में पहला रिकॉर्ड अपेक्षाकृत उच्च random_field मान का है, तो क्या यह लगभग हर समय वापस नहीं आएगा?
द इहैटस

2
Thehaitus सही है, यह होगा - यह किसी भी उद्देश्य के लिए उपयुक्त नहीं है
हेप्टिक

7
यह समाधान पूरी तरह से गलत है, एक यादृच्छिक संख्या को जोड़ना (चलो 0 से 2 ^ 32-1 के बीच की कल्पना करें) किसी भी अच्छे वितरण की गारंटी नहीं देता है और $ जीटीई का उपयोग करना इसे और भी बदतर बना देता है, आपके यादृच्छिक चयन के कारण भी करीब नहीं होगा। एक छद्म यादृच्छिक संख्या के लिए। मेरा सुझाव है कि इस अवधारणा का उपयोग कभी न करें।
मैक्सिमिलियानो रियोस

1

जब मुझे एक समान समाधान का सामना करना पड़ा, तो मैं पीछे हट गया और पाया कि व्यापार अनुरोध वास्तव में प्रस्तुत किए जा रहे इन्वेंट्री के रोटेशन के कुछ रूप बनाने के लिए था। उस मामले में, बहुत बेहतर विकल्प हैं, जिनके पास सोल जैसे खोज इंजनों के उत्तर हैं, न कि मोंगोबीडी जैसे डेटा स्टोर।

संक्षेप में, सामग्री को "बुद्धिमानी से घुमाने" की आवश्यकता के साथ, हमें सभी दस्तावेजों में यादृच्छिक संख्या के बजाय क्या करना चाहिए, इसमें एक व्यक्तिगत q स्कोर संशोधक शामिल है। इसे स्वयं लागू करने के लिए, उपयोगकर्ताओं की एक छोटी आबादी को मानते हुए, आप प्रति उपयोगकर्ता एक उत्पाद को स्टोर कर सकते हैं जिसमें productId, इंप्रेशन काउंट, क्लिक-थ्रू काउंट, अंतिम बार देखी गई तारीख, और जो भी अन्य कारक हैं जो व्यवसाय को aq स्कोर की गणना करने के लिए सार्थक लगता है। संशोधक। प्रदर्शित करने के लिए सेट को पुनः प्राप्त करते समय, आमतौर पर आप अंतिम उपयोगकर्ता द्वारा अनुरोधित डेटा स्टोर से अधिक दस्तावेजों का अनुरोध करते हैं, फिर q स्कोर संशोधक को लागू करते हैं, अंतिम उपयोगकर्ता द्वारा अनुरोध किए गए रिकॉर्ड की संख्या लेते हैं, फिर परिणामों के पृष्ठ को यादृच्छिक करते हैं, एक छोटा सेट, इसलिए केवल एप्लिकेशन लेयर में दस्तावेजों को क्रमबद्ध करें (मेमोरी में)।

यदि उपयोगकर्ताओं का ब्रह्मांड बहुत बड़ा है, तो आप उपयोगकर्ता के बजाय व्यवहार समूह द्वारा उपयोगकर्ताओं को व्यवहार समूहों और अनुक्रमणिका में वर्गीकृत कर सकते हैं।

यदि उत्पादों का ब्रह्मांड काफी छोटा है, तो आप प्रति उपयोगकर्ता एक इंडेक्स बना सकते हैं।

मैंने इस तकनीक को बहुत अधिक कुशल पाया है, लेकिन सॉफ़्टवेयर समाधान का उपयोग करने का प्रासंगिक, सार्थक अनुभव बनाने में अधिक महत्वपूर्ण रूप से अधिक प्रभावी है।


1

गैर-समाधानों ने मेरे लिए अच्छा काम किया। खासकर जब बहुत सारे अंतराल होते हैं और सेट छोटा होता है। यह मेरे लिए बहुत अच्छा काम किया (php में):

$count = $collection->count($search);
$skip = mt_rand(0, $count - 1);
$result = $collection->find($search)->skip($skip)->limit(1)->getNext();

आप भाषा निर्दिष्ट करते हैं, लेकिन लाइब्रेरी का उपयोग नहीं कर रहे हैं?
बेंजामिन

FYI, यहाँ एक दौड़ की स्थिति है अगर पहली और तीसरी पंक्ति के बीच कोई दस्तावेज़ हटा दिया जाता है। इसके अलावा find+ skipबहुत बुरा है, आप सिर्फ एक का चयन करने के लिए सभी दस्तावेजों को वापस कर रहे हैं: एस।
मार्टिन कॉनेनी


1

रैंडम समाधान द्वारा मेरा PHP / MongoDB सॉर्ट / ऑर्डर। आशा है कि यह किसी को भी मदद करता है।

नोट: मेरे पास मेरे MongoDB संग्रह में संख्यात्मक ID है जो MySQL डेटाबेस रिकॉर्ड को संदर्भित करता है।

पहले मैं 10 यादृच्छिक रूप से उत्पन्न संख्याओं के साथ एक सरणी बनाता हूं

    $randomNumbers = [];
    for($i = 0; $i < 10; $i++){
        $randomNumbers[] = rand(0,1000);
    }

मेरे एकत्रीकरण में, मैं $ arrayFlemAt और $ mod (मापांक) के साथ संयुक्त $ addField पाइपलाइन ऑपरेटर का उपयोग करता हूं। मापांक ऑपरेटर मुझे 0 - 9 से एक नंबर देगा जो मैं तब यादृच्छिक उत्पन्न संख्याओं के साथ सरणी से नंबर लेने के लिए उपयोग करता हूं।

    $aggregate[] = [
        '$addFields' => [
            'random_sort' => [ '$arrayElemAt' => [ $randomNumbers, [ '$mod' => [ '$my_numeric_mysql_id', 10 ] ] ] ],
        ],
    ];

उसके बाद आप सॉर्ट पाइपलाइन का उपयोग कर सकते हैं।

    $aggregate[] = [
        '$sort' => [
            'random_sort' => 1
        ]
    ];

0

यदि आपके पास एक साधारण आईडी कुंजी है, तो आप सभी आईडी को एक सरणी में संग्रहीत कर सकते हैं, और फिर एक यादृच्छिक आईडी चुन सकते हैं। (माणिक उत्तर):

ids = @coll.find({},fields:{_id:1}).to_a
@coll.find(ids.sample).first

0

मैप / रिड्यूस का उपयोग करके, आप निश्चित रूप से एक यादृच्छिक रिकॉर्ड प्राप्त कर सकते हैं, बस जरूरी नहीं कि परिणामी फ़िल्टर किए गए संग्रह के आकार के आधार पर आपके साथ काम करना समाप्त हो।

मैंने 50,000 दस्तावेज़ों के साथ इस पद्धति का परीक्षण किया है (फ़िल्टर इसे लगभग 30,000 तक कम कर देता है), और यह एक इंटेल i3 पर लगभग 400ms में 16GB RAM और एक SATA3 HDD के साथ निष्पादित करता है ...

db.toc_content.mapReduce(
    /* map function */
    function() { emit( 1, this._id ); },

    /* reduce function */
    function(k,v) {
        var r = Math.floor((Math.random()*v.length));
        return v[r];
    },

    /* options */
    {
        out: { inline: 1 },
        /* Filter the collection to "A"ctive documents */
        query: { status: "A" }
    }
);

मानचित्र फ़ंक्शन क्वेरी से मेल खाने वाले सभी दस्तावेज़ों की आईडी का एक सरणी बनाता है। मेरे मामले में मैंने 50,000 संभावित दस्तावेजों में से लगभग 30,000 के साथ इसका परीक्षण किया।

रिड्यूस फ़ंक्शन केवल सरणी में 0 और आइटमों की संख्या (-1) के बीच एक यादृच्छिक पूर्णांक चुनता है, और उसके बाद __id देता है को सरणी से ।

400ms एक लंबे समय की तरह लगता है, और यह वास्तव में है, अगर आपके पास पचास हजार के बजाय पचास लाख रिकॉर्ड थे, तो यह ओवरहेड को उस बिंदु तक बढ़ा सकता है जहां यह बहु-उपयोगकर्ता स्थितियों में अनुपयोगी हो जाता है।

इस सुविधा को कोर में शामिल करने के लिए MongoDB के लिए एक खुला मुद्दा है ... https://jira.mongodb.org/browse/SERVER-533

यदि यह "यादृच्छिक" चयन आईडी में किसी सरणी को इकट्ठा करने और फिर एक का चयन करने के बजाय एक इंडेक्स-लुकअप में बनाया गया था, तो यह अविश्वसनीय रूप से मदद करेगा। (इसे वोट दें)


0

यह अच्छा काम करता है, यह तेज़ है, कई दस्तावेजों के साथ काम करता है और इसे आबादी वाले randक्षेत्र की आवश्यकता नहीं होती है , जो अंततः खुद को आबाद करेगा:

  1. अपने संग्रह में .rand फ़ील्ड में अनुक्रमणिका जोड़ें
  2. उपयोग खोजें और ताज़ा करें, कुछ इस तरह:
// Install packages:
//   npm install mongodb async
// Add index in mongo:
//   db.ensureIndex('mycollection', { rand: 1 })

var mongodb = require('mongodb')
var async = require('async')

// Find n random documents by using "rand" field.
function findAndRefreshRand (collection, n, fields, done) {
  var result = []
  var rand = Math.random()

  // Append documents to the result based on criteria and options, if options.limit is 0 skip the call.
  var appender = function (criteria, options, done) {
    return function (done) {
      if (options.limit > 0) {
        collection.find(criteria, fields, options).toArray(
          function (err, docs) {
            if (!err && Array.isArray(docs)) {
              Array.prototype.push.apply(result, docs)
            }
            done(err)
          }
        )
      } else {
        async.nextTick(done)
      }
    }
  }

  async.series([

    // Fetch docs with unitialized .rand.
    // NOTE: You can comment out this step if all docs have initialized .rand = Math.random()
    appender({ rand: { $exists: false } }, { limit: n - result.length }),

    // Fetch on one side of random number.
    appender({ rand: { $gte: rand } }, { sort: { rand: 1 }, limit: n - result.length }),

    // Continue fetch on the other side.
    appender({ rand: { $lt: rand } }, { sort: { rand: -1 }, limit: n - result.length }),

    // Refresh fetched docs, if any.
    function (done) {
      if (result.length > 0) {
        var batch = collection.initializeUnorderedBulkOp({ w: 0 })
        for (var i = 0; i < result.length; ++i) {
          batch.find({ _id: result[i]._id }).updateOne({ rand: Math.random() })
        }
        batch.execute(done)
      } else {
        async.nextTick(done)
      }
    }

  ], function (err) {
    done(err, result)
  })
}

// Example usage
mongodb.MongoClient.connect('mongodb://localhost:27017/core-development', function (err, db) {
  if (!err) {
    findAndRefreshRand(db.collection('profiles'), 1024, { _id: true, rand: true }, function (err, result) {
      if (!err) {
        console.log(result)
      } else {
        console.error(err)
      }
      db.close()
    })
  } else {
    console.error(err)
  }
})

ps। मोंगोडब प्रश्न में यादृच्छिक रिकॉर्ड कैसे प्राप्त करें इस प्रश्न के डुप्लिकेट के रूप में चिह्नित किया गया है। अंतर यह है कि इस सवाल का स्पष्ट रूप से हो रही यादृच्छिक दस्तावेज के बारे में एक दूसरे के रूप में एक रिकॉर्ड के बारे में स्पष्ट रूप से पूछता है रों


-2

यदि आप डॉक्यूमेंट-टू-ऑब्जेक्ट रैपर का उपयोग कर रहे हैं, तो आप रूबी में निम्न कार्य कर सकते हैं। (मान लें कि आपका मॉडल उपयोगकर्ता है)

User.all.to_a[rand(User.count)]

अपने .irbrc में, मेरे पास है

def rando klass
    klass.all.to_a[rand(klass.count)]
end

इसलिए रेल कंसोल में, मैं कर सकता हूँ, उदाहरण के लिए,

rando User
rando Article

किसी भी संग्रह से बेतरतीब ढंग से दस्तावेज़ प्राप्त करने के लिए।


1
यह बहुत ही अयोग्य है क्योंकि यह पूरे संग्रह को एक सारणी में पढ़ेगा और फिर एक रिकॉर्ड चुनेगा।
जॉनीएचके

ठीक है, शायद अक्षम है, लेकिन निश्चित रूप से सुविधाजनक है। यदि आपका डेटा आकार बहुत बड़ा नहीं है, तो इसे आज़माएँ
Zack Xu

3
ज़रूर, लेकिन मूल सवाल 100 मिलियन डॉक्स के संग्रह के लिए था, इसलिए यह उस मामले के लिए एक बहुत बुरा समाधान होगा!
जॉनीएचके

-2

अपनी क्वेरी निष्पादित करने के बाद आप फेरबदल-सरणी का भी उपयोग कर सकते हैं

var फेरबदल = आवश्यकता ('फेरबदल-सरणी');

लेखा.सेफ़ (क्यारी, फ़ंक्शन (ग़लती, परिणाम_कार)) {newIndexArr = फेरबदल (results_array);


-7

यह कुशलतापूर्वक और मज़बूती से काम करता है:

प्रत्येक दस्तावेज़ में "यादृच्छिक" नामक एक फ़ील्ड जोड़ें और इसे एक यादृच्छिक मान असाइन करें, यादृच्छिक क्षेत्र के लिए एक सूचकांक जोड़ें और निम्नानुसार आगे बढ़ें:

मान लें कि हमारे पास "लिंक" नामक वेब लिंक का एक संग्रह है और हम इसमें से एक यादृच्छिक लिंक चाहते हैं:

link = db.links.find().sort({random: 1}).limit(1)[0]

उसी लिंक को दूसरी बार पॉप अप करने के लिए सुनिश्चित करने के लिए, एक नए यादृच्छिक संख्या के साथ उसके यादृच्छिक क्षेत्र को अपडेट करें:

db.links.update({random: Math.random()}, link)

2
जब आप सिर्फ एक अलग यादृच्छिक कुंजी का चयन कर सकते हैं तो डेटाबेस को अपडेट क्यों करें ?
जेसन एस

आपके पास यादृच्छिक रूप से चयन करने के लिए कुंजियों की सूची नहीं हो सकती है।
माइक

तो आपको हर बार पूरे संग्रह को क्रमबद्ध करना होगा? और उन अशुभ रिकॉर्डों के बारे में क्या है जिन्हें बड़े यादृच्छिक संख्याएं मिलीं? उनका कभी चयन नहीं होगा।
18

1
आपको ऐसा करना होगा क्योंकि अन्य समाधान, विशेष रूप से MongoDB पुस्तक में सुझाए गए, काम नहीं करते हैं। यदि पहली खोज विफल हो जाती है, तो दूसरी खोज हमेशा आइटम को सबसे छोटे यादृच्छिक मान के साथ लौटाती है। यदि आप यादृच्छिक रूप से अनुक्रमणित करते हैं तो पहली क्वेरी हमेशा आइटम को सबसे बड़े यादृच्छिक संख्या के साथ लौटाती है।
ट्रेनव्रेक

प्रत्येक दस्तावेज़ में एक फ़ील्ड जोड़ना? मुझे लगता है कि यह उचित नहीं है।
CS_noob
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.