Группировка ands и ors в AREL

Я пытаюсь запросить эквивалент этого фрагмента sql, используя arel:

WHERE (("participants"."accepted" = 'f' AND "participants"."contact_id" = 1) 
  OR "participants"."id" IS NULL)

Так что я хочу (accepted && contact_id=1) OR NULL

Вот что у меня есть в AREL

participants[:accepted].eq(false).and(participants[:contact_id].eq(1).
  or(participants[:id].is(nil)

Проблема в том, что это генерирует:

("participants"."accepted" = 'f' AND "participants"."contact_id" = 1 OR "participants"."id" IS NULL)

Обратите внимание на отсутствие круглых скобок вокруг условий my и . Я полагаю, что в соответствии с приоритетом оператора я получаю:

accepted && (contact_id=1 OR NULL)

Добавление круглых скобок в запрос AREL не влияет. есть идеи?


person brad    schedule 10.11.2011    source источник


Ответы (3)


Я считаю, что в соответствии с приоритетом оператора

Проблема в том, что И имеет более высокий приоритет, чем ИЛИ. Итак, 1 AND 2 OR 3 эквивалентно (1 AND 2) OR 3. В качестве примечания: если вы используете оболочку, подобную этой, вы можете написать более привлекательный код. с Арелем:

User.where((User[:created_at] > 3.days.ago) & (User[:enabled] == true))
person tokland    schedule 10.11.2011
comment
Это именно то, что дает вам Арел. Как это слишком низкий уровень? - person Marnen Laibow-Koser; 10.11.2011
comment
@Marnen, ты работал со шкворнем? это то, что я бы назвал (хорошей) высокоуровневой оболочкой над Arel. Пример, который я показал выше, предназначен только для простой оболочки, он просто выглядит немного лучше (субъективно). - person tokland; 10.11.2011
comment
Хотя Squeel выглядит великолепно (я не знал об этом). Но называть его менее низкоуровневым, чем Arel, не совсем точно — это просто модификация синтаксиса. - person Marnen Laibow-Koser; 10.11.2011
comment
@Марнен. Низкий уровень, высокий уровень, это довольно субъективные термины. Я не пытаюсь быть догматиком (таким образом, на мой вкус), просто мое скромное мнение. Однако обратите внимание, что некоторые люди думают так же: из rubyreflector.com/ARel: Недавно Аарон занимался настройкой производительности Rails. 3, переписав низкоуровневую библиотеку реляционной алгебры ARel. - person tokland; 10.11.2011
comment
Учитывая контекст, мне не ясно, что низкий уровень предназначен для ссылки на библиотеку в целом — похоже, что он больше предназначен для ссылки на внутренности, которые писал Аарон. - person Marnen Laibow-Koser; 10.11.2011
comment
И нет, низкий уровень и высокий уровень не субъективны, по крайней мере, как относительные понятия. Ты сказал, что Сквил был более высокого уровня, чем Арел. Насколько я могу судить, это просто модификация синтаксиса Arel, и поэтому работает на том же уровне. Исключение составляют фильтры, которые абстрагируют Arel и поэтому работают на более высоком уровне. - person Marnen Laibow-Koser; 10.11.2011
comment
ммм... да ты прав. На самом деле я пытался перевести LEFT OUTER JOIN participants, написанное от руки, чтобы использовать includes(:participants). Проблема в том, что у меня есть дополнительные условия присоединения. Так что я надеялся, что смогу просто прикрепить их к where, но они не выглядят эквивалентными. Придется изменить мой вопрос - person brad; 10.11.2011
comment
тьфу, я здесь вдвойне запутался, причина, по которой я думал, что это не удается, заключалась в том, что мои ранее написанные тесты терпели неудачу (фактический запрос немного сложнее). арель... упс. - person brad; 10.11.2011
comment
Спасибо за предложение обертки, кстати! Мне действительно нравится работать с ARel, я обнаружил, что могу выполнять более сложные запросы, чем позволяет ActiveRecord, хотя синтаксис оболочки довольно приятный, я обычно просто использую arel_table - person brad; 11.11.2011

Вы можете создать скобки, используя Arel::Nodes::Grouping.

participants = Arel::Table.new("participants")

arel = participants.grouping(
  participants[:accepted].eq(false).and(participants[:contact_id].eq(1))
).or(participants[:id].eq(nil))

arel.to_sql # => (("participants"."accepted" = 'f' AND "participants"."contact_id" = 1) OR "participants"."id" IS NULL)
person Brian Hahn    schedule 10.05.2013
comment
Спасибо, .grouping отлично сработал. Вместо participants = Arel::Table.new("participants") я использовал participants = Participant.arel_table - person anu; 18.02.2014
comment
Это решение работает элегантно и решает проблему под рукой. Это был бы мой предпочтительный ответ на проблему. Зато везде отличные варианты :-) - person Josh Frankel; 25.05.2016

Почему бы не перевернуть их. Должно быть эквивалентно:

participants[:id].is(nil).or(participants[:accepted].eq(false).and(participants[:contact_id].eq(1))

надеюсь, я правильно расставил скобки выше, но вы понимаете, что я имею в виду...

person Taryn East    schedule 10.11.2011
comment
Да, я думаю, что ошибался в том, что мне это нужно, Токланд прав, приоритет - это не моя проблема, на самом деле мой запрос отличается от того, что мне нужно. - person brad; 10.11.2011