2017-02-13 7 views
0

У меня есть список, который я хотел бы, чтобы включить на п where с помощью or S:Multiple ИЛИ статьи

my_list = [ 
    %{min: 1, max: 10}, 
    %{min: 4, max: 16}, 
    %{min: 5, max: 21} 
] 

where Пунктом Я намерен построить собирается быть частью другого, более крупный запрос.

Прямо сейчас я должен написать следующий код (обратите внимание на дублирование):

query = 
    my_list 
    |> Enum.with_index() 
    |> Enum.reduce(bigger_query, fn {item, index}, q -> 
     if index == 0 do 
     where(q, [table], fragment("? BETWEEN ? AND ?", table.age, ^item["min"], ^item["max"])) 
     else 
     or_where(q, [table], fragment("? BETWEEN ? AND ?", table.age, ^item["min"], ^item["max"])) 
     end 
    end) 

Может ли этот запрос быть переписан проще с точки зрения Экто? Если да, то как?

Обновление. Рассмотрим следующий пример:

query = from posts in Post, where: posts.author_id == ^12 

query = 
    my_list 
    |> Enum.with_index() 
    |> Enum.reduce(bigger_query, fn {item, index}, q -> 
     if index == 0 do 
     where(q, [posts], fragment("? BETWEEN ? AND ?", posts.author_age, ^item["min"], ^item["max"])) 
     else 
     or_where(q, [posts], fragment("? BETWEEN ? AND ?", posts.author_age, ^item["min"], ^item["max"])) 
     end 
    end) 

query = where(q, [posts], posts.published == ^true) 

Запрос генерируется правильно:

SELECT * FROM posts WHERE author_id = 12 AND ((age BETWEEN 1 AND 10) OR (age BETWEEN 4 AND 16) AND (age BETWEEN 5 AND 21)) 

Что бы самый компактный Ecto код для достижения этой цели?

ответ

0

Вам не нужно ставить первое условие в where. or_where работает просто отлично, даже если в запросе уже нет where. Следующий должен работать нормально:

query = Enum.reduce(my_list, bigger_query, fn item, q -> 
    or_where(q, [table], fragment("? BETWEEN ? AND ?", table.age, ^item["min"], ^item["max"])) 
end) 

можно упростить в дальнейшем с помощью сопоставления с образцом (предполагая, что ключи являются строками, и вы поставите атомы в качестве ключей в первом фрагменте кода в вопросе по ошибке):

query = Enum.reduce(my_list, bigger_query, fn %{"min": min, "max": max}, q -> 
    or_where(q, [table], fragment("? BETWEEN ? AND ?", table.age, ^min, ^max)) 
end) 
+0

из [docs в 'or_where'] (https://hexdocs.pm/ecto/2.1.3/Ecto.Query.html#or_where/2): * (он) ведет себя точно так же, как и где, кроме объединения с любым предыдущим выражением с использованием OR *. Я вижу это точное поведение: если 'large_query' уже имеет хотя бы одно предложение' where', то запуская 'or_where' будет' OR' в это уже существующее предложение, что нежелательно. Вместо этого мне нужно иметь 'AND (bunch_of_ORs)' – gmile

+0

. Но тогда работает ли более длинный код, который у вас был в вопросе? – Dogbert

+0

'|> где ([p], 1 == 1) |> где ([p], 2 == 2) |> or_where ([p], 3 == 3)' производит этот SQL для меня: 'WHERE ((1 = 1) И (2 = 2)) ИЛИ (3 = 3), т.е. все существующие 'WHERE' объединены и' OR''d против первого 'или_where'. – Dogbert