빠르게 문자를 부분검색하고 싶으면 search index를 만들어둡시다.
다른 데이터베이스에서는 full text index라고 부르기도 합니다.
mongodb 사이트에 search 아니면 search index 어쩌구 버튼이 컬렉션 들어가보면 있을텐데 그거 누릅시다.
mongodb를 로컬하드에 설치해놓고 쓰는 분들은 아마 못쓸 수도 있습니다.
search index 동작원리
근데 그 전에 search index는 어떻게 만들어지는 것인지 궁금할까봐 잠깐 원리설명하자면
1. 일단 index를 만들 때 document에 있는 문장들을 가져와서 조사나 쓸데없는 불용어들을 다 제거합니다.
그러니까 and or the ~s 이런거 제거해준다는 소리입니다.
한국어도 비슷하게 을, 를, 이, 가, 그리고, 또는 이런걸 제거해줍니다.
2. 단어들을 다 뽑아서 정렬합니다.
그럼 앞으로 이 단어들을 되게 쉽게 빠르게 찾을 수 있겠죠?
3. 그 다음에 이 단어들이 어떤 document에 등장했는지 그 document id같은걸 함께 단어 옆에 기재해둡니다.
4. 그럼 이제 seafood라고 검색했을 때 오른쪽 index에서 빠르게 찾을 수 있는데
근데 이게 1번, 2번 document에 들어있었다고 기재해놨기 때문에 그 1번, 2번 document를 빠르게 가져올 수 있습니다.
이런 식으로 search index가 동작합니다.
- 예를 들어 seafood chicken 이렇게 검색하면
seafood랑 chicken 단어들이 어떤 document에 들어있는지 찾아본 다음에 전부 다 가져옴
- 찾아온 document가 좀 많으면 내부적으로 중요해보이는 document들을 맨 위로 올려서 가져와줍니다. 똑똑함
- 검색어 자동완성기능, 유사단어 검색기능 이런 것도 설정할 수 있습니다.
search index 만들기
사이트에서 search index 만들기 버튼이 어딘가 있을텐데 눌러봅시다.
▲ index 이름은 자유롭게 작명하고 어떤 컬렉션에 index 만들지 선택합시다.
▲ index를 커스터마이징 하는 부분이 있습니다
- 거기서 analyzer를 lucene.korean이라고 선택하고
- dynamic mapping 은 끄고
- field mapping 을 수정합시다.
lucene.korean이 뭐냐면 lucene이라는 라이브러리를 이용해서
한국어에 있는 쓸데없는 불용어나 조사를 제거해주라고 설정한 것입니다.
그래서 이거 선택하면 이제 '바보'라고 검색하면 '바보들' '바보가' '바보임' 이런게 다 검색가능
dynamic mapping 설정은 이 document에 있는 모든 필드들을 indexing할 것인지 선택하는 것입니다.
필드를 직접 설정하고 싶으면 끕시다.
▲ field mapping 수정버튼 누르면 어떤 필드에 index를 만들 것인지 설정할 수 있습니다.
저는 title에만 만들어봤습니다. 여러분은 다른 필드도 검색되게 만들고 싶으면 추가하셈
나머지는 뭐 따로 건드릴 필요없고 필요할 때 찾아서 적용해봅시다.
▲ 그럼 좀 기다리면 index가 생성되는데
query 버튼을 누르면 직접 검색 잘 되는지 테스트도 해볼 수 있습니다.
한국어 검색은 아주 완벽하지는 않긴 한데
검색엔진 서비스 만드는 것도 아닌데 이 정도면 일반 사이트에서 충분한 성능입니다.
그리고 score 라는 좋은 기능도 있는데
검색결과가 많을 경우 얘가 document마다 점수를 매겨줍니다.
단어가 document에 자주 출몰할 수록, 정확할 수록 score가 높아지고
높은 score 가진 document를 자동으로 위로 올려주기 때문에 검색 순위같은 것도 여러분이 구현할 필요 없음
서버에서 search index 쓰려면
본격적으로 서버에서 search index 이용해서 검색요청을 날리려면
.find() 대신 .aggregate() 를 보통 씁니다.
그냥 .find() 와 비슷한건데 여러 조건을 넣고 싶을 때 주로 사용합니다.
app.get('/search', async (요청, 응답) => {
let 검색조건 = [
{$search : {
index : '사용할 인덱스 이름',
text : { query : '검색어', path : '검색할 필드이름' }
}}
]
let result = await db.collection('post').aggregate(검색조건).toArray()
응답.render('search.ejs', { 글목록 : result })
})
귀찮으니 복붙해서 쓰고 필요한 부분만 바꿔서 씁시다.
1. aggregate() 안에는 [{조건1}, {조건2} … ] 이런 식으로 검색조건을 여러개 집어넣을 수 있습니다.
2. $search 연산자를 쓰면 search index를 이용해서 검색을 해옵니다.
3. $search 쓰려면 인덱스 이름, 검색어, 어떤 필드에서 검색할지 잘 채우면 끝입니다.
result 변수에 진짜로 검색결과 잘 가져오는지 출력해봅시다.
잘 나오면 ejs 파일에도 보내보쇼
여러가지 검색용 연산자
.aggregate([ ]) 안에 {조건} 이런걸 여러개 입력할 수 있다고 했는데
이런 것들도 넣을 수 있습니다.
let 검색조건 = [
{$search : {
index : '사용할 인덱스 이름',
text : { query : '검색어', path : '검색할 필드이름' }
}},
{ $sort : { _id : 1 } },
{ $limit : 10 },
{ $project : { 제목 : 1, _id : 0 } }
]
- $sort 쓰면 검색 결과를 정렬해주는데 _id를 기입하면 _id 순으로 정렬해줍니다.
안쓰면 기본적으로 score 순으로 정렬됨
- $limit쓰면 결과를 제한해줍니다. 검색결과 중에 맨 위의 10개 document만 가져올 수 있습니다.
당연히 { $skip : 5 } 이거 연산자도 쓸 수 있습니다. 그래서 이런거 쓰면 페이지네이션도 구현가능
- $project쓰면 찾아온 결과 중에 원하는 필드만 가져오라고 걸러줄 수 있습니다.
예를 들어 {title : 1, content : 0} 이러면 title은 보여줌, content는 숨김이라는 뜻입니다.
aggregate에서 쓸 수 있는 나머지 연산자들이 수십개 있는데 그런건 필요할 때 검색해서 쓰도록 합시다.
오늘의 응용사항 :
심심하면 집에 가서 검색결과 페이지네이션 기능도 한번 구현해봅시다.
처음엔 3개 게시물만 보여주고
다음 버튼 누르면 그 다음 3개 게시물 보여주고 그럽시다.
그러고 싶으면 skip 연산자랑 limit 연산자 잘 쓰면 되겠군요.
'Node.js' 카테고리의 다른 글
node>댓글기능 만들기 (document간의 종속) (0) | 2024.09.19 |
---|---|
node>게시판에 회원기능을 넣자 & 비정규화 (0) | 2024.09.19 |
node>검색기능 만들기 2 (index 설명) (0) | 2024.09.19 |
node>검색기능 만들기 1 (0) | 2024.09.19 |
node>AWS에 Node.js 서버 배포하기 (Elastic Beanstalk) (0) | 2024.09.19 |