Search code examples
ruby-on-railsdatabasemongodbruby-on-rails-7enumerator

Use Enumerators in Rails 7 with MongoDB


I'm building a new project with Rails 7 and MongoDB 8. And I wanted to use enumerators for multiple fields ( states etc .. )

I wanted to use the gem mongoid-enum but it doesn't work with Mongo 8.

Is switching to SQL database a solution ? Or is there any other way ? I've checked on Mongo's doc and found a Phantom Custom Field Types but it looks like it's not saving in the db. In the rails console, I'll do the Model.status = "open" then saving it, it doesn't return any errors. So I close the console then open it again. Run the Model.status and it returns nil.

Thank you for reading and trying to help me !


Solution

  • First of all, there is good and bad in both MongoDB and PostgreSQL, it depends of the kind of features you need, see: https://www.geeksforgeeks.org/difference-between-postgresql-and-mongodb/

    About the Phantom Custom Field Types, this is indeed doing the same stuff than ActiveRecord::Enum but asking more code to write. Could you share the not working code that you've run for your test please ?

    Edit 07/02/22:

    Here is an example of you could use enum in mongo without writing too much code:

    module MongoEnum
      # Takes application-scope value and converts it to how it would be
      # stored in the database. Converts invalid values to nil.
      def mongoize(object)
        mapping[object]
      end
    
      # Get the value as it was stored in the database, and convert to
      # application-scope value. Converts invalid values to nil.
      def demongoize(object)
        inverse_mapping[object]
      end
    
      # Converts the object that was supplied to a criteria and converts it
      # into a query-friendly form. Returns invalid values as is.
      def evolve(object)
        mapping.fetch(object, object)
      end
    
      def mapping
        @mapping ||= self.const_get(:MAPPING).freeze
      end
    
      def inverse_mapping
        @inverse_mapping ||= mapping.invert.freeze
      end
    end
    
    class RoleEnum
      extend MongoEnum
    
      MAPPING = {
        'admin' => 0,
        'user' => 1,
      }.freeze
    end
    
    class ColorEnum
      extend MongoEnum
    
      MAPPING = {
        'black' => 0,
        'white' => 1,
      }.freeze
    end
    
    class Profile
      include Mongoid::Document
      field :color, type: ColorEnum
    end
    
    class User
      include Mongoid::Document
      field :role, type: RoleEnum
    end
    

    Disclaimer: I didn't test it in a real app, let me know if it does not work.