Поиск полного и частичного текста MongoDB
Env:
Коллекция:
Создание текстового индекса:
BasicDBObject keys = new BasicDBObject();
keys.put("name","text");
BasicDBObject options = new BasicDBObject();
options.put("name", "userTextSearch");
options.put("unique", Boolean.FALSE);
options.put("background", Boolean.TRUE);
userCollection.createIndex(keys, options); // using MongoTemplate
Документ
Запросы
-
db.users.find( { "$text" : { "$search" : "LEONEL" } } )
= > НАЙДЕНО
-
db.users.find( { "$text" : { "$search" : "LEONEL" } } )
= > НАЙДЕНО (регистр поиска нечувствителен)
-
db.users.find( { "$text" : { "$search" : "LEONÉL" } } )
= > НАЙДЕН (поиск с диакритической чувствительностью ложный)
-
db.users.find( { "$text" : { "$search" : "LEONE" } } )
= > НАЙДЕН (Частичный поиск)
-
db.users.find( { "$text" : { "$search" : "LEO" } } )
= > НЕ НАЙДЕН (Частичный поиск)
-
db.users.find( { "$text" : { "$search" : "L" } } )
= > НЕ НАЙДЕН (Частичный поиск)
Любая идея, почему я получаю 0 результатов, используя в качестве запроса "LEO" или "L"?
Regex с индексом индекса не допускается.
db.getCollection('users')
.find( { "$text" : { "$search" : "/LEO/i",
"$caseSensitive": false,
"$diacriticSensitive": false }} )
.count() // 0 results
db.getCollection('users')
.find( { "$text" : { "$search" : "LEO",
"$caseSensitive": false,
"$diacriticSensitive": false }} )
.count() // 0 results
Монго Документация:
Ответы
Ответ 1
Как и в MongoDB 3.4, функция текстового поиска разработана для поддержки нечувствительного к регистру поиска текстового содержимого с помощью языковых правил для стоп-слов и основ. Правила происхождения для поддерживаемых языков основаны на стандартных алгоритмах, которые обычно обрабатывают общие глаголы и существительные, но не знают собственных имен.
Не существует явной поддержки частичных или нечетких совпадений, но термины, основанные на сходном результате, могут работать как таковые. Например: "вкус", "вкус" и "вкус" - все это связано с "вкусом". Попробуйте демонстрационную страницу "Steamming" в Snowball, чтобы поэкспериментировать с другими словами и алгоритмами.
Ваши результаты, которые соответствуют, являются вариациями одного и того же слова "LEONEL" и отличаются только регистром и диакритическим знаком. Если "LEONEL" не может быть ограничен чем-то более коротким по правилам выбранного вами языка, это единственный тип вариантов, который будет соответствовать.
Если вы хотите делать эффективные частичные совпадения, вам нужно использовать другой подход. Для некоторых полезных идей см.:
Есть соответствующий запрос на улучшение, который вы можете просмотреть/повысить в системе отслеживания проблем MongoDB: SERVER-15090: Улучшить текстовые индексы для поддержки частичного совпадения слов.
Ответ 2
Так как Mongo в настоящее время не поддерживает частичный поиск по умолчанию...
Я создал простой статический метод.
import mongoose from 'mongoose'
const PostSchema = new mongoose.Schema({
title: { type: String, default: '', trim: true },
body: { type: String, default: '', trim: true },
});
PostSchema.index({ title: "text", body: "text",},
{ weights: { title: 5, body: 3, } })
PostSchema.statics = {
searchPartial: function(q, callback) {
return this.find({
$or: [
{ "title": new RegExp(q, "gi") },
{ "body": new RegExp(q, "gi") },
]
}, callback);
},
searchFull: function (q, callback) {
return this.find({
$text: { $search: q, $caseSensitive: false }
}, callback)
},
search: function(q, callback) {
this.searchFull(q, (err, data) => {
if (err) return callback(err, data);
if (!err && data.length) return callback(err, data);
if (!err && data.length === 0) return this.searchPartial(q, callback);
});
},
}
export default mongoose.models.Post || mongoose.model('Post', PostSchema)
Как пользоваться:
import Post from '../models/post'
Post.search('Firs', function(err, data) {
console.log(data);
})
Ответ 3
Я завернул ответ @Ricardo Canelas в плагин для мангустов здесь на npm
Два изменения сделаны:
- Использует обещания
- Поиск по любому полю с типом String
Вот важный исходный код:
// mongoose-partial-full-search
module.exports = exports = function addPartialFullSearch(schema, options) {
schema.statics = {
...schema.statics,
makePartialSearchQueries: function (q) {
if (!q) return {};
const $or = Object.entries(this.schema.paths).reduce((queries, [path, val]) => {
val.instance == "String" &&
queries.push({
[path]: new RegExp(q, "gi")
});
return queries;
}, []);
return { $or }
},
searchPartial: function (q, opts) {
return this.find(this.makePartialSearchQueries(q), opts);
},
searchFull: function (q, opts) {
return this.find({
$text: {
$search: q
}
}, opts);
},
search: function (q, opts) {
return this.searchFull(q, opts).then(data => {
return data.length ? data : this.searchPartial(q, opts);
});
}
}
}
exports.version = require('../package').version;
Usage
Usage
// PostSchema.js
import addPartialFullSearch from 'mongoose-partial-full-search';
PostSchema.plugin(addPartialFullSearch);
// some other file.js
import Post from '../wherever/models/post'
Post.search('Firs').then(data => console.log(data);)
Ответ 4
Без создания индекса мы могли бы просто использовать:
db.users.find({ name: /<full_or_partial_text>/i})
(без учета регистра)
Ответ 5
import re
db.collection.find({"$or": [{"your field name": re.compile(text, re.IGNORECASE)},{"your field name": re.compile(text, re.IGNORECASE)}]})