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

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

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

Теги:
database

8 ответов

73
Лучший ответ

Решение Chris Fulstow будет работать (+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.

  • 0
    Отличный ответ, мой подход к регулярным выражениям действительно замедляется, когда приходится сканировать несколько миллионов документов.
  • 24
    Это на самом деле не совсем правильно, потому что вы можете найти «Эндрю что-то», ища «Эндрю». Поэтому измените регулярное выражение на: new RegExp('^'+ username + '$', "i") чтобы получить точное совпадение.
Показать ещё 7 комментариев
47

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

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

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

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

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

  • 5
    Знаете ли вы, как сделать это с помощью Node.js mongoose?
  • 1
    Интересно, насколько хорошо это будет работать с большими коллекциями. Вы потеряли бы преимущество своего рода functinon
Показать ещё 4 комментария
11

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

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

Если вы хотите запросить "нечувствительное к регистру точное сопоставление", вы можете пойти так.

var thename =  '^Andrew$';
db.collection.find({'name': {'$regex': thename,$options:'i'}});
  • 0
    Спасибо, это сработало отлично!
5

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

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

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

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

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

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

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

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

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

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

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

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

Чтобы найти строку, не учитывающую регистр букв:

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

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

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

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

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню