Node.js +_MongoDB 검색3(colection.aggregate(), $regex)
(참고) 프론트엔드에서 Query string을 쉽게 만들려면
jQuery 문법 중에 param, serialize를 쓰면 됩니다.
var 자료 = { 이름1 : '값1', 이름2 : '값2' }
$param(자료) //이름1=값1&이름2=값2 이거가 남음
이렇게 하면 object 자료를 query string으로 쉽게 변환가능하고
$(폼태그를찾고).serialize()
이렇게 하면 폼태그 안에 있는 모든 <input>을 query string으로 쉽게 변환해줍니다.
다만 <input>에 name 속성이 있어야합니다.
인덱스를 활용하여 검색하려면
정확한 문법을 쓰셔야합니다.
app.get('/search', (요청, 응답)=>{
console.log(요청.query);
db.collection('post').find( { $text : { $search: 요청.query.value }} ).toArray((에러, 결과)=>{
console.log(결과)
응답.render('search.ejs', {posts : 결과})
})
})
find() 안에 저렇게 $text 어쩌구로 시작하시면 만들어둔 text 인덱스에서 검색이 가능합니다.
이렇게 기능개발해놓으면 간단한 검색엔진처럼 검색도 가능한데
검색창에
이닦기 글쓰기라고 검색하면 이닦기 or 글쓰기가 포함된 모든 문서를 찾아줌
이닦기 -글쓰기라고 검색하면 이닦기인데 글쓰기라는 단어 제외 검색
"이닦기 글쓰기" 라고 검색하면 정확히 이닦기 글쓰기라는 phrase가 포함된 문서 검색
이렇게 가능합니다.
심각한 단점
글쓰기라고 검색하라면 글쓰기입니다~ 이런 문장은 못찾아줍니다.
영어는 상관없는데 영어가 아닌 언어들은 그래서 text search 기능을 쓸 수가 없습니다.
그래서 그냥 영어서비스 개발할거면 쓰시고 아니라면 지웁시다.
그럼 100만개에서 '글쓰기'라는 단어가 포함된 문서를 검색해야하면 어떻게 하죠 ㄷㄷ
해결책 1. 검색할 문서의 양을 제한을 둡니다.
DB에다가 검색요청을 날릴 때 특정 날짜에서만 검색하라고 요구할 수도 있고
skip(), limit() 이런 함수를 이용하시면 pagination 기능을 개발할 수 있습니다.
그니까 맨 처음 검색할 땐 맨앞에 20개만 찾아줘~
그 다음엔 다음 20개를 찾아줘~
이렇게 요구할 수 있다는 겁니다. 대부분의 게시판들은 이런 방법을 이용합니다.
해결책 2. text search 기능을 굳이 쓰고 싶으면
MongoDB를 님들이 직접 설치하셔야합니다.
그리고 indexing할 때 띄어쓰기 단위로 글자들을 indexing하지말고
다른 알고리즘을 써라~ 라고 셋팅할 수 있습니다.
nGram 이런 알고리즘을 쓰면 된다고 하는데 이걸 언제하고 있습니까 패스합시다
해결책 3. Search index를 사용합니다.(글자마다 잘라서 index만들기)
MongoDB Atlas에서만 제공하는 기능인데
클러스터 들어가보시면 아마 Search 어쩌구라는 메뉴가 있을겁니다. 그거 누르시면 됩니다.
▲ 그러면 Search index라는걸 만들 수 있습니다.
전에 만든 text index랑 비슷한 기능을 제공하는데
아무튼 이름 잘 지어서 만들어주십시오.
▲ index 이름은 자유 작명이고
어떤 collection에 있는 항목을 indexing 할건지 선택하면 됩니다.
▲ 그리고 Analyzer를 설정하는 부분이 있습니다.
이걸 lucene.korean으로 바꿔주시면 똑똑하게 한국어에 딱 맞게 인덱싱을 해줍니다.
lucene이 뭐냐면 그 형태소분석기 이런건데 한국어는 쓸데없는 조사 이런게 붙지 않습니까
글쓰기를
글쓰기입니다
글쓰기지만
글쓰기라도
이런 식으로 단어 뒤에 쓸데없는 조사가 붙는데 이걸 다 제거하고 필요한 단어만 남긴다고 보시면 됩니다.
아무튼 이렇게 하시면 Search index를 만들 수 있습니다. 끝
Q. Atlas만 되는건가요?
A. 다른 DB호스팅 서비스들도 이런 유사한 기능이 있을겁니다.
Search index 이용해서 검색요청하는 법
app.get('/search', (요청, 응답)=>{
var 검색조건 = [
{
$search: {
index: '님이만든인덱스명', // 위에서 만든 인덱스이름(titleSearch)
text: {
query: 요청.query.value,
path: '제목' // 제목날짜 둘다 찾고 싶으면 ['제목', '날짜']
}
}
}
]
console.log(요청.query);
db.collection('post').aggregate(검색조건).toArray((에러, 결과)=>{
console.log(결과)
응답.render('search.ejs', {posts : 결과})
})
})
aggregate() 함수를 쓰는데 이건 검색조건 여러개를 붙이고 싶을 때 유용한 함수입니다.
aggregate() 안에 [ {검색조건1}, {검색조건2} ... ] 이렇게 조건을 여러개 집어넣을 수 있습니다.
지금은 하나만 집어넣어봄
그리고 연산자인 $search를 넣으면 search index에서 검색이 된다고 하는군요.
뭔가 길어보이지만 search index쓰는 방법을 그대로 카피해서 썼을 뿐입니다. 이것도 원리이해보다는 복붙의 영역임
아무튼 저렇게 쓰시면 '글쓰기' 라고 검색했을 때 '글쓰기합니다~' 이런 문장들도 잘 검색해줍니다. 끝
여러가지 검색용 연산자
var 검색조건 = [
{
$search: {
index: '님이만든인덱스명',
text: {
query: 요청.query.value,
path: '제목' // 제목날짜 둘다 찾고 싶으면 ['제목', '날짜']
}
}
},
{ $sort : { _id : 1 } },
{ $limit : 10 },
{ $project : { 제목 : 1, _id : 0 } }
]
aggregate() 안에 [ {검색조건1}, {검색조건2} ... ] 이렇게 여러개 넣을 수 있댔는데
그래서 여러개 저렇게 넣으시면 됩니다.
$sort를 쓰면 결과를 정렬해서 가져옵니다. _id를 오름차순으로 정렬해주세요~ 라고 썼습니다.
$limit을 쓰면 결과를 제한해줍니다. 맨위의 10개만 가져오라고 시켰습니다.
$project를 쓰면 찾아온 결과 중에 원하는 항목만 보여줍니다. 0은 안보여주고 1은 보여주라는 뜻입니다. 위의 코드는 _id는 빼고 제목만 가져오겠군요.
이 외에도 백만개의 $연산자가 있다고 합니다.
이걸 다 어떻게 외움 필요할 때 찾아서 씁니다.
단점
한글은 포함검색 가능하지만 영어는 안된다.
다른 방법
db에 데이터가 많아 속도가 느릴순 있지만 영어 한글 다호환이 가능한 코드이다.
즉 이코드는 하나하나 다확인을 하여 느리다 그리하여 검색 갯수의 제한을 주던가 할것이다.
app.get('/search', (요청, 응답)=>{
var 검색조건 = {
'제목' : {$regex : 요청.query.value , $options:'i'}
}
console.log(요청.query);
db.collection('post').find(검색조건).toArray((에러, 결과)=>{
console.log(결과)
응답.render('search.ejs', {posts : 결과})
})
})