sql >> Databáze >  >> NoSQL >> MongoDB

Kombinujte plný text s jiným rejstříkem

Hlavním případem je, že „textový“ výsledek vyhledávání má obecně přednost před ostatními podmínkami filtru v dotazu, a proto je nutné „nejprve“ získat výsledky z komponenty „text“ a poté v podstatě „skenovat“ další podmínky v dokumentu.

Tento typ vyhledávání může být obtížné optimalizovat spolu s „rozsahem“ nebo jakýmkoli typem podmínky shody „nerovnosti“ ve spojení s výsledky textového vyhledávání a je to většinou způsobeno tím, jak MongoDB zpracovává tento „speciální“ typ indexu.

Pro krátkou ukázku zvažte následující základní nastavení:

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

Takže pokud byste se na to chtěli podívat s podmínkou textového vyhledávání a také s ohledem na rozsah v druhém poli ( { "$lt": "c" } ), pak byste mohli postupovat následovně:

db.texty.find({ "a": { "$lt": "c" }, "$text": { "$search": "something" } }).explain()

S výstupem vysvětlení, jako je (důležitá část):

           "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ž v podstatě znamená "první získejte textové výsledky a poté filtrujte výsledky získané jinou podmínkou“ . Je tedy zřejmé, že se zde používá pouze „textový“ index a všechny výsledky, které vrací, jsou následně filtrovány zkoumáním obsahu.

To není optimální ze dvou důvodů, protože je pravděpodobné, že data jsou nejlépe omezena podmínkou "rozsah" spíše než shodami z textového vyhledávání. Za druhé, i když existuje index na jiných datech, není zde použit pro srovnání. Spíše se tedy pro každý výsledek načte celý dokument a otestuje se filtr.

Pak byste zde mohli uvažovat o „složeném“ indexovém formátu a zpočátku by se zdálo logické, že pokud je „rozsah“ specifičtější pro výběr, zahrňte jej jako předponu pořadí indexovaných klíčů:

db.texty.dropIndexes();
db.texty.createIndex({ "a": 1, "text": "text" })

Ale je tu háček, protože když se pokusíte spustit dotaz znovu:

db.texty.find({ "a": { "$lt": "c" }, "$text": { "$search": "something" } })

Výsledkem by byla chyba:

Chyba:chyba:{"waitedMS" :NumberLong(0),"ok" :0,"errmsg" :"chyba při zpracování dotazu:ns=test.textyTree:$and\n a $lt \"c\"\n TEXT :query=something, language=english, caseSensitive=0, diacriticSensitive=0, tag=NULL\nSeřadit:{}\nProj:{}\n plánovač vrátil chybu:nepodařilo se použít textový index k uspokojení $textového dotazu (pokud je textový index složené, jsou predikáty rovnosti uvedeny pro všechna pole předpon?)","code" :2}

Takže i když se to může zdát „optimální“, způsob, jakým MongoDB zpracovává dotaz (a skutečně výběr indexu) pro speciální „textový“ index, prostě není možné, aby toto „vyloučení“ mimo rozsah bylo možné.

Na tom však můžete provést "rovnostní" shodu velmi účinným způsobem:

db.texty.find({ "a": "b", "$text": { "$search": "something" } }).explain()

S výstupem vysvětlení:

           "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" : {

                                            }
                                    }
                            }
                    }
            },

Index se tedy použije a lze jej zobrazit pro "předfiltrování" obsahu poskytnutého do textové shody výstupem druhé podmínky.

Pokud si však ponecháte "předponu" indexu jako "textová" pole, která chcete hledat:

db.texty.dropIndexes();

db.texty.createIndex({ "text": "text", "a": 1 })

Poté proveďte vyhledávání:

db.texty.find({ "a": { "$lt": "c" }, "$text": { "$search": "something" } }).explain()

Potom uvidíte podobný výsledek jako výše uvedená shoda „rovnosti“:

            "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" : {

                                            }
                                    }
                            }
                    }
            },

Velký rozdíl oproti prvnímu pokusu je v tom, kde filter je umístěn v řetězci zpracování, což naznačuje, že i když nejde o shodu "předpony" (což je nejoptimálnější), obsah je skutečně skenován z indexu "před" odesláním do fáze "text".

Je tedy „předfiltrován“, ale samozřejmě ne tím nejoptimálnějším způsobem, a to je způsobeno samotnou povahou toho, jak se „textový“ index používá. Takže pokud jste uvažovali pouze o prostém rozsahu na indexu samotném:

db.texty.createIndex({ "a": 1 })
db.texty.find({ "a": { "$lt": "c" } }).explain()

Potom výstup vysvětlení:

            "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\")"
                                    ]
                            }
                    }
            },

Pak to alespoň získalo indexBounds zvážit a podívat se pouze na tu část indexu, která spadala do těchto mezí.

Takže tady jsou rozdíly. Použití "složené" struktury by vám zde mělo ušetřit několik iteračních cyklů tím, že budete moci zúžit výběr, ale stále musí profiltrovat všechny položky rejstříku, a to samozřejmě ne být prvkem "prefix" v indexu, pokud na něm nemůžete použít shodu s rovností.

Bez složené struktury v indexu vždy vracíte textové výsledky „nejprve“ a poté na tyto výsledky aplikujete jakékoli další podmínky. Také není možné "kombinovat/protínat" výsledky z pohledu na "textový" index a "normální" index kvůli zpracování dotazovacího stroje. To obecně nebude optimální přístup, takže plánování úvah je důležité.

Stručně řečeno, ideálně složené s "rovností" odpovídající "prefixu", a pokud ne, zahrňte do indexu "za" definici textu.




  1. Porovnání mangoose _id a řetězců

  2. Mongo Triple Compound Index

  3. MongoDB:CHYBA:podřízený proces se nezdařil, ukončeno s chybou číslo 14

  4. Chyba:queryTxt ETIMEOUT při připojování k MongoDb Atlas pomocí mongoose