单指数多类型 - 轮胎弹性搜索索引

时间:2012-12-06 12:08:57

标签: ruby-on-rails elasticsearch ruby-on-rails-2 tire

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

我为每个帐户都有一个单独的索引,其中指定了每个模型的映射(用户和评论 - 只是一个示例实际应用程序有很多模型)。在这种情况下,如果对用户模型或注释模型进行了任何更改,则必须更新为相关帐户创建的索引。这可能吗?如果是,请告诉我。

我想这就是我在我的情况下指定映射的方式。如果我错了,请纠正我。

帐户模型:

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 :(得分:3)

此问题是从Github问题Multiple model single index approach交叉发布的。在这里改写答案。


假设我们有一个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”订阅的帐户可以在两周后搜索,但是具有“基本”订阅的帐户只能在本周搜索。

另请注意,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别名的索引;无论哪个组件正在搜索,搜索任何别名或索引对特定组件都有意义。

答案 1 :(得分:0)

你可能想要使用像rubberband https://github.com/grantr/rubberband这样的另一个gem来设置你想要的索引,事先,也许你在after_create回调中创建帐户

然后在映射您的用户和评论模型时,您可以使用Tire执行以下操作:

tire.mapping :_routing => { :required => true, :path => :account_id } do
  index_name 'account_name_here'
  ...
  ...
end

棘手的部分是将account_id或name添加到index_name字符串/参数中,可能很容易或者很难,还没有尝试过动态分配index_name

希望这有帮助!