Comment faire des requêtes insensibles à la casse sur Mongodb?


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

Comment faire une requête insensible à la casse? Je veux trouver le résultat même si "andrew";



Une note à tous ceux qui essaieront d'utiliser une réponse impliquant des expressions régulières: les expressions régulières doivent être nettoyées.
sean

Réponses:


126

La solution de Chris Fulstow fonctionnera (+1), cependant, elle peut ne pas être efficace, surtout si votre collection est très grande. Les expressions régulières non enracinées (celles qui ne commencent pas par ^, qui ancrent l'expression régulière au début de la chaîne) et celles qui utilisent l' iindicateur d'insensibilité à la casse n'utiliseront pas d'index, même s'ils existent.

Une autre option que vous pourriez envisager est de dénormaliser vos données pour stocker une version minuscule du namechamp, par exemple sous la forme name_lower. Vous pouvez ensuite interroger cela efficacement (surtout s'il est indexé) pour les correspondances exactes insensibles à la casse comme:

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

Ou avec une correspondance de préfixe (une expression régulière enracinée) comme:

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

Ces deux requêtes utiliseront un index sur name_lower.


1
Excellente réponse, mon approche regex ralentit vraiment une fois qu'elle doit numériser quelques millions de documents.
Chris Fulstow

34
Ce n'est en fait pas tout à fait correct, car vous pourriez trouver "Andrew quelque chose" en recherchant "Andrew". Ajustez donc l'expression régulière sur: new RegExp('^'+ username + '$', "i")pour être une correspondance exacte.
Tarion

9
Selon le site Web de MongoDB, toute expression régulière insensible à la casse n'est pas efficace pour l'index "$ regex ne peut utiliser un index efficacement que lorsque l'expression régulière a une ancre pour le début (c'est-à-dire ^) d'une chaîne et est une correspondance sensible à la casse "
Ryan Schumacher

2
Avec Mongoose, cela a fonctionné pour moi: User.find ({'username': {$ regex: new RegExp ('^' + username.toLowerCase (), 'i')}}, function (err, res) {if (err ) throw err; next (null, res);});
ChrisRich

5
N'oubliez jamais d'échapper le nom lorsque vous travaillez avec des expressions régulières. Nous ne voulons pas que les injections prennent le dessus sur la beauté de mongodb. Imaginez simplement que vous avez utilisé ce code pour une page de connexion et que le nom d'utilisateur était ".*".
Tobias

90

Vous auriez besoin d'utiliser une expression régulière insensible à la casse pour celui-ci, par exemple

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

Pour utiliser le modèle d'expression régulière de votre thenamevariable, construisez un nouvel objet RegExp :

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

Mise à jour: pour une correspondance exacte, vous devez utiliser l'expression régulière "name": /^Andrew$/i. Merci à Yannick L.


7
Savez-vous comment faire cela en utilisant la mangouste Node.js?
user847495

1
Je me demande comment cela fonctionnera avec de grandes collections. Vous perdriez l'avantage d'une sorte de functinon
Wilfred Springer

5
C'est faux, il correspondra à n'importe quel document contenant "andrew" pour name, pas seulement égal.
Jonathan Cremin

14
@JonathanCremin pour aider les gens, vous devriez poster la bonne réponse:{ "name": /^Andrew$/i }
Yannick Loriot

@YannickL. 1+ pour faire le bon sens. Je passais juste pas ce que je cherchais.
Lpc_dark

38

Je l'ai résolu comme ça.

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

Si vous souhaitez interroger sur la «correspondance exacte insensible à la casse», vous pouvez procéder comme suit.

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

7

MongoDB 3.4 inclut désormais la possibilité de créer un véritable index insensible à la casse, ce qui augmentera considérablement la vitesse des recherches insensibles à la casse sur de grands ensembles de données. Il est fait en spécifiant un classement avec une force de 2.

Le moyen le plus simple de le faire est probablement de définir un classement dans la base de données. Ensuite, toutes les requêtes héritent de ce classement et l'utiliseront:

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

Vous pouvez également le faire comme ceci:

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

Et utilisez-le comme ceci:

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

Cela renverra les villes nommées «New York», «New York», «New York», etc.

Pour plus d'informations: https://jira.mongodb.org/browse/SERVER-90


force: 1 suffit pour une indexation insensible à la casse, insensible aux signes diacritiques. docs.mongodb.com/manual/reference/collation
Gaurav Ragtah

7
  1. Avec Mongoose (et Node), cela a fonctionné:

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

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

  2. Dans MongoDB, cela a fonctionné:

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

Les deux lignes sont insensibles à la casse. L'e-mail dans la base de données pourrait être NaMe@CompanY.Comet les deux lignes trouveront toujours l'objet dans la base de données.

De même, nous pourrions utiliser /^NaMe@CompanY.Com$/iet il trouverait toujours des e-mails: name@company.comdans la base de données.



4

Je viens de résoudre ce problème il y a quelques heures.

var thename = 'Andrew'
db.collection.find({ $text: { $search: thename } });
  • La sensibilité à la casse et la sensibilité diacritique sont définies sur false par défaut lors des requêtes de cette manière.

Vous pouvez même développer cela en sélectionnant les champs dont vous avez besoin à partir de l'objet utilisateur d'Andrew en procédant de cette manière:

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

Référence: https://docs.mongodb.org/manual/reference/operator/query/text/#text


1
$ text effectue une recherche textuelle sur le contenu des champs indexés avec un index textuel.
SSH ce

4

... avec mangouste sur NodeJS qui interroge:

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$

Un exemple de code complet en Javascript, NodeJS avec Mongoose ORM sur 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 requête suivante trouvera les documents avec la chaîne requise de manière insensible et avec une occurrence globale également

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

1

Pour rechercher une chaîne littérale insensible à la casse:

Utilisation de regex (recommandé)

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

Utilisation de l'index minuscule (plus rapide)

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

Les expressions régulières sont plus lentes que la correspondance de chaîne littérale. Cependant, un champ minuscule supplémentaire augmentera la complexité de votre code. En cas de doute, utilisez des expressions régulières. Je suggérerais de n'utiliser un champ explicitement minuscule que s'il peut remplacer votre champ, c'est-à-dire que vous ne vous souciez pas du cas en premier lieu.

Notez que vous devrez échapper le nom avant regex. Si vous voulez des caractères génériques saisis par l'utilisateur, préférez les ajouter .replace(/%/g, '.*')après l'échappement afin de pouvoir faire correspondre "a%" pour trouver tous les noms commençant par "a".


1

Vous pouvez utiliser des index insensibles à la casse :

L'exemple suivant crée une collection sans classement par défaut, puis ajoute un index sur le champ de nom avec un classement insensible à la casse. Composants internationaux pour 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 } } )

Pour utiliser l'index, les requêtes doivent spécifier le même classement.

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 vous pouvez créer une collection avec le classement par défaut:

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

-3

Un moyen simple serait d'utiliser $ toLower comme ci-dessous.

db.users.aggregate([
    {
        $project: {
            name: { $toLower: "$name" }
        }
    },
    {
        $match: {
            name: the_name_to_search
        }
    }
])
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.