22

我是 ElasticSearch 的新手,但需要使用它来返回产品列表。请不要包含引用已弃用的轮胎宝石的答案或旧答案的链接。

宝石文件

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

我有几个有关系的模型。我包括了下面的关系。

模型和关系

product.rb 包括可搜索的

  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

集合.rb

  include Searchable

  has_many :products

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

家庭.rb

  include Searchable

  has_many :products

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

福利.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 只是一个关注点,包括所有模型中的弹性搜索和回调

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

弹性搜索

我将破折号分开的蛞蝓分为不同的家庭、收藏品和福利。我能够搜索具有特定系列或集合的产品并返回正确的结果。我也可以返回结果以获得一个好处,但它们似乎并不准确。搜索多种好处也会产生奇怪的结果。我想要所有字段搜索的“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”查询、嵌套查询设置/映射的结果(以确保我没有遗漏任何内容,以及您可以想到的任何其他相关信息你们解决这个问题。

更新

Searchable 问题已更新以反映收到的答案。我翻译了映射 json 对象以适应 elasticsearch-model 语法。当我尝试以类似的方式翻译查询时,我剩下的困惑发生了。

第二次更新

对 elasticsearch-rails 示例应用程序的searchable.rb 的大部分关注是基本的。我已经更新了 searchable.rb 以反映这段代码,当我得到结果时,它们不是“AND”执行的结果。当我应用两个好处时,我会从所有具有其中一个好处的产品中得到结果。

4

1 回答 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"
                  }
               ]
            }
         }
      ]
   }
}

在查询时,我们必须使用带有“和执行”的术语过滤器,因此它只会检索包含所有术语的文档。

于 2015-04-18T13:33:51.943 回答