MongoDB 전체 및 부분 텍스트 검색
환경:
- MongoDB(3.2.0)와 Mongoose
컬렉션:
- 사용자
텍스트 색인 작성:
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" } } )
(는 false)=> FOUND (검색 는 false)db.users.find( { "$text" : { "$search" : "LEONÉL" } } )
(는 false)=> FOUND ('Sensitive'는 false)db.users.find( { "$text" : { "$search" : "LEONE" } } )
> )=> FOUND(찾아온 부분검색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
MongoDB 문서:
MongoDB 3.4에서와 같이 텍스트 검색 기능은 언어별 스톱워드 및 스트링 규칙을 사용하여 텍스트 콘텐츠에 대한 대소문자를 구분하지 않는 검색을 지원하도록 설계되었습니다.지원되는 언어에 대한 스팅 규칙은 일반적으로 공통 동사와 명사를 다루지만 고유 명사를 인식하지 못하는 표준 알고리즘에 기초한다.
부분 일치 또는 퍼지 일치에 대한 명시적인 지원은 없지만, 유사한 결과를 나타내는 용어가 그렇게 작동하는 것처럼 보일 수 있습니다.예를 들어 "taste", "tastes", "tasteul"은 모두 "taste"에서 유래합니다.Snowball Streaming 데모 페이지를 사용하여 더 많은 단어와 스탬핑 알고리즘을 실험해 보십시오.
일치하는 결과는 모두 같은 단어 "LEONEL"의 변형이며 대소문자 및 분음 부호만 다릅니다.선택한 언어의 규칙에 따라 "LEONEL"을 짧게 할 수 없는 한, 이 두 가지 유형의 변형만 일치합니다.
효율적인 부분 매칭을 수행하려면 다른 방법을 사용해야 합니다.도움이 되는 아이디어에 대해서는, 다음을 참조하십시오.
MongoDB 문제 트래커에 관련 개선 요청이 있습니다.SERVER-15090: 부분 단어 일치를 지원하도록 텍스트 색인을 개선합니다.
현재 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);
})
인덱스를 작성하지 않아도 간단하게 다음을 사용할 수 있습니다.
db.users.find({ name: /<full_or_partial_text>/i})
와 소문자를 구분하지 않음대소문자를 구분하지 않음)
MongoDB의 풀 텍스트 검색의 이점을 모두 활용하여 부분적인 일치(아마도 자동 완료)를 원하는 경우, Shrikant Prabhu가 언급한 n그램 기반의 접근방식이 저에게 적합한 솔루션이었습니다.물론 주행 거리가 다를 수 있으며, 대용량 문서를 인덱싱할 때는 이 방법이 실용적이지 않을 수 있습니다.
제 경우, 주로 부분적인 매치가 필요했습니다.title
필드(및 기타 몇 개의 짧은 필드)를 선택합니다.
엣지 n그램 방식을 사용했습니다.그게 무슨 의미죠?요컨대 끈을 돌리면"Mississippi River"
같은 끈으로"Mis Miss Missi Missis Mississ Mississi Mississip Mississipp Mississippi Riv Rive River"
.
Liu Gen의 코드에서 영감을 얻어 다음과 같은 방법을 생각해냈습니다.
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-그램으로 입력합니다.또, 작성한다."text"
의 인덱스searchTitle
들판.
그 후, 이 명령어는 제외합니다.searchTitle
투영을 사용하여 검색 결과에서 필드를 입력합니다.
db.collection('my-collection')
.find({ $text: { $search: mySearchTerm } }, { projection: { searchTitle: 0 } })
나는 @Ricardo Canelas의 답변을 여기 npm에 mongoose 플러그인으로 포장했다.
두 가지 변경 사항: - 약속 사용 - 유형이 있는 모든 필드에서 검색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;
사용.
// 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);)
변수를 사용하여 검색할 문자열 또는 값을 저장하는 경우:
다음과 같이 Regex와 함께 동작합니다.
{ collection.find({ name of Mongodb field: new RegExp(variable_name, 'i') }
여기서 I는 ignore-case 옵션용입니다.
빠르고 지저분한 해결책: 텍스트 검색을 먼저 사용하고 아무것도 발견되지 않으면 regexp를 사용하여 다른 쿼리를 만듭니다.두 가지 질문을 하고 싶지 않은 경우:$or
또한 동작하지만 쿼리의 모든 필드를 인덱싱해야 합니다.
또한 rx는 인덱스에 의존할 수 없으므로 대소문자를 구분하지 않는 것이 좋습니다.저 같은 경우에는 사용된 필드의 소문자 복사본을 만들었습니다.
퍼지 매칭을 위한 좋은 n그램 기반 접근법에 대해 설명합니다(또한 프리픽스 매칭을 사용한 결과에서 더 높은 점수를 얻는 방법에 대해서도 설명합니다). https://medium.com/xeneta/fuzzy-search-with-mongodb-and-python-57103928ee5d
주의: n그램 기반의 접근법은 저장공간이 넓을 수 있으며 mongodb 수집 사이즈가 증가합니다.
검색할 문서 내의 모든 필드를 결합하는 추가 필드를 만듭니다.그리고 regex를 사용합니다.
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
'순수한' 운석 프로젝트를 위한 몽고드B의 완전한/부분적인 탐색
Meteor-Collections와 simpleSchema를 사용하지만 mongoose를 사용하지 않도록 flash의 코드를 추가했습니다(means: remove the use of the meteor-Collections 및 simpleSchema)..plugin()
-외부턴schema.path
(플래시 코드에서 simpleSchema-attribute로 보여도 해결되지 않았습니다.)그리고 커서 대신 결과 어레이를 재시도합니다.
누군가에게 도움이 될 것 같아서 공유합니다.
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);
import re
db.collection.find({"$or": [{"your field name": re.compile(text, re.IGNORECASE)},{"your field name": re.compile(text, re.IGNORECASE)}]})
언급URL : https://stackoverflow.com/questions/44833817/mongodb-full-and-partial-text-search
'programing' 카테고리의 다른 글
같은 ASP에 대한 여러 AJAX 콜이 동시에 이루어지는 이유는 무엇입니까?NET MVC 작업으로 인해 브라우저가 차단됩니까? (0) | 2023.03.27 |
---|---|
복수의 디렉티브[ 디렉티브 #1, 디렉티브 #2]에 대해 격리된 범위를 요구하는 (0) | 2023.03.27 |
Wordpress REST API에서 카테고리/태그 목록을 검색하는 방법 (0) | 2023.03.27 |
React Formik 외부에서 submit Form 사용 (0) | 2023.03.27 |
노드/도커를 빌드할 때 uid/gid를 가져올 수 없습니다. (0) | 2023.03.27 |