如何使用ElasticSearch-Rails查询DSL返回相关联的关系

22

我对ElasticSearch很陌生,但需要使用它来返回产品列表。请不要包含旧答案或引用已弃用的tire gem的链接。

gemfile

ruby '2.2.0'
gem 'rails', '4.0.3'
gem 'elasticsearch-model', '~> 0.1.6'
gem 'elasticsearch-rails', '~> 0.1.6'

我有几个带关系的模型。下面是包含这些关系的代码。

模型和关系

product.rb 包括 Searchable 模块。

  belongs_to :family
  belongs_to :collection
  has_many :benefits_products
  has_many :benefits, :through => :benefits_products

  def as_indexed_json(options={})
    as_json(
        include: {:benefits => { :only => [ :id, :name ] },
                  :categories => { :only => [ :id, :name ] } }
    )
  end

collection.rb

  include Searchable

  has_many :products

  def as_indexed_json(options={})
    as_json(
      include: [:products]
    )
  end

family.rb

  include Searchable

  has_many :products

  def as_indexed_json(options={})
    as_json(
      include: [:products]
    )
  end

benefit.rb

  include Searchable

  has_many :benefits_products
  has_many :products, :through => :benefits_products

  def as_indexed_json(options={})
    as_json(
      include: [:products]
    )
  end

Serachable.rb 是一个 concern,它包含了 Elastic search 和在所有模型中的回调函数。

module Searchable
  extend ActiveSupport::Concern

  included do
    include Elasticsearch::Model
    include Elasticsearch::Model::Callbacks

    settings index: { number_of_shards: 1, number_of_replicas: 0 } do
      mapping do

        indexes :id, type: 'long'
        indexes :name, type: 'string'
        indexes :family_id, type: 'long'
        indexes :collection_id, type: 'long'
        indexes :created_at, type: 'date'
        indexes :updated_at, type: 'date'

        indexes :benefits, type: 'nested' do
          indexes :id, type: 'long'
          indexes :name, type: 'string'
        end

        indexes :categories, type: 'nested' do
          indexes :id, type: 'long'
          indexes :name, type: 'string'
        end

      end
    end

    def self.search(options={})
      __set_filters = lambda do |key, f|

        @search_definition[:filter][:and] ||= []
        @search_definition[:filter][:and]  |= [f]
      end

      @search_definition = {
        query: {
          filtered: {
            query: {
              match_all: {}
            }
          }
        },
        filter: {}
      }

      if options[:benefits]
        f = { term: { "benefits.id": options[:benefits] } }

        __set_filters.(:collection_id, f)
        __set_filters.(:family_id, f)
        __set_filters.(:categories, f)
      end

      def as_indexed_json(options={})
        as_json(
          include: {:benefits => { :only => [ :id, :name ] },
                    :categories => { :only => [ :id, :name ] } }
        )
      end

      if options[:categories]
        ...
      end

      if options[:collection_id]
        ...
      end

      if options[:family_id]
        ...
      end

      __elasticsearch__.search(@search_definition)
    end

  end
end

ElasticSearch

我将破折号分隔的标识符拆分为不同的家族、系列和优点。我能够搜索具有特定家族或系列的产品,并返回正确的结果。我也可以返回一个优点的结果,但它们似乎不准确。同时搜索多个优点会产生奇怪的结果。我希望使用所有字段搜索的“AND”组合,但我的结果似乎不是“AND”或“OR”的结果。所以这让我很困惑。

我需要向Product.search方法传递什么参数才能得到期望的结果?

感谢您提供的任何帮助!

编辑

我现在已经验证了产品上索引的优点。我使用curl -XGET 'http://127.0.0.1:9200/products/_search?pretty=1',它产生了一个json响应,看起来像这样:

{
  "id":4,
  "name":"product name"
  "family_id":16
  "collection_id":6
  "created_at":"2015-04-13T12:49:42.000Z"
  "updated_at":"2015-04-13T12:49:42.000Z"
  "benefits":[
    {"id":2,"name":"my benefit 2"},
    {"id":6,"name":"my benefit 6"},
    {"id":7,"name":"my benefit 7"}
  ],
  "categories":[
    {"id":2,"name":"category 2"}
  ]}
},
{...}

现在我只需要找出如何在ElasticSearch中搜索具有2、6和7号福利的产品,如果我想要上面的示例产品。 我特别寻找提交给elasticsearch #search方法以获得嵌套“AND”查询结果的语法,嵌套查询设置/映射(以确保我没有遗漏任何内容),以及您可以考虑的任何其他相关信息来解决此问题。

更新

可搜索关注点已更新以反映所收到的答案。 我将映射json对象翻译成适合elasticsearch-model语法的形式。 当我尝试以类似的方式翻译查询时,我的剩余困惑发生了。

第二次更新

我基本上是根据elasticsearch-rails示例应用程序创建我的可搜索.rb关注点。 我已更新可搜索.rb以反映此代码,并且虽然我正在获得结果,但它们不是“AND”执行的结果。 当我应用两个福利时,我会得到所有具有任一福利的产品的结果。

你是否在维护名为id的外部字段? - monu
如果我理解正确的话 - 不,那只是Rails在所有情况下生成的id列。但如果我误解了,请告诉我。 - Thomas
我添加了一个解决方案,请检查并告诉我。 - monu
你能否在筛选器部分将“term”更改为“terms”? - monu
1个回答

4
默认情况下,如果您使用动态映射来加载数据,则ES会将嵌套对象创建为平面对象,因此会失去各种嵌套属性之间的关系。为了维护适当的关系,我们可以使用嵌套对象父子关系。

现在我将使用嵌套对象来实现所需的结果:

映射:

PUT /index-3
{
  "mappings": {
    "products":{
      "properties": {
        "id": {
          "type": "long"
        },
        "name":{
          "type": "string"
        },
        "family_id":{
          "type": "long"
        },
        "collection_id":{
          "type": "long"
        },
        "created_at":{
          "type": "date"
        },
        "updated_at":{
          "type": "date"
        },
        "benefits":{
          "type": "nested",
          "include_in_parent": true,
          "properties": {
            "id": {
              "type": "long"
            },
            "name":{
              "type":"string"
            }
          }
        },
        "categories":{
          "type": "nested",
          "include_in_parent": true,
          "properties": {
            "id":{
              "type": "long"
            },
            "name":{
              "type":"string"
            }
          }
        }
      }
    }
  }
}

如果您注意到,我已将子对象视为嵌套映射并包含在父对象中。

现在是一些示例数据:

PUT /index-3/products/4
{
  "name":"product name 4",
  "family_id":15,
  "collection_id":6,
  "created_at":"2015-04-13T12:49:42.000Z",
  "updated_at":"2015-04-13T12:49:42.000Z",
  "benefits":[
    {"id":2,"name":"my benefit 2"},
    {"id":6,"name":"my benefit 6"},
    {"id":7,"name":"my benefit 7"}
  ],
  "categories":[
    {"id":2,"name":"category 2"}
  ]
}
PUT /index-3/products/5
{
  "name":"product name 5",
  "family_id":16,
  "collection_id":6,
  "created_at":"2015-04-13T12:49:42.000Z",
  "updated_at":"2015-04-13T12:49:42.000Z",
  "benefits":[
    {"id":5,"name":"my benefit 2"},
    {"id":6,"name":"my benefit 6"},
    {"id":7,"name":"my benefit 7"}
  ],
  "categories":[
    {"id":3,"name":"category 2"}
  ]
}
PUT /index-3/products/6
{
  "name":"product name 6",
  "family_id":15,
  "collection_id":5,
  "created_at":"2015-04-13T12:49:42.000Z",
  "updated_at":"2015-04-13T12:49:42.000Z",
  "benefits":[
    {"id":5,"name":"my benefit 2"},
    {"id":55,"name":"my benefit 6"},
    {"id":7,"name":"my benefit 7"}
  ],
  "categories":[
    {"id":3,"name":"category 2"}
  ]
}

现在是查询部分:

GET index-3/products/_search
{
  "query": {
    "filtered": {
      "query": {
        "match_all": {}
      },
      "filter": {
        "terms": {
          "benefits.id": [
            5,6,7
          ],
          "execution": "and"
        }
      }
    }
  }
}

生成以下结果:
{
   "took": 1,
   "timed_out": false,
   "_shards": {
      "total": 1,
      "successful": 1,
      "failed": 0
   },
   "hits": {
      "total": 1,
      "max_score": 1,
      "hits": [
         {
            "_index": "index-3",
            "_type": "products",
            "_id": "5",
            "_score": 1,
            "_source": {
               "name": "product name 5",
               "family_id": 16,
               "collection_id": 6,
               "created_at": "2015-04-13T12:49:42.000Z",
               "updated_at": "2015-04-13T12:49:42.000Z",
               "benefits": [
                  {
                     "id": 5,
                     "name": "my benefit 2"
                  },
                  {
                     "id": 6,
                     "name": "my benefit 6"
                  },
                  {
                     "id": 7,
                     "name": "my benefit 7"
                  }
               ],
               "categories": [
                  {
                     "id": 3,
                     "name": "category 2"
                  }
               ]
            }
         }
      ]
   }
}

在查询时,我们必须使用带有“并执行”的术语过滤器,以便仅检索具有所有术语的文档。


感谢您的回复。我已更新我的Searchable.rb以反映索引。我仍然有点困惑如何将查询翻译成符合elastic search-rails dsl的格式,如此处所示:https://github.com/elastic/elasticsearch-rails/tree/master/elasticsearch-model - Thomas
抱歉我的问题有误,我已经更新了它以更准确。我也更新了 searchable.rb 来反映我的当前进展。 - Thomas
1
我给你打赏是因为我知道你的答案是正确的。这个奖励即将到期,我不想因为我的问题表述不清而浪费你的努力。如果你对最新的更新有任何见解,我很乐意听取。如果没有,我仍然感激你的帮助和我所取得的进展。谢谢! - Thomas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接