Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MongoMapper: maintaining sort order of an association based off of a key as an array

So I created a relationship as such:

Class Business
    include MongoMapper::Document

    key  :published_review_ids , Array         , typecast: 'BSON::ObjectId'
    many :published_reviews    , class: Review , in: :published_review_ids
end

I use the published_review_ids to maintain the sort order of my reviews, this moving them up and down in the array.

So, accessing Business.first.published_review_ids gives me all my ids in the proper order. Accessing Business.first.published_reviews brings me back an array of reviews, but sorted by the default order (generation time).

Is there a way for me to tell this association to always sort based on the order of the array it is based off of?

As a side note, array.first and array.last don't appear to function properly on the returned array of Business.first.published_reviews. Here is a gist that shows some examples of the behavior: https://gist.github.com/09c9a0a23dc67f30a76d

like image 786
Cory LaNou Avatar asked Nov 19 '25 04:11

Cory LaNou


1 Answers

No. The list is unsorted because of how MongoDB works. The query that hits Mongo looks like...

{
  "_id": { "$in" => [ObjectId('...'), ObjectId('...'), ObjectId('...')] }
}

...and Mongo's only guarantee is that it will return all the documents that match the query.

If you want to have a default order for your association, you should be able to add it to the declaration.

many :published_reviews, class: Review , in: :published_review_ids, order: :published_at.desc

You could also do this to solve your problem more exactly:

def sorted_published_reviews
  published_review_ids.map do |id|
    # to_a will only fire a Mongo query the first time through this loop
    published_reviews.to_a.find { |review| review.id == id }
  end
end

And to your aside:

Calling first and last directly on an association fires queries. Without a sort order, you won't get anything different for them. (see the plucky source)

The following will load the whole association and pull the first/last out of the internal Ruby array:

my_business.published_reviews[0]
my_business.published_reviews[-1]
my_business.published_reviews.to_a.first
my_business.published_reviews.to_a.last
like image 119
Brian Hempel Avatar answered Nov 20 '25 18:11

Brian Hempel



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!