多模型单指标方法 - 轮胎弹性搜索

时间:2012-11-14 06:53:08

标签: ruby-on-rails elasticsearch tire

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

通过Tire gem使用Elasticsearch。

Rails 2.3 app - 已应用更改以根据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

如上所示,这里有两个模型用户和评论。这是使用多个模型解决单个索引的正确方法。

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

2 个答案:

答案 0 :(得分:0)

通常在索引模型时,最好将自身属性及其关联编入索引。因此,在这种情况下,如果您想要索引用户及其提交,您应该在用户模型中具有索引并索引其关联引用的注释,以便轮胎回调应用于用户模型以重新索引用户对象(如果模型中的任何属性)改变了。这仅适用于您拥有索引的模型。

如果您想索引关联,则需要在保存/销毁用户/评论模型后使用挂钩来索引帐户对象。或者你也可以使用:touch =>在更改用户/评论时触摸帐户模型的真实选项。

示例:如果您想要索引用户和评论,

  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.comments是一个关联。希望这个例子解释

答案 1 :(得分:0)

轮胎所有者Karmi发布的问题答案如下:

假设我们有一个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!', ...

每个用户/帐户都有一个单独的索引在某些情况下是完美的 - 但在你有数十或数十万个索引(或更多)的情况下肯定不是很好。通过正确设置过滤器和路由,索引别名在这种情况下的性能会更好。我们会根据租户身份切片数据,但会基于时间。

让我们看看第二种情况,从大量简化的卷曲 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”订阅的帐户可以在两周后搜索,但是具有“基本”订阅的帐户只能在本周搜索。

另请注意,articles_current别名指向当前周的ehm(我在2012-07-12周四写这篇文章)。下周的索引就在那里,铺设和等待 - 当时机到来时,后台工作(cron,Resque worker,自定义脚本......)将更新别名。在Tire集成测试套件中的“滑动窗口”场景中有一个带有别名的漂亮示例。

我们现在不要看看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与articles_currentindex别名对话,后者会定期更新到当前一周的后台进程。通过这种方式,我们在应用程序的不同组件中分离了用于设置索引名称的所有逻辑,因此我们不需要访问索引代理中的Article或Account类(它在单独的服务器上运行),或任何应用程序的组件。无论哪个组件是索引,都是针对articles_current别名的索引;无论哪个组件正在搜索,搜索任何别名或索引对特定组件都有意义。