0

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

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

我想这是我在我的案例中指定映射的方式。如我错了请纠正我。

账户模式:

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

使用帐户索引的两个映射正确创建了索引。但是,当映射中指定的任何模型发生更改时,我看不到可以更新索引的方法。

每当添加新用户或更新用户时,必须更新为相应帐户创建的索引。

4

2 回答 2

3

This question is cross-posted from Github issue Multiple model single index approach. Crossposting the answer here.


Let's say we have an Account class and we deal in articles entities.

In that case, our Account class would have following:

class Account
  #...

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

So, whenever we need to access articles for a particular account, either for searching or for indexing, we can simply do:

@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!', ...

Having a separate index per user/account works perfect in certain cases -- but definitely not well in cases where you'd have tens or hundreds of thousands of indices (or more). Having index aliases, with properly set up filters and routing, would perform much better in this case. We would slice the data not based on the tenant identity, but based on time.

Let's have a look at a second scenario, starting with a heavily simplified curl http://localhost:9200/_aliases?pretty output:

{
  "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" : {
    }
  }
}

You can see that we have three indices, one per week. You can see there are two similar aliases: articles_plan_pro and articles_plan_basic -- obviously, accounts with the “pro” subscription can search two weeks back, but accounts with the “basic” subscription can search only this week.

Notice also, that the the articles_current alias points to, ehm, current week (I'm writing this on Thu 2012-07-12). The index for next week is just there, laying and waiting -- when the time comes, a background job (cron, Resque worker, custom script, ...) will update the aliases. There's a nifty example with aliases in “sliding window” scenario in the Tire integration test suite.

Let's not look on the articles_shared alias right now, let's look at what tricks we can play with this setup:

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

Again, we're setting up an index_name for the Article class, which holds our documents. When the current account has a valid subscription, we get the plan_code out of the subscription, and direct searches for this account into relevant index: “basic” or “pro”.

If the account has no subscription -- he's probably a “visitor” type -- , we direct the searches to the articles_shared alias. Using the interface is as simple as previously, eg. in ArticlesController:

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

We are not using the Article class as a gateway for indexing in this case; we have a separate indexing component, a Sinatra application serving as a light proxy to elasticsearch Bulk API, providing HTTP authentication, document validation (enforcing rules such as required properties or dates passed as UTC), and uses the bare Tire::Index#import and Tire::Index#store APIs.

These APIs talk to the articles_currentindex alias, which is periodically updated to the current week with said background process. In this way, we have decoupled all the logic for setting up index names in separate components of the application, so we don't need access to the Article or Account classes in the indexing proxy (it runs on a separate server), or any component of the application. Whichever component is indexing, indexes against articles_current alias; whichever component is searching, searches against whatever alias or index makes sense for the particular component.

于 2012-12-12T20:54:02.543 回答
0

您可能想使用另一个像橡皮筋https://github.com/grantr/rubberband这样的宝石来按照您想要的方式设置索引,事先,也许在创建帐户时,您可以在 after_create 回调中执行此操作

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

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

棘手的部分是将 account_id 或 name 放入 index_name 字符串/参数中,可能容易或困难,尚未尝试动态分配 index_name

希望这可以帮助!

于 2012-12-08T08:23:39.370 回答