Вопрос по ruby-on-rails-3, ruby-on-rails, ruby – Построитель SQL-запросов Rails… Или построитель запросов ActiveRecord

4

Мне нужно запустить SQL-запрос, как

sql = 'SELECT * FROM users WHERE id != ' + self.id.to_s + ' AND id NOT IN (SELECT artner_id FROM encounters WHERE user_id = ' + self.id.to_s + ')'
sql += ' AND id NOT IN (SELECT user_id FROM encounters WHERE partner_id = ' + self.id.to_s + ' AND predisposition = ' + Encounter::Negative.to_s + ')'
sql += ' AND cfg_sex = ' + self.sex.to_s + ' AND cfg_country = ' + self.country.to_s + ' AND cfg_city = ' + self.city.to_s
sql += ' ORDER BY rand() LIMIT 1'

Это может быть выполнено AR.find_by_sql, но код до этого плохо читается. Есть ли конструктор запросов, который может построить этот запрос?

Например, Kohana (это PHP-фреймворк, я разработчик php, но я хочу изменить этот язык для детей на ruby / rails) имеет конструктор запросов, который работает следующим образом:

$sql = DB::select('*')->from('users');
$sql->where('id', 'NOT_IN', DB::expr('SELECT partner_id FROM encounters WHERE user_id = '.$user->id));
$sql->where('id', 'NOT_IN', DB::expr('SELECT user_id FROM encounters WHERE partner_id = '.$user->id.' AND predisposition = '.Encounter::Negative));
....
etc
...

Запрос, построенный с помощью построителя запросов, такого как построитель запросов Kohana, более читабелен и понятен.

Есть ли драгоценный камень, чтобы решить эту проблему?

Ваш Ответ

3   ответа
3

Здесь тот же запрос, приведенный к терминалам AREL. Это еще не красиво - это сложный запрос в целом.

User.where("id = ? AND "
           "id NOT IN (SELECT artner_id FROM encounters WHERE user_id = ?) AND " +  
           "id NOT IN (SELECT user_id FROM encounters WHERE partner_id = ? AND predisposition = ? ) AND " + 
            "cfg_sex = ? AND cfg_country = ? AND cfg_city = ?)", 
            self.id, self.id, self.id, Encounter::Negative, 
            self.sex, self.country, self.city).order(" rand() ").limit(1)

(Я не проверял это, поэтому возможно, что в нем могут быть опечатки.)

Я рекомендую пару вещей:

Если у вас есть сложные, где предложения, они могут быть объединены в цепочку, и AREL соберет их вместе, как правило, довольно хорошо. Это позволяет вам использовать области в ваших модельных классах и связывать их вместе.

Например, вы можете сделать это:

User < ActiveRecord::Base
  self.def in_city_state_country(city, state, country)
    where("cfg_sex = ? AND cfg_country = ? AND cfg_city = ?", city, state, country)
  end
  self.def is_of_sex(sex)
    where("cfg_sex = ?", sex)
  end
end

Затем вы можете переписать эти части запроса следующим образом:

User.is_of_sex(user.sex).in_city_state_country(user.city, user.state, user.country)

и так далее.

Разбиение запросов на более мелкие части также облегчает их тестирование с помощью rspecs. В результате получается более модульный, поддерживаемый код.

Для более подробной информации, проверьтеРуководство по Rails - Интерфейс запросов Active Record

3

Существует библиотека ruby, которая использует реляционную алгебру. Это называетсяAREL, Если вы используете Rails 3.x, то у вас уже есть.

ids   = Partner.where(user_id: self.id).pluck(:partner_id) << self.id
users = User.where("id NOT IN #{ ids.join(',') }")
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded Mark Pegasov
4

Вам нужноsqueel драгоценный камень. Этоextends AR с блоками и делает очень сложные запросы с легкостью.

Всего несколько функций:

# not_in == cool! )
Product.where{id.not_in LineItem.select{product_id}}
# SELECT "products".* FROM "products" WHERE "products"."id" NOT IN 
# (SELECT "line_items"."product_id" FROM "line_items" )

# outer joins on pure Ruby:
LineItem.joins{product.outer}
# LineItem Load (0.0ms)  SELECT "line_items".* FROM "line_items" 
# LEFT OUTER JOIN "products" ON "products"."id" = "line_items"."product_id"

# calcs, aliasing:
Product.select{[avg(price).as(middle)]}
# SELECT avg("products"."price") AS middle FROM "products"

# comparison
Product.where{id != 100500}
Product.where{price<10}

# logical OR
Product.where{(price<10) | (title.like '%rails%')}
# SELECT "products".* FROM "products" WHERE (("products"."price" < 10 OR
# "products"."title" LIKE '%rails%'))

# xxx_any feature (also available xxx_all)
Product.where{title.like_any %w[%ruby% %rails%]}
# SELECT "products".* FROM "products" WHERE (("products"."title" LIKE '%ruby%' OR 
# "products"."title" LIKE '%rails%'))    

Обратите внимание на использование блоков:{...} здесь нет хэшей. Также обратите внимание на отсутствие символов.

If you decide to pick it, read the section that starts with "This carries with it an important implication"

Похожие вопросы