মঙ্গোডিবি থেকে র্যান্ডম রেকর্ড


336

আমি একটি বিশাল (100 মিলিয়ন রেকর্ড) থেকে এলোমেলো রেকর্ড পেতে চাইছি mongodb

এটি করার দ্রুত এবং সবচেয়ে কার্যকর উপায় কী? ডেটা ইতিমধ্যে সেখানে আছে এবং এমন কোনও ক্ষেত্র নেই যেখানে আমি একটি এলোমেলো সংখ্যা তৈরি করতে এবং এলোমেলো সারি পেতে পারি।

কোন পরামর্শ?


2
"মঙ্গোতে এলোমেলোভাবে একটি ফলাফল অর্ডার করা" শিরোনামে এই এই প্রশ্নটিও দেখুন । এলোমেলোভাবে কোনও ফলাফল সেট অর্ডার করার বিষয়ে চিন্তা করা এই প্রশ্নের আরও সাধারণ সংস্করণ - আরও শক্তিশালী এবং আরও কার্যকর।
ডেভিড জে

11
এই প্রশ্নটি পপিং আপ রাখে। মঙ্গোডিবি টিকিট ট্র্যাকারের কোনও সংগ্রহ থেকে র্যান্ডম আইটেমগুলি পেতে অনুরোধ করা বৈশিষ্ট্যের অনুরোধে সর্বশেষতম তথ্য সম্ভবত পাওয়া যাবে । স্থানীয়ভাবে প্রয়োগ করা হলে এটি সম্ভবত সবচেয়ে কার্যকর বিকল্প হবে। (আপনি যদি বৈশিষ্ট্যটি চান তবে এটিতে ভোট দিন))
ডেভিড জে

এটি কি তীক্ষ্ণ সংগ্রহ?
ডিলান টং

3
সঠিক উত্তর নীচে @ জননিএইচকে দিয়েছেন: db.mycoll.aggregate ($ $ নমুনা: {আকার: 1}})
ফ্লোরিয়ান

কেউ কি জানেন যে এটি কেবল প্রথম রেকর্ড নেওয়ার চেয়ে কতটা ধীর? আমি এটির জন্য বিতর্ক করছি যে এটি কোনও ক্রম করে কিছু করার জন্য এলোমেলো নমুনা গ্রহণ করা উপযুক্ত।
ডেভিড কং

উত্তর:


248

মঙ্গোডিবিয়ের ৩.২ প্রকাশের সাথে শুরু করে, আপনি $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
@ ম্যাথিউস আরাউজো যা আপনার কোনও রেকর্ড তবে ভাল পয়েন্ট চাইলে তাতে কিছু যায় আসে না
টবি

3
পেডেন্টিক হতে হবে না তবে প্রশ্নটি কোনও মঙ্গোডিবি সংস্করণ নির্দিষ্ট করে না, তাই আমি ধরে নিচ্ছি যে সাম্প্রতিকতম সংস্করণটি যুক্তিযুক্ত।
দালানমিলার

2
@Nepoxx দেখুন ডক্স প্রক্রিয়াকরণ জড়িত সংক্রান্ত।
জনিএইচকে

2
@ ব্রাইজেল যদি $ নমুনা পর্যায়ে কোনও মিলে যাওয়া নথি নির্বাচন না করে তবে কোনও কিছুর সাথে মিল না দেওয়ার মারাত্মক ত্রুটি থাকতে পারে।
জনিএইচকে

115

সমস্ত রেকর্ডের একটি গণনা করুন, 0 এবং গণনার মধ্যে একটি এলোমেলো সংখ্যা তৈরি করুন এবং তারপর করুন:

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

139
দুর্ভাগ্যক্রমে স্কিপ () বরং অকার্যকর যেহেতু এটিতে অনেকগুলি নথি স্ক্যান করতে হয়। এছাড়াও, একটি রেসের শর্ত রয়েছে যদি গণনা পাওয়ার এবং ক্যোরি চালানোর মধ্যে সারিগুলি সরানো হয়।
মিশরীয়

6
নোট করুন যে এলোমেলো সংখ্যা 0 এবং গণনা (একচেটিয়া) এর মধ্যে হওয়া উচিত। উদাহরণস্বরূপ, যদি আপনার 10 টি আইটেম থাকে তবে এলোমেলো সংখ্যা 0 এবং 9 এর মধ্যে হওয়া উচিত Otherwise
ম্যাট

4
ধন্যবাদ, আমার উদ্দেশ্যগুলির জন্য পুরোপুরি কাজ করেছে। @ এমস্টেরন, দক্ষতা এবং জাতি উভয় শর্তাবলী সম্পর্কে আপনার মন্তব্যগুলি বৈধ, তবে সংগ্রহগুলির ক্ষেত্রে যেখানে কোনটিই গুরুত্বপূর্ণ নয় (যেখানে রেকর্ডগুলি মুছে ফেলা হয় না এমন সংগ্রহে এককালীন সার্ভার-সাইড ব্যাচ এক্সট্রাক্ট)), এটি হ্যাকির চেয়ে যথেষ্ট উন্নত (আইএমও) is মঙ্গো কুকবুকের সমাধান।
মাইকেল মউসা

4
-1 এর সীমাটি কী করবে?
MonkeyBonkey

@ মোঙ্কিবোনকি ডকসস.মঙ্গোদবি.আর. / মেটা- ড্রাইভার/ লেটস্ট / গ্যালসি / "" যদি সংখ্যাটোরেটরান 0 হয়, ডিবি ডিফল্ট রিটার্ন আকার ব্যবহার করবে If যদি নম্বরটি নেতিবাচক হয়, তবে ডাটাবেসটি সেই নম্বরটি ফিরে আসবে এবং কার্সারটি বন্ধ করবে। "
ceejayoz

86

মঙ্গোডিবি ৩.২ এর জন্য আপডেট

3.2 একত্রিত পাইপলাইনে নমুনা প্রবর্তিত ।

এটি ব্যবহারে প্রয়োগ করার জন্য একটি ভাল ব্লগ পোস্টও রয়েছে।

পুরানো সংস্করণগুলির জন্য (পূর্ববর্তী উত্তর)

এটি আসলে একটি বৈশিষ্ট্য অনুরোধ ছিল: http://jira.mongodb.org/browse/SERVER-533 তবে এটি "উইল ফিক্স" এর অধীনে দায়ের করা হয়েছিল।

কোনও সংগ্রহের বাইরে এলোমেলো নথি নির্বাচন করার জন্য রান্নাঘরের একটি খুব ভাল রেসিপি রয়েছে: 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
এবং সংগ্রহের প্রতিটি নথিতে এলোমেলো ক্ষেত্র যুক্ত করার একটি সহজ উপায়। ফাংশন সেটআরন্ডম () b db.topics.find ()। forEach (ফাংশন (আপত্তি)। obj.random = ম্যাথ.রান্ডম (); db.topics.save (আপত্তি);}); b db.eval (setRandom);
জেফ্রি

8
এটি এলোমেলোভাবে একটি দস্তাবেজ নির্বাচন করে, তবে আপনি যদি এটি একাধিকবার করেন, তবে অনুসন্ধানগুলি স্বাধীন হয় না। এলোমেলো সুযোগের আদেশের চেয়ে আপনি একই ডকুমেন্টটি পরপর দু'বার পাওয়ার সম্ভাবনা বেশি।
অভাব 2

12
বিজ্ঞপ্তি হ্যাশিং এর খারাপ প্রয়োগের মত দেখাচ্ছে। এটি অভাবীদের বলার চেয়েও খারাপ: এমনকি একটি চেহারাও পক্ষপাতদুষ্ট কারণ এলোমেলো সংখ্যা সমানভাবে বিতরণ করা হয় নি। এটি সঠিকভাবে করতে, আপনার ডকুমেন্টে 10 টি এলোমেলো সংখ্যার একটি সেট প্রয়োজন। আপনি প্রতিটি নথিতে যত বেশি এলোমেলো সংখ্যা ব্যবহার করবেন, আউটপুট বিতরণ ততই অভিন্ন হবে।
টমাস

4
মঙ্গোডিবি জিরার টিকিটটি এখনও বেঁচে আছে: jira.mongodb.org/browse/SERVER-533 মন্তব্যটি যান এবং যদি আপনি বৈশিষ্ট্যটি চান তবে ভোট দিন।
ডেভিড জে।

1
উল্লিখিত ক্যাভিয়েটের ধরণটি নোট করুন। এটি অল্প পরিমাণে দস্তাবেজগুলির সাথে দক্ষতার সাথে কাজ করে না। 3 ও 63 এর র্যান্ডম কী দিয়ে দুটি আইটেম দেওয়া ডকুমেন্ট # 63 আরো ঘন ঘন নির্বাচন করা হবে যেখানে $gteপ্রথম। বিকল্প সমাধান স্ট্যাকওভারফ্লো.com/a/9499484/79201 এক্ষেত্রে আরও ভাল কাজ করবে।
রায়ান শুমাচার

56

এলোমেলো সংখ্যায় 'নিকটতম' নথিটি নির্বাচন করতে আপনি মঙ্গোডিবি-র জিওপ্যাটিয়াল ইনডেক্সিং বৈশিষ্ট্যটিও ব্যবহার করতে পারেন।

প্রথমত, সংগ্রহের স্থানে জিওপ্যাটিয়াল সূচক সক্ষম করুন:

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 )

এর জন্য কেবলমাত্র একটি কোয়েরি প্রয়োজন এবং নাল চেক নেই, এবং কোডটি পরিষ্কার, সহজ এবং নমনীয়। এমনকি আপনি জিওপয়েন্টের ওয়াই-অক্ষগুলি আপনার ক্যোয়ারিতে দ্বিতীয় এলোমেলো মাত্রা যুক্ত করতে ব্যবহার করতে পারেন।


8
আমি এই উত্তরটি পছন্দ করি, এটি আমি সবচেয়ে কার্যকর এটি দেখেছি যে এটিতে সার্ভারের দিক থেকে গোলমাল করার দরকার নেই।
টনি মিলিয়ন

4
এটি নথিগুলির প্রতি পক্ষপাতদুষ্ট যেগুলির আশেপাশে কয়েকটি পয়েন্ট থাকে।
টমাস

6
এটি সত্য, এবং এছাড়াও অন্যান্য সমস্যাগুলি রয়েছে: নথিগুলি তাদের র্যান্ডম কীগুলির সাথে দৃ strongly়ভাবে সম্পর্কিত হয়, তাই আপনি একাধিক নথি নির্বাচন করলে কোন দলিল দল হিসাবে ফিরে আসবে তা অত্যন্ত অনুমানযোগ্য। এছাড়াও, সীমানা (0 এবং 1) এর নিকটবর্তী নথিগুলি বেছে নেওয়ার সম্ভাবনা কম। পরবর্তীগুলি গোলাকার জিওম্যাপিং ব্যবহার করে সমাধান করা যেতে পারে যা প্রান্তগুলিতে প্রায় আবৃত থাকে। তবে, আপনার এই উত্তরটি একটি নিখুঁত এলোমেলো নির্বাচন ব্যবস্থা হিসাবে নয়, রান্নাঘরের রেসিপিটির উন্নত সংস্করণ হিসাবে দেখতে হবে। বেশিরভাগ উদ্দেশ্যে এটি এলোমেলো।
নিকো ডি পোয়েল

@ নিকডপোয়েল, আপনার উত্তরটি আপনার মতামতও আমার পছন্দ হয়েছে! এবং আপনার জন্য আমার কয়েকটি প্রশ্ন রয়েছে: 1- আপনি কীভাবে জানবেন যে 0 এবং 1 এর সীমাটির নিকটবর্তী পয়েন্টগুলি বেছে নেওয়ার সম্ভাবনা কম, এটি কি কোনও গাণিতিক ভিত্তির উপর ভিত্তি করে ?, 2- আপনি গোলাকার জিওম্যাপিংয়ের উপর আরও বিস্তৃত করতে পারেন, এটি কীভাবে এলোমেলো নির্বাচনের উন্নতি করবে এবং এটি মঙ্গোডিবিতে কীভাবে করবেন? ... প্রশংসা!
সিকিউরকুরভে

আপনার ধারণা প্রশংসিত। অবশেষে, আমার কাছে একটি দুর্দান্ত কোড যা অনেকটা সিপিইউ এবং র‌্যাম বান্ধব! আপনাকে ধন্যবাদ
কায়স ভশরত

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()(সিজেজয়ের) পদ্ধতির চেয়ে অনেক দ্রুত এবং মাইকেল দ্বারা রিপোর্টিত "কুকবুক" পদ্ধতির চেয়ে আরও অভিন্ন র্যান্ডম নথি তৈরি করে:

1,000,000 উপাদান সহ সংগ্রহের জন্য:

  • এই পদ্ধতিটি আমার মেশিনে মিলিসেকেন্ডের চেয়ে কম সময় নেয়

  • skip()পদ্ধতি গড়ে 180 MS লাগে

কুকবুক পদ্ধতিটি বিপুল সংখ্যক দস্তাবেজকে কখনই বাছাই করতে না পারে কারণ এলোমেলো সংখ্যা তাদের পছন্দ করে না।

  • এই পদ্ধতিটি সময়ের সাথে সাথে সমস্ত উপাদানকে সমানভাবে চয়ন করবে।

  • আমার মানদণ্ডে এটি কুকবুক পদ্ধতির চেয়ে 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মান হিসাবে পূর্ণসংখ্যা ব্যবহার করা মূলত সরল তবে পয়েন্টগুলিতে একই প্রাথমিক ধারণা idea


আমার 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 v4.2 মধ্যে অসমর্থিত হয়েছে।
ব্যবহারকারী3848207

8

এখন আপনি সমষ্টিটি ব্যবহার করতে পারেন। উদাহরণ:

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

ডকটি দেখুন


3
দ্রষ্টব্য: $ নমুনা একাধিকবার একই দস্তাবেজটি পেতে পারে
সামান শাফি

6

অফ করার জন্য কোনও ডেটা না থাকলে এটি শক্ত। _ম ক্ষেত্র কি? তারা কি মোগডব অবজেক্ট আইডি? যদি তা হয় তবে আপনি সর্বোচ্চ এবং সর্বনিম্ন মান পেতে পারেন:

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
কোনও ধারণা কীভাবে পিএইচপি-তে লাগবে? বা কমপক্ষে আপনি কোন ভাষাটি উপরে ব্যবহার করেছেন? এটা কি পাইথন?
মার্সিন

6

পাইথন (পাইমোঙ্গো) ব্যবহার করে সামগ্রিক ফাংশনও কাজ করে।

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

এই পদ্ধতিরটি এলোমেলো সংখ্যার (যেমন: সংগ্রহ.ফাইন্ড ([র্যান্ডম_িন্ট]) কোয়েরি চালানোর চেয়ে অনেক দ্রুত । এটি বিশেষত বৃহত সংগ্রহের ক্ষেত্রে।


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), আনলাইন এড়িয়ে যাওয়া () বা গণনা () এ এখানে অন্যান্য সমাধানগুলিতে ব্যবহৃত হয়
মার্মোর

4

পিএইচপি-তে আমার সমাধান:

/**
 * 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

আমি মানচিত্র / হ্রাস ব্যবহার করার পরামর্শ দেব, যেখানে আপনি মানচিত্রের ফাংশনটি কেবলমাত্র নির্গত করতে ব্যবহার করেন যখন কোনও র্যান্ডম মান একটি নির্দিষ্ট সম্ভাবনার উপরে থাকে above

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') নির্গত হয়।

"সম্ভাব্যতা" এর মানটি "সুযোগ" এ সংজ্ঞায়িত করা হয়, যখন ম্যাপরেড (...) চালনার সময়

এই জাতীয় মানচিত্রের ব্যবহার একটি শার্পড ডিবিতে ব্যবহারযোগ্য হবে।

আপনি যদি ডিবি থেকে এম ডকুমেন্টগুলির ঠিক এন নির্বাচন করতে চান তবে আপনি এটি এটি করতে পারেন:

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

যেখানে "কাউন্টটোটাল" (এম) হ'ল ডিবিতে নথির সংখ্যা এবং "কাউন্টসউবসেট" (এন) হ'ল নথিগুলির পুনরুদ্ধার করার সংখ্যা।

এই পদ্ধতির ধারালো ডাটাবেসগুলিতে কিছু সমস্যা হতে পারে।


4
1 টি উপাদান ফেরত দেওয়ার জন্য একটি সম্পূর্ণ সংগ্রহের স্ক্যান করছে ... এটি করার জন্য এটি সর্বনিম্ন দক্ষ কৌশল হতে হবে।
টমাস

1
কৌশলটি হ'ল এটি এলোমেলো উপাদানগুলির একটি স্বেচ্ছাসেবী সংখ্যার ফিরিয়ে আনার জন্য একটি সাধারণ সমাধান - যা ক্ষেত্রে> 2 এলোমেলো উপাদান পাওয়ার সময় এটি অন্যান্য সমাধানগুলির চেয়ে দ্রুততর হয় be
torbenl

2

আপনি এলোমেলো _আইডি বাছাই করতে পারেন এবং সংশ্লিষ্ট বস্তুটি ফিরে আসতে পারেন:

 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

আমি প্রতিটি বস্তুর সাথে একটি এলোমেলো ইন ফিল্ড যুক্ত করার পরামর্শ দিই। তারপরে আপনি কেবল একটি করতে পারেন

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

একটি এলোমেলো নথি বাছাই। কেবলমাত্র নিশ্চিত করুন যে আপনি সূচকটি নিশ্চিত করেছেন (om র্যান্ডম_ফিল্ড: 1})


2
যদি আপনার সংগ্রহে প্রথম রেকর্ডটির তুলনামূলকভাবে উচ্চ র্যান্ডম_ফিল্ডের মান থাকে তবে এটি প্রায় পুরোপুরি ফিরে আসবে না?
thehiatus

2
থাইথাসটি সঠিক, এটি হবে - এটি কোনও কাজের জন্য উপযুক্ত নয়
হেপটিক

7
এই সমাধানটি সম্পূর্ণরূপে ভুল, একটি এলোমেলো সংখ্যার যোগ করা (আসুন কল্পনা করুন 0 থেকে 2 ^ 32-1 এর মধ্যে) কোনও ভাল বিতরণের গ্যারান্টি দেয় না এবং $ gte ব্যবহার করা এটি আরও খারাপ করে তোলে, আপনার এলোমেলো নির্বাচনের কারণে এমনকি নিকটবর্তী হবে না ছদ্ম-এলোমেলো সংখ্যায়। আমি এই ধারণাটি কখনও ব্যবহার না করার পরামর্শ দিই।
ম্যাক্সিমিলিয়ানো রিওস

1

আমার যখন অনুরূপ সমাধানের মুখোমুখি হয়েছিলাম তখন আমি ব্যাকট্র্যাক করে দেখতে পেলাম যে ব্যবসায়ের অনুরোধটি উপস্থাপিত হচ্ছে এমন কিছু আবর্তনের ঘূর্ণন তৈরির জন্য। সেক্ষেত্রে আরও অনেক ভাল বিকল্প রয়েছে, যার সোলারের মতো অনুসন্ধান ইঞ্জিনগুলির উত্তর রয়েছে, মঙ্গোডিবিয়ের মতো ডেটা স্টোর নয়।

সংক্ষেপে, কন্টেন্টকে "বুদ্ধিমানভাবে ঘোরানোর" প্রয়োজনীয়তার সাথে, সমস্ত নথির মধ্যে একটি এলোমেলো সংখ্যার পরিবর্তে আমাদের কী করা উচিত তা হল ব্যক্তিগত কিউ স্কোর সংশোধক অন্তর্ভুক্ত করা। এটি ব্যবহারকারীর একটি অল্প সংখ্যক জনসংখ্যা ধরে ধরে নিজেকে বাস্তবায়নের জন্য, আপনি ব্যবহারকারী প্রতি নথি সংরক্ষণ করতে পারেন যা প্রোডাক্ট আইডি, ছাপ গণনা, ক্লিক-থ্রো গণনা, সর্বশেষ দেখা তারিখ এবং অন্যান্য যে কোনও কারণের সাথে ব্যবসা একা স্কোর গণনা করতে অর্থবহ বলে মনে করে পরিবর্তক। প্রদর্শনের জন্য সেটটি পুনরুদ্ধার করার সময়, সাধারণত আপনি শেষ ব্যবহারকারী দ্বারা অনুরোধের চেয়ে ডেটা স্টোর থেকে আরও ডকুমেন্টের জন্য অনুরোধ করেন, তারপরে কিউ স্কোর সংশোধক প্রয়োগ করুন, শেষ ব্যবহারকারীর দ্বারা অনুরোধ করা রেকর্ডের সংখ্যাটি নিন, তারপরে ফলাফলের পৃষ্ঠাটি এলোমেলো করুন, একটি ক্ষুদ্র সেট করুন, তাই সহজেই অ্যাপ্লিকেশন স্তরের নথিকে বাছাই করুন (মেমরিতে)

ব্যবহারকারীদের মহাবিশ্ব যদি খুব বড় হয় তবে আপনি ব্যবহারকারীর পরিবর্তে আচরণ গ্রুপ এবং সূচকগুলিতে ব্যবহারকারীদের শ্রেণীবদ্ধ করতে পারেন।

পণ্যগুলির মহাবিশ্ব যদি যথেষ্ট ছোট হয় তবে আপনি ব্যবহারকারী প্রতি সূচক তৈরি করতে পারেন।

আমি এই কৌশলটি অনেক বেশি দক্ষ হিসাবে খুঁজে পেয়েছি তবে সফ্টওয়্যার সমাধানটি ব্যবহারের একটি প্রাসঙ্গিক, সার্থক অভিজ্ঞতা তৈরিতে আরও গুরুত্বপূর্ণভাবে কার্যকর।


1

সমাধানগুলি আমার পক্ষে ভাল কাজ করেছে। বিশেষত যখন অনেকগুলি ফাঁক থাকে এবং সেটটি ছোট হয়। এটি আমার জন্য খুব ভাল কাজ করেছে (পিএইচপি তে):

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

আপনি ভাষাটি নির্দিষ্ট করেছেন, তবে আপনি যে লাইব্রেরিটি ব্যবহার করছেন তা নয়?
বেনিয়ামিন

এফওয়াইআই, প্রথম এবং তৃতীয় লাইনের মধ্যে কোনও নথি সরানো থাকলে এখানে একটি রেসের শর্ত রয়েছে। এছাড়াও find+ skipবেশ খারাপ, আপনি কেবলমাত্র একটি বেছে নিতে সমস্ত নথি ফিরিয়ে দিচ্ছেন: এস।
মার্টিন কনেকনি


1

আমার পিএইচপি / মঙ্গোডিবি সাজান / RANDOM সমাধান দ্বারা অর্ডার। আশা করি এটি যে কাউকে সাহায্য করবে।

দ্রষ্টব্য: আমার মংগোডিবি সংগ্রহের মধ্যে আমার কাছে সংখ্যার আইডি রয়েছে যা একটি মাইএসকিউএল ডাটাবেস রেকর্ডকে উল্লেখ করে।

প্রথমে আমি এলোমেলোভাবে 10 টি এলোমেলোভাবে উত্পন্ন সংখ্যা সহ তৈরি করি

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

আমার সমষ্টিতে আমি $ অ্যারেএলেম্যাট এবং $ মোড (মডুলাস) এর সাথে মিলিত $ অ্যাডফিল্ড পাইপলাইন অপারেটরটি ব্যবহার করি। মডুলাস অপারেটর আমাকে 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 এ হ্রাস করে) এবং এটি প্রায় 4০০ মিমি ইনটেল আই 3 তে 16 জিবি র‌্যাম এবং একটি এসএটিএ 3 এইচডি সহ কার্যকর করে ...

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 ফিরিয়ে দেয়।

400 মিমিগুলি দীর্ঘ সময়ের মতো শোনাচ্ছে এবং সত্যই এটি হ'ল যদি আপনার পঞ্চাশ হাজারের পরিবর্তে পঞ্চাশ মিলিয়ন রেকর্ড থাকে তবে এটি ওভারহেডটি এমন পর্যায়ে বাড়িয়ে দিতে পারে যেখানে এটি বহু ব্যবহারকারীর পরিস্থিতিতে অকার্যকর হয়ে পড়ে।

এই বৈশিষ্ট্যটি মূলটিতে অন্তর্ভুক্ত করার জন্য মংগোডিবিয়ের জন্য একটি উন্মুক্ত সমস্যা রয়েছে ... https://jira.mongodb.org/browse/SERVER-533

যদি এই "এলোমেলো" নির্বাচনটি একটি অ্যারেতে আইডি সংগ্রহ করার পরিবর্তে সূচি-দৃষ্টিতে তৈরি করা হয় এবং একটি নির্বাচন করা হয়, এটি অবিশ্বাস্যরূপে সহায়তা করবে help (ভোট দিন!)


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

পুনশ্চ. মংডোব প্রশ্নে এলোমেলো রেকর্ডগুলি কীভাবে পাওয়া যায় এই প্রশ্নের সদৃশ হিসাবে চিহ্নিত করা হয়। পার্থক্য হচ্ছে এই প্রশ্নের স্পষ্টভাবে পেয়ে র্যান্ডম ডকুমেন্ট সম্পর্কে অন্যান্য এক হিসাবে একক রেকর্ড সম্পর্কে স্পষ্টভাবে জিজ্ঞেস করল গুলি


-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
এটি মারাত্মকভাবে অক্ষম কারণ এটি পুরো সংগ্রহটি একটি অ্যারেতে পড়বে এবং তারপরে একটি রেকর্ড চয়ন করবে।
জনিএইচকে

ঠিক আছে, সম্ভবত অদক্ষ, তবে অবশ্যই সুবিধাজনক। আপনার ডেটার আকার খুব বড় না হলে এটি ব্যবহার করে দেখুন
জ্যাক Xu

3
অবশ্যই, তবে মূল প্রশ্নটি ছিল 100 মিলিয়ন ডক্স সহ একটি সংগ্রহের জন্য তাই এই ক্ষেত্রে এটির জন্য খুব খারাপ সমাধান হতে পারে!
জনিএইচকে

-2

আপনি আপনার ক্যোয়ারি চালানোর পরে শফল -অ্যারে ব্যবহার করতে পারেন

var shuffle = প্রয়োজনীয় ('শিফেল-অ্যারে');

অ্যাকাউন্টস.ফাইন্ড (ক্রি, ফাংশন (ত্রুটি, ফলাফল_আররে)) {নতুনআইডেক্সএআরআর = শ্যাফেল (ফলাফল_আররে);


-7

দক্ষতা এবং নির্ভরযোগ্যভাবে যা কাজ করে তা হ'ল:

প্রতিটি নথিতে "এলোমেলো" নামে একটি ক্ষেত্র যুক্ত করুন এবং এটিকে একটি এলোমেলো মান নির্ধারণ করুন, এলোমেলো ক্ষেত্রের জন্য একটি সূচক যুক্ত করুন এবং নীচে এগিয়ে যান:

ধরে নেওয়া যাক আমাদের কাছে "লিঙ্কগুলি" নামে পরিচিত ওয়েব লিঙ্কগুলির একটি সংগ্রহ রয়েছে এবং আমরা এটি থেকে একটি এলোমেলো লিঙ্ক চাই:

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

একই লিঙ্কটি দ্বিতীয়বার পপ আপ হবে না তা নিশ্চিত করতে, একটি নতুন এলোমেলো সংখ্যার সাথে এর এলোমেলো ক্ষেত্রটি আপডেট করুন:

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

2
আপনি যখন অন্য কোনও এলোমেলো কী নির্বাচন করতে পারেন তখন কেন ডাটাবেস আপডেট করবেন?
জেসন এস

এ থেকে এলোমেলোভাবে নির্বাচন করার জন্য আপনার কাছে কীগুলির একটি তালিকা নাও থাকতে পারে।
মাইক

সুতরাং আপনি প্রতিটি সময় পুরো সংগ্রহ বাছাই করতে হবে? এবং দুর্ভাগ্য রেকর্ডগুলির কী যে বড় এলোমেলো নম্বর পেয়েছে? তাদের কখনও নির্বাচিত করা হবে না।
ফ্যানটিয়াস

1
আপনাকে এটি করতে হবে কারণ অন্যান্য সমাধানগুলি, বিশেষত মঙ্গোডিবি বইয়ের প্রস্তাবিত প্রস্তাবগুলি কার্যকর হয় না। যদি প্রথম সন্ধান ব্যর্থ হয় তবে দ্বিতীয় সন্ধানটি সর্বদা স্বল্পতম এলোমেলো মান সহ আইটেমটি ফিরিয়ে দেয়। যদি আপনি এলোমেলোভাবে উত্সাহিত করে প্রথম ক্যোয়ারী সর্বদা বৃহত্তম এলোমেলো সংখ্যার সাথে আইটেমটি ফেরত দেয়।
ট্রেনের বিধ্বস্ত

প্রতিটি নথিতে একটি ক্ষেত্র যুক্ত? আমি মনে করি এটি পরামর্শযুক্ত নয়।
সিএস_নুব
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.