Активная запись, добавляющая простой индекс sql с ошибкой регистра [duplicate]

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

Парсер вернет контейнер структур key / value при представлении опций от пользователя. Если параметр передается несколько раз, тогда у контейнера будет отдельная запись для каждой подачи параметра.

Вот пример, который выводит каждую опцию ввода нескольких токенов на отдельной строке:

#include 
#include 
#include 

#include 

namespace po = boost::program_options;

int main(int argc, char *argv[]) {
   // Define a multi-token option.
   po::options_description desc("Allowed options");
   desc.add_options()
      ("list", po::value>()->multitoken(), "multiple values");

   // Just parse the options without storing them in a map.
   po::parsed_options parsed_options = po::command_line_parser(argc, argv)
      .options(desc)
      .run();

   // Build list of multi-valued option instances. We iterate through
   // each command-line option, whether it is repeated or not. We
   // accumulate the values for our multi-valued option in a
   // container.
   std::vector> lists;
   for (const po::option& o : parsed_options.options) {
      if (o.string_key == "list")
         lists.push_back(o.value);
   }

   // If we had other normal options, we would store them in a map
   // here. In this demo program it isn't really necessary because
   // we are only interested in our special multi-valued option.
   po::variables_map vm;
   po::store(parsed_options, vm);

   // Print out the multi-valued option, each separate instance on its
   // own line.
   for (size_t i = 0; i < lists.size(); ++i) {
      for (size_t j = 0; j < lists[i].size(); ++j)
         std::cout << lists[i][j] << ' ';
      std::cout << '\n';
   }

   return 0;
}

И вот пример вызова ( live at coliru ):

$ ./po --list 1 2 3 --list foo bar --list how now brown cow
1 2 3 
foo bar 
how now brown cow 

22
задан Mike Woodhouse 1 November 2011 в 12:34
поделиться

8 ответов

Я бы предложил (как возможность рассмотреть среди других) использовать два отдельных поля:

  • email
  • email_original

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

Очевидно, что в модели пользователя необходимо настроить электронную почту на основе email_original при каждом сохранении и запретить прямое манипулирование с полем электронной почты.

0
ответ дан Hero Qu 17 August 2018 в 18:03
поделиться

Я бы упростил это ...

В вашей модели:

before_validation :downcase_email

def downcase_email
  self.email = email.downcase
end

Таким образом, индекс является агностиком базы данных, а ваши электронные письма в базе данных являются строчными.

6
ответ дан Jesse Wolgamott 17 August 2018 в 18:03
поделиться
  • 1
    Будет работать, но не может быть оценено, если вы отправляете электронное письмо людям, которые хотят видеть, что случай сохраняется на их адресах электронной почты (например, BobSmith@example.com). – Mark Berry 23 June 2012 в 00:38
  • 2
    @MarkBerry Не могу сказать, троллинг ли ты. – Jesse Wolgamott 23 June 2012 в 18:40
  • 3
    Извините, если моя фраза отключена. Я пытаюсь сказать «да», вы можете упростить свои индексы, если вы отбрасываете информацию о делах по электронной почте или все, что хотите индексировать, прежде чем сохранять ее в базе данных. С адресами электронной почты вы можете уйти от него - адреса электронной почты все равно будут работать, но некоторым получателям это не понравится. В целом я считаю, что сохранение информации, предоставляемой пользователем (включая случай, знаки акцента и т. Д.), Должно иметь приоритет над техническим упрощением. – Mark Berry 24 June 2012 в 21:42
  • 4
    self.email = email.downcase if email, чтобы уловить нуль, иначе Ruby выдаст исключение undefined method 'downcase' for nil:NilClass – scarver2 15 October 2012 в 21:38
  • 5
    @ scarver2: Или даже email.try(:downcase!) ... – robinjam 30 March 2013 в 09:07

Поскольку индексы MySQL уже не учитывают регистр, я предполагаю, что вы имеете дело с PostgreSQL, который по умолчанию создает индексы с учетом регистра. Я отвечаю здесь на основе Rails 3.2.3 и PostgreSQL 8.4.

Кажется, функциональные индексы - еще один пример того, что ActiveRecord не может генерировать. Внешние ключи и столбцы UUID - еще два, которые приходят на ум. Таким образом, нет выбора (кроме использования ActiveRecord для обезьян), но для использования execute операторов.

Это означает, что для точного дампа вашей базы данных вам нужно отказаться от схемы агностиков DB. rb в пользу структуры DB-specific.sql. См. Руководство по Rails по переходам, раздел 6.2 Типы дампов схемы . Это устанавливается следующим образом:

config / application.rb

config.active_record.schema_format = :sql

db / structure.sql следует обновлять автоматически при выполнении миграции. Вы можете сгенерировать его вручную с помощью этой команды:

rake db:structure:dump

Файл является чистым Postgres SQL. Несмотря на то, что вы не указали, когда используете rake -T для списка задач рейка, кажется, что вы можете использовать эту команду для загрузки базы данных из файла struct.sql:

rake db:structure:load

Здесь нет ничего волшебного: [ исходный код (показано здесь из Rails 3.2.16) просто вызывает psql на struct.sql.

Наконец, вот моя миграция, чтобы удалить старое, чувствительное к регистру ограничение по электронной почте и добавить функциональный индекс, чувствительный к регистру:

class FixEmailUniqueIndexOnUsers < ActiveRecord::Migration
  def up
    remove_index :users, :email
    execute "CREATE UNIQUE INDEX index_users_on_lowercase_email 
             ON users USING btree (lower(email));"
  end

  def down
    execute "DROP INDEX index_users_on_lowercase_email;"
    add_index :users, :email, :unique => true
  end
end
31
ответ дан Mark Berry 17 August 2018 в 18:03
поделиться
  • 1
    Отличный ответ, но интересно: в PostgreSQL (по крайней мере, & gt; = 8.3) метод индекса по умолчанию btree - почему вы указали его явно? – Richard Michael 8 December 2012 в 16:11
  • 2
    Не помню. Либо я использовал старую документацию, либо я отключил скрипт из графического интерфейса. – Mark Berry 13 December 2012 в 03:46
  • 3
    @meagar, почему вы отредактировали этот пост? Я просто пытался назвать, что я обновил сообщение 4 февраля 2014 года, чтобы исправить неработающие ссылки, один из которых теперь конкретно указывает на Rails 3.2.16. Это обескураживает тот, кто пытается внести свой вклад, чтобы получить пощечину тривиальным модом на шестилетний пост, мода, которая, на мой взгляд, удаляет соответствующую информацию. – Mark Berry 11 July 2018 в 15:29
  • 4
    @MarkBerry Meta информация об изменениях должна идти в & quot; Редактировать резюме & quot; когда вы отправляете редактирование, такая информация просто загромождает ответ. Никто не «хлопнул» кто-то здесь, мое редактирование никоим образом не было наказанием, и нет абсолютно никакой причины для вас обижаться на него. Это была безобидная рутинная очистка. – meagar♦ 11 July 2018 в 15:39
  • 5
    @meagar, я не согласен с тем, где принадлежит метаинформация (я сомневаюсь, что большинство людей знают, как просматривать изменения), но теперь, когда вы объяснили политику, я буду помнить об этом. Возможно, вы также можете использовать «Редактировать резюме», объяснять изменения - он будет и обучать, и сводить к минимуму преступление. Кажется странным обсуждать это здесь, но я исследовал, как спросить о редактировании: meta.stackexchange.com/a/75666/251926 . – Mark Berry 12 July 2018 в 17:28

Я думаю, что вам нужны имена столбцов, как показано ниже

   add_index "users", [email], {:name => "index_users_on_lower_email_index", :unique => true }

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

в зависимости от используемого вами синтаксиса db, который вы используете, может отличаться, но

alter table [users] alter column [email] varchar(250) collate utf8_general_ci ...

, и когда вы добавляете индекс в этот столбец, регистр будет нечувствительным к регистру.

-2
ответ дан Milan Jaric 17 August 2018 в 18:03
поделиться
  • 1
    Это не то, что я спросил. Мне нужен регистр, нечувствительный к регистру. Я знаю, как это сделать, но рельсы не распознают мое решение. – Binary Logic 31 October 2011 в 01:05
  • 2
    Для вашего вопроса все зависит от сортировки, которую вы выбираете для своей таблицы, или только одного поля в случае, если вы хотите «ожидаемый». поведение, иначе вам понадобится взломанный код или прекратите исправление базы данных множеством SQL-запросов. дампа схемы считывает эти «метаданные», из вашей базы данных и поскольку это универсальное решение для всей базы данных, иногда некоторые вещи отсутствуют, если вы не следуете соглашениям. Я не уверен в вашем случае, но я не понимаю, зачем нужен столбец, чувствительный к регистру, в таблице с индексом без учета регистра? – Milan Jaric 14 August 2013 в 20:59

Документация неясно, как это сделать, но этот источник выглядит следующим образом:

def add_index(table_name, column_name, options = {})
  index_name, index_type, index_columns = add_index_options(table_name, column_name, options)
  execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})"
end

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

add_index "users", ['lower(email)'], :name => "index_users_on_lower_email_index", :unique => true

Вы замечаете, что вы пробовали этот, но он не работал (добавив, что к вашему вопросу может быть хорошей идеей). Похоже, ActiveRecord просто не понимает индексы по вычисленному значению. Я могу подумать об уродливом взломе, который будет выполнен, но он уродлив:

  1. Добавьте столбец email_lc.
  2. Добавьте before_validation или before_save
  3. Поместите свой уникальный индекс на email_lc.

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

1
ответ дан mu is too short 17 August 2018 в 18:03
поделиться
  • 1
    Уже пробовал, не работает. Он говорит, что столбец не существует. – Binary Logic 31 October 2011 в 01:20
  • 2
    @Binary: у меня есть еще один (уродливый kludge) вариант для работы с повреждением мозга AR. – mu is too short 31 October 2011 в 01:31
  • 3
    @BinaryLogic, Этот ответ объясняет, почему вы не получаете & quot; не существует & quot; сообщение, и предлагает, как изменить исходный код, чтобы обойти его. Не пробовал. – Mark Berry 18 May 2012 в 22:39

Если вы используете PostgreSQL, вы можете изменить свой тип столбца на citext - строку без учета регистра. Он также делает поиск независимым от регистра.

def change
  enable_extension :citext
  change_column :users, :email, :citext
  add_index :users, :email, unique: true
end
21
ответ дан pragma 17 August 2018 в 18:03
поделиться
  • 1
    Красиво: работает как шарм. Единственным недостатком является то, что столбец "базовый" type text, а не string (в Postgres, character varying), что может иметь последствия производительности и хранения для массивных таблиц пользователей. (?) – aec 23 January 2016 в 01:18
  • 2
    @aec не должно быть разницы в хранении или производительности. См .: postgresql.org/docs/9.1/static/datatype-character.html – Tijn 23 March 2016 в 14:09
  • 3
    Обратите внимание, что PostgreSQL еще не оптимизирует условия LIKE или ILIKE для полей citext. См. dba.stackexchange.com/questions/105244/… – Jeff Tsay 1 August 2016 в 07:26

Для Rails 4.2 создайте уникальный индекс без учета регистра в таблице пользователей в столбце имени.

Создайте новый файл миграции с пустым методом изменения:

$ rails generate migration add_index_in_users_on_name

Добавьте вызов add_index метод для пустого метода изменения:

add_index :users, 'lower(name)', name: 'index_users_on_lower_name', unique: true

Run Rake db: migrate task:

$ rake db:migrate

В результате индекс будет добавлен правильно и файл db / schema.rb будет содержать правильные add_index:

add_index "users", ["LOWER(\"NAME\")"], name: "index_users_on_lower_name", unique: true

Это проверено только с RDB Oracle.

-1
ответ дан sadnix 17 August 2018 в 18:03
поделиться
  • 1
    На самом деле это не работает. Я получаю: CREATE UNIQUE INDEX & quot; index_users_on_lower_email & quot; ON & quot; users & quot; («ниже (электронная почта)») PG :: UndefinedColumn: ERROR: столбец «ниже (электронная почта)») не существует – Rob 27 May 2015 в 19:06
  • 2
    Версия Rails? В этом случае столбец должен быть указан как (ниже («электронная почта»)). В вашем примере двойные кавычки установлены неправильно. – sadnix 27 May 2015 в 22:23
  • 3
    Кто-нибудь еще проверил это решение? Я использую rails 4.2.3 и postgresql 9.4, и это решение не работает независимо от места кавычек. Я получаю PG :: UndefinedColumn: ERROR. – Jeremen 11 July 2015 в 14:07
  • 4
    Rails 4.2.5 все еще обертывает кавычки вокруг столбцов индекса, поэтому нарушает любую попытку использования add_index для выражений. – maia 27 November 2015 в 21:57
  • 5
    Работает для Rails 5.1.4 + PostgresQL – Alex Tatarnikov 28 January 2018 в 15:00

Считаете ли вы использование schema_plus ( https://github.com/lomba/schema_plus )? Среди прочего (поддержка принудительного использования внешних ключей в базе данных и для представлений), он поддерживает установку индексов без учета регистра для баз данных PostgreSQL и обрабатывает их сброс в схеме. Из Readme: «Если вы используете Postgresql, SchemaPlus обеспечивает поддержку условий, выражений, индексных методов и индексов без учета регистра».

4
ответ дан tovodeverett 17 August 2018 в 18:03
поделиться
Другие вопросы по тегам:

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