#ruby-on-rails #ruby-on-rails-4 #activerecord #rails-activerecord #ruby-on-rails-4.2
#ruby-on-rails #ruby-on-rails-4 #activerecord #rails-activerecord #ruby-on-rails-4.2
Вопрос:
У меня есть список объектов недвижимости, каждая из которых имеет некоторые особенности. Затем у меня есть 2 таблицы:
- real_estates, для хранения всех объектов недвижимости, добавляемых пользователями.
- re_home_features, для хранения всех функций по умолчанию, добавленных администратором, таких как бассейн, шкаф, офис, сад и множество функций, которые может иметь недвижимость
Один и тот же real_estate может иметь много функций, и одна и та же функция может иметь много объектов недвижимости. Я создал эти модели:
real_estate.rb
class RealEstate < ActiveRecord::Base
has_many :real_estate_home_features
has_many :re_home_features, as: :home_features, through: :real_estate_home_features, dependent: :destroy
end
re_home_features.rb
class ReHomeFeature < ActiveRecord::Base
has_many :real_estate_home_features
has_many :real_estates, through: :real_estate_home_features
end
real_estate_home_feature.rb
class RealEstateHomeFeature < ActiveRecord::Base
belongs_to :real_estate
belongs_to :re_home_feature
end
При этом отношение многие ко многим работает нормально.
У меня есть поиск по недвижимости с некоторыми параметрами, например:
- Количество жилых комнат
- Количество ванных комнат
- Цена продажи (от минимальной до максимальной)
- Общая площадь (от минимальной до максимальной)
- Код недвижимости
- И множество других параметров
Мой поиск такой:
real_estates_controller.rb
def search
r = real_estates
r = r.where(code: params['code']) if params['code'].present?
r = r.where(city_name: params['city']) if params['city'].present?
r = r.where(garage: params['garage']) if params['garage'].present?
r = r.where(restrooms: params['restrooms']) if params['restrooms'].present?
r = r.paginate(:page => params[:page], :per_page => 10)
r
end
Этот поиск тоже работает нормально. С этим проблем нет, потому что все параметры находятся в одной таблице real_estates.
Но теперь поиск немного сложнее. Я должен искать объекты недвижимости с определенными функциями. Пример: мне нужна вся недвижимость, в которой есть 4 туалета, 2 машины в гараже И бассейн.
В моем примере поиск в недвижимости с 4 туалетами и 2 автомобилями вернул мне 50 объектов недвижимости, но только 15 из этих объектов недвижимости имеют пул.
Как я могу отфильтровать эти 50 объектов недвижимости, чтобы показывать только записи, связанные с «функцией пула»?
Я не могу проверить в представлении, потому что вызывает неправильный номер на странице. Я думаю, что фильтр должен выполняться в момент запроса к базе данных, непосредственно перед разбиением на страницы.
I appreciate any help!
environment
rails -v: Rails 4.2.1
ruby -v: ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-linux]
so: Ubuntu 16.04.5 LTS
localhost database: MySQL (development)
server database: Postgres (production)
EDIT 1
Based on @jvillian answer, I will change my controller, adding a JOIN in the query.
if params['home_features'].present? amp;amp; params['home_features'].any?
r = r.joins(:re_home_features).where(re_home_features: {id: params['home_features']}).distinct(:id)
end
In my tests, I had:
params['home_features'] = [1 , 2, 3]
У меня есть 1 объект недвижимости, который имеет эти 3 функции. Но, с точки зрения, я получил один и тот же сайт, показанный 3 раза. Если я добавил distinct, недвижимость отображается только один раз. НО … если я изменю параметры на:
params['home_features'] = [1 , 2, 3, 500]
У меня нет недвижимости с этими 4 функциями, но результаты те же.
Без distinct недвижимость отображается 3 раза. С помощью dinstict недвижимость отображается один раз. Ожидаемый результат — нулевые результаты, потому что мне нужны объекты недвижимости со всеми выбранными функциями.
Но я думаю, что мы почти у цели! Я предоставлю некоторую информацию о моих моделях:
таблица real_estates
id | title | description | code | city_name | garage | ...
7 | Your House | Awesome... | 1234 | Rio de Janeiro | 4
таблица re_home_features
id | name
1 | Pool
2 | Garden
3 | Tenis court
4 | Closet
таблица real_estate_home_features — ассоциация многие ко многим
id | real_estate_id | re_home_feature_id
1 | 7 | 1
2 | 7 | 2
3 | 7 | 3
Если я запущу:
r = r.joins(:re_home_features).where(re_home_features: {id: [1,2,3,500]}).distinct(:id)
Я получил этот запрос (консоль rails):
SELECT DISTINCT `real_estates`.* FROM `real_estates` INNER JOIN `real_estate_home_features` ON `real_estate_home_features`.`real_estate_id` = `real_estates`.`id` INNER JOIN `re_home_features` ON `re_home_features`.`id` = `real_estate_home_features`.`re_home_feature_id` WHERE `re_home_features`.`id` IN (1, 2, 3, 500)
И он возвращает 1 результат. Идентификатор недвижимости 7. Если я удалю distinct, у меня будет 3 результата: одна и та же недвижимость, 3 раза.
Ожидаемый результат равен нулю. Если params['home_features'] = [1 , 2, 3]
я ожидаю 1 результат.
РЕДАКТИРОВАТЬ 2
Этот метод объединения работает, но он возвращает запрос типа «ИЛИ». В моем случае мне нужен запрос объединения с «И». Запрос должен быть: «Вернуть все объекты недвижимости, которые имеют функции 1, 2 И 3».
Спасибо!
Ответ №1:
Я не уверен, что это «расширенный поиск». Поиск по объединенным моделям описан в руководстве в разделе 12.1.4 с указанием условий для объединенных таблиц. Это будет выглядеть примерно так:
real_estates.joins(:re_home_features).where(re_home_features: {feature_name: 'pool'})
Естественно, это, вероятно, не совсем сработает, потому что вы мало рассказываете нам о том, как найти «функцию пула». Но это должно дать вам правильное направление.
Кстати, причина этого:
params['home_features'] = [1 , 2, 3, 500]
возвращает записи, которые имеют any
значение home_features
is, потому что в нем используется ‘или’. Если вы хотите real_estates
со всеми home_features
, тогда вам нужны «и». Вы можете поискать в Google по этому поводу.
Я думаю, я бы попробовал что-то вроде:
def search
r = real_estates.joins(:re_home_features)
%i(
code
city_name
garage
restrooms
).each do |attribute|
r = r.where(attribute => params[attribute]) unless params[attribute].blank?
end
params[:home_features].each do |home_feature_id|
r = r.where(re_home_features: {id: home_feature_id})
end unless params[:home_features].blank?
r = r.uniq
r = r.paginate(:page => params[:page], :per_page => 10)
r
end
ПРИМЕЧАНИЕ: я изменил params[:city]
params[:city_name]
, чтобы сделать этот первый each
итератор более чистым. Вам нужно будет изменить свой вид, если вы хотите сделать это таким образом. Если вы не хотите делать это таким образом, вы можете вернуться к неитераторному подходу, который у вас уже есть.
Это не проверено, поэтому я не уверен, что оно будет работать точно так, как представлено.
Комментарии:
1. Привет @jvillian, спасибо за ваш ответ! Я попробую прямо сейчас!
2. Не работает, но это хороший способ. Я должен идти сейчас, но через 2 часа я буду тестировать больше!
3. ДА. Я догадался в своем ответе, что это не сработает, потому что вы недостаточно рассказываете нам о своей
ReHomeFeature
модели. Добавьте больше деталей в свой вопрос для лучшего ответа.4. Привет, @jvillian! Ваш путь верен! Теперь я вижу результаты. Я обновлю свой вопрос с результатами и предоставлю дополнительную информацию о моих моделях.
5. Отлично! Не стесняйтесь голосовать / принимать для будущих поисковиков.