【问题标题】:Multiple model single index approach - elasticsearch via tire多模型单索引方法 - 通过轮胎进行弹性搜索
【发布时间】:2012-11-14 06:53:08
【问题描述】:

在我的多租户应用程序(基于每个帐户的用户数的帐户)中,当用户文档发生更改时,我将如何更新特定帐户的索引。

通过 Tire gem 使用 Elasticsearch。

Rails 2.3 应用程序 - 按照loe/tire's commit 应用更改以启用对 Rails 2.3 的支持

帐户模型:

  include Tire::Model::Search

  Tire.index('account_1') do
    create(
      :mappings => {
        :user => {
          :properties => {
            :name => { :type => :string, :boost => 10 },
            :company_name => { :type => :string, :boost => 5 }
          }
        },
        :comments => {
          :properties => {
            :description => { :type => :string, :boost => 5 }
          }
        }
      }
    )
  end

正如您在上面看到的,这里有两个模型 user 和 cmets。使用多个模型处理单个索引是否正确?

在这种情况下,当单独更改用户文档或评论文档时如何更新索引?

【问题讨论】:

    标签: ruby-on-rails elasticsearch tire


    【解决方案1】:

    通常,当您为模型编制索引时,最好将自身属性与其关联一起编制索引。因此,在这种情况下,如果您想要索引用户及其评论,您应该在用户模型中拥有索引并索引其关联引用的 cmets,以便轮胎回调应用于用户模型以重新索引用户对象(如果模型中有任何属性)被改变。这仅适用于您拥有索引的模型。

    如果你想索引关联,你需要有钩子,在保存/销毁用户/cmets模型之后索引帐户对象。或者您也可以使用 :touch => true 选项在用户/cmets 更改时触摸帐户模型。

    例如:如果你想要索引用户和 cmets,

      include Tire::Model::Search
      include Tire::Model::Callbacks
    
         mapping do
            indexes :id,                  :type => 'integer', :index    => :not_analyzed
            indexes :about_me,            :type => 'string',  :index    => :snowball
            indexes :name,                :type => 'string',  :index    => :whitespace
    
            indexes :comments do
              indexes :content,                  :type => 'string', :analyzer => 'snowball'
            end
        end
    

    所以这里的索引是在用户模型上,而 user.cmets 是一个关联。希望这个例子能解释

    【讨论】:

    • 感谢 Baaju 的回复...在我的情况下,回调已被禁用。在我的情况下,我将使用后挂钩。参考指定的行你需要有钩子在保存/销毁用户/cmets模型后索引帐户对象有没有办法根据索引名称更新索引?这样我就可以在用户或 cmets 文档更改时通过 after 挂钩更新索引。
    • 如果 Account 模型中只有一个索引,您可以尝试 Account.index.import Account.all(或更改的帐户对象数组)。
    【解决方案2】:

    轮胎车主Karmi对问题的回答如下:

    假设我们有一个 Account 类并且我们处理文章实体。

    在这种情况下,我们的 Account 类将具有以下内容:

    class Account
      #...
    
      # Set index name based on account ID
      #
      def articles
          Article.index_name "articles-#{self.id}"
          Article
      end
    end
    

    因此,每当我们需要访问特定帐户的文章时,无论是搜索还是索引,我们都可以这样做:

    @account = Account.find( remember_token_or_something_like_that )
    
    # Instead of `Article.search(...)`:
    @account.articles.search { query { string 'something interesting' } }
    
    # Instead of `Article.create(...)`:
    @account.articles.create id: 'abc123', title: 'Another interesting article!', ...
    

    在某些情况下,为每个用户/帐户设置一个单独的索引是完美的——但在您拥有数万或数十万个(或更多)索引的情况下绝对不是很好。在这种情况下,拥有索引别名并正确设置过滤器和路由会表现得更好。我们不会根据租户身份,而是根据时间对数据进行切片。

    让我们看看第二种情况,从一个高度简化的 curl http://localhost:9200/_aliases?pretty 输出开始:

    {
      "articles_2012-07-02" : {
        "aliases" : {
          "articles_plan_pro" : {
          }
        }
      },
      "articles_2012-07-09" : {
        "aliases" : {
          "articles_current" : {
          },
          "articles_shared" : {
          },
          "articles_plan_basic" : {
          },
          "articles_plan_pro" : {
          }
        }
      },
      "articles_2012-07-16" : {
        "aliases" : {
        }
      }
    }
    

    您可以看到我们有三个索引,每周一个。您可以看到有两个相似的别名:articles_plan_pro 和articles_plan_basic - 显然,订阅“pro”的帐户可以搜索两周前,但订阅“basic”的帐户只能搜索本周。

    还要注意,articles_current 别名指向,嗯,本周(我在 2012 年 7 月 12 日星期四写这篇文章)。下周的索引就在那里,等待中——到时候,后台作业(cron、Resque worker、自定义脚本……)将更新别名。在轮胎集成测试套件的“滑动窗口”场景中有一个漂亮的别名示例。

    我们现在先不看articles_shared别名,看看我们可以用这个设置玩什么花样:

    class Account
      # ...
    
      # Set index name based on account subscription
      #
      def articles
        if plan_code = self.subscription && self.subscription.plan_code
          Article.index_name "articles_plan_#{plan_code}"
        else
          Article.index_name "articles_shared"
        end
        return Article
      end
    end
    

    同样,我们正在为 Article 类设置一个 index_name,该类保存我们的文档。当当前账号有有效订阅时,我们从订阅中取出plan_code,直接在相关索引中搜索该账号:“basic”或“pro”。

    如果帐户没有订阅——他可能是“访问者”类型——我们会将搜索定向到articles_shared 别名。使用界面和以前一样简单,例如。在 ArticlesController 中:

    @account  = Account.find( remember_token_or_something_like_that )
    @articles = @account.articles.search { query { ... } }
    # ...
    

    在这种情况下,我们没有使用 Article 类作为索引的网关;我们有一个单独的索引组件,一个 Sinatra 应用程序,作为 elasticsearch Bulk API 的轻代理,提供 HTTP 身份验证、文档验证(强制执行规则,如所需属性或作为 UTC 传递的日期),并使用裸 Tire::Index#import和 Tire::Index#store API。

    这些 API 与 article_currentindex 别名对话,该别名通过所述后台进程定期更新到当前周。通过这种方式,我们解耦了在应用程序的单独组件中设置索引名称的所有逻辑,因此我们不需要访问索引代理中的 Article 或 Account 类(它运行在单独的服务器上),或任何应用程序的组成部分。无论哪个组件正在索引,都针对articles_current 别名进行索引;无论哪个组件正在搜索,搜索对特定组件有意义的任何别名或索引。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-03-13
      • 1970-01-01
      • 1970-01-01
      • 2013-07-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多