JPQL 쿼리로 직접 검색 방식 정하기
@Repository
public interface ElasticBookSearchRepository extends ElasticsearchRepository<ElasticsearchBook, Long>, ElasticCustomBookSearchRepository {
// 이건 개발자가 직접 연산부를 작성하는 방식임
// 제어권이 개발자에 있음
//List<ElasticsearchBook> findByBookNameContains(String keyword);
// Elasticsearch API의 쿼리문을 작성해서 변경해보는 방법
// 이건 제어권이 Spring Data Elasticsearch 에 있음
// 쿼리를 SPE가 분석함
//QueryStringQuery - 엘라스틱서치 내장 기본 쿼리
@Query("{\"query_string\": {\"query\": \"?0\"}}")
List<ElasticsearchBook> findByQueryStringQuery(String queryString, Pageable pageable);
//조건검색(bool)
//@Query("{\"bool\": {\"must\": {\"query_string\": {\"query\": \"?0\"}}}}")
//List<ElasticsearchBook> findByQueryStringQuery(String queryString, Pageable pageable);
/*
must: [필드] AND [컬럼] = [조건]
must_not: [필드] AND [컬럼] != [조건]
should: [필드] OR [컬럼] = [조건]
filter: [필드] [컬럼] IN ( [조건] )
*/
}
Java
복사
아래 쿼리를 @Query 어노테이션을 통해서 작성하여 검색의 방식을 변경 할 수 있다. 기존 직접 내부 로직을 구성하는 방식과 다르게, 마치 JPQL을 사용하는것 처럼 쿼리문을 작성 할 수 있다. 그럼 마찬가지로 쿼리문이 컴파일 단계에서 검사되지 않는 단점이 있을 것으로 보여진다.
QueryDSL로 구현해보기
build.gradle 의존성 추가
dependencies {
implementation 'com.querydsl:querydsl-apt:4.x.x'
implementation 'com.querydsl:querydsl-elasticsearch:4.x.x'
}
Java
복사
QueryDSL 플러그인 및 태스크 설정
plugins {
id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"
}
querydsl {
jpa = false
querydslSourcesDir = 'src/main/generated'
}
sourceSets {
main {
java {
srcDir 'src/main/generated'
}
}
}
configurations {
querydsl.extendsFrom compileClasspath
}
compileQuerydsl {
options.annotationProcessorPath = configurations.querydsl
}
Java
복사
ElasticBookRepository를 상속받은 ElasticBookSearchRepositoryImpl.java 구현체에서 다음처럼 구현한다. Q객체를 통해서 쿼리팩토리를 통해 쿼리를 메소드형태로 작성하게 된다. 이러면 컴파일 단계에서 개발자의 오류를 찾을 수 있다.
import com.querydsl.core.types.dsl.StringPath;
import com.querydsl.elasticsearch.ElasticsearchQuery;
import com.querydsl.elasticsearch.ElasticsearchQueryFactory;
public class ElasticBookSearchRepositoryImpl {
private final ElasticsearchQueryFactory queryFactory;
public ElasticBookSearchRepositoryImpl(ElasticsearchQueryFactory queryFactory) {
this.queryFactory = queryFactory;
}
public ElasticsearchQuery<ElasticsearchBook> findByQueryStringQuery(String queryString, Pageable pageable) {
QElasticsearchBook elasticsearchBook = QElasticsearchBook.elasticsearchBook;
StringPath bookName = elasticsearchBook.bookName;
return queryFactory
.selectFrom(elasticsearchBook)
.where(bookName.contains(queryString))
.offset(pageable.getOffset())
.limit(pageable.getPageSize());
}
}
Java
복사
아래는 JPQL로 직접 쿼리를 보낼 때의 각 검색 API이다.
1. 검색API Template
{
"size": "[페이징]몇개의 결과를 반환할지 결정한다(default: 10)",
"from": "[페이징]어느 위치부터 반환할지 결정한다.(defalut: 0)",
"timeout": "제한시간을 설정하여 제한시간까지 조회된 문서만 조회한다.(default: 무한대)",
"_source": {
// 특정필드만 결과로 반환받고 싶을때 정의한다},
"sort": {
// 특정필드를 기준으로 정렬할지 정의한다},
"query": {
/*
* [전문검색]
* 검색될 조건을 정의한다.
* 분석기에 의해 분석.
*/},
"filter": {
/*
* [조건검색]
* 검색 결과 중 특정한 값을 다시 보여준다
* 결과 내에서 재검색 때 사용하는 기능 중 하나다.
* 다만 필터를 사용하게 되면 자동으로 score 값이 정렬되지 않는다.
* (_score 정렬을 추가하면 가능하다.)
*/},
"aggs": {
// 통계 또는 집계데이터 사용시 정의한다.}
}
JSON
복사
2. 검색 파라메터
2.1 Multi Index검색
# 특정인덱스 정의
POST [인덱스1],[인덱스2]/_search
# *(와일드카드) 사용
POST [인덱스명]*/_search
Shell
복사
문법 | 연산자 |
lt | < |
gt | > |
lte | <= |
gte | >= |
POST [인덱스]/_search
{
"query": {
"range": {
"필드명": {
"gte": "2016",// >= 2016"lte": "2017"// <= 2017}
}
}
}
JSON
복사
2.3 operator
•
AND조회
POST [인덱스]/_search
{
"query": {
"match": {
"필드명": {
"query": "자전차왕 엄복동",
"operator": "and"}
}
}
}
/*
OR연산 결과
자전차왕 엄복동
자전차왕 홍길동
오토바이왕 엄복동
....
AND연산 결과
자전차왕 엄복동
자전차왕 엄복동 김복동
...
*/
JSON
복사
2.4 minimum_should_match
•
매치 최소개수 지정
2.5 boost
•
특정필드에 가중치 부여하여 score을 높이고 상위에 노출하도록 함
3. 문자열 검색
match와 term의 차이
query | 필드 타입 | 설명 |
match | text | 쿼리를 수행하기전에 분석기를 통해 텍스트르 분석한 후 검색을 수행. text 데이터타입을 검색할때 match를 사용. |
term | keyword | 별도의 분석작업을 수행하지않고 입력된 텍스트가 존재하는 문서를 찾음. keyword 데이터타입을 검색할때 term를 사용. |
query_string | - | 엘라스틱서치에 내장된 쿼리분석기로 검색. |
3.1 match
3.1.1 match_all
•
색인된 모든문서 조회
POST [인덱스]/_search
{
"query": {
"match_all": {}
}
}
JSON
복사
3.1.2 match / multi_match
•
문장을 형태소 분석을 통해 텀으로 분리 후 텀으로 검색질의 수행.
•
검색어가 분석되야 할 경우 사용한다.
POST [인덱스]/_search
{
"query": {
"match": {
"[필드]": "[검색할 키워드]"}
}
}
POST [인덱스]/_search
{
"query": {
"multi_match": {
"query": "[검색할 키워드]",
"fields": ["[필드1]", "[필드2]"]
}
}
}
JSON
복사
3.2 term
•
정확히 매칭되지않거나, 대소문자가 다른경우 조회되지 않음.
POST [인덱스]/_search
{
"query": {
"term": {
"[필드]": "[검색할 키워드]"}
}
}
JSON
복사
3.3 query_string
•
엘라스틱서치에 기본적으로 내장된 쿼리분석기로 검색
// 전체검색
POST [인덱스]/_search
{
"query": {
"query_string": {
"query": "[검색할 키워드]"}
}
}
// 필드검색
POST [인덱스]/_search
{
"query": {
"query_string": {
"default_field": "[필드명]",
"query": "[검색할 키워드]"}
}
}
JSON
복사
4. 조건검색
4.1 bool
•
AND, OR, IN 조건을 설정한다.
ElasticSearch | SQL |
must: [필드] | AND [컬럼] = [조건] |
must_not: [필드] | AND [컬럼] != [조건] |
should: [필드] | OR [컬럼] = [조건] |
filter: [필드] | [컬럼] IN ( [조건] ) |
조건 :
장르 = '코미디'
AND 국가 = '한국'
AND 타입 != '단편'
POST [인덱스]/_search
{
"query": {
"bool": {
"must": [
{
"term": {
"장르": "코미디"}
},
{
"match": {
"국가": "한국"}
}
],
"must_not": [
{
"match": {
"타입": "단편"}
}
]
}
}
}
JSON
복사
5. 그외..
5.1 Prefix Query
•
접두어가 있는 문서를 검색하고자 할때
5.2 Exists Query
•
실제 값이 존재하는 문서만 찾고싶을때.
5.3 Wildcard Query
•
와일드카드와 일치하는 구문을 찾는다.
5.4 Nested Query
•
부모자식 관계 형태(Object)의 데이터타입인 Nested타입 필드 검색할때 사용.
5.5 Multi Search API
•
여러건의 검색을 한번에 요청할때
POST _msearch
{"index" : "[인덱스1]"}
{
"query" : {
// 조건},
"from": 0,
"size": 10}
{"index" : "[인덱스2]"}
{
"query" : {
// 조건},
"from": 0,
"size": 10}
JSON
복사
5.6 Count API
•
문서 개수 조회
POST [인덱스]/_count
{
"query" : {
// 조건}
}
JSON
복사
5.7 Explain API (_score)
•
_score값이 어떻게 계산된것인지 확인하고싶을때 사용.
5.8 Profile API (튜닝/디버깅)
•
쿼리에 대한 상세한 수행계획과 수행계획별 수행된 시간 조회.