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


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

¿Cómo consulto mayúsculas y minúsculas? Quiero encontrar el resultado incluso si "andrew";



Una nota para todos los que intentarán usar una respuesta que involucre expresiones regulares: las expresiones regulares deben desinfectarse.
sean

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.


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.


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

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


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: /^name@company.com$/i })

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

  2. En MongoDB, esto funcionó:

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

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

Del mismo modo, podríamos usar /^NaMe@CompanY.Com$/iy aún encontraría correo electrónico: name@company.comen la base de datos.



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


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

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

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'.


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

-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
        }
    }
])
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.