在我的多租户应用(基于每个帐户的用户数的帐户)中,如何更改用户文档时如何更新特定帐户的索引。
通过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
如上所示,这里有两个模型用户和评论。这是使用多个模型解决单个索引的正确方法。
在这种情况下,如果单独更改用户文档或评论文档,如何更新索引?
答案 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别名的索引;无论哪个组件正在搜索,搜索任何别名或索引对特定组件都有意义。