为postgresql jsonb字段创建部分索引

brg*_*brg 5 postgresql activerecord ruby-on-rails jsonb postgresql-9.5

我正在尝试为postgresql jsonb列添加部分索引.jsonb列名为:email_provider.我已尝试在列上添加部分索引,如下所示,但它会抛出不同的错误,例如PG :: UndefinedColumn:ERROR:列"email_povider"不存在,有时它会引发错误:PG :: AmbiguousFunction:ERROR:运算符不是唯一的:未知 - >未知

rails-5迁移中的部分索引如下所示:

add_index  :accounts, :name, name:  "index_accounts_on_email_provider_kind", using: :gin, where: "('email_provider' -> 'sparkpost' ->> 'smtp_host' = 'www.sparkpost.com') AND ('email_povider' -> 'amazon ses' ->> 'smtp_host' = 'www.awses.com')"
Run Code Online (Sandbox Code Playgroud)

email_provider列的json如下所示:

 {
   "email_provider": {
     "sparkpost": {
        "smtp_host": "www.sparkpost.com",
        "smtp_port": ""
      },
      "aws ses": {
        "smtp_host": "www.amazon/ses.com ",
        "smtp_port": " ",
        "username": " ",
        "password": " "
      }
  }
}
Run Code Online (Sandbox Code Playgroud)

该表如下所示:

class CreateAccounts < ActiveRecord::Migration[5.0]
   def change
      create_table :accounts do |t|
        t.string :name, null: false
        t.jsonb :email_provider, null: false
        t.jsonb :social_account, default: '[]', null: false
        t.timestamps
   end
      add_index  :accounts, :name, name: "index_accounts_on_email_provider_kind", using: :gin, where: "('email_provider' -> 'sparkpost' ->> 'smtp_host' = 'www.sparkpost.com') AND ('email_povider' -> 'amazon ses' ->> 'smtp_host' = 'www.awses.com')"

      add_index  :accounts, :name, name: "index_accounts_on_social_account_type", using: :gin, where: " 'social_account' @> [{'type': 'facebook'}] AND 'social_account' @> [{'type': 'twitter'}]"
   end
end
Run Code Online (Sandbox Code Playgroud)

更新

基于对下面接受的答案的轻微调整,我用来在rails activerecord中创建btree而不是gin索引的代码如下所示.它创建,因为我们使用的是一个B树索引的名字列是字符串数据类型的,而不是jsonb描述在这里:

add_index :accounts, :name, name:  "index_accounts_on_name_email_provider", where: "email_provider -> 'sparkpost' ->> 'smtp_host' = 'www.sparkpost.com' AND email_provider -> 'amazon ses' ->> 'smtp_host' = 'www.awses.com' "

add_index  :accounts, :name, name: "index_accounts_on_name_social_account", where: " social_account @> '[{\"type\": \"facebook\"}]'::jsonb AND social_account @> '[{\"type\": \"twitter\"}]'::jsonb"
Run Code Online (Sandbox Code Playgroud)

red*_*neb 9

我想你需要这样的东西:

add_index :accounts, :email_provider, name:  "index_accounts_on_email_provider_kind", using: :gin, where: "(email_provider -> 'email_provider' -> 'sparkpost' ->> 'smtp_host' = 'www.sparkpost.com') AND (email_provider -> 'email_povider' -> 'amazon ses' ->> 'smtp_host' = 'www.awses.com')"
Run Code Online (Sandbox Code Playgroud)

我已经改变了第二个参数add_index:email_provider,因为这是列名.此外,对于该where条款,我改变了

'email_provider' -> 'sparkpost' ->> 'smtp_host' = 'www.sparkpost.com'
Run Code Online (Sandbox Code Playgroud)

email_provider -> 'email_provider' -> 'sparkpost' ->> 'smtp_host' = 'www.sparkpost.com'
Run Code Online (Sandbox Code Playgroud)

因为->运算符期望它将参数保留为json(b)值,但是您提供了一个字符串.因此,例如从名为的列中email_provider -> 'email_provider'提取对应的值.请注意,最后一行可以更紧凑地编写为:email_provideremail_provider

email_provider #>> '{email_provider,sparkpost,smtp_host}' = 'www.sparkpost.com'
Run Code Online (Sandbox Code Playgroud)

通过使用#>>从josn(b)对象中提取"路径".所以add_index声明可以写成:

add_index :accounts, :email_provider, name:  "index_accounts_on_email_provider_kind", using: :gin, where: "(email_provider #>> '{email_provider,sparkpost,smtp_host}' = 'www.sparkpost.com') AND (email_provider -> '{email_povider,amazon ses,smtp_host}' = 'www.awses.com')"
Run Code Online (Sandbox Code Playgroud)

对于你的第二个索引,你应该尝试类似的东西:

where: " social_account -> 'social_account' @> '[{\"type\": \"facebook\"}]'::jsonb AND social_account -> 'social_account' @> '[{\"type\": \"twitter\"}]'::jsonb"
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我在第一种情况下对列进行了相同的思考.我也改变了正确的参数@>,它必须是一个jsonb值.所以你必须将它定义为一个JSON字符串,它需要字符串的双引号(还要注意我们必须为ruby转义它们),然后我输入该字符串jsonb以获得所需的类型.