правильный способ избежать %% при построении LIKE-запросов в Rails 3 / ActiveRecord - PullRequest
20 голосов
/ 19 апреля 2011

Я хочу сопоставить поле URL с префиксом URL (который может содержать знаки процента), например, .where("url LIKE ?", "#{some_url}%"). Какой самый рельсовый способ?

Ответы [ 4 ]

22 голосов
/ 19 апреля 2011

Если я правильно понимаю, вы беспокоитесь о том, что "%" появляется внутри some_url, и это правильно;Вы также должны беспокоиться о встроенных подчеркиваниях («_»), они похожи на «».в регулярном выраженииЯ не думаю, что есть какой-то специфичный для Rails способ сделать это, поэтому у вас остается gsub:

.where('url like ?', some_url.gsub('%', '\\\\\%').gsub('_', '\\\\\_') + '%')

Здесь также нет необходимости в интерполяции строк.Вам нужно удвоить обратную косую черту, чтобы избежать их значения из анализатора строк базы данных, чтобы анализатор LIKE увидел простое "\%" и знал, что должен игнорировать экранированный знак процента.

Вам следует проверить свои журналы, чтобы убедиться, чтодве обратные косые черты проходят.Я получаю запутанные результаты при проверке вещей в irb, использование пяти (!) Дает правильный вывод, но я не вижу в этом смысла;если кто-то увидит смысл в пяти из них, будет благодарен за пояснительный комментарий.

ОБНОВЛЕНИЕ : Джейсон Кинг любезно предложил упрощение для кошмара сбежавших персонажей.Это позволяет вам указать временный управляющий символ , чтобы вы могли делать такие вещи:

.where("url LIKE ? ESCAPE '!'", some_url.gsub(/[!%_]/) { |x| '!' + x })

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

Это стандартный синтаксис SQL92, поэтому он будет работать в любой БД, поддерживающей это, включая PostgreSQL, MySQL и SQLite.

Встраивание одного языка в другой всегда немного кошмарно.и вы не можете ничего с этим поделать.Всегда найдутся уродливые маленькие кусочки, которые вам просто нужно улыбаться и нести.

17 голосов
/ 23 августа 2015

В Rails версии 4.2.x существует метод активной записи, называемый sanitize_sql_like.Таким образом, вы можете сделать в своей модели область поиска, например:

scope :search, -> search { where('"accounts"."name" LIKE ?', "#{sanitize_sql_like(search)}%") }

, и вызвать область действия следующим образом:

Account.search('Test_%')

Полученная экранированная строка sql:

SELECT "accounts".* FROM "accounts" WHERE ("accounts"."name" LIKE 'Test\_\%%')

Подробнее здесь: http://edgeapi.rubyonrails.org/classes/ActiveRecord/Sanitization/ClassMethods.html

6 голосов
/ 06 сентября 2012

https://gist.github.com/3656283

С этим кодом

Item.where(Item.arel_table[:name].matches("%sample!%code%"))

правильно экранирует % между «образцом» и «кодом» и соответствует «AAAsample%»codeBBB ", но не для" AAAsampleBBBcodeCCC "на MySQL, PostgreSQL и SQLite3 по крайней мере.

0 голосов
/ 19 апреля 2011
Post.where('url like ?', "%#{some_url + '%'}%)
...