¿Cómo hago consultas que no distinguen entre mayúsculas y minúsculas en Mongodb?

Respuestas:

126

La solución de Chris Fulstow funcionará (+1), sin embargo, puede que no sea eficiente, especialmente si su colección es muy grande. Las expresiones regulares no arraigadas (las que no comienzan con ^, que anclan la expresión regular al comienzo de la cadena) y las que usan la ibandera para no distinguir entre mayúsculas y minúsculas no usarán índices, incluso si existen.

Una opción alternativa que podría considerar es desnormalizar sus datos para almacenar una versión en minúscula del namecampo, por ejemplo como name_lower. Luego puede consultar eso de manera eficiente (especialmente si está indexado) para coincidencias exactas que no distinguen entre mayúsculas y minúsculas, como:

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

O con una coincidencia de prefijo (una expresión regular arraigada) como:

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

Ambas consultas utilizarán un índice en name_lower.

dcrosta
fuente
1
Excelente respuesta, mi enfoque de expresiones regulares realmente se ralentiza una vez que tiene que escanear algunos millones de documentos.
Chris Fulstow
34
En realidad, esto no es del todo correcto, porque puede encontrar "Andrew algo" mientras busca "Andrew". Así que ajusta la expresión regular a: new RegExp('^'+ username + '$', "i")para que sea una coincidencia exacta.
Tarion
9
Según el sitio web de MongoDB, cualquier expresión regular que no distinga entre mayúsculas y minúsculas no es eficiente en el índice "$ regex solo puede usar un índice de manera eficiente cuando la expresión regular tiene un ancla para el principio (es decir, ^) de una cadena y es una coincidencia que distingue entre mayúsculas y minúsculas "
Ryan Schumacher
2
Con Mongoose esto funcionó para mí: User.find ({'username': {$ regex: new RegExp ('^' + username.toLowerCase (), 'i')}}, function (err, res) {if (err ) arrojar err; siguiente (nulo, res);});
ChrisRich
5
Nunca olvides escapar del nombre cuando trabajes con expresiones regulares. No queremos que las inyecciones se apoderen de la belleza de mongodb. Imagínese que utilizó este código para una página de inicio de sesión y el nombre de usuario fue ".*".
Tobias
90

Necesitaría usar una expresión regular que no distinga entre mayúsculas y minúsculas para esta, por ejemplo

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

Para usar el patrón de expresiones regulares de su thenamevariable, construya un nuevo objeto RegExp :

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

Actualización: para una coincidencia exacta, debe usar la expresión regular "name": /^Andrew$/i. Gracias a Yannick L.

Chris Fulstow
fuente
7
¿Sabes cómo hacer esto usando Node.js mongoose?
user847495
1
Me pregunto qué tan bien funcionará esto con colecciones grandes. Perderías el beneficio de una función de tipo
Wilfred Springer
5
Esto es incorrecto, coincidirá con cualquier documento que contenga "andrew" para name, no solo igualar.
Jonathan Cremin
14
@JonathanCremin para ayudar a las personas, debe publicar la respuesta correcta:{ "name": /^Andrew$/i }
Yannick Loriot
@YannickL. 1+ por hacer cosas de sentido común. Estaba pasando por no lo que estaba buscando.
Lpc_dark
38

Lo he resuelto así.

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

Si desea realizar una consulta sobre 'concordancia exacta que no distingue entre mayúsculas y minúsculas', puede hacerlo así.

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

MongoDB 3.4 ahora incluye la capacidad de hacer un verdadero índice que no distingue entre mayúsculas y minúsculas, lo que aumentará drásticamente la velocidad de las búsquedas que no distinguen entre mayúsculas y minúsculas en grandes conjuntos de datos. Se realiza especificando una colación con una fuerza de 2.

Probablemente la forma más sencilla de hacerlo es establecer una intercalación en la base de datos. Entonces todas las consultas heredan esa intercalación y la usarán:

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

También puedes hacerlo así:

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

Y utilícelo así:

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

Esto devolverá las ciudades llamadas "nueva york", "Nueva York", "Nueva york", etc.

Para más información: https://jira.mongodb.org/browse/SERVER-90

usuario3413723
fuente
fuerza: 1 es suficiente para la indexación que no distingue entre mayúsculas y minúsculas ni diacríticos. docs.mongodb.com/manual/reference/collation
Gaurav Ragtah
7
  1. Con Mongoose (y Node), esto funcionó:

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

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

  2. En MongoDB, esto funcionó:

Ambas líneas no distinguen entre mayúsculas y minúsculas. El correo electrónico en la base de datos podría ser [email protected]y ambas líneas todavía encontrarán el objeto en la base de datos.

Del mismo modo, podríamos usar /^[email protected]$/iy aún encontraría correo electrónico: [email protected]en la base de datos.

Raymond Gan
fuente
5

Para encontrar una cadena insensible a mayúsculas, use esto

var thename = "Andrew";
db.collection.find({"name":/^thename$/i})
Pranit
fuente
1
¿Por qué está agregando una respuesta duplicada si ya está allí en stackoverflow.com/a/7101868/4273915
Shrabanee
4

Acabo de resolver este problema hace unas horas.

var thename = 'Andrew'
db.collection.find({ $text: { $search: thename } });
  • La sensibilidad a mayúsculas y minúsculas y la sensibilidad a los diacríticos se establecen en falso de forma predeterminada cuando se realizan consultas de esta manera.

Incluso puede ampliar esto seleccionando los campos que necesita del objeto de usuario de Andrew haciéndolo de esta manera:

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

Referencia: https://docs.mongodb.org/manual/reference/operator/query/text/#text

Briant Anthony
fuente
1
$ text realiza una búsqueda de texto en el contenido de los campos indexados con un índice de texto.
SSH Este
4

... con mangosta en NodeJS esa consulta:

const countryName = req.params.country;

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

o

const countryName = req.params.country;

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

// ^australia$

o

const countryName = req.params.country;

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

// ^turkey$

Un ejemplo de código completo en Javascript, NodeJS con Mongoose ORM en 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
fuente
1

La siguiente consulta encontrará los documentos con la cadena requerida de forma insensible y con ocurrencia global también

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

Para buscar cadenas de literales que no distinguen entre mayúsculas y minúsculas:

Usando expresiones regulares (recomendado)

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

Usar índice en minúsculas (más rápido)

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

Las expresiones regulares son más lentas que la coincidencia de cadenas literales. Sin embargo, un campo en minúsculas adicional aumentará la complejidad de su código. En caso de duda, utilice expresiones regulares. Sugeriría usar solo un campo explícitamente en minúsculas si puede reemplazar su campo, es decir, no le importa el caso en primer lugar.

Tenga en cuenta que deberá escapar del nombre antes de la expresión regular. Si desea caracteres comodín de entrada de usuario, prefiera agregar .replace(/%/g, '.*')después de escapar para que pueda hacer coincidir "a%" para encontrar todos los nombres que comienzan con 'a'.

Yeti
fuente
1

Puede utilizar índices que no distinguen entre mayúsculas y minúsculas :

El siguiente ejemplo crea una colección sin intercalación predeterminada y luego agrega un índice en el campo de nombre con una intercalación que no distingue entre mayúsculas y minúsculas. Componentes internacionales 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 utilizar el índice, las consultas deben especificar la misma intercalación.

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

o puede crear una colección con intercalación predeterminada:

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

Una forma fácil sería usar $ toLower como se muestra a continuación.

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