【问题标题】:Can rspec change and use an innodb fulltext index in the same test?rspec 可以在同一个测试中更改和使用 innodb 全文索引吗?
【发布时间】:2025-12-14 07:20:33
【问题描述】:

我有一个奇怪的具体问题。假设我在 Rails 项目中有这张表:

create_table "documents", force: true do |t|
   t.text    "tags"
end
add_index "documents", ["tags"], name: "index_documents_on_tags", type: :fulltext

我有一个集成测试,它创建了一些具有不同标签组合的Document 实例,我尝试测试的方法应该通过全文搜索返回。不幸的是,事实证明 InnoDB 在当前事务结束之前不会重建其全文索引,这意味着我的搜索结果为空。

如果我在夹具中构建测试数据(例如,提前,在 rspec 用于每个测试的事务之外)一切正常,但是我有什么方法可以调整数据并在其中运行搜索同样的测试?

【问题讨论】:

    标签: mysql ruby-on-rails rspec innodb


    【解决方案1】:

    棘手但可修复。忍受我。

    步骤 1

    @mattias (https://*.com/a/7703220/537648) 添加这个出色的助手

    def without_transactional_fixtures(&block)
      self.use_transactional_fixtures = false
    
      before(:all) do
        DatabaseCleaner.strategy = :truncation
      end
    
      yield
    
      after(:all) do
        DatabaseCleaner.strategy = :transaction
      end
    end
    
    

    第二步

    将此 before 块添加到您的 rspec 示例中

    示例用法:

    describe "doing my thing" do
      before do
        # This will rebuild the indexes. You need it before each example
        ActiveRecord::Base.connection.execute("ANALYZE TABLE `searchables`")
      end
    
      without_transactional_fixtures do
        it "does something without transaction fixtures" do
          ...
        end
      end
    end
    

    奖励步骤

    如果您收到此错误:

    ActiveRecord::StatementInvalid: Mysql2::Error: SAVEPOINT active_record_1 does not exist: ROLLBACK TO SAVEPOINT active_record_1
    

    使用FactoryBot/FactoryGirl 时要小心。如果您需要为可搜索表创建对象,请使用 let! 而不是 let

    例子:

    describe '.search' do
        without_transactional_fixtures do
          let! (:campaign0) { create(:campaign, io_number: 'C0-1234-4321', status: 'completed') }
          let! (:campaign1) { create(:campaign, io_number: "C1-4321-4321") }
    
          before do
            ActiveRecord::Base.connection.execute("ANALYZE TABLE `searchables`")
          end
    ...
    

    谢谢@awaage (https://*.com/a/13732210/537648)

    【讨论】:

      【解决方案2】:

      我有同样的问题,但没有找到很好的解决方案。您可以做的一件事是使用 DatabaseCleaner 之类的工具,并将这些测试的策略从“事务”更改为“截断”。

      【讨论】:

      • 是的,我也没有找到好的解决方案。截断清理不是一种选择,我们最终还是放弃了全文索引。如果我们坚持下去,在这一点上,我想我会咬紧牙关,用我能在固定装置上做的任何事情来做。
      【解决方案3】:

      我遇到了同样的问题,并通过在测试中手动创建全文索引来解决它。 示例:针对您的情况

      create_table "documents", force: true do |t|
         t.text    "tags"
      end
      add_index "documents", ["tags"], name: "index_documents_on_tags", type: :fulltext
      

      在你的测试中:

      before do
        @index_key = "index_documents_on_tags_#{Time.current.to_i}"
        ActiveRecord::Base.connection.execute("CREATE FULLTEXT INDEX #{@index_key} ON documents(tags)")
      end
      
      after do
        ActiveRecord::Base.connection.execute("ALTER TABLE documents DROP INDEX #{@index_key}")
      end
      

      【讨论】: