MongoDB
 sql >> Baza danych >  >> NoSQL >> MongoDB

Połącz pełny tekst z innym indeksem

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.




  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Instalowanie MongoDB na Ubuntu 16.04

  2. $in wymaga tablicy jako drugiego argumentu, znaleziono:brak

  3. Błędne obliczanie odległości za pomocą MongoDB

  4. Uzyskaj konkretną część dokumentu

  5. Jak znaleźć przecięcie zestawów między dokumentami w jednej kolekcji w MongoDB?