MongoDB: Можно ли сделать запрос без учета регистра?

Пример:

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

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

Ответ 1

Вы можете использовать regex.

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

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

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

Ответ 2

UPDATE:

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

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

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

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

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

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

Ответ 3

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

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

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

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

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

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

Ответ 4

Если вам нужно создать regexp из переменной, это гораздо лучший способ сделать это: fooobar.com/questions/31035/...

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

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

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

Ответ 5

Начиная с 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 также рискуют ввести пользовательский ввод.

Ответ 6

db.zipcodes.find({city : "NEW YORK"}); // Case-sensitive
db.zipcodes.find({city : /NEW york/i}); // Note the 'i' flag for case-insensitivity

Ответ 7

TL; DR

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

Не используйте RegExp

Go natural И использовать встроенную индексацию 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:

Необходимо создать индекс в зависимости от того, какое ТЕКСТ поле, которое вы хотите искать, без запроса индексирования будет чрезвычайно медленным

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

Ответ 8

db.company_profile.find({ "companyName" : { "$regex" : "Nilesh" , "$options" : "i"}});

Ответ 9

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

Ответ 10

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

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

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

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

Ответ 11

Используя Mongoose, это сработало для меня:

var find = function(username, next){
    User.find({'username': {$regex: new RegExp('^' + username, 'i')}}, function(err, res){
        if(err) throw err;
        next(null, res);
    });
}

Ответ 12

Предположим, что вы хотите найти "столбец" в "Таблице", и вы хотите, чтобы поиск в insensstive. Лучший и эффективный способ, как показано ниже:

//create empty JSON Object
mycolumn = {};

//check if column has valid value
if(column) {
    mycolumn.column = {$regex: new RegExp(column), $options: "i"};
}
Table.find(mycolumn);

Выше кода просто добавляет ваше значение поиска как RegEx и выполняется поиск с использованием критериев insensitve, установленных с опцией "i".

Все самое лучшее.

Ответ 13

Одна важная вещь, которую следует иметь в виду при использовании запроса на основе Regex. Когда вы делаете это для системы входа в систему, избегайте каждого символа, который вы ищете, и не забывайте операторы ^ и $. У Lodash есть хорошая функция для этого, если вы уже используете его:

db.stuff.find({$regex: new RegExp(_.escapeRegExp(bar), $options: 'i'})

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

Ответ 14

Структура агрегации была введена в mongodb 2.2. Вы можете использовать строковый оператор "$ strcasecmp", чтобы сделать нечувствительное к регистру сравнение строк. Это более рекомендуется и проще, чем при использовании регулярного выражения.

Здесь официальный документ оператора оператора агрегации: https://docs.mongodb.com/manual/reference/operator/aggregation/strcasecmp/#exp._S_strcasecmp.

Ответ 15

Вы можете использовать Нечувствительные к регистру индексы:

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

/* strength: CollationStrength.Secondary
* Secondary level of comparison. Collation performs comparisons up to secondary * differences, such as diacritics. That is, collation performs comparisons of 
* base characters (primary differences) and diacritics (secondary differences). * Differences between base characters takes precedence over secondary 
* differences.
*/
db.users.createIndex( { name: 1 }, collation: { locale: 'tr', strength: 2 } } )

Чтобы использовать индекс, запросы должны указывать одну и ту же сортировку.

db.users.insert( [ { name: "Oğuz" },
                            { name: "oğuz" },
                            { name: "OĞUZ" } ] )

// does not use index, finds one result
db.users.find( { name: "oğuz" } )

// uses the index, finds three results
db.users.find( { name: "oğuz" } ).collation( { locale: 'tr', strength: 2 } )

// does not use the index, finds three results (different strength)
db.users.find( { name: "oğuz" } ).collation( { locale: 'tr', strength: 1 } )

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

db.createCollection("users", { collation: { locale: 'tr', strength: 2 } } )
db.users.createIndex( { name : 1 } ) // inherits the default collation

Ответ 16

Для поиска и экранирования переменной:

const escapeStringRegexp = require('escape-string-regexp')
const name = 'foo'
db.stuff.find({name: new RegExp('^' + escapeStringRegexp(name) + '$', 'i')})   

Выход из переменной защищает запрос от атак с помощью '. *' Или другого регулярного выражения.

бежать строка-регулярное выражение

Ответ 17

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

private Func<string, BsonRegularExpression> CaseInsensitiveCompare = (field) => 
            BsonRegularExpression.Create(new Regex(field, RegexOptions.IgnoreCase));

Затем вы просто фильтруете поле в следующем виде.

db.stuff.find({"foo": CaseInsensitiveCompare("bar")}).count();

Ответ 18

Использование фильтра работает для меня на С#.

string s = "searchTerm";
    var filter = Builders<Model>.Filter.Where(p => p.Title.ToLower().Contains(s.ToLower()));
                var listSorted = collection.Find(filter).ToList();
                var list = collection.Find(filter).ToList();

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

Это также позволяет избежать проблемы

var filter = Builders<Model>.Filter.Eq(p => p.Title.ToLower(), s.ToLower());

что mongodb будет думать, что p.Title.ToLower() является свойством и не будет правильно отображаться.

Ответ 19

Используйте RegExp, в случае, если какие-либо другие варианты не работают для вас, RegExp является хорошим вариантом. Это делает строку нечувствительной к регистру.

var username = new RegExp("^" + "John" + "$", "i");;

использовать имя пользователя в запросах, а затем все готово.

Я надеюсь, что это сработает и для вас. Всего наилучшего.

Ответ 20

Для любого, кто использует Golang и хочет иметь полнотекстовый поиск с учетом регистра с помощью mongodb и библиотеки globalsign mgo godoc.

collation := &mgo.Collation{
    Locale:   "en",
    Strength: 2, 
}


err := collection.Find(query).Collation(collation)

Ответ 22

Они были протестированы для поиска строк

{'_id': /.*CM.*/}               ||find _id where _id contains   ->CM
{'_id': /^CM/}                  ||find _id where _id starts     ->CM
{'_id': /CM$/}                  ||find _id where _id ends       ->CM

{'_id': /.*UcM075237.*/i}       ||find _id where _id contains   ->UcM075237, ignore upper/lower case
{'_id': /^UcM075237/i}          ||find _id where _id starts     ->UcM075237, ignore upper/lower case
{'_id': /UcM075237$/i}          ||find _id where _id ends       ->UcM075237, ignore upper/lower case

Ответ 23

Я столкнулся с подобной проблемой, и это то, что работает для меня:

  const flavorExists = await Flavors.findOne({
    'flavor.name': { $regex: flavorName, $options: 'i' },
  });