meder omuraliev
meder omuraliev

Reputation: 186572

Retrieve unique random items from a mongodb collection?

I run an IRC bot and I have a function which returns 1 random url using Math.random at the moment, from my Mongodb collection.

I would like to refactor it to return x number of unique items, and for each subsequent invocation of the url fetching command .getlinks I would like that it keeps everything unique, so that a user doesn't see the same link unless all the possible links have been already returned.

Is there some algorithm or native mongodb function I could use for this?

Here's a sample scenario:

I have a total of 9 records in the collection. They have a _id and url field.

user a: .getlinks()
bot returns: http://unique-link-1, http://unique-link-2, http://unique-link-3, http://unique-link-4

user a: .getlinks()
bot returns: http://unique-link-5, http://unique-link-6, http://unique-link-7, http://unique-link-8

user a: .getlinks()
bot returns: http://unique-link-9, http://unique-link-6, http://unique-link-1, http://unique-link-3

Background information:

Currently the only thing I can think of is keeping an array of all returned items, and grabbing all items from the collection at once and getting a random one 4 times and making sure it's unique and hasn't been shown already.

var shown = [], amountToReturn = 4;
function getLinks() {
   var items = links.find(), returned = [];
   for ( var i = 0; i<amountToReturn; i++ ) {
      var rand = randItem( items );
      if ( shown.indexOf( rand.url ) == -1 && shown.length < items.length ) ) {
         returned.push( rand.url );
      }
   }
   message.say( returned.join(',') );
}

Upvotes: 1

Views: 2844

Answers (2)

iron9light
iron9light

Reputation: 1205

Get random records form mongodb via map/reduce

// map
function() { 
    emit(0, {k: this, v: Math.random()}) 
}

// reduce
function(k, v) {
  var a = []
  v.forEach(function(x) {
    a = a.concat(x.a ? x.a : x)
  })
  return {a:a.sort(function(a, b) {
    return a.v - b.v;
  }).slice(0, 3 /*how many records you want*/)}; 
}

// finalize
function(k, v) {
  return v.a.map(function(x) {
    return x.k
  })
}

Upvotes: 1

Justin Jenkins
Justin Jenkins

Reputation: 27080

You should find a number of possible options to get random item(s) from Collection here ...

http://jira.mongodb.org/browse/SERVER-533

Another intersting method is documented here ...

http://cookbook.mongodb.org/patterns/random-attribute/

The method mentioned above basically creates a new key/value on the document using Math.random()

> db.docs.drop()
> db.docs.save( { key : 1, ..., random : Math.random() } )
> db.docs.save( { key : 1, ..., random : Math.random() } )
> db.docs.save( { key : 2, ..., random : Math.random() } )
... many more insertions with 'key : 2' ...
> db.docs.save( { key : 2, ..., random : Math.random() } )
...

Upvotes: 2

Related Questions