Как мне делать запросы без учета регистра в Mongodb?


93
var thename = 'Andrew';
db.collection.find({'name':thename});

Как сделать запрос без учета регистра? Я хочу найти результат, даже если "андрей";



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

Ответы:


126

Решение Криса Фулстоу будет работать (+1), однако оно может быть неэффективным, особенно если ваша коллекция очень большая. Некорневые регулярные выражения (те, которые не начинаются с ^, которые привязывают регулярное выражение к началу строки) и те, которые используют iфлаг нечувствительности к регистру, не будут использовать индексы, даже если они существуют.

Альтернативный вариант, который вы можете рассмотреть, - денормализовать данные для хранения версии nameполя в нижнем регистре , например, как name_lower. Затем вы можете эффективно запросить это (особенно если оно проиндексировано) для точных совпадений без учета регистра, например:

db.collection.find({"name_lower": thename.toLowerCase()})

Или с совпадением префикса (корневое регулярное выражение) как:

db.collection.find( {"name_lower":
    { $regex: new RegExp("^" + thename.toLowerCase(), "i") } }
);

Оба этих запроса будут использовать индекс name_lower.


1
Отличный ответ, мой подход с регулярным выражением действительно замедляется, когда ему приходится сканировать несколько миллионов документов.
Крис Фулстоу

34
На самом деле это не совсем правильно, потому что вы можете найти «Эндрю что-то» при поиске «Эндрю». Поэтому настройте регулярное выражение на: new RegExp('^'+ username + '$', "i")чтобы оно было точным.
Tarion

9
Согласно веб-сайту MongoDB, любое регулярное выражение без учета регистра не является эффективным с точки зрения индекса «$ regex может эффективно использовать индекс только в том случае, если регулярное выражение имеет привязку для начала (т. Е. ^) Строки и является совпадением с учетом регистра »,
Райан Шумахер,

2
С Mongoose это сработало для меня: User.find ({'username': {$ regex: new RegExp ('^' + username.toLowerCase (), 'i')}}, function (err, res) {if (err ) throw err; next (null, res);});
ChrisRich

5
Никогда не забывайте экранировать имя при работе с регулярными выражениями. Мы не хотим, чтобы инъекции захватили красоту mongodb. Только представьте, что вы использовали этот код для страницы входа, а имя пользователя было ".*".
Тобиас

90

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

db.collection.find( { "name" : { $regex : /Andrew/i } } );

Чтобы использовать шаблон регулярного выражения из вашей thenameпеременной, создайте новый объект RegExp :

var thename = "Andrew";
db.collection.find( { "name" : { $regex : new RegExp(thename, "i") } } );

Обновление: для точного совпадения вы должны использовать регулярное выражение "name": /^Andrew$/i. Благодаря Яннику Л.


7
Вы знаете, как это сделать с помощью Node.js mongoose?
user847495

1
Интересно, насколько хорошо это будет работать с большими коллекциями. Вы потеряете преимущества функции сортировки,
Уилфред Спрингер

5
Это неправильно, он будет соответствовать любому документу, содержащему "andrew" for name, а не только равенству.
Джонатан Кремин

14
@JonathanCremin, чтобы помочь людям, вы должны опубликовать правильный ответ:{ "name": /^Andrew$/i }
Янник Лориот,

@YannickL. 1+ для здравого смысла. Я просто проходил мимо не то, что искал.
Lpc_dark

38

Я решил это так.

 var thename = 'Andrew';
 db.collection.find({'name': {'$regex': thename,$options:'i'}});

Если вы хотите запросить «точное соответствие без учета регистра», вы можете сделать это следующим образом.

var thename =  '^Andrew$';
db.collection.find({'name': {'$regex': thename,$options:'i'}});

7

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

Вероятно, самый простой способ сделать это - установить параметры сортировки в базе данных. Затем все запросы наследуют это сопоставление и будут использовать его:

db.createCollection("cities", { collation: { locale: 'en_US', strength: 2 } } )
db.names.createIndex( { city: 1 } ) // inherits the default collation

Вы также можете сделать это так:

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

И используйте это так:

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

Это вернет города с названиями «Нью-Йорк», «Нью-Йорк», «Нью-Йорк» и т. Д.

Для получения дополнительной информации: https://jira.mongodb.org/browse/SERVER-90


Strength: 1 достаточно для индексации без учета регистра и без учета диакритических знаков. docs.mongodb.com/manual/reference/collation
Гаурав Рагта,

7
  1. С Mongoose (и Node) это сработало:

    • User.find({ email: /^name@company.com$/i })

    • User.find({ email: new RegExp(`^ $ {emailVariable} $`, 'i')})

  2. В MongoDB это сработало:

    • db.users.find({ email: { $regex: /^name@company.com$/i }})

Обе строки нечувствительны к регистру. Электронная почта в БД может быть, NaMe@CompanY.Comи обе строки все равно найдут объект в БД.

Точно так же мы могли бы использовать, /^NaMe@CompanY.Com$/iи он все равно находил бы электронную почту: name@company.comв БД.



4

Я решил эту проблему несколько часов назад.

var thename = 'Andrew'
db.collection.find({ $text: { $search: thename } });
  • При выполнении запросов таким способом чувствительность к регистру и диакритическая чувствительность по умолчанию установлены на false.

Вы даже можете расширить это, выбрав нужные поля в пользовательском объекте Andrew, сделав это следующим образом:

db.collection.find({ $text: { $search: thename } }).select('age height weight');

Ссылка: https://docs.mongodb.org/manual/reference/operator/query/text/#text


1
$ text выполняет текстовый поиск по содержимому полей, проиндексированных с помощью текстового индекса.
SSH это

4

... с мангустом на NodeJS этот запрос:

const countryName = req.params.country;

{ 'country': new RegExp(`^${countryName}$`, 'i') };

или

const countryName = req.params.country;

{ 'country': { $regex: new RegExp(`^${countryName}$`), $options: 'i' } };

// ^australia$

или

const countryName = req.params.country;

{ 'country': { $regex: new RegExp(`^${countryName}$`, 'i') } };

// ^turkey$

Полный пример кода на Javascript, NodeJS с Mongoose ORM на MongoDB

// get all customers that given country name
app.get('/customers/country/:countryName', (req, res) => {
    //res.send(`Got a GET request at /customer/country/${req.params.countryName}`);

    const countryName = req.params.countryName;

    // using Regular Expression (case intensitive and equal): ^australia$

    // const query = { 'country': new RegExp(`^${countryName}$`, 'i') };
    // const query = { 'country': { $regex: new RegExp(`^${countryName}$`, 'i') } };
    const query = { 'country': { $regex: new RegExp(`^${countryName}$`), $options: 'i' } };

    Customer.find(query).sort({ name: 'asc' })
        .then(customers => {
            res.json(customers);
        })
        .catch(error => {
            // error..
            res.send(error.message);
        });
});

1

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

db.collection.find({name:{
                             $regex: new RegExp(thename, "ig")
                         }
                    },function(err, doc) {
                                         //Your code here...
                  });

1

Чтобы найти строку литералов без учета регистра:

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

db.collection.find({
    name: {
        $regex: new RegExp('^' + name.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') + '$', 'i')
    }
});

Использование строчного индекса (быстрее)

db.collection.find({
    name_lower: name.toLowerCase()
});

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

Обратите внимание, что вам нужно будет экранировать имя до регулярного выражения. Если вам нужны подстановочные знаки, вводимые пользователем .replace(/%/g, '.*'), лучше добавлять их после экранирования, чтобы вы могли сопоставить «a%», чтобы найти все имена, начинающиеся с «a».


1

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

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

/*
* 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

-3

Простым способом было бы использовать $ toLower, как показано ниже.

db.users.aggregate([
    {
        $project: {
            name: { $toLower: "$name" }
        }
    },
    {
        $match: {
            name: the_name_to_search
        }
    }
])
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.