あなたがいる場所に最も近いホテルをユーザーが検索できるAndroidアプリを開発したいとします。これは、たとえばAirBnbなど、最近のアプリでは非常に一般的です。
これは私が使用しているデータセットです:
{
"name" : "The Most Amazing Hotel",
"city" : "India",
"type": "Point"
"coord": [
-56.16082,
61.15392
]
}
{
"name" : "The Most Incredible Hotel",
"city" : "India",
"type": "Point"
"coord": [
-56.56285,
61.34590
]
}
{
"name" : "The Fantastic GuestHouse",
"city" : "India",
"type": "Point"
"coord": [
-56.47085,
61.11357
]
}
ここで、フィールドにテキストインデックスを作成して、name
名前で検索し、座標に基づいて地理空間インデックスで並べ替えるようにします。
したがって、「The Most」という単語を検索すると、名前で「The Most」という単語が検索され、「The Mostinthemost」という単語が含まれる最寄りのホテルが返されます。
mongodbはこのタイプの検索もサポートしていますか?
ここでmongodbのガイダンスを読んでいます:https://docs.mongodb.org/manual/core/index-text/
複合テキストインデックスには、マルチキーや地理空間インデックスフィールドなどの他の特別なインデックスタイプを含めることはできません。
私が理解している限り、私は複合テキストインデックスを作成していません。これは単純なテキストインデックスです。name
つまり、city
ANDname
フィールドではなく、フィールドのテキストのみにインデックスを付けています。
このような操作のユースケースを正当化するのは非常に難しいため、これをまったく必要としないという公正なケースがあります。「ホテルの検索」は、「テキスト」の組み合わせではないことを主張します。 「」と「geoSpatial」検索が実際に適用されます。
実際には、「ほとんどの人」は、主要な基準の一部として、ある場所に近い、または訪問したいさまざまな場所に近いものを探している可能性が高く、他の「勝者」は「コスト」に重きを置いている可能性があります。 "、"評価 "、"ブランド "、"施設 "、そしておそらく飲食店などへの近さ。
そのリストに「テキスト検索」を追加することは非常に異なることであり、この特定のアプリケーションではあまり実用的ではない可能性があります。
それでも、これはおそらく説明に値するものであり、少なくともこのユースケースでは2つの概念が実際には「メッシュ」にならない理由について、ここで理解すべきいくつかの概念があります。
まず、データスキーマを少し「微調整」することを提案したいと思います。
{
"name" : "The Most Amazing Hotel",
"city" : "India",
"location": {
"type": "Point",
"coordinates": [
72.867804,
19.076033
]
}
}
これは少なくとも"location"
インデックス作成用の有効なGeoJSONオブジェクトとして提供され、一般にクエリとストレージのオプションが増えるため、従来の座標ペアではなくGeoJSONが必要です。さらに、距離は同等ではなくメートルに標準化されます。世界中のラジアン」。
したがって、一度に複数の特別なインデックスを使用することはできないという点で、基本的に正しい読み方です。最初に複合インデックスの定義を見てください。
db.hotels.createIndex({ "name": "text", "location": "2dsphere" })
{"ok":0、 "errmsg": "不正なインデックスキーパターン{名前:\" text \ "、場所:\" 2dsphere \ "}:1つのインデックスに複数のインデックスプラグインを使用することはできません。"、 「コード」:67}
だからそれはできません。個別に検討しても:
db.hotels.createIndex({ "name": "text" })
db.hotels.createIndex({ "location": "2dsphere" })
次に、クエリを実行してみてください。
db.hotels.find({
"location": {
"$nearSphere": {
"$geometry": {
"type": "Point",
"coordinates": [
72.867804,
19.076033
]
}
}
},
"$text": { "$search": "Amazing" }
})
エラー:コマンドが失敗しました:{"waitedMS":NumberLong(0)、 "ok":0、 "errmsg": "テキストとgeoNearは同じクエリでは許可されていません"、 "コード":2}:未定義
これは、これが複合インデックスで3つの方法で定義できなかった理由を実際に裏付けています。
初期エラーが示すように、MongoDBでこれらの「特別な」インデックスを処理する方法では、基本的に、選択したインデックスタイプの「特別な」ハンドラーに「分岐」する必要があり、2つのハンドラーは同じ場所にありません。
個別のインデックスを使用する場合でも、ロジックは基本的に「and」条件であるため、MongoDBはとにかく複数のインデックスを実際に選択することはできません。また、両方のクエリ句で「特別な」処理が必要になるため、実際にはそうする必要があります。そして、それはできません。
これが論理的な$or
条件であったとしても、基本的にポイント1に戻ります。ここでは、「インデックスの交差」を適用しても、クエリ操作の「トップレベル」で順番に適用する必要がある「特別な」インデックスの別のプロパティがあります。インデックスを選択できるようにします。これらをラップする$or
ということは、MongoDBがそれを実行できないため、許可されないことを意味します。
したがって、基本的にそれぞれが排他的である必要があり、それらを一緒に使用することはできません。しかしもちろん、検索のどの順序があなたにとってより重要であるかに応じて、あなたはいつでも「ごまかす」ことができます。
最初に「場所」で:
db.hotels.aggregate([
{ "$geoNear": {
"near": {
"type": "Point",
"coordinates": [
72.867804,
19.076033
]
},
"spherical": true,
"maxDistance": 5000,
"distanceField": "distance",
"query": {
"name": /Amazing/
}
}}
])
あるいは:
db.hotels.find({
"location": {
"$nearSphere": {
"$geometry": {
"type": "Point",
"coordinates": [
72.867804,
19.076033
]
},
"$maxDistance": 5000
}
},
"name": /Amazing/
})
または最初にテキスト検索で:
db.hotels.find({
"$text": { "$search": "Amazing" },
"location": {
"$geoWithin": {
"$centerSphere": [[
72.867804,
19.076033
], 5000 ]
}
}
})
これで、各アプローチの選択オプション.explain()
を詳しく見て、何が起こっているかを確認できますが、基本的なケースは、それぞれが使用する特別なインデックスを1つだけ選択することです。
最初のケースでは、プライマリに使用されるのはコレクションのgeoSpatialインデックスであり、最初に指定された場所への近接度に基づいて結果を検索し、次にname
フィールドに指定された正規表現引数でフィルタリングします。
2番目のケースでは、「text」インデックスを使用して一次選択を行い(したがって、最初に「Amazing」のものを見つけます)、それらの結果から、geoSpatialフィルター(インデックスを使用しない)を適用します。$geoWithin
この場合、基本的には、指定された距離内のポイントの周りの円$near
内を検索してそこで結果をフィルタリングすることにより、aが実行していることと同等です。
ただし、考慮すべき重要な点は、アプローチごとに異なる結果が返される可能性が非常に高いということです。最初に場所を絞り込むことにより、検査できるデータは指定された距離内の場所のみであるため、距離外で「驚くべき」ものは追加のフィルターによって考慮されません。
2番目のケースでは、テキスト用語が一次検索であるため、「Amazing」のすべての結果が考慮され、二次フィルターによって返されるアイテムは、最初のテキストから返されることが許可されたアイテムのみです。フィルタ。
2つのクエリ操作(「text」と「geoSpatial」の両方)は非常に異なることを達成しようとするため、これは全体的な考慮事項において非常に重要です。「テキスト」の場合、指定された用語の「上位の結果」を探し、その用語に一致する限られた数の結果のみをランク付けされた順序で返します。これは、他のフィルター条件を適用する場合、その最初の条件を満たしたアイテムの多くが追加の基準を満たさない可能性が高いことを意味します。
要するに、「「驚くべき」すべてのものが必ずしもクエリされたポイントの近くにあるわけではありません」。つまり、のような現実的な制限が100 results
あり、最もよく一致するのは、これらの100に「近い」アイテムもすべて含まれていない可能性が高いということです。
また、$text
オペレーターは実際には、それ自体で結果を実際に「ソート」することはありません。実際の主な目的は、フレーズを「一致」させるだけでなく、結果を「スコアリング」して「最良の」一致を一番上に浮かび上がらせることです。これは通常、クエリ自体の「後に」実行され、前述のように、予測値は「ソート」され、ほとんどの場合「制限」されます。集約パイプラインでそれを実行してから2番目のフィルターを適用することは可能ですが、前述のように、これは他の目的で「近い」ものを除外する可能性があります。
逆もまた真である可能性があります(「ポイントから遠く離れたところに多くの「驚くべき」ものがあります」)が、現実的な距離制限があると、これは起こりにくくなります。しかし、与えられた他の考慮事項は、これは真のテキスト検索ではなく、与えられた用語に一致する正規表現を使用することです。
最後に、私は常に"Amazing"
ここでの例のフレーズとして使用して"Most"
おり、質問で提案されているものではありません。これは、「and」、「or」、「the」、さらには「in」のように、特定の用語が無視されるという点で、ここのテキストインデックス(およびほとんどの専用テキスト検索製品)で「ステミング」がどのように機能するかによるものです。「テキスト検索が行うことであるフレーズにとって実際には価値があるとは見なされないので、同様になります。
したがって、実際に正規表現が必要な場合でも、実際にはそのような用語のマッチングに優れているということは事実です。
これは、「テキスト」クエリが実際にはここに属していないという点で、元のポイントに完全に戻ります。他の便利なフィルターは、通常、真の「geoSpatial」検索基準と連携して機能し、真の「テキスト検索」は重要なもののリストに含まれていません。
より可能性が高いのは、訪問したい目的地からの距離の*「交差点の設定」内、または少なくとも一部またはほとんどに十分近い場所を望んでいることです。そしてもちろん、前述のような他の要素(*「価格」、「サービス」など)は、人々が一般的に考慮したいものです。
この方法で結果を探すのは、実際には「適切」ではありません。本当に必要だと思う場合は、「チート」アプローチの1つを適用するか、実際には異なるクエリを使用してから、他のロジックを使用して結果の各セットをマージします。しかし、サーバーがこれを単独で行うことは実際には意味がありません。そのため、サーバーは試行しません。
したがって、最初に地理空間の一致を正しく取得することに焦点を当て、次に結果に重要な他の基準を適用します。しかし、私は「テキスト検索」がとにかくそれらの1つであることが本当に有効であるとは本当に信じていません。代わりに「チート」しますが、本当に必要な場合に限ります。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加