Głównym przypadkiem jest to, że wynik wyszukiwania „tekst” ma generalnie pierwszeństwo przed innymi warunkami filtrowania w zapytaniu i jako taki konieczne staje się „najpierw” uzyskanie wyników z komponentu „tekst”, a następnie „przeskanowanie” w poszukiwaniu inne warunki w dokumencie.
Ten typ wyszukiwania może być trudny do zoptymalizowania wraz z „zakresem” lub dowolnym rodzajem „nierówności” w połączeniu z wynikami wyszukiwania tekstowego, a wynika to głównie ze sposobu, w jaki MongoDB obsługuje ten „specjalny” typ indeksu.
W celu przeprowadzenia krótkiej demonstracji rozważ następującą podstawową konfigurację:
db.texty.drop();
db.texty.insert([
{ "a": "a", "text": "something" },
{ "a": "b", "text": "something" },
{ "a": "b", "text": "nothing much" },
{ "a": "c", "text": "something" }
])
db.texty.createIndex({ "text": "text" })
db.texty.createIndex({ "a": 1 })
Więc jeśli chcesz spojrzeć na to za pomocą warunku wyszukiwania tekstowego, a także rozważenia zakresu w innym polu ( { "$lt": "c" }
), możesz postępować w następujący sposób:
db.texty.find({ "a": { "$lt": "c" }, "$text": { "$search": "something" } }).explain()
Z wyjaśnieniem wyjściowym, takim jak (ważna część):
"winningPlan" : {
"stage" : "FETCH",
"filter" : {
"a" : {
"$lt" : "c"
}
},
"inputStage" : {
"stage" : "TEXT",
"indexPrefix" : {
},
"indexName" : "text_text",
"parsedTextQuery" : {
"terms" : [
"someth"
],
"negatedTerms" : [ ],
"phrases" : [ ],
"negatedPhrases" : [ ]
},
"inputStage" : {
"stage" : "TEXT_MATCH",
"inputStage" : {
"stage" : "TEXT_OR",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"_fts" : "text",
"_ftsx" : 1
},
"indexName" : "text_text",
"isMultiKey" : true,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "backward",
"indexBounds" : {
}
}
}
}
}
},
Co w zasadzie oznacza „pierwszy daj mi wyniki tekstowe, a następnie odfiltruj te wyniki pobrane według innego warunku” . Tak więc wyraźnie używany jest tutaj tylko indeks „tekstowy”, a następnie wszystkie zwracane przez niego wyniki są następnie filtrowane przez badanie treści.
Nie jest to optymalne z dwóch powodów, ponieważ prawdopodobnie dane są najlepiej ograniczone przez warunek „zakresu”, a nie przez dopasowania z wyszukiwania tekstowego. Po drugie, mimo że istnieje indeks dla innych danych, nie jest on tutaj używany do porównania. Więc raczej cały dokument jest ładowany dla każdego wyniku, a filtr jest testowany.
Możesz wtedy rozważyć format indeksu „złożonego” i początkowo wydawałoby się logiczne, że jeśli „zakres” jest bardziej specyficzny dla selekcji, to uwzględnij go jako kolejność indeksowanych kluczy z przedrostkiem:
db.texty.dropIndexes();
db.texty.createIndex({ "a": 1, "text": "text" })
Ale jest tu pewien haczyk, ponieważ przy próbie ponownego uruchomienia zapytania:
db.texty.find({ "a": { "$lt": "c" }, "$text": { "$search": "something" } })
Spowodowałoby to błąd:
Error:error:{"waitedMS" :NumberLong(0),"ok" :0,"errmsg" :"błąd przetwarzania zapytania:ns=test.textyTree:$and\n a $lt \"c\"\n TEKST :query=coś, język=angielski, caseSensitive=0, diacriticSensitive=0, tag=NULL\nSort:{}\nProj:{}\n Planner zwrócił błąd:nie można użyć indeksu tekstowego do spełnienia zapytania $text (jeśli indeks tekstowy jest złożony, czy predykaty równości są podane dla wszystkich pól prefiksu?)","kod" :2}
Więc nawet jeśli może się to wydawać "optymalne", sposób, w jaki MongoDB przetwarza zapytanie (a tak naprawdę wybór indeksu) dla specjalnego indeksu "tekstowego", po prostu nie jest możliwe, aby to "wykluczenie" poza zakresem było możliwe.
Możesz jednak przeprowadzić w tym przypadku bardzo efektywne dopasowanie:
db.texty.find({ "a": "b", "$text": { "$search": "something" } }).explain()
Z wyjaśnieniem wyjścia:
"winningPlan" : {
"stage" : "TEXT",
"indexPrefix" : {
"a" : "b"
},
"indexName" : "a_1_text_text",
"parsedTextQuery" : {
"terms" : [
"someth"
],
"negatedTerms" : [ ],
"phrases" : [ ],
"negatedPhrases" : [ ]
},
"inputStage" : {
"stage" : "TEXT_MATCH",
"inputStage" : {
"stage" : "TEXT_OR",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"a" : 1,
"_fts" : "text",
"_ftsx" : 1
},
"indexName" : "a_1_text_text",
"isMultiKey" : true,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "backward",
"indexBounds" : {
}
}
}
}
},
Tak więc indeks jest używany i można go pokazać w celu „wstępnego filtrowania” treści dostarczonej do tekstu zgodnego z wynikiem drugiego warunku.
Jeśli jednak zachowasz „prefiks” w indeksie jako pole (pola) „tekstowe” do wyszukiwania:
db.texty.dropIndexes();
db.texty.createIndex({ "text": "text", "a": 1 })
Następnie przeprowadź wyszukiwanie:
db.texty.find({ "a": { "$lt": "c" }, "$text": { "$search": "something" } }).explain()
Następnie zobaczysz podobny wynik do powyższego dopasowania „równości”:
"winningPlan" : {
"stage" : "TEXT",
"indexPrefix" : {
},
"indexName" : "text_text_a_1",
"parsedTextQuery" : {
"terms" : [
"someth"
],
"negatedTerms" : [ ],
"phrases" : [ ],
"negatedPhrases" : [ ]
},
"inputStage" : {
"stage" : "TEXT_MATCH",
"inputStage" : {
"stage" : "TEXT_OR",
"filter" : {
"a" : {
"$lt" : "c"
}
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"_fts" : "text",
"_ftsx" : 1,
"a" : 1
},
"indexName" : "text_text_a_1",
"isMultiKey" : true,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "backward",
"indexBounds" : {
}
}
}
}
},
Duża różnica w porównaniu z pierwszą próbą polega na tym, że filter
jest umieszczana w łańcuchu przetwarzania, co wskazuje, że chociaż nie jest to dopasowanie „prefiksu” (co jest najbardziej optymalne), zawartość jest rzeczywiście skanowana z indeksu „przed” wysłaniem do etapu „tekst”.
Jest więc „wstępnie filtrowany”, ale oczywiście nie w najbardziej optymalny sposób, a wynika to z samej natury tego, w jaki sposób używany jest indeks „tekstowy”. Jeśli więc rozważyłeś zwykły zakres w samym indeksie:
db.texty.createIndex({ "a": 1 })
db.texty.find({ "a": { "$lt": "c" } }).explain()
Następnie wyjaśnij wyjście:
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"a" : 1
},
"indexName" : "a_1",
"isMultiKey" : false,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"a" : [
"[\"\", \"c\")"
]
}
}
},
Wtedy przynajmniej dostałem indexBounds
do rozważenia i spojrzał tylko na tę część indeksu, która mieściła się w tych granicach.
Oto różnice. Korzystanie ze struktury „złożonej” powinno zaoszczędzić tutaj kilka cykli iteracyjnych, ponieważ można zawęzić wybór, ale nadal musi skanować wszystkie wpisy indeksu w celu filtrowania i oczywiście nie być elementem "prefix" w indeksie, chyba że możesz użyć w nim dopasowania równości.
Bez struktury złożonej w indeksie zawsze zwracasz wyniki tekstowe „najpierw”, a następnie stosujesz do tych wyników wszelkie inne warunki. Ponadto nie jest możliwe „łączenie/przecinanie” wyników wyszukiwania indeksu „tekstowego” i „normalnego” ze względu na obsługę mechanizmu zapytań. To na ogół nie będzie optymalne podejście, dlatego ważne jest planowanie pod kątem rozważań.
Krótko mówiąc, najlepiej połączyć z „równością” dopasowującą „przedrostek”, a jeśli nie, to dołącz do indeksu „za” definicją tekstu.