MongoDB: Можно ли сделать запрос без учета регистра? - PullRequest
265 голосов
/ 08 декабря 2009

Пример:

> db.stuff.save({"foo":"bar"});

> db.stuff.find({"foo":"bar"}).count();
1
> db.stuff.find({"foo":"BAR"}).count();
0

Ответы [ 23 ]

297 голосов
/ 08 декабря 2009

Вы можете использовать регулярное выражение .

В вашем примере это будет:

db.stuff.find( { foo: /^bar$/i } );

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

190 голосов
/ 14 декабря 2010

UPDATE:

Первоначальный ответ устарел. Mongodb теперь поддерживает расширенный полнотекстовый поиск со многими функциями.

ОРИГИНАЛЬНЫЙ ОТВЕТ:

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

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

В качестве альтернативы вы можете сохранить заглавную копию и выполнить поиск по ней. Например, у меня есть таблица User с именем пользователя в смешанном регистре, но id является заглавной копией имени пользователя. Это гарантирует, что дублирование с учетом регистра невозможно (наличие «Foo» и «foo» не допускается), и я могу выполнить поиск по id = username.toUpperCase (), чтобы получить поиск имени пользователя без учета регистра.

Если у вас большое поле, такое как тело сообщения, дублирование данных, вероятно, не очень хороший вариант. Я считаю, что использование постороннего индексатора, такого как Apache Lucene, является лучшим вариантом в этом случае.

58 голосов
/ 03 июня 2011

Имейте в виду, что предыдущий пример:

db.stuff.find( { foo: /bar/i } );

приведет к тому, что все записи, содержащие bar , будут соответствовать запросу (bar1, barxyz, openbar), это может быть очень опасно для поиска имени пользователя в функции аутентификации ...

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

db.stuff.find( { foo: /^bar$/i } );

См. http://www.regular -expressions.info / для получения справки по синтаксису регулярных выражений

56 голосов
/ 13 июля 2012

Если вам нужно создать регулярное выражение из переменной, это гораздо лучший способ сделать это: https://stackoverflow.com/a/10728069/309514

Затем вы можете сделать что-то вроде:

var string = "SomeStringToFind";
var regex = new RegExp(["^", string, "$"].join(""), "i");
// Creates a regex of: /^SomeStringToFind$/i
db.stuff.find( { foo: regex } );

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

32 голосов
/ 01 декабря 2016

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

Я лично написал одному из основателей, чтобы он заработал, и он сделал это возможным! Это была проблема в JIRA с 2009 года , и многие просили эту функцию. Вот как это работает:

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

db.cities.createIndex(
  { city: 1 },
  { 
    collation: {
      locale: 'en',
      strength: 2
    }
  }
);

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

db.createCollection('cities', { collation: { locale: 'en', strength: 2 } } );

В любом случае, чтобы использовать регистр без учета регистра, необходимо указать тот же порядок сортировки в операции find, которая использовалась при создании индекса или коллекции:

db.cities.find(
  { city: 'new york' }
).collation(
  { locale: 'en', strength: 2 }
);

Это вернет "Нью-Йорк", "Нью-Йорк", "Нью-Йорк" и т. Д.

Другие заметки

  • Ответы, предлагающие использовать полнотекстовый поиск, неправильны в этом случае (и потенциально опасны ). Вопрос был в том, чтобы сделать запрос без учета регистра, например, username: 'bill' соответствует BILL или Bill, а не полнотекстовый поисковый запрос, который также будет соответствовать основанным словам bill, таким как Bills, billed и т. Д.
  • Ответы, предлагающие использовать регулярные выражения, являются медленными, потому что даже с индексами документация заявляет :

    "Запросы регулярного выражения без учета регистра обычно не могут эффективно использовать индексы. Реализация $ regex не учитывает параметры сортировки и не может использовать индексы без учета регистра."

    $regex ответы также рискуют ввод данных пользователем .

16 голосов
/ 17 декабря 2015
db.zipcodes.find({city : "NEW YORK"}); // Case-sensitive
db.zipcodes.find({city : /NEW york/i}); // Note the 'i' flag for case-insensitivity
14 голосов
/ 27 августа 2016

TL; DR

Правильный способ сделать это в монго

Не использовать RegExp

Вперед и используйте встроенную индексацию mongodb, поиск

Шаг 1:

db.articles.insert(
   [
     { _id: 1, subject: "coffee", author: "xyz", views: 50 },
     { _id: 2, subject: "Coffee Shopping", author: "efg", views: 5 },
     { _id: 3, subject: "Baking a cake", author: "abc", views: 90  },
     { _id: 4, subject: "baking", author: "xyz", views: 100 },
     { _id: 5, subject: "Café Con Leche", author: "abc", views: 200 },
     { _id: 6, subject: "Сырники", author: "jkl", views: 80 },
     { _id: 7, subject: "coffee and cream", author: "efg", views: 10 },
     { _id: 8, subject: "Cafe con Leche", author: "xyz", views: 10 }
   ]
)

Шаг 2:

Необходимо создать индекс для любого поля TEXT , которое вы хотите найти, без индексации запрос будет очень медленным

db.articles.createIndex( { subject: "text" } )

шаг 3:

db.articles.find( { $text: { $search: "coffee",$caseSensitive :true } } )  //FOR SENSITIVITY
db.articles.find( { $text: { $search: "coffee",$caseSensitive :false } } ) //FOR INSENSITIVITY
9 голосов
/ 04 сентября 2018
db.company_profile.find({ "companyName" : { "$regex" : "Nilesh" , "$options" : "i"}});
8 голосов
/ 24 октября 2011

Mongo (текущая версия 2.0.0) не разрешает поиск без учета регистра по индексированным полям - см. их документацию . Для неиндексированных полей регулярные выражения, перечисленные в других ответах, должны подойти.

6 голосов
/ 20 апреля 2011

Лучший метод на выбранном вами языке: при создании обёртки модели для ваших объектов, пусть ваш метод save () выполняет итерацию по набору полей, которые вы будете искать, по которым также есть индекс; этот набор полей должен иметь строчные буквы, которые затем используются для поиска.

Каждый раз, когда объект сохраняется снова, свойства нижнего регистра проверяются и обновляются с любыми изменениями основных свойств. Это позволит вам эффективно выполнять поиск, но при этом каждый раз обновлять поля lc будут скрыты.

Строчные поля могут быть хранилищем объектов ключ: значение или просто именем поля с префиксом lc_. Я использую второй для упрощения запросов (глубокие запросы к объектам могут иногда сбивать с толку).

Примечание: вы хотите индексировать поля lc_, а не основные поля, на которых они основаны.

...