【问题标题】:Ransack: how to join table multiple times with different alias?Ransack:如何使用不同的别名多次加入表?
【发布时间】:2015-03-17 09:41:25
【问题描述】:

假设我有 :items 与 has_many 关联与 :properties,然后我可以搜索具有名称为“a_name”和值为“a_value”的属性的所有项目,如下所示

q: { properties_name_eq: 'a_name', properties_value_eq: 'a_value' }

现在,如果我想搜索具有名称为“a_name”和值为“a_value”的属性以及名称为“another_name”和值为“another_value”的属性的所有项目,该怎么办?

以下不起作用,因为它只加入属性表一次

q: {
  g: {
    '0' => { properties_name_eq: 'a_name', properties_value_eq: 'a_value' },
    '1' => { properties_name_eq: 'another_name', properties_value_eq: 'another_value'}
  }
}

生成的 SQL 看起来像这样

  SELECT DISTINCT "items".* FROM "items"
  LEFT OUTER JOIN "properties" ON "properties"."item_id" = "items"."id"
  INNER JOIN ((SELECT "items".* FROM "items")) AS sel_111 on sel_111.id
  WHERE
  (("properties"."name" = 'a_name' AND "properties"."value" = 'a_value') AND ("properties"."name" = 'another_name' AND "properties"."value" = 'another_value'))

编辑:

为了更清楚我的目标,我将在下面粘贴一个规范。

Item.create name: 'ab', properties_attributes: [{ name: 'a', value: 'a1'}, {name: 'b', value: 'b1'}]
Item.create name: 'a', properties_attributes: [{ name: 'a', value: 'a1'}]
Item.create name: 'b', properties_attributes: [{name: 'b', value: 'b1'}]
Item.create name: 'ax', properties_attributes: [{ name: 'a', value: 'a1'}, {name: 'b', value: 'x'}]
Item.create name: 'bx', properties_attributes: [{ name: 'a', value: 'x'}, {name: 'b', value: 'b1'}]
Item.create name: 'other', properties_attributes: [{ name: 'other', value: '123'}]

get :index, q: { properties_name_eq: 'a', properties_value_eq: 'a1' }
names = JSON.parse(response.body).map{|u| u['name']}
expect(names).to match_array ['ab', 'a', 'ax'] # OK!

get :index, 
  q: {
    m: 'or',
    g: {
      '0' => { properties_name_eq: 'a', properties_value_eq: 'a1' },
      '1' => { properties_name_eq: 'b', properties_value_eq: 'b1'}
    }
  }
names = JSON.parse(response.body).map{|u| u['name']}
expect(names).to match_array ['ab'] #FAILS!

【问题讨论】:

标签: ruby-on-rails ransack


【解决方案1】:

只需使用Model.search(params[:q].try(:merge, m: 'or')),使用您的示例:

q: {
  m: 'or',
  g: {
    '0' => { properties_name_eq: 'a_name', properties_value_eq: 'a_value' },
    '1' => { properties_name_eq: 'another_name', properties_value_eq: 'another_value'}
  }
}

您可以找到更多信息here

您需要在查询的 where 级别使用 or,因为 properties.name 不能同时等于“a_name”和“another_name”。不需要表的第二个别名。

【讨论】:

  • 我肯定想要一个 AND,但我想使用同一张表的不同别名来组合它们。如问题中所述,只有 1 个属性名称和值的简单案例有效。
  • 感谢您的编辑,我们越来越近了。为了澄清我在寻找什么,我在问题中添加了一个规范。您编辑的代码将为规范提供 ["a", "ab", "ax", "b", "bx"],而不是 AND。
  • 我明白了。我一直在研究这个案例,还没有找到解决方法。我把我的答案放在这里,也许它可以帮助别人。
【解决方案2】:

您可以通过使用多个查询来解决此问题。

  1. 对于每个名称 + 值属性,获取具有此属性的所有项目 ID
  2. 将每个属性的生成 ID 相交到 item_ids
  3. 在 :items 的最终查询中,添加子句 WHERE id IN (item_ids)

这是执行步骤 1 和 2 的代码示例:

def property_item_ids(conditions)
  conditions.inject([]) do |result, (key, condition)|
    result.method(result.empty? ? '+' : '&').(Property.ransack(m: "and", g: condition).pluck(:item_id).to_a)
  end
end

获取具有所有属性的项目ID:

conditions = {
  '0' => { properties_name_eq: 'a', properties_value_eq: 'a1' },
  '1' => { properties_name_eq: 'b', properties_value_eq: 'b1'}
}

item_ids = property_item_ids(conditions)

对于第 3 步,使用 item_ids 调用 ransack:

q: {
  m: 'and',
  g: {
    '0' => { item_id_in: item_ids }
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-08-15
    • 2012-02-27
    • 2018-07-21
    • 1970-01-01
    • 2011-05-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多