Como faço consultas que não diferenciam maiúsculas de minúsculas no Mongodb?

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

Como faço uma consulta sem distinção entre maiúsculas e minúsculas? Quero encontrar o resultado mesmo que "andrew";

user847495
fonte
Uma observação para todos que tentarem usar uma resposta envolvendo regexes: Regexes precisam ser higienizados.
sean

Respostas:

128

A solução de Chris Fulstow funcionará (+1), entretanto, pode não ser eficiente, especialmente se sua coleção for muito grande. Expressões regulares não enraizadas (aquelas que não começam com ^, que ancoram a expressão regular no início da string) e aquelas que usam o isinalizador para insensibilidade a maiúsculas e minúsculas não usarão índices, mesmo se eles existirem.

Uma opção alternativa que você pode considerar é desnormalizar seus dados para armazenar uma versão em minúsculas do namecampo, por exemplo como name_lower. Você pode então consultar isso de forma eficiente (especialmente se for indexado) para correspondências exatas que não diferenciam maiúsculas de minúsculas como:

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

Ou com uma correspondência de prefixo (uma expressão regular enraizada) como:

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

Ambas as consultas usarão um índice em name_lower.

dcrosta
fonte
1
Ótima resposta, minha abordagem regex realmente fica mais lenta, uma vez que precisa digitalizar alguns milhões de documentos.
Chris Fulstow
35
Na verdade, isso não está totalmente correto, porque você pode encontrar "Andrew alguma coisa" enquanto procura por "Andrew". Portanto, ajuste o regex para: new RegExp('^'+ username + '$', "i")para ser uma correspondência exata.
Tarion de
9
De acordo com o site do MongoDB, qualquer regex insensível a maiúsculas e minúsculas não é eficiente no índice "$ regex só pode usar um índice de forma eficiente quando a expressão regular tem uma âncora para o início (ou seja, ^) de uma string e é uma correspondência que diferencia maiúsculas de minúsculas "
Ryan Schumacher,
2
Com o Mongoose, isso funcionou para mim: User.find ({'username': {$ regex: new RegExp ('^' + username.toLowerCase (), 'i')}}, function (err, res) {if (err ) lançar err; próximo (nulo, res);});
ChrisRich
5
Nunca se esqueça de escapar do nome ao trabalhar com expressões regulares. Não queremos que as injeções controlem a beleza do mongodb. Imagine que você usou este código para uma página de login e o nome de usuário era ".*".
Tobias
91

Você precisaria usar uma expressão regular que não diferencia maiúsculas de minúsculas para este, por exemplo

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

Para usar o padrão regex de sua thenamevariável, construa um novo objeto RegExp :

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

Atualização: para correspondência exata, você deve usar o regex "name": /^Andrew$/i. Graças a Yannick L.

Chris Fulstow
fonte
7
Você sabe como fazer isso usando o Node.js mongoose?
user847495
1
Eu me pergunto como isso funcionará bem com grandes coleções. Você perderia o benefício de uma função de classificação
Wilfred Springer
5
Isso está errado, ele irá corresponder a qualquer documento contendo "andrew" para name, não apenas igualando.
Jonathan Cremin
14
@JonathanCremin para ajudar as pessoas, você deve postar a resposta correta:{ "name": /^Andrew$/i }
Yannick Loriot
@YannickL. 1+ para fazer a coisa do bom senso. Eu estava apenas passando, não o que eu estava procurando.
Lpc_dark
39

Eu resolvi assim.

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

Se você quiser fazer uma consulta sobre 'correspondência exata que não diferencia maiúsculas de minúsculas', pode fazer o seguinte.

var thename =  '^Andrew$';
db.collection.find({'name': {'$regex': thename,$options:'i'}});
RIPAN
fonte
8
  1. Com o Mongoose (e Node), isso funcionou:

    • User.find({ email: /^[email protected]$/i })

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

  2. No MongoDB, isso funcionou:

Ambas as linhas não diferenciam maiúsculas de minúsculas. O email no DB pode ser [email protected]e ambas as linhas ainda encontrarão o objeto no DB.

Da mesma forma, poderíamos usar /^[email protected]$/ie ainda encontraria email: [email protected]no DB.

Raymond Gan
fonte
7

O MongoDB 3.4 agora inclui a capacidade de criar um índice que não diferencia maiúsculas de minúsculas, o que aumentará drasticamente a velocidade das pesquisas que não diferenciam maiúsculas de minúsculas em grandes conjuntos de dados. É feito especificando um agrupamento com uma intensidade de 2.

Provavelmente, a maneira mais fácil de fazer isso é definir um agrupamento no banco de dados. Então, todas as consultas herdam esse agrupamento e o usarão:

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

Você também pode fazer assim:

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

E use-o assim:

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

Isso retornará cidades chamadas "nova york", "Nova York", "Nova york" etc.

Para mais informações: https://jira.mongodb.org/browse/SERVER-90

user3413723
fonte
força: 1 é suficiente para indexação insensível a maiúsculas e minúsculas e insensível a diacríticos. docs.mongodb.com/manual/reference/collation
Gaurav Ragtah
5

Para encontrar strings que não diferenciam maiúsculas de minúsculas, use isso,

var thename = "Andrew";
db.collection.find({"name":/^thename$/i})
Pranit
fonte
1
Por que você está adicionando uma resposta duplicada, já que ela já está lá em stackoverflow.com/a/7101868/4273915
Shrabanee
4

Acabei de resolver esse problema há algumas horas.

var thename = 'Andrew'
db.collection.find({ $text: { $search: thename } });
  • A sensibilidade a maiúsculas e minúsculas e a sensibilidade diacrítica são definidas como falsas por padrão ao fazer consultas dessa maneira.

Você pode até mesmo expandir isso selecionando os campos necessários do objeto de usuário de Andrew, fazendo isso desta maneira:

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

Referência: https://docs.mongodb.org/manual/reference/operator/query/text/#text

Briant Anthony
fonte
1
$ text executa uma pesquisa de texto no conteúdo dos campos indexados com um índice de texto.
SSH em
4

... com mangusto em NodeJS que consulta:

const countryName = req.params.country;

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

ou

const countryName = req.params.country;

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

// ^australia$

ou

const countryName = req.params.country;

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

// ^turkey$

Um exemplo de código completo em Javascript, NodeJS com Mongoose ORM no 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);
        });
});
Aygunyilmaz
fonte
2

Você pode usar índices que não diferenciam maiúsculas de minúsculas :

O exemplo a seguir cria uma coleção sem agrupamento padrão e, em seguida, adiciona um índice no campo de nome com um agrupamento que não diferencia maiúsculas de minúsculas. Componentes Internacionais para 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 } } )

Para usar o índice, as consultas devem especificar o mesmo agrupamento.

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 } )

ou você pode criar uma coleção com agrupamento padrão:

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

A consulta a seguir encontrará os documentos com a string necessária de forma insensível e com ocorrência global também

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

Para encontrar strings de literais que não diferenciam maiúsculas de minúsculas:

Usando regex (recomendado)

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

Usando índice em minúsculas (mais rápido)

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

As expressões regulares são mais lentas do que a correspondência de string literal. No entanto, um campo adicional em minúsculas aumentará a complexidade do código. Em caso de dúvida, use expressões regulares. Eu sugeriria usar apenas um campo explicitamente em minúsculas se ele puder substituir o seu campo, ou seja, você não se importa com o caso em primeiro lugar.

Observe que você precisará escapar do nome antes do regex. Se você quiser caracteres curinga de entrada do usuário, prefira acrescentar .replace(/%/g, '.*')depois de escapar para que possa corresponder a "a%" para encontrar todos os nomes que começam com 'a'.

Yeti
fonte
-3

Uma maneira fácil seria usar $ toLower conforme abaixo.

db.users.aggregate([
    {
        $project: {
            name: { $toLower: "$name" }
        }
    },
    {
        $match: {
            name: the_name_to_search
        }
    }
])
user2661738
fonte