ここでの主なケースは、「テキスト」検索結果がクエリ内の他のフィルタ条件よりも一般的に優先されるため、「最初に」「テキスト」コンポーネントから結果を取得し、次に基本的に「スキャン」する必要がある場合です。ドキュメント内の他の条件。
このタイプの検索は、テキスト検索結果と組み合わせて「範囲」または任意のタイプの「不等式」一致条件とともに最適化するのが難しい場合があります。これは主に、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" : {
}
}
}
}
}
},
これは基本的に"最初と言っています テキストの結果を取得してから、他の条件によってフェッチされた結果をフィルタリングします。 " 。したがって、ここでは明らかに「テキスト」インデックスのみが使用されており、それが返すすべての結果は、その後、コンテンツを調べることによってフィルタリングされます。
これは2つの理由で最適ではありません。データは、テキスト検索からの一致ではなく、「範囲」条件によって最もよく制約される可能性があるためです。第二に、他のデータにインデックスがありますが、ここでは比較のために使用されていません。したがって、結果ごとにドキュメント全体が読み込まれ、フィルターがテストされます。
次に、ここで「複合」インデックス形式を検討します。最初は、「範囲」が選択に固有である場合は、インデックス付きキーのプレフィックス順としてそれを含めるのが論理的であるように思われます。
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 TEXT: query =something、language =english、caseSensitive =0、diacriticSensitive =0、tag =NULL \ n並べ替え:{} \ nプロジェクト:{} \ nプランナーがエラーを返しました:テキストインデックスを使用して$ textクエリを満たすことができませんでした(テキストインデックスが複合、すべてのプレフィックスフィールドに等式述語が指定されていますか?) "、" code ":2}
したがって、これは「最適」に見えるかもしれませんが、MongoDBが特別な「テキスト」インデックスのクエリ(および実際にはインデックス選択)を処理する方法では、範囲外のこの「除外」が可能になることは不可能です。
ただし、これに対して非常に効率的な方法で「同等」の一致を実行できます。
db.texty.find({ "a": "b", "$text": { "$search": "something" } }).explain()
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
を取得しました それらの範囲内にあるインデックスのその部分のみを検討し、検討します。
これがここでの違いです。 「複合」構造を使用すると、選択範囲を絞り込むことができるため、ここでの反復サイクルを節約できますが、それでもすべてのインデックスエントリをスキャンしてフィルタリングする必要があり、もちろんしない必要があります。 等価一致を使用できない限り、インデックスの「プレフィックス」要素になります。
インデックスに複合構造がない場合は、常にテキストの結果を「最初に」返し、次にそれらの結果に他の条件を適用します。また、クエリエンジンの処理により、「テキスト」インデックスと「通常」インデックスを調べた結果を「結合/交差」することはできません。これは一般的に最適なアプローチではないため、考慮事項を計画することが重要です。
要するに、理想的には「等式」が「プレフィックス」と一致し、そうでない場合はテキスト定義の「後」のインデックスに含めるのが理想的です。