持久性配置
文档存储
Mongoid默认将文档存储在以类名复数形式命名的集合中。对于以下Person
类,文档将存储在名为people
的集合中。
class Person include Mongoid::Document end
模型类名不能以"s"结尾,因为这会被视为单词的复数形式。例如,“Status”会被视为“Statu”的复数形式,这会导致一些已知问题。
这是Mongoid使用的ActiveSupport::Inflector#classify
的限制,它用于将文件名和集合名转换为类名。您可以通过为模型类指定自定义屈折规则来克服这个问题。例如,以下代码将处理名为Status
的模型。
ActiveSupport::Inflector.inflections do |inflect| inflect.singular("status", "status") end
如果希望将文档持久化到其他地方,可以在类级别更改模型的集合。您还可以更改模型持久化的数据库和客户端,而不是使用默认值。
class Person include Mongoid::Document store_in collection: "citizens", database: "other", client: "analytics" end
store_in
宏还可以接受lambda函数——这种情况下通常用于多租户应用程序。
class Band include Mongoid::Document store_in database: ->{ Thread.current[:database] } end
当文档存储在数据库中时,Ruby对象将被序列化为BSON,并具有如下结构
{ "_id" : ObjectId("4d3ed089fb60ab534684b7e9"), "title" : "Sir", "name" : { "_id" : ObjectId("4d3ed089fb60ab534684b7ff"), "first_name" : "Durran" }, "addresses" : [ { "_id" : ObjectId("4d3ed089fb60ab534684b7e0"), "city" : "Berlin", "country" : "Deutschland" } ] }
持久化上下文属性
Mongoid在模型类上提供了client_name
、database_name
和collection_name
方法,以确定用于持久化的客户端、数据库和集合名称。
Band.client_name # => :default Band.database_name # => "mongoid" Band.collection_name # => :bands
自定义
在某些情况下,您可能希望将文档持久化到默认值之外的不同来源,或者使用与默认值不同的选项。Mongoid为此也提供了运行时支持,以及基于每个模型的支撑。
模型级别持久化选项
在单个模型级别上,您可以指定存储到自定义集合名称、不同的数据库或不同的客户端。以下示例将默认将 Band 类存储在名为 "artists" 的集合中,该集合位于名为 "music" 的数据库中,使用名为 "analytics" 的客户端。
请注意,提供给 client
选项的值必须在您的 mongoid.yml 文件中的 clients
下进行配置。
class Band include Mongoid::Document store_in collection: "artists", database: "music", client: "analytics" end
如果没有提供 store_in
宏,Mongoid 将将模型存储在默认客户端的默认数据库中名为 "bands" 的集合中。
运行时持久化选项
可以通过在模型类或实例上使用with
方法来更改一组操作使用的客户端、数据库和集合,以及MongoDB客户端的任何选项,用于持久化。
Band.with(database: "music-non-stop") do |klass| klass.create(...) band = Band.first Band.create(...) end Band.with(collection: "artists") do |klass| klass.delete_all Band.delete_all end band.with(client: :tertiary) do |band_object| band_object.save! band.save! end
with
方法创建一个临时的持久化上下文和用于在上下文中执行操作的MongoDB客户端。在代码块期间,调用with
的模型类或实例上的持久化上下文将更改为临时持久化上下文。为了方便,调用with
的模型类或实例将传递给代码块。
临时持久化上下文适用于查询和写入。
在跨不同持久化上下文执行持久化操作时应小心。例如,如果在临时持久化上下文中保存文档,则它可能不在默认持久化上下文中,导致后续更新失败。
band = Band.new(name: "Scuba") band.with(collection: "artists") do |band_object| band_object.save! end # This will not save - updates the collection "bands" which does not have # the Scuba band band.update_attribute(likes: 1000) # This will update the document. band.with(collection: "artists") do |band_object| band_object.update_attribute(likes: 1000) end
从Mongoid 6.0开始,必须始终使用代码块调用with
方法,并且临时持久化上下文仅存在于代码块期间。这是因为底层创建了一个新的客户端,该客户端将with
传递的选项。为了确保此客户端被关闭并释放其相关资源,必须很好地定义此客户端可以使用的范围。
全局覆盖
如果您想在运行时切换所有操作的持久化上下文,但不想在代码中到处使用with,Mongoid提供在客户端和数据库级别全局执行此操作的能力。为此提供的方法是Mongoid.override_client
和Mongoid.override_database
。这种情况的一个有用例子是国际化的应用程序,该应用程序在不同的数据库或客户端中存储不同区域的信息,但每个数据库或客户端的架构保持不变。
class BandsController < ApplicationController before_action :switch_database after_action :reset_database private def switch_database I18n.locale = params[:locale] || I18n.default_locale Mongoid.override_database("my_db_name_#{I18n.locale}") end def reset_database Mongoid.override_database(nil) end end
在上面的示例中,所有持久化操作都将存储在替代数据库中,直到此线程上的所有剩余操作。这就是为什么在请求之后将覆盖设置回nil的原因 - 它确保后续请求(没有本地参数)使用默认选项。
持久化上下文适用于读取和写入操作。例如,可以按照以下方式执行辅助读取
Band.with(read: {mode: :secondary}) do Band.count end
客户端和集合访问
如果您想降低到驱动程序级别执行操作,您可以从模型或文档实例中获取Mongo客户端或集合
Band.mongo_client band.mongo_client Band.collection band.collection
从这里,您也可以使用客户端的#with
使用相同的运行时持久性选项
client = Band.mongo_client.with(write: { w: 0 }, database: "musik") client[:artists].find(...)
您还可以使用集合的#with
覆盖集合的:读取或:写入选项
collection_w_0 = Band.collection.with(write: { w: 0 }) collection_w_0[:artists].find(...)