2016-08-26 12 views
6

Po stwierdzeniu, że 3.3.11 supports case insensitive index (używając sortowania), przebudowałem moją bazę danych zawierającą 40 milionów rekordów, aby można było z nią grać. Alternatywą było dodanie np. małe litery specyficzne dla wyszukiwań niewrażliwych na wielkość liter i indeksuj je.Indeks niewrażliwy na wielkość liter MongoDB "zaczyna się od" problemów z wydajnością

To, co zrobiłem, to poprosić MongoDB o wsparcie sortowania w mojej kolekcji w czasie tworzenia jako suggested here. Więc zrobiłem to, aby umożliwić case niewrażliwość dla całej kolekcji:

db.createCollection("users", {collation:{locale:"en",strength:1}}) 

Po załadowaniu kolekcję Próbowałem bezpośrednie pytania jak:

db.users.find({full_name:"john doe"}) 

... a ci powrót w ~ 10ms z 50 wyników . Jest niewrażliwy na wielkość liter - wszystko jest świetne. Ale potem spróbuj coś takiego:

db.users.find({full_name:/^john/}) 

... albo ...

db.users.find({full_name:/^john/i}) 

... i to trwa dłużej niż 5 minut. Byłem bardzo rozczarowany. Po wykonaniu explain() okazało się, że indeks był najwyraźniej używany, ale zapytanie nadal trwa zbyt długo, aby wykonać. Czy można to przypisać błędnemu lub niekompletnemu wydaniu rozwojowemu, czy też robię coś zasadniczo nie tak?

Podczas wyszukiwania wyrażenia regularnego "zaczyna się od" zapytanie powinno być błyskawiczne. Jakieś pomysły?

+0

Witam! planujemy przejść do wersji 3.4 i znaleźliśmy Twoje pytanie. Czy znalazłeś przyczynę problemu? czy działa dobrze z najnowszą wersją 3.3.x? dzięki .. – Ruben

+1

Właściwie to nie działa. Zainstalowałem 3.4, zaktualizowaną bazę danych do 3.4 za pomocą https://docs.mongodb.com/master/reference/command/setFeatureCompatibilityVersion/#dbcmd.setFeatureCompatibilityVersion, utworzyłem nową kolekcję (z domyślnym sortowaniem, en, strength 2), zaimportowałem 11 milionów dokumentów, utworzyłem indeks i wciąż widzę minuty przed zwrotem zapytania regex (regex podobny do/^ find/i) – wpfwannabe

+0

Nawet z całkiem nowym db i wszystkimi krokami z góry, nie jest to natychmiastowe. Być może mój komentarz był mylący. Działa - ale jest wolny z wyrażeniem regularnym. Jeśli wyszukujesz według pełnego słowa (nie częściowego dopasowania, jest bardzo szybki). – wpfwannabe

Odpowiedz

1

Edycja: istnieje możliwe do zastosowania obejście. Zasadniczo, jeśli szukane słowo to "bob", możesz wyszukać $ lt: "boc", (gdzie inkrementujesz ostatni znak o jeden) i $ gte "bob". Spowoduje to użycie indeksu. Można użyć następujących funkcji zrobiłem poniżej (ostrzeżenie jego niekoniecznie wolny od błędów, ale dość dużo prac) tak:

var searchCriteria = {}; 
addStartsWithQuery(searchCriteria, "firstName", "bo"); 
People.find(searchCriteria).then(...); 

//searchCriteria will be 
/* 
{ 
    $and:[ 
     {firstName:{$gte:"bo"}}, 
     {firstName:{$lt:"bp"}} 
    ] 
} 
*/ 


//now library functions that will automatically generate the correct query and add it to `searchCriteria`. Of course for complicated queries you may have to modifiy it a bit. 
function getEndStr(str) { 
    var endStrArr = str.toLocaleLowerCase('en-US').split(""); 
    for (var i = endStrArr.length - 1; i >= 0; --i) { 
     var lastChar = endStrArr[i]; 
     if(lastChar === "z"){ 
      return endStrArr.join("") + "zzzzzzzzzzzz"; 
     } 
     var nextChar = String.fromCharCode(lastChar.charCodeAt(0) + 1); 
     if (nextChar === ":") 
      nextChar = "a"; 
     if (nextChar !== false) { 
      endStrArr[i] = nextChar; 
      return endStrArr.join(""); 
     } 
     endStrArr.pop(); 
    } 
} 
function addStartsWithQuery(searchCriteria, propertyName, str) { 
    if (!(typeof str === 'string') || !str.length) 
     return; 
    var endStr = getEndStr(str); 
    if (endStr) { 
     if (!searchCriteria.$and) 
      searchCriteria.$and = []; 
     searchCriteria.$and.push({ 
      [propertyName]: { 
       $gte: str 
      } 
     }); 
     searchCriteria.$and.push({ 
      [propertyName]: { 
       $lt: endStr 
      } 
     }); 
    } else { 
     searchCriteria[propertyName] = { 
      $gte: str 
     } 
    } 
} 

Cóż okazuje się, MongoDB oficjalnie nie obsługuje! Połączyłem problem z JIRA, który jasno to wyjaśnia. To sprawia, że ​​niestety zbiory są znacznie mniej przydatne. Zajmijmy się nimi, aby to naprawić wkrótce! Z technicznego punktu widzenia zauważyłem, że chociaż używa indeksu, indeks używa "[\"\", {})", jako jednej z jego granic indeksu, która zawsze zwraca wszystkie elementy indeksu, więc skanowanie indeksu jest bezużyteczne. Następny etap kwerendy filtruje te wyniki jak normalnie.

https://jira.mongodb.org/browse/DOCS-9933

Zagłosuj na ten problem, aby je naprawić! https://jira.mongodb.org/browse/SERVER-29865

+0

Dzięki za wyjaśnienie. Spróbuję rozwiązania, które pozwoli uniknąć użycia indeksu niezależnego od wielkości liter w mongodie, używając denormilizowanego małego pola. – Panoptik

+0

@Panoptik jest dobrym rozwiązaniem na dole problemu. – user3413723

Powiązane problemy