Use Enumerators in Rails 7 with MongoDB

207 Views Asked by At

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 !

1

There are 1 best solutions below

3
Sébastien P. On BEST ANSWER

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.