web-dev-qa-db-fra.com

MongoDB: Est-il possible de faire une requête insensible à la casse?

Exemple:

> db.stuff.save({"foo":"bar"});

> db.stuff.find({"foo":"bar"}).count();
1
> db.stuff.find({"foo":"BAR"}).count();
0
252
Luke Dennis

Vous pouvez utiliser un regex .

Dans votre exemple, ce serait:

db.stuff.find( { foo: /^bar$/i } );

Je dois dire, cependant, que vous pourriez peut-être simplement minimiser (ou augmenter) la valeur ajoutée au lieu d'entrer sans encourir le coût supplémentaire chaque fois que vous la trouverez. Évidemment, cela ne fonctionnera pas pour les noms des personnes et autres, mais peut-être des cas d'utilisation comme des balises.

285
rfunduk

METTRE À JOUR:

La réponse originale est maintenant obsolète. Mongodb supporte maintenant la recherche avancée en texte intégral, avec de nombreuses fonctionnalités.

RÉPONSE ORIGINALE:

Il convient de noter que la recherche avec/i insensible à la casse de regex signifie que mongodb ne peut pas effectuer de recherche par index. Par conséquent, les requêtes sur des ensembles de données volumineux peuvent prendre beaucoup de temps. 

Même avec de petits ensembles de données, ce n'est pas très efficace. Vous prenez un taux de réussite du processeur beaucoup plus important que ne le justifie votre requête, ce qui pourrait devenir un problème si vous essayez d'atteindre un objectif d'échelle.

Au lieu de cela, vous pouvez stocker une copie en majuscule et effectuer une recherche dans ce sens. Par exemple, j'ai une table utilisateur qui a un nom d'utilisateur qui est une casse mixte, mais l'id est une copie majuscule du nom d'utilisateur. Cela garantit que la duplication sensible à la casse est impossible (avoir "Foo" et "foo" ne seront pas autorisés), et je peux effectuer une recherche par id = username.toUpperCase () pour obtenir une recherche de nom d'utilisateur insensible à la casse.

Si votre champ est grand, tel qu'un corps de message, la duplication de données n'est probablement pas une bonne option. Je pense que l'utilisation d'un indexeur étranger comme Apache Lucene est la meilleure option dans ce cas.

190
Dan

Gardez à l'esprit que l'exemple précédent:

db.stuff.find( { foo: /bar/i } );

toutes les entrées contenant bar correspondront à la requête (bar1, barxyz, openbar), cela pourrait être très dangereux pour une recherche de nom d'utilisateur sur une fonction d'auth ...

Vous devrez peut-être faire correspondre uniquement le terme recherché en utilisant la syntaxe d'expressions rationnelle appropriée, comme suit:

db.stuff.find( { foo: /^bar$/i } );

Voir http://www.regular-expressions.info/ pour obtenir de l'aide sur la syntaxe des expressions régulières

58
jflaflamme

Si vous avez besoin de créer l’expression rationnelle à partir d’une variable, c’est une bien meilleure façon de le faire: https://stackoverflow.com/a/10728069/309514

Vous pouvez ensuite faire quelque chose comme:

var string = "SomeStringToFind";
var regex = new RegExp(["^", string, "$"].join(""), "i");
// Creates a regex of: /^SomeStringToFind$/i
db.stuff.find( { foo: regex } );

Cela a l'avantage d'être plus programmatique ou vous pouvez obtenir une amélioration des performances en le compilant à l'avance si vous le réutilisez beaucoup.

56
Fotios

A partir de Mongodb 3.4, vous devez utiliser un index de classement non sensible à la casse. C'est le moyen le plus rapide de faire une recherche insensible à la casse sur des jeux de données de plus en plus volumineux. J'ai personnellement envoyé un courrier électronique à l'un des fondateurs pour que cela fonctionne, et il y est parvenu! (C'était un problème sur JIRA pendant environ 5 ans, et beaucoup ont demandé la fonctionnalité). Voici comment ça fonctionne:

Un index insensible à la casse est créé en spécifiant un classement avec une force de 1 ou 2. Vous pouvez créer un index insensible à la casse de la manière suivante:

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

Ou vous pouvez le faire pour la collection entière par défaut lorsque vous créez la base de données comme suit:

db.createCollection("Cities",{collation: {locale: "en",strength:2}});

Et utilisez-le comme ceci:

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

Cela retournera "New York", "New York", etc.

Vous pouvez également faire en sorte que tous les index utilisent un classement par défaut lorsque vous créez la collection de la manière suivante:

db.createCollection("cities",{collation:{locale: "en", strength: 2}});

L'avantage de cette méthode est une efficacité et une vitesse nettement améliorées sur des jeux de données plus volumineux.

Pour plus d'informations: https://jira.mongodb.org/browse/SERVER-90 , https://docs.mongodb.com/manual/reference/collation/

26
user3413723
db.zipcodes.find({city : "NEW YORK"}); // Case-sensitive
db.zipcodes.find({city : /NEW york/i}); // Note the 'i' flag for case-insensitivity
16
rshivamca

TL; DR

Manière correcte de faire ceci en Mongo

Ne pas utiliser RegExp

Allez naturel et utilisez l'indexation intégrée de mongodb, recherchez

Étape 1 :

db.articles.insert(
   [
     { _id: 1, subject: "coffee", author: "xyz", views: 50 },
     { _id: 2, subject: "Coffee Shopping", author: "efg", views: 5 },
     { _id: 3, subject: "Baking a cake", author: "abc", views: 90  },
     { _id: 4, subject: "baking", author: "xyz", views: 100 },
     { _id: 5, subject: "Café Con Leche", author: "abc", views: 200 },
     { _id: 6, subject: "Сырники", author: "jkl", views: 80 },
     { _id: 7, subject: "coffee and cream", author: "efg", views: 10 },
     { _id: 8, subject: "Cafe con Leche", author: "xyz", views: 10 }
   ]
)

Étape 2 :

Nécessité de créer un index sur le champ TEXT que vous souhaitez rechercher, sans requête d'indexation extrêmement lente 

db.articles.createIndex( { subject: "text" } )

étape 3 :

db.articles.find( { $text: { $search: "coffee",$caseSensitive :true } } )  //FOR SENSITIVITY
db.articles.find( { $text: { $search: "coffee",$caseSensitive :false } } ) //FOR INSENSITIVITY
13
vijay

Mongo (version actuelle 2.0.0) n'autorise pas la recherche insensible à la casse des champs indexés - voir leur documentation . Pour les champs non indexés, les expressions rationnelles répertoriées dans les autres réponses devraient convenir.

9
Aidan Feldman

Utiliser Mongoose a fonctionné pour moi:

var find = function(username, next){
    User.find({'username': {$regex: new RegExp('^' + username, 'i')}}, function(err, res){
        if(err) throw err;
        next(null, res);
    });
}
6
ChrisRich

Il est important de garder à l’esprit lorsque vous utilisez une requête basée sur Regex - lorsque vous le faites pour un système de connexion, échappez à chaque caractère que vous recherchez, et n’oubliez pas les opérateurs ^ et $. Lodash a une fonction Nice pour cela , si vous l'utilisez déjà:

db.stuff.find({$regex: new RegExp(_.escapeRegExp(bar), $options: 'i'})

Pourquoi? Imaginez un utilisateur qui saisit .* comme nom d'utilisateur. Cela correspondrait à tous les noms d'utilisateurs, permettant une connexion en devinant simplement le mot de passe de l'utilisateur.

5
Nick Kamer

La meilleure méthode consiste à choisir la langue de votre choix. Lors de la création d'un wrapper de modèle pour vos objets, demandez à votre méthode save () de parcourir un ensemble de champs sur lesquels vous effectuerez une recherche et qui sont également indexés. ces ensembles de champs doivent avoir des équivalents minuscules qui sont ensuite utilisés pour la recherche.

Chaque fois que l'objet est à nouveau enregistré, les propriétés en minuscule sont ensuite vérifiées et mises à jour avec les modifications apportées aux propriétés principales. Ainsi, vous pourrez effectuer une recherche efficace tout en masquant le travail supplémentaire nécessaire pour mettre à jour les champs LC à chaque fois.

Les champs minuscules peuvent être une clé: magasin d'objets de valeur ou juste le nom du champ avec un préfixe lc_. J'utilise le second pour simplifier l'interrogation (l'interrogation d'objet en profondeur peut être parfois déroutante). 

Remarque: vous souhaitez indexer les champs lc_ et non les champs principaux sur lesquels ils sont basés. 

5
RobKohr

Supposons que vous souhaitiez rechercher "colonne" dans "Table" et que vous souhaitiez une recherche sans observation de la casse. Le meilleur et efficace moyen est comme ci-dessous;

//create empty JSON Object
mycolumn = {};

//check if column has valid value
if(column) {
    mycolumn.column = {$regex: new RegExp(column), $options: "i"};
}
Table.find(mycolumn);

Le code ci-dessus ajoute simplement votre valeur de recherche en tant que RegEx et effectue une recherche avec des critères insensibles définis avec "i" comme option.

Bonne chance.

5
Ankur Soni
db.company_profile.find({ "companyName" : { "$regex" : "Nilesh" , "$options" : "i"}});
4
Nilesh

Le cadre d'agrégation a été introduit dans mongodb 2.2. Vous pouvez utiliser l'opérateur de chaîne "$ strcasecmp" pour effectuer une comparaison insensible à la casse entre les chaînes. C'est plus recommandé et plus facile que d'utiliser regex.

Voici le document officiel sur l'opérateur de commande d'agrégation: https://docs.mongodb.com/manual/reference/operator/aggregation/strcasecmp/#exp._S_strcasecmp .

3
Jogue Wasin

Vous pouvez utiliser 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 ne respectant pas 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 un classement par défaut:

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

Pour rechercher une variable et y échapper:

const escapeStringRegexp = require('escape-string-regexp')
const name = 'foo'
db.stuff.find({name: new RegExp('^' + escapeStringRegexp(name) + '$', 'i')})   

Échapper à la variable protège la requête contre les attaques avec '. *' Ou autre regex. 

escape-string-regexp

1
davidivad

L'utilisation d'un filtre fonctionne pour moi en C #.

string s = "searchTerm";
    var filter = Builders<Model>.Filter.Where(p => p.Title.ToLower().Contains(s.ToLower()));
                var listSorted = collection.Find(filter).ToList();
                var list = collection.Find(filter).ToList();

Il peut même utiliser l'index car je crois que les méthodes sont appelées une fois le retour effectué, mais je ne l'ai pas encore testé.

Cela évite également un problème de 

var filter = Builders<Model>.Filter.Eq(p => p.Title.ToLower(), s.ToLower());

que mongodb pensera que p.Title.ToLower () est une propriété et ne mappera pas correctement.

1
A_Arnold

Utilisez RegExp, Si d'autres options ne vous conviennent pas, RegExp est une bonne option. Cela rend la chaîne insensible à la casse.

var username = new RegExp("^" + "John" + "$", "i");;

utilisez le nom d'utilisateur dans les requêtes, puis c'est fait.

J'espère que cela fonctionnera pour vous aussi. Bonne chance.

0
Gouri Shankar

Comme vous pouvez le constater dans mongo docs - depuis la version 3.2, $text index est insensible à la casse par défaut: https://docs.mongodb.com/manual/core/index-text/#text-index-case-insensitivity

Créez un index de texte et utilisez l’opérateur $ text dans votre requête .

0
avalanche1

Ceux-ci ont été testés pour les recherches de chaînes

{'_id': /.*CM.*/}               ||find _id where _id contains   ->CM
{'_id': /^CM/}                  ||find _id where _id starts     ->CM
{'_id': /CM$/}                  ||find _id where _id ends       ->CM

{'_id': /.*UcM075237.*/i}       ||find _id where _id contains   ->UcM075237, ignore upper/lower case
{'_id': /^UcM075237/i}          ||find _id where _id starts     ->UcM075237, ignore upper/lower case
{'_id': /UcM075237$/i}          ||find _id where _id ends       ->UcM075237, ignore upper/lower case
0
Ar maj

J'avais été confronté à un problème similaire et voici ce qui a fonctionné pour moi:

  const flavorExists = await Flavors.findOne({
    'flavor.name': { $regex: flavorName, $options: 'i' },
  });
0
Woppi

Pour tous ceux qui utilisent Golang et souhaitent avoir une recherche plein texte sensible à la casse avec mongodb et la bibliothèque mgo godoc globalsign .

collation := &mgo.Collation{
    Locale:   "en",
    Strength: 2, 
}


err := collection.Find(query).Collation(collation)
0
okandas

J'ai créé un simple Func pour la regex insensible à la casse, que j'utilise dans mon filtre.

private Func<string, BsonRegularExpression> CaseInsensitiveCompare = (field) => 
            BsonRegularExpression.Create(new Regex(field, RegexOptions.IgnoreCase));

Ensuite, vous filtrez simplement sur un champ comme suit.

db.stuff.find({"foo": CaseInsensitiveCompare("bar")}).count();
0
Nitesh