MongoDB
 sql >> база данни >  >> NoSQL >> MongoDB

Комбинирайте пълния текст с друг индекс

Основният случай тук е, че резултатът от търсенето на „текст“ обикновено има предимство пред другите условия на филтриране в заявката и като такъв става необходимо „първо“ да се получат резултати от компонента „текст“ и след това основно „сканиране“ за други условия в документа.

Този тип търсене може да бъде труден за оптимизиране заедно с „обхват“ или какъвто и да е тип условие за съответствие „неравенство“ във връзка с резултатите от текстовото търсене и се дължи най-вече на това как MongoDB обработва този „специален“ тип индекс.

За кратка демонстрация помислете за следната основна настройка:

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

Така че, ако искате да разгледате това с условие за текстово търсене, както и разглеждане на диапазон в другото поле ( { "$lt": "c" } ), тогава можете да се справите по следния начин:

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

С изхода за обяснение като ( важна част ):

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

                                                    }
                                            }
                                    }
                            }
                    }
            },

Което по същество означава първи вземете ми текстовите резултати и след това филтрирайте тези резултати, извлечени от другото условие" . Така че ясно е, че тук се използва само индексът "текст", а след това всички резултати, които връща, впоследствие се филтрират чрез изследване на съдържанието.

Това не е оптимално поради две причини, тъй като може да се окаже, че данните са най-добре ограничени от условието "обхват", а не от съвпаденията от текстовото търсене. Второ, въпреки че има индекс за другите данни, той не се използва тук за сравнение. Така че по-скоро целият документ се зарежда за всеки резултат и филтърът се тества.

След това можете да помислите за "съставен" индексен формат тук и първоначално ще изглежда логично, че ако "диапазонът" е по-специфичен за избора, тогава го включете като префиксния ред на индексираните ключове:

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

Но тук има уловка, тъй като когато се опитате да изпълните заявката отново:

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

Това би довело до грешка:

Грешка:грешка:{"waitedMS" :NumberLong(0),"ok" :0,"errmsg" :"грешка при обработка на заявката:ns=test.textyTree:$and\n a $lt \"c\"\n ТЕКСТ:query=something, language=english, caseSensitive=0, diacriticSensitive=0, tag=NULL\nСортиране:{}\nProj:{}\n планерът върна грешка:неуспешно използване на текстов индекс за удовлетворяване на заявка $text (ако текстовият индекс е съединение, дадени ли са предикатите за равенство за всички префиксни полета?)","code" :2}

Така че, въпреки че това може да изглежда „оптимално“, начинът, по който MongoDB обработва заявката (и наистина селекцията на индекс) за специалния „текст“ индекс, просто не е възможно това „изключване“ извън диапазона да е възможно.

Можете обаче да извършите съвпадение за "равенство" по много ефективен начин:

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

С обяснения изход:

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

                                            }
                                    }
                            }
                    }
            },

Така че индексът се използва и може да се покаже, че „предварително филтрира“ съдържанието, предоставено на текста, съвпадащ от изхода на другото условие.

Ако наистина запазите „префикса“ към индекса като поле(а) „текст“ за търсене обаче:

db.texty.dropIndexes();

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

След това извършете търсенето:

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

След това виждате резултат, подобен на горното съвпадение за "равенство":

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

                                            }
                                    }
                            }
                    }
            },

Голямата разлика тук от първия опит е къде filter се поставя във веригата за обработка, което показва, че макар да не съответства на "префикс" (което е най-оптималното), съдържанието наистина се сканира от индекса "преди" да бъде изпратено на етап "текст".

Така че той е "предварително филтриран", но не разбира се по най-оптималния начин и това се дължи на самото естество на това как се използва индексът "текст". Така че, ако току-що разгледате обикновения диапазон на индекс сам по себе си:

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

След това обяснява изходът:

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

Тогава поне получи indexBounds да разгледа и разгледа само тази част от индекса, която попада в тези граници.

Така че това са разликите тук. Използването на "сложна" структура би трябвало да ви спести някои итерационни цикли тук, като сте в състояние да стесните избора, но все пак трябва да сканира всички записи в индекса, за да филтрира, и разбира се не бъде елементът "prefix" в индекса, освен ако не можете да използвате съвпадение за равенство върху него.

Без съставна структура в индекса, вие винаги връщате текстовите резултати "първо", а след това прилагате всякакви други условия към тези резултати. Също така не е възможно да се "комбинират/пресичат" резултатите от разглеждането на "текстов" индекс и "нормален" индекс поради обработката на машината за заявки. Това обикновено няма да е оптималният подход, така че планирането с оглед на съображенията е важно.

Накратко, в идеалния случай съставен с „равенство“ съвпада с „префикс“ и ако не, тогава включете в индекса „след“ текстовата дефиниция.




  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. сървърът върна грешка на стъпката за удостоверяване на SASL:Удостоверяването не бе успешно

  2. Естествено маскиране на MongoDB (3-ти метод)

  3. MongoDB заявка с условие „или“.

  4. Конфигурирайте регистриране за драйвера на MongoDB Java

  5. Изпълнете javascript скрипт (.js файл) в mongodb, включително друг файл вътре в js