Search code examples
sortingmongodbmongomappermodel-associations

Monggomapper : Sort data with its Association Model's keys


This is basically a simple task, but I can't find a way how to perform a sort/order data in Mongomapper Model with keys that belongs from its association.

Like suppose I have two models :

class Product 
 include MongoMapper::Document

 key :name, String
 key :product_category_id, ObjectId

 belongs_to :product_category
end

class ProductCategory
 include MongoMapper::Document

 key :name, String, :required => true, :unique => true
end

All I want to do is simple get the data from 'Product' that sorted by its 'Product Category's Name'.. I've already tried :

Product.where(:name => /#{@keyword}/i).sort("product_category.name".to_sym)

but not working, and many others ways that perform an error in return :(...

Is anybody could help me to solve this ?? Really appreciate a lot...

Thanks

Regards, Ronny


Solution

  • As specified in your question, Product and ProductCategory are mapped to separate collections in MongoDB. As such, you cannot do a SQL join, sort, select, and unique. MongoDB will not access more than one collection for a normal query. You can sort the products in local memory, but this requires fetching both the products and their associated product_categories.

    Products.all.sort{|a, b| a.product_category.name <=> b.product_category.name}
    

    However, you can take advantage of embedding so that the data is all in one collection, and therefore easily sortable by MongoDB.

    class Product
      include MongoMapper::Document
    
      key :name, String
      one :product_category
    end
    
    class ProductCategory
      include MongoMapper::EmbeddedDocument
    
      key :name, String
    end
    

    Note that ProductCategory is an EmbeddedDocument. The test below shows a MongoMapper sort clause by 'product_category.name'. Hope that this helps your understanding and progress.

    test/unit/product_test.rb

    require 'test_helper'
    
    class ProductTest < ActiveSupport::TestCase
      def setup
        Product.delete_all
      end
    
      test "association sort" do
        Product.create(name: 'Act of Valor', product_category: ProductCategory.new(name: 'movie'))
        Product.create(name: 'Oliver Twist', product_category: ProductCategory.new(name: 'book'))
        assert_equal(2, Product.count)
    
        products_by_db_order = Product.all.to_a
        assert_equal(['Act of Valor', 'Oliver Twist'], products_by_db_order.map(&:name))
        p products_by_db_order.map(&:name)
    
        products_by_category_order = Product.sort('product_category.name').to_a
        assert_equal(['Oliver Twist', 'Act of Valor'], products_by_category_order.map(&:name))
        p products_by_category_order.map(&:name)
      end
    end
    

    output

    Run options: --name=test_association_sort
    
    # Running tests:
    
    ["Act of Valor", "Oliver Twist"]
    ["Oliver Twist", "Act of Valor"]
    .
    
    Finished tests in 0.042368s, 23.6027 tests/s, 70.8082 assertions/s.
    
    1 tests, 3 assertions, 0 failures, 0 errors, 0 skips