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" } } )
=> FOUND(搜索caseSensitive为false)db.users.find( { "$text" : { "$search" : "LEONÉL" } } )
=> FOUND(使用diacriticSensitive搜索是错误的)db.users.find( { "$text" : { "$search" : "LEONE" } } )
=>找到(部分搜索)db.users.find( { "$text" : { "$search" : "LEO" } } )
=>未找到(部分搜索)db.users.find( { "$text" : { "$search" : "L" } } )
=>未找到(部分搜索)我知道为什么我使用查询“LEO”或“L”获得0结果?
不允许使用带有文本索引搜索的正则表达式。
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
Mongo文档:
答案 0 :(得分:41)
与MongoDB 3.4一样,text search功能旨在支持对文本内容进行不区分大小写的搜索,并使用针对停用词和词干的特定于语言的规则。 supported languages的词干规则基于标准算法,这些算法通常处理常见的动词和名词但不知道专有名词。
没有对部分或模糊匹配的明确支持,但是产生类似结果的术语似乎可以正常工作。例如:"品味","品味",品味"一切都是为了#t; tast"。试试Snowball Stemming Demo页面,试验更多单词和词干算法。
您匹配的结果是同一个单词" LEONEL"的所有变体,并且仅根据大小写和变音符号而有所不同。除非" LEONEL"可以根据所选语言的规则缩短某些内容,这些是唯一可以匹配的变体类型。
如果你想进行有效的部分匹配,你需要采取不同的方法。对于一些有用的想法,请参阅:
您可以在MongoDB问题跟踪器中观看/提升相关的改进请求:SERVER-15090: Improve Text Indexes to support partial word match。
答案 1 :(得分:4)
由于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);
})
答案 2 :(得分:4)
如果您想利用 MongoDB 全文搜索的所有优势并希望部分匹配(可能用于自动完成),Shrikant Prabhu 提到的基于 n-gram 的方法对我来说是正确的解决方案。显然,您的里程可能会有所不同,这在索引大型文档时可能不切实际。
在我的例子中,我主要需要部分匹配来处理文档的 title
字段(以及其他一些短字段)。
我使用了边缘 n-gram 方法。那是什么意思?简而言之,您将像 "Mississippi River"
这样的字符串变成像 "Mis Miss Missi Missis Mississ Mississi Mississip Mississipp Mississippi Riv Rive River"
这样的字符串。
受刘根的this code启发,我想出了这个方法:
function createEdgeNGrams(str) {
if (str && str.length > 3) {
const minGram = 3
const maxGram = str.length
return str.split(" ").reduce((ngrams, token) => {
if (token.length > minGram) {
for (let i = minGram; i <= maxGram && i <= token.length; ++i) {
ngrams = [...ngrams, token.substr(0, i)]
}
} else {
ngrams = [...ngrams, token]
}
return ngrams
}, []).join(" ")
}
return str
}
let res = createEdgeNGrams("Mississippi River")
console.log(res)
现在为了在 Mongo 中使用它,我在文档中添加了一个 searchTitle
字段,并通过使用上述函数将实际的 title
字段转换为边 n-gram 来设置其值。我还为 "text"
字段创建了一个 searchTitle
索引。
然后我使用投影从搜索结果中排除 searchTitle
字段:
db.collection('my-collection')
.find({ $text: { $search: mySearchTerm } }, { projection: { searchTitle: 0 } })
答案 3 :(得分:1)
无需创建索引,我们可以简单地使用:
db.users.find({ name: /<full_or_partial_text>/i})
(不区分大小写)
答案 4 :(得分:1)
我将@Ricardo Canelas的答案包装在猫鼬插件中on npm
进行了两项更改:
-兑现承诺
-搜索类型为if Account.enabled_feature?('Mango')
{
id: 5,
name: 'Mango'
}
这是重要的源代码:
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;
答案 5 :(得分:1)
如果您使用变量存储要搜索的字符串或值:
它将与Regex一起使用,如下:
{ collection.find({ name of Mongodb field: new RegExp(variable_name, 'i') }
这里,我是忽略大小写选项
答案 6 :(得分:1)
在 MongodB 中完整/部分搜索“纯”流星项目
我修改了 flash 的代码以将它与 Meteor-Collections 和 simpleSchema 一起使用,但没有使用 mongoose(意思是:删除 .plugin()
-method 和 schema.path
的使用(尽管这看起来是一个 simpleSchema-attribute in flash 的代码,它没有为我解析)) 并返回结果数组而不是游标。
认为这可能对某人有所帮助,所以我分享了它。
export function partialFullTextSearch(meteorCollection, searchString) {
// builds an "or"-mongoDB-query for all fields with type "String" with a regEx as search parameter
const makePartialSearchQueries = () => {
if (!searchString) return {};
const $or = Object.entries(meteorCollection.simpleSchema().schema())
.reduce((queries, [name, def]) => {
def.type.definitions.some(t => t.type === String) &&
queries.push({[name]: new RegExp(searchString, "gi")});
return queries
}, []);
return {$or}
};
// returns a promise with result as array
const searchPartial = () => meteorCollection.rawCollection()
.find(makePartialSearchQueries(searchString)).toArray();
// returns a promise with result as array
const searchFull = () => meteorCollection.rawCollection()
.find({$text: {$search: searchString}}).toArray();
return searchFull().then(result => {
if (result.length === 0) throw null
else return result
}).catch(() => searchPartial());
}
这将返回一个 Promise,因此可以这样调用它(即作为服务器端异步 Meteor-Method searchContact
的返回)。
这意味着您在调用此方法之前将 simpleSchema 附加到您的集合。
return partialFullTextSearch(Contacts, searchString).then(result => result);
答案 7 :(得分:0)
对我有用的快速而肮脏的解决方案:首先使用文本搜索,如果没有找到任何内容,然后使用正则表达式进行另一个查询。如果您不想进行两个查询-$or
也可以,但是requires all fields in query to be indexed。
此外,您最好不要使用不区分大小写的rx,因为it can't rely on indexes。就我而言,我已经复制了使用过的字段的小写字母。
答案 8 :(得分:0)
这里解释了基于n-gram的良好的模糊匹配方法 (还介绍了如何使用前缀匹配为结果评分更高) https://medium.com/xeneta/fuzzy-search-with-mongodb-and-python-57103928ee5d
注意:基于n-gram的方法可能会扩展存储范围,并且mongodb集合的大小会增加。
答案 9 :(得分:0)
我创建了一个附加字段,它将我要搜索的文档中的所有字段组合在一起。然后我只使用正则表达式:
user = {
firstName: 'Bob',
lastName: 'Smith',
address: {
street: 'First Ave',
city: 'New York City',
}
notes: 'Bob knows Mary'
}
// add combined search field with '+' separator to preserve spaces
user.searchString = `${user.firstName}+${user.lastName}+${user.address.street}+${user.address.city}+${user.notes}`
db.users.find({searchString: {$regex: 'mar', $options: 'i'}})
// returns Bob because 'mar' matches his notes field
// TODO write a client-side function to highlight the matching fragments
答案 10 :(得分:-4)
import re
db.collection.find({"$or": [{"your field name": re.compile(text, re.IGNORECASE)},{"your field name": re.compile(text, re.IGNORECASE)}]})