Jak tworzyć zapytania bez rozróżniania wielkości liter w Mongodb?

Odpowiedzi:

126

Rozwiązanie Chrisa Fulstowa zadziała (+1), jednak może nie być wydajne, zwłaszcza jeśli twoja kolekcja jest bardzo duża. Niezakorzenione wyrażenia regularne (te, które nie rozpoczynają się od ^, które zakotwiczają wyrażenie regularne na początku ciągu) oraz te, które używają iflagi dla niewrażliwości na wielkość liter, nie będą używać indeksów, nawet jeśli istnieją.

Alternatywną opcją, którą możesz rozważyć, jest denormalizacja danych w celu zapisania wersji namepola z małymi literami , na przykład jako name_lower. Następnie możesz efektywnie zapytać (zwłaszcza jeśli jest indeksowany) o dokładne dopasowania bez rozróżniania wielkości liter, takie jak:

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

Lub z dopasowaniem prefiksu (zakorzenione wyrażenie regularne) jako:

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

Oba te zapytania będą używać indeksu name_lower.

dcrosta
źródło
1
Świetna odpowiedź, moje podejście do wyrażeń regularnych naprawdę zwalnia, gdy musi przeskanować kilka milionów dokumentów.
Chris Fulstow,
34
W rzeczywistości nie jest to do końca poprawne, ponieważ możesz znaleźć „Andrew coś”, szukając słowa „Andrew”. Więc dostosuj wyrażenie regularne do: new RegExp('^'+ username + '$', "i")aby być dokładnym dopasowaniem.
Tarion,
9
Według witryny MongoDB żadne wyrażenie regularne bez rozróżniania wielkości liter nie jest indeksowane "$ regex może efektywnie używać indeksu tylko wtedy, gdy wyrażenie regularne ma kotwicę na początku (tj. ^) Ciągu i jest dopasowywane z rozróżnianiem wielkości liter "
Ryan Schumacher
2
Z Mongoose to zadziałało dla mnie: User.find ({'username': {$ regex: new RegExp ('^' + username.toLowerCase (), 'i')}}, function (err, res) {if (err ) rzucać błąd; next (null, res);});
ChrisRich,
5
Podczas pracy z wyrażeniami regularnymi nigdy nie zapomnij o ucieczce przed nazwą. Nie chcemy, aby zastrzyki przejęły piękno mongodb. Wyobraź sobie, że użyłeś tego kodu na stronie logowania, a nazwa użytkownika to ".*".
Tobias
90

W tym przypadku musiałbyś użyć wyrażenia regularnego bez rozróżniania wielkości liter , np

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

Aby użyć wzorca wyrażenia regularnego ze thenamezmiennej, utwórz nowy obiekt RegExp :

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

Aktualizacja: aby uzyskać dokładne dopasowanie, użyj wyrażenia regularnego "name": /^Andrew$/i. Dzięki Yannick L.

Chris Fulstow
źródło
7
Czy wiesz, jak to zrobić za pomocą mangusty Node.js?
user847495
1
Ciekawe, jak dobrze to zadziała przy dużych kolekcjach. Stracisz korzyść z pewnego rodzaju funkcji
Wilfred Springer
5
To jest złe, będzie pasować do każdego dokumentu zawierającego słowo „andrew” dla name, a nie tylko z równaniem.
Jonathan Cremin
14
@JonathanCremin, aby pomóc ludziom, powinieneś zamieścić poprawną odpowiedź:{ "name": /^Andrew$/i }
Yannick Loriot
@YannickL. 1+ za robienie rzeczy zdrowego rozsądku. Po prostu mijałem, a nie to, czego szukałem.
Lpc_dark,
38

Rozwiązałem to w ten sposób.

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

Jeśli chcesz zapytać o „dopasowanie ścisłe bez rozróżniania wielkości liter”, możesz to zrobić w ten sposób.

var thename =  '^Andrew$';
db.collection.find({'name': {'$regex': thename,$options:'i'}});
RIPAN
źródło
7

MongoDB 3.4 zawiera teraz możliwość tworzenia prawdziwego indeksu bez rozróżniania wielkości liter, co znacznie zwiększy szybkość wyszukiwania bez rozróżniania wielkości liter w dużych zestawach danych. Dokonuje się tego przez określenie zestawienia o sile 2.

Prawdopodobnie najłatwiejszym sposobem jest ustawienie sortowania w bazie danych. Następnie wszystkie zapytania dziedziczą to sortowanie i będą z niego korzystać:

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

Możesz to również zrobić w ten sposób:

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

I użyj tego w ten sposób:

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

Spowoduje to zwrócenie miast o nazwach „Nowy Jork”, „Nowy Jork”, „Nowy Jork” itp.

Więcej informacji: https://jira.mongodb.org/browse/SERVER-90

user3413723
źródło
siła: 1 jest wystarczająca do indeksowania bez rozróżniania wielkości liter i znaków diakrytycznych. docs.mongodb.com/manual/reference/collation
Gaurav Ragtah
7
  1. W przypadku Mongoose (i Node) to zadziałało:

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

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

  2. W MongoDB to zadziałało:

W obu wierszach wielkość liter nie jest rozróżniana. E-mail w bazie danych mógłby być[email protected] a obie linie nadal będą znajdować obiekt w bazie danych.

Podobnie moglibyśmy użyć /^[email protected]$/ii nadal znajdowałby email: [email protected]w bazie danych.

Raymond Gan
źródło
5

Aby znaleźć ciąg niewrażliwy na wielkość liter, użyj tego,

var thename = "Andrew";
db.collection.find({"name":/^thename$/i})
Pranit
źródło
1
Dlaczego dodajesz zduplikowaną odpowiedź, ponieważ znajduje się ona już na stackoverflow.com/a/7101868/4273915
Shrabanee
4

Właśnie rozwiązałem ten problem kilka godzin temu.

var thename = 'Andrew'
db.collection.find({ $text: { $search: thename } });
  • Podczas wykonywania zapytań w ten sposób rozróżnianie wielkości liter i znaków diakrytycznych jest domyślnie ustawione na fałsz.

Możesz nawet rozszerzyć ten zakres, wybierając potrzebne pola z obiektu użytkownika Andrew, robiąc to w ten sposób:

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

Źródła: https://docs.mongodb.org/manual/reference/operator/query/text/#text

Briant Anthony
źródło
1
$ text wyszukuje tekst w zawartości pól indeksowanych indeksem tekstowym.
SSH
4

... z mongoose w NodeJS, które zapytanie:

const countryName = req.params.country;

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

lub

const countryName = req.params.country;

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

// ^australia$

lub

const countryName = req.params.country;

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

// ^turkey$

Przykład pełnego kodu w JavaScript, NodeJS z Mongoose ORM w 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
źródło
1

Następujące zapytanie znajdzie dokumenty z wymaganym ciągiem bez uwzględnienia, a także z wystąpieniem globalnym

db.collection.find({name:{
                             $regex: new RegExp(thename, "ig")
                         }
                    },function(err, doc) {
                                         //Your code here...
                  });
prodeveloper
źródło
1

Aby znaleźć ciąg literałów bez rozróżniania wielkości liter:

Korzystanie z wyrażenia regularnego (zalecane)

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

Korzystanie z małych liter (szybciej)

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

Wyrażenia regularne są wolniejsze niż dopasowywanie literałów. Jednak dodatkowe pole z małą literą zwiększy złożoność kodu. W razie wątpliwości użyj wyrażeń regularnych. Sugerowałbym użycie pola z wyraźnie małymi literami tylko wtedy, gdy może ono zastąpić twoje pole, to znaczy nie przejmujesz się wielkością liter.

Pamiętaj, że przed wprowadzeniem wyrażenia regularnego będziesz musiał uciec przed nazwą. Jeśli chcesz .replace(/%/g, '.*')używać symboli wieloznacznych wprowadzanych przez użytkownika, preferuj dołączanie po znaku ucieczki, aby można było dopasować „a%”, aby znaleźć wszystkie nazwy zaczynające się od „a”.

Yeti
źródło
1

Możesz użyć indeksów bez rozróżniania wielkości liter :

Poniższy przykład tworzy kolekcję bez domyślnego sortowania, a następnie dodaje indeks do pola nazwy z sortowaniem bez uwzględniania wielkości liter. Komponenty międzynarodowe dla 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 } } )

Aby użyć indeksu, kwerendy muszą określać to samo sortowanie.

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

lub możesz utworzyć kolekcję z domyślnym sortowaniem:

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

Prostym sposobem byłoby użycie $ toLower, jak poniżej.

db.users.aggregate([
    {
        $project: {
            name: { $toLower: "$name" }
        }
    },
    {
        $match: {
            name: the_name_to_search
        }
    }
])
user2661738
źródło