Wie mache ich bei Mongodb Abfragen ohne Berücksichtigung der Groß- und Kleinschreibung?

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

Wie frage ich zwischen Groß- und Kleinschreibung ab? Ich möchte ein Ergebnis finden, auch wenn "andrew";

user847495
quelle
Ein Hinweis an alle, die versuchen werden, eine Antwort mit regulären Ausdrücken zu verwenden: Regexe müssen bereinigt werden.
Sean

Antworten:

122

Die Lösung von Chris Fulstow funktioniert (+1), ist jedoch möglicherweise nicht effizient, insbesondere wenn Ihre Sammlung sehr groß ist. Nicht verwurzelte reguläre Ausdrücke (diejenigen, die nicht mit beginnen ^und den regulären Ausdruck am Anfang der Zeichenfolge verankern) und diejenigen, die das iFlag für Groß- und Kleinschreibung verwenden, verwenden keine Indizes, selbst wenn sie vorhanden sind.

Eine alternative Option, die Sie in Betracht ziehen könnten, besteht darin, Ihre Daten zu denormalisieren, um eine Kleinbuchstabenversion des nameFelds zu speichern , z name_lower. Sie können dies dann effizient abfragen (insbesondere wenn es indiziert ist), um genaue Übereinstimmungen ohne Berücksichtigung der Groß- und Kleinschreibung zu berücksichtigen, z.

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

Oder mit einer Präfixübereinstimmung (einem verwurzelten regulären Ausdruck) als:

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

Beide Abfragen verwenden einen Index für name_lower.

dcrosta
quelle
1
Tolle Antwort, mein Regex-Ansatz verlangsamt sich wirklich, wenn ein paar Millionen Dokumente gescannt werden müssen.
Chris Fulstow
33
Dies ist eigentlich nicht ganz richtig, da Sie möglicherweise "Andrew etwas" finden, während Sie nach "Andrew" suchen. Passen Sie den regulären Ausdruck also an: new RegExp('^'+ username + '$', "i")um eine genaue Übereinstimmung zu erhalten.
Tarion
9
Laut der MongoDB-Website ist ein regulärer Ausdruck ohne Berücksichtigung des Index nicht indexwirksam. "$ Regex kann einen Index nur dann effizient verwenden, wenn der reguläre Ausdruck einen Anker für den Anfang (dh ^) eines Strings hat und zwischen Groß- und Kleinschreibung unterscheidet "
Ryan Schumacher
2
Bei Mongoose funktionierte dies für mich: User.find ({'Benutzername': {$ regex: new RegExp ('^' + Benutzername.toLowerCase (), 'i')}}, Funktion (err, res) {if (err ) wirf err; next (null, res);});
ChrisRich
5
Vergessen Sie niemals, dem Namen zu entkommen, wenn Sie mit regulären Ausdrücken arbeiten. Wir wollen nicht, dass Injektionen die Schönheit von Mongodb übernehmen. Stellen Sie sich vor, Sie haben diesen Code für eine Anmeldeseite verwendet und der Benutzername war ".*".
Tobias
86

Sie müssten für diesen einen regulären Ausdruck verwenden, bei dem die Groß- und Kleinschreibung nicht berücksichtigt wird , z

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

Erstellen Sie thenameein neues RegExp- Objekt, um das Regex-Muster aus Ihrer Variablen zu verwenden :

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

Update: Für eine genaue Übereinstimmung sollten Sie den regulären Ausdruck verwenden "name": /^Andrew$/i. Vielen Dank an Yannick L.

Chris Fulstow
quelle
7
Wissen Sie, wie man das mit Node.js Mungo macht?
user847495
1
Ich frage mich, wie gut das mit großen Sammlungen funktionieren wird. Sie würden den Vorteil einer Art Funktion verlieren
Wilfred Springer
4
Dies ist falsch, es stimmt mit jedem Dokument überein , das "andrew" enthält name, nicht nur gleich.
Jonathan Cremin
13
@ JonathanCremin, um Menschen zu helfen, sollten Sie die richtige Antwort posten:{ "name": /^Andrew$/i }
Yannick Loriot
@YannickL. 1+ für den gesunden Menschenverstand. Ich ging gerade vorbei, nicht das, wonach ich suchte.
Lpc_dark
37

Ich habe es so gelöst.

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

Wenn Sie nach "exakter Übereinstimmung ohne Berücksichtigung der Groß- und Kleinschreibung" fragen möchten, können Sie wie folgt vorgehen.

var thename =  '^Andrew$';
db.collection.find({'name': {'$regex': thename,$options:'i'}});
RIPAN
quelle
7
  1. Mit Mongoose (und Node) funktionierte dies:

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

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

  2. In MongoDB funktionierte dies:

Bei beiden Zeilen wird die Groß- und Kleinschreibung nicht berücksichtigt. Die E-Mail in der DB könnte sein[email protected] und beide Zeilen finden das Objekt weiterhin in der Datenbank.

Ebenso könnten wir verwenden /^[email protected]$/iund es würde immer noch E-Mail finden: [email protected]in der DB.

Raymond Gan
quelle
6

MongoDB 3.4 bietet jetzt die Möglichkeit, einen echten Index ohne Berücksichtigung der Groß- und Kleinschreibung zu erstellen, wodurch die Geschwindigkeit der Suche nach Groß- und Kleinschreibung bei großen Datenmengen drastisch erhöht wird. Es wird durch Angabe einer Kollatierung mit einer Stärke von 2 erstellt.

Der wahrscheinlich einfachste Weg, dies zu tun, besteht darin, eine Sortierung in der Datenbank festzulegen. Dann erben alle Abfragen diese Sortierung und verwenden sie:

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

Sie können es auch so machen:

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

Und benutze es so:

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

Dadurch werden Städte mit den Namen "New York", "New York", "New York" usw. zurückgegeben.

Für weitere Informationen: https://jira.mongodb.org/browse/SERVER-90

user3413723
quelle
5

Verwenden Sie diese Zeichenfolge, um eine Zeichenfolge ohne Berücksichtigung der Groß- und Kleinschreibung zu finden.

var thename = "Andrew";
db.collection.find({"name":/^thename$/i})
Pranit
quelle
1
Warum fügen Sie eine doppelte Antwort hinzu, da diese bereits in stackoverflow.com/a/7101868/4273915
Shrabanee
4

Ich habe dieses Problem erst vor ein paar Stunden gelöst.

var thename = 'Andrew'
db.collection.find({ $text: { $search: thename } });
  • Bei solchen Abfragen werden die Groß- und Kleinschreibung und die diakritische Empfindlichkeit standardmäßig auf false gesetzt.

Sie können dies sogar erweitern, indem Sie die Felder auswählen, die Sie aus Andrews Benutzerobjekt benötigen. Gehen Sie dazu folgendermaßen vor:

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

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

Briant Anthony
quelle
1
$ text führt eine Textsuche für den Inhalt der mit einem Textindex indizierten Felder durch.
SSH Diesen
3

... mit Mungo auf NodeJS diese Abfrage:

const countryName = req.params.country;

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

oder

const countryName = req.params.country;

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

// ^australia$

oder

const countryName = req.params.country;

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

// ^turkey$

Ein vollständiges Codebeispiel in Javascript, NodeJS mit Mongoose ORM in 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
quelle
1

Die folgende Abfrage findet die Dokumente mit der erforderlichen Zeichenfolge unempfindlich und auch mit globalem Vorkommen

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

So finden Sie eine Zeichenfolge, bei der die Groß- und Kleinschreibung nicht berücksichtigt wird:

Verwendung von Regex (empfohlen)

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

Kleinbuchstabenindex verwenden (schneller)

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

Reguläre Ausdrücke sind langsamer als der Literal-String-Abgleich. Ein zusätzliches Kleinbuchstabenfeld erhöht jedoch die Codekomplexität. Verwenden Sie im Zweifelsfall reguläre Ausdrücke. Ich würde vorschlagen, ein explizit klein geschriebenes Feld nur zu verwenden, wenn es Ihr Feld ersetzen kann, das heißt, Sie interessieren sich überhaupt nicht für den Fall.

Beachten Sie, dass Sie den Namen vor dem regulären Ausdruck maskieren müssen. Wenn Sie Platzhalter für Benutzereingaben wünschen, ziehen Sie es vor, .replace(/%/g, '.*')nach dem Escapezeichen anzuhängen , damit Sie mit "a%" übereinstimmen können, um alle Namen zu finden, die mit "a" beginnen.

Yeti
quelle
1

Sie können Indizes verwenden, bei denen die Groß- und Kleinschreibung nicht berücksichtigt wird :

Im folgenden Beispiel wird eine Sammlung ohne Standardkollatierung erstellt und anschließend ein Index für das Namensfeld mit einer Sortierung hinzugefügt, bei der die Groß- und Kleinschreibung nicht berücksichtigt wird. Internationale Komponenten für 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 } } )

Um den Index verwenden zu können, müssen Abfragen dieselbe Sortierung angeben.

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

oder Sie können eine Sammlung mit Standardkollatierung erstellen:

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

Ein einfacher Weg wäre, $ toLower wie unten zu verwenden.

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