Mongoid random document

前端 未结 9 1391
迷失自我
迷失自我 2021-02-06 00:49

Lets say I have a Collection of users. Is there a way of using mongoid to find n random users in the collection where it does not return the same user twice? For now lets say

相关标签:
9条回答
  • 2021-02-06 01:23

    Just encountered such a problem. Tried

    Model.all.sample
    

    and it works for me

    0 讨论(0)
  • 2021-02-06 01:25

    MongoDB 3.2 comes to the rescue with $sample (link to doc)

    EDIT : The most recent of Mongoid has implemented $sample, so you can call YourCollection.all.sample(5)

    Previous versions of mongoid

    Mongoid doesn't support sample until Mongoid 6, so you have to run this aggregate query with the Mongo driver :

    samples = User.collection.aggregate([ { '$sample': { size: 3 } } ])
    # call samples.to_a if you want to get the objects in memory
    

    What you can do with that

    I believe the functionnality should make its way soon to Mongoid, but in the meantime

    module Utility
      module_function
      def sample(model, count)
        ids = model.collection.aggregate([ 
          { '$sample': { size: count } }, # Sample from the collection
          { '$project': { _id: 1} }       # Keep only ID fields
        ]).to_a.map(&:values).flatten     # Some Ruby magic
    
        model.find(ids)
      end
    end
    
    Utility.sample(User, 50)
    
    0 讨论(0)
  • 2021-02-06 01:27

    If you really want simplicity you could use this instead:

    class Mongoid::Criteria
    
      def random(n = 1)
        indexes = (0..self.count-1).sort_by{rand}.slice(0,n).collect!
    
        if n == 1
          return self.skip(indexes.first).first
        else
          return indexes.map{ |index| self.skip(index).first }
        end
      end
    
    end
    
    module Mongoid
      module Finders
    
        def random(n = 1)
          criteria.random(n)
        end
    
      end
    end
    

    You just have to call User.random(5) and you'll get 5 random users. It'll also work with filtering, so if you want only registered users you can do User.where(:registered => true).random(5).

    This will take a while for large collections so I recommend using an alternate method where you would take a random division of the count (e.g.: 25 000 to 30 000) and randomize that range.

    0 讨论(0)
提交回复
热议问题