MongoDB obsługuje wyrażenia regularne używające operatora $regex. Jednak te zapytania MongoDB mają wadę, ponieważ wszystkie typy wyrażeń regularnych z wyjątkiem jednego źle wykorzystują indeksy i powodują problemy z wydajnością. W przypadku serwera produkcyjnego z dużą ilością danych nieprawidłowe zapytanie o wyrażenie regularne może rzucić serwer na kolana.
Zapytania oparte na wyrażeniach regularnych MongoDB są dość powszechnymi zapytaniami w większości aplikacji korzystających z MongoDB. Jest to podobne do operacji „LIKE” obsługiwanej w większości relacyjnych baz danych. Składnia polecenia jest następująca
{ $regex: /pattern/, $options: '<options>' } E.g. { name: { $regex: /^acme.*test/}}
Więcej szczegółowych informacji na temat operacji regex i dodatkowych opcji można znaleźć w dokumentacji MongoDB
Do końca tej dyskusji założymy, że pole, do którego dopasowujesz, ma indeks. Jeśli nie zindeksujesz, spowoduje to skanowanie kolekcji i bardzo słabą wydajność. Jednak nawet jeśli pole jest indeksowane, może to skutkować niską wydajnością. Powodem jest to, że MongoDB może dobrze wykorzystać indeksy tylko wtedy, gdy twoje wyrażenie regularne jest „wyrażeniem przedrostkowym” – są to wyrażenia zaczynające się od znaku „^”.
Np. { nazwa: { $regex: /^acme/}}
Pozwala to MongoDB na zidentyfikowanie zakresu wpisów indeksu, które są istotne dla tego zapytania i skutkuje wydajnymi zapytaniami. Każde inne zapytanie powoduje skanowanie indeksu, ponieważ MongoDB nie jest w stanie zawęzić skanowania do zakresu wpisów indeksu. Skanowanie indeksu jest szczególnie złe, ponieważ wszystkie indeksy muszą być stronicowane do pamięci, co wpływa na zestaw roboczy serwera (w rzeczywistości skanowanie indeksu może prowadzić do gorszej wydajności niż skanowanie kolekcji – skutkuje dwukrotnie większą liczbą błędów stron ).
Przyjrzyjmy się kilku przykładom i wynikowym planom zapytań. Do naszych celów testowych skonfigurowałem kolekcję zawierającą 100 tys. dokumentów. Każdy dokument ma pole imię, które składa się z 16 znaków.
Przykład 1: { nazwa:{ $regex:/^acme/}}
Wynik:Efektywne wykorzystanie indeksu
Plan zapytań:
executionStats" : { "executionSuccess" : true, "nReturned" : 0, "executionTimeMillis" : 0, "totalKeysExamined" : 1, "totalDocsExamined" : 0,
Przykład 2: { nazwa:{ $regex:/^acme/i}}
Wynik:Nieefektywne skanowanie indeksu ze względu na wymaganie nieuwzględniające wielkości liter. Tak więc w zasadzie opcja /i neguje „wyrażenie przedrostkowe”
Plan zapytania:
"executionStats" : { "executionSuccess" : true, "nReturned" : 0, "executionTimeMillis" : 137, "totalKeysExamined" : 100000, "totalDocsExamined" : 0,
Przykład 3: { nazwa:{ $regex:/acme.*corp/}}
Wynik:Nieefektywne skanowanie indeksu
Plan zapytań:
"executionSuccess" : true, "nReturned" : 0, "executionTimeMillis" : 167, "totalKeysExamined" : 100000, "totalDocsExamined" : 0,
Przykład 4: { nazwa:{ $regex:/acme/}}
Wynik:nieefektywne skanowanie indeksu
"executionStats" : { "executionSuccess" : true, "nReturned" : 0, "executionTimeMillis" : 130, "totalKeysExamined" : 100000, "totalDocsExamined" : 0,