MongoDB prefix wildcard: fulltext-search ($ text) найти часть с поисковой строкой
У меня есть mongodb с $text-Index
и элементами вроде этого:
{
foo: "my super cool item"
}
{
foo: "your not so cool item"
}
Если я выполняю поиск с помощью
mycoll.find({ $text: { $search: "super"} })
я получаю первый элемент (правильный).
Но я также хочу искать с помощью "uper", чтобы получить элемент fist, но если я попробую:
mycoll.find({ $text: { $search: "uper"} })
Я не получаю никаких результатов.
Мой вопрос:
Если есть способ использовать $text, чтобы найти его результаты с частью строки поиска? (например, как '%uper%'
в mysql
)
Внимание: я не запрашиваю только поиск по регулярному выражению - я запрашиваю регулярный поиск в текстовом поиске $
Ответы
Ответ 1
Невозможно сделать это с помощью оператора $text
.
Текстовые индексы создаются с помощью терминов, включенных в строковое значение или в массив строк, и поиск основан на этих идексах.
Вы можете группировать термины только на основе pharse, но не принимать их.
Прочтите $text
ссылку оператора и описание текстовых индексов.
Ответ 2
То, что вы пытаетесь сделать в своем втором примере, - это поиск подстановочных подстановок в вашей коллекции mycoll
в поле foo
. Это не то, для чего предназначена функция textearch, и это невозможно сделать с помощью оператора $text
. Это поведение не включает подстановочный префикс поиска по любому указанному токену в индексированном поле. Однако вы также можете выполнять поиск по регулярному выражению, как и другие. Вот мое прохождение:
>db.mycoll.find()
{ "_id" : ObjectId("53add9364dfbffa0471c6e8e"), "foo" : "my super cool item" }
{ "_id" : ObjectId("53add9674dfbffa0471c6e8f"), "foo" : "your not so cool item" }
> db.mycoll.find({ $text: { $search: "super"} })
{ "_id" : ObjectId("53add9364dfbffa0471c6e8e"), "foo" : "my super cool item" }
> db.mycoll.count({ $text: { $search: "uper"} })
0
Оператор $text
поддерживает поиск одного слова, поиск одного или нескольких слов или поиск фразы. Тип поиска, который вы хотите, не поддерживается
Решение регулярного выражения:
> db.mycoll.find({foo:/uper/})
{ "_id" : ObjectId("53add9364dfbffa0471c6e8e"), "foo" : "my super cool item" }
>
Ответ на ваш последний вопрос: сделать mysql style %super%
в mongoDB, который вам, скорее всего, придется сделать:
db.mycoll.find( { foo : /.*super.*/ } );
Ответ 3
Он должен работать с /uper/
.
Подробнее см. http://docs.mongodb.org/manual/reference/operator/query/regex/.
Edit:
В соответствии с запросом в комментариях:
Решение не обязательно предназначалось для того, чтобы фактически дать то, что запросил ОП, но что ему нужно для решения проблемы.
Так как поиск $regex
не работает с текстовыми индексами, простой поиск регулярных выражений по индексированному полю должен давать ожидаемый результат, но не использовать запрошенные средства.
На самом деле это довольно легко сделать:
db.collection.insert( {foo: "my super cool item"} )
db.collection.insert( {foo: "your not so cool item"})
db.collection.ensureIndex({ foo: 1 })
db.collection.find({'foo': /uper/})
дает ожидаемый результат:
{ "_id" : ObjectId("557f3ba4c1664dadf9fcfe47"), "foo" : "my super cool item" }
Добавленное объяснение показывает нам, что индекс использовался эффективно:
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "test.collection",
"indexFilterSet" : false,
"parsedQuery" : {
"foo" : /uper/
},
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"filter" : {
"foo" : /uper/
},
"keyPattern" : {
"foo" : 1
},
"indexName" : "foo_1",
"isMultiKey" : false,
"direction" : "forward",
"indexBounds" : {
"foo" : [
"[\"\", {})",
"[/uper/, /uper/]"
]
}
}
},
"rejectedPlans" : [ ]
},
"serverInfo" : {
// skipped
},
"ok" : 1
}
Короче говоря: нет, вы не можете повторно использовать индекс $text
, но можете эффективно выполнять запрос. Как написано в Внедрить функцию автозаполнения с помощью поиска MongoDB, возможно, было бы еще более эффективно использовать подход map/reduce, исключающий избыточность и ненужные слова остановки из индексов, за счет того, что он больше не в режиме реального времени.
Ответ 4
Как сказал francadaval, текстовый индекс ищет термины, но если вы объедините regex
и text-index
, вы должны быть хорошими.
mycoll.find({ $or: [
{
$text: { $search: "super"}
}, {"Columname":{
$regex: 'uper',
$options: 'i'
}
] })
Кроме того, убедитесь, что у вас есть нормальный индекс, применяемый к столбцу, отличному от текстового индекса
Ответ 5
У меня недостаточно репутации, чтобы комментировать решение jasenkoh, но это, безусловно, лучший способ справиться с этой ситуацией.
В ситуации OP я бы:
db.mycoll.createIndex( { foo: "text" } )
db.mycoll.createIndex( { foo: 1 } )
db.mycoll.find({$or: [{$text: {$search: 'uper'}}, {foo: {$regex: 'uper'}}]})
Для улучшения характеристик (но несколько разных результатов) замените последнюю строку следующим образом:
db.mycoll.find({$or: [{$text: {$search: 'uper'}}, {foo: {$regex: '^uper'}}]})