Як зробити запити, що не враховують регістр, на Mongodb?


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

Як мені зробити запит, що не враховує регістр? Я хочу знайти результат, навіть якщо "andrew";



Примітка кожному, хто спробує використати відповідь, що стосується регулярних виразів: Регекси повинні бути дезінфіковані.
Sean

Відповіді:


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")щоб точно відповідати.
Таріон

9
Згідно з веб-сайтом MongoDB, будь-який нечутливий до регістру регулярний вираз не ефективний до індексу "$ regex може ефективно використовувати індекс лише тоді, коли регулярний вираз має прив'язку до початку (тобто ^) рядка і відповідає регістру "
Райан Шумахер,

2
З Mongoose це працювало у мене: User.find ({'username': {$ regex: new RegExp ('^' + username.toLowerCase (), 'i')}}, function (err, res) {if (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" для 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


міцність: 1 достатньо для індексації без урахування регістру та діакритики. docs.mongodb.com/manual/reference/collation
Gaurav Ragtah

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.

Ви навіть можете розширити це, вибравши потрібні поля з об'єкта користувача Ендрю, виконавши це таким чином:

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.