Full Text Search
개요
포털사이트에서 검색을 하면 그 검색어랑 완전히 일치하는 글만 나오는 것이 아니라 포함되어있는 글들도 나온다.
이는 MySQL의 like 문법을 사용해 구현할 수 있다.
그러나 한 가지 한계점이 있다. SELECT 문법의 WHERE 절의 조건으로 사용되는 속성에 인덱스를 걸어 사용하는 경우가 일반적인데 이렇게 일치하는 결과만 뽑는 것이 아니라 포함하고 있는 결과도 뽑으려면 와일드 카드를 이용해야한다.
예를 들면 이렇다.
SELECT * FROM post WHERE title LIKE '%test%'
이렇게 하면 testPost, Potestst… 등등 제목에 test를 포함하고 있는 결과들이 출력된다.
쿼리가 실행될 때 DB의 쿼리 옵티마이저가 판단해서 인덱스를 사용하는 것이 빠른 경우, 인덱스를 사용할 수 있는 경우 등등을 파악해서 쿼리를 실행시킨다. 인덱스 테이블은 원래 값의 첫 글자부터 시작해서 정렬된다. 그러나 첫 글자가 무엇인지 알 수 없다면 인덱스를 사용할 수 없다.
따라서 와일드 카드가 앞뒤로 붙은 경우 인덱스를 타지 않고 테이블을 풀 스캔한다.
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | club | NULL | ALL | NULL | NULL | NULL | NULL | 9718 | 11.11 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
인덱스가 존재함에도 possible_keys
가 null
인 걸 볼 수 있다.
이는 좋지 못한 쿼리에 해당한다.
Full Text Search
그럼 이러한 부분 검색에 인덱스를 사용하여 검색 성능을 높이듯이 성능을 개선할 수 있는 방법이 뭐가있을까?
Mysql에서는 문자열 검색을 위해 Full Text Search를 지원한다.
이는 단어나 구문을 잘게 쪼개어 인덱스를 생성하는 방법이다.
검색 키워드와 관련이 있는 데이터들도 뽑아낼 수 있다.
Full Text Search에 대해 알아보자.
Full Text Search란 하나의 문자열을 일정한 규칙으로 쪼개어 사용하는 것을 말한다.
예를 들어 “안녕하세요”라는 문자열이 있다면 이를 “안녕”, “녕하”, “하세”, “세요”와 같이 쪼개는 것이다. 이와 같은 방식을 N-gram이라고 하며 n은 문자열을 몇 개의 문자 크기로 쪼갤 것인지를 의미한다. 위와 같은 경우는 n이 2가 된다.
이 방법은 N-gram Parser를 이용한 방법이다.
또 다른 방법은 중지어를 기준으로 쪼개는 것이다.
“자바와 파이썬의 차이점에 대해 알아봅시다”와 같은 문자열이 있다고 해보자.
우선 띄어쓰기를 기준으로 문자를 쪼개볼 수 있다. 그리고 큰 의미없이 자주쓰여서 식별 값으로 적절하지 않은 단어가 있다.
예를 들어 기술서적에 “따라서”가 등장하는 페이지가 몇백개 있는데 “따라서”와 같은 단어도 맨 뒤 찾아보기 페이지에 적어놓았다고 해보면 그 것만큼 의미없는 것이 없다. 이를 중지단어라고 한다.
이 방법은 Built-in Parser를 이용한 방법이다.
이렇게 인덱스를 생성하고 해당하는 자료를 빠르게 찾을 수 있도록 MySQL에서 지원하는 기능이 바로 Full Text Search다.
이제 인덱스를 직접 생성해보자. 테이블을 만들 때 정의해줄 수도 있지만 공식문서에 따르면 큰 데이터셋을 사용할 경우 테이블을 만들 때 full text index를 같이 정의하고 데이터를 넣는 것 보다 데이터를 넣고 인덱스를 만드는 것이 훨씬 빠르다고한다.
For large data sets, it is much faster to load your data into a table that has no FULLTEXT index and then create the index after that, than to load data into a table that has an existing FULLTEXT index.
ALTER TABLE 테이블명 ADD FULLTEXT 인덱스이름 (필드명) WITH PARSER ngram;
create문으로도 생성이 가능하다.
CREATE FULLTEXT INDEX 인덱스명 on 테이블명(필드명) WITH PARSER ngram;
생성된 인덱스를 확인하려면 다음 명령어를 입력하면 된다.
SET GLOBAL innodb_ft_aux_table = 'DB명/테이블명';
SELECT * FROM information_schema.innodb_ft_index_table;
DROP INDEX 인덱스 이름 ON 테이블 명;
만약 데이터가 영어로 구성되어있었다면 생성된 인덱스 목록에서 in, or과 같은 단어는 빠져있다. 중지단어이기 때문이다.
중지단어는 디폴트로 생성되어있고 in, or, and 같은 단어들이 등록되있다. 만약 커스텀하여 중지단어를 생성하고 싶은 경우 다음의 명령어를 사용하면 된다.
CREATE TABLE user_stopword (value VARCHAR(30));
INSERT INTO user_stopword values('그리고'), ('그러나');
set global innodb_ft_server_stopword_table = '디비 명/중지단어 테이블 명';
show global variables like 'innodb_ft_server_stopword_table' ;
Full Text Index를 사용하는 방법은 3가지가 있다.
첫 번째론 자연어 검색이다. 특별한 옵션을 지정하지 않았을 때 디폴트로 적용되는 검색 방법으로 각 단어들이 얼마나 많이 존재하는지를 측정해 가중치가 계산되고 적게 존재할수록 높은 가중치를 가진다. 이 가중치에 따라 순서대로 정렬된다. 사람이 읽고 해석하는 방식과 비슷하게 동작한다는 안내가 있다.
자연어 검색은 의미 기반의 검색으로 단순히 일치하는 단어를 찾는 것 뿐만 아니라 관련된 개념을 포괄적으로 처리할 수 있다.
예를 들어 오만과 편견이라는 검색어를 입력하면 다른 키워드나 주제를 포함한 결과도 포함되어 나올 수 있다. 이를 통해 사용자 경험을 향상시키고 연관 검색어를 산출하는데 적합하다.
두 번째론 불리언 검색이다.
불리언 검색은 보다 정밀한 규칙을 적용하여 검색할 수 있다.
자연어 검색은 일종의 해석이 들어가는 것과 달르게 불리언 검색은 정규표현식과 같이 검색어에 대한 결과를 규칙에 따라 제한할 수 있다.
예를 들어 “오만” “편견”이 둘 다 포함되어있는 결과를 찾고싶다면 +오만 +편견 과 같이 검색하면 이 둘이 동시에 포함되어있는 결과를 찾는다.
“+오만과 -편견”와 같이 검색하면 오만과는 반드시 포함하고 편견은 포함하지 않아 검색결과가 없다.
만약 “오만과 편견”을 있는 그대로 포함하는 결과를 얻고싶다면 이를 하나의 단어로 취급해야한다. ‘“오만과 편견”‘이렇게 종류가 다른 따옴표로 감싸면 된다.
+
: 검색어의 앞에 붙이며 해당 단어가 반환되는 행에 반드시 포함되어야함을 의미한다.-
: 검색어의 앞에 붙이며 반환되는 행에 검색어가 포함되지 않아야함을 의마한다.연산자 없음
: 디폴트로 적용되며 검색어의 일치 여부는 옵셔널하다. 검색어를 포함한 행은 높은 가중치를 적용받는다. IN BOOLEAN MODE를 사용하지 않은 것처럼 동작한다.> <
: 가중치를 낮추거나 높인다.()
: 검색어를 하위 표현식으로 그룹화한다.~
: 부정 연산자의 역할을 하며 검색어의 관련성에 대한 단어의 기여도를 음수로 만든다. 이렇게 하면 다른 단어보다 낮은 평가를 받지만 -기호와는 다르게 완전히 제거되진 않는다.*
: 와일드카드에 해당하며 단위의 앞에 붙일 수 없고 뒤에 붙여 해당 검색어로 시작하는 데이터를 검색할 때 사용한다. 만약 최소 단어의 길이가 4라면'+word +the*'
는'+word +the'
보다 적은 결과를 리턴한다. 왜냐하면 두 번째 쿼리는 너무 짧은 단어인 the를 무시하기 때문이다."
: 만약 검색어가 단어가 아닌 문장일 경우 정확히 일치하는 데이터를 찾지 못할 수 있다. 따라서 이를 정의되어있는 토큰 사이즈로 분할하여 인덱스 테이블에서 검색한다. ‘“test phrase”‘는 “test, phrase”와 일치하는 것으로 취급된다. 일치 항목이 구문과 정확히 동일한 단어와 동일한 순서로 포함되는 결과를 리턴한다는 의미이다.
만약 검색어가 인덱스에 등록되어있지 않다면 결과는 비어있게된다. 단어가 인덱스에 등록되지 않는 경우는 등록된 적이 없는 경우, 중지어인 경우, 지정된 토큰 사이즈보다 작은 경우다.
예를 들어 ‘apple banana’를 검색할 경우 최소 하나 이상을 포함한 결과를 리턴한다.
'+apple +juice'
의 경우 두 단어를 모두 포함한 결과를 리턴한다.
'+apple macintosh'
의 경우 apple은 반드시 포함하는 결과가 리턴되고 macintosh 또한 포함한 결과라면 높은 우선순위를 갖게된다.
'+apple -macintosh'
의 경우 apple은 포함하되 macintosh는 포함하지 않는 결과를 리턴한다.
'+apple ~macintosh'
위에서 본대로 단어의 우선 순위를 낮게 만들어서 apple이 포함되어있고 말하자면 macintosh가 드물게 포함되어있는 결과를 리턴한다.
'+apple +(>turnover <strudel)'
는 반드시 apple을 포함하고 apple turnover의 가중치는 높게, apple strudel의 가중치는 낮게 만들어 조회한 결과를 리턴한다.
'+apple*'
의 경우 apple, apples, applet등의 결과를 리턴한다.
'"some words"'
의 경우 ““안의 검색어와 정확히 일치하는 결과를 반환한다. 예를 들어 “some words of wisdom”은 일치하는 것으로 보지만 “some noise words”는 아니다.
“string”이라는 값이 테이블에 있고 토큰 사이즈가 2인 경우 s를 검색어로 넣었을 경우 토큰 사이즈보다 작기때문에 겸색결과는 비어있다. 또한 in으로 검색했을 경우에도 중지어기 때문에 검색 결과가 없다.
‘“오만과 편견”‘으로 검색했을 경우 하나의 단위로 묶여 검색된다. “재미없는 오만과 편견”도 나온다. 그러나 쌍따옴표로 감싸지 않고 검색을 했을 경우 각 단어의 일치 여부만 판단하는 것으로 보인다. 리턴 결과는 편견과 오만, 오만과 편견 등이 나온다.
문법
Full Text Search의 문법은 MATCH..AGAINST
이다.
일반적인 select where 절에서 다음과 같이 작성해주면 된다.
SELECT...FROM...
WHERE MATCH(칼럼) AGAINST('keyword' NATURAL LANGUAGE MODE);
SELECT...FROM...
WHERE MATCH(칼럼) AGAINST('keyword' BOOLEAN MODE);
테스트
데이터는 총 10001건
1부터 순차적으로 증가하는 10000개의 타이틀 데이터와 full text index를 확인하기 위해 문자열로 구성된 자료 1건 사용
ngram parser를 이용했으며 token_size는 2로 설정함.
쿼리 성능은 EXPLAIN ANALYZE 키워드를 사용함.
10번씩의 쿼리를 날려 측정함.
Full Text Search를 적용하지 않았을 때
-> Filter: (club.title like '%500%') (cost=1029 rows=1116) (actual time=0.443..7.55 rows=20 loops=1)
-> Table scan on club (cost=1029 rows=10043) (actual time=0.0536..5.8 rows=10001 loops=1)
——
> Filter: (club.title like '%500%') (cost=1029 rows=1116) (actual time=0.517..7.81 rows=20 loops=1)
-> Table scan on club (cost=1029 rows=10043) (actual time=0.0726..5.97 rows=10001 loops=1)
——
-> Filter: (club.title like '%500%') (cost=1029 rows=1116) (actual time=0.482..7.91 rows=20 loops=1)
-> Table scan on club (cost=1029 rows=10043) (actual time=0.0716..6.04 rows=10001 loops=1)
——
-> Filter: (club.title like '%500%') (cost=1029 rows=1116) (actual time=0.533..7.71 rows=20 loops=1)
-> Table scan on club (cost=1029 rows=10043) (actual time=0.0705..5.93 rows=10001 loops=1)
——
-> Filter: (club.title like '%500%') (cost=1029 rows=1116) (actual time=0.424..7.06 rows=20 loops=1)
-> Table scan on club (cost=1029 rows=10043) (actual time=0.0554..5.44 rows=10001 loops=1)
——
-> Filter: (club.title like '%500%') (cost=1029 rows=1116) (actual time=0.429..7.44 rows=20 loops=1)
-> Table scan on club (cost=1029 rows=10043) (actual time=0.0534..5.73 rows=10001 loops=1)
——
-> Filter: (club.title like '%500%') (cost=1029 rows=1116) (actual time=0.445..7.63 rows=20 loops=1)
-> Table scan on club (cost=1029 rows=10043) (actual time=0.0775..5.9 rows=10001 loops=1)
——
-> Filter: (club.title like '%500%') (cost=1029 rows=1116) (actual time=0.555..7.68 rows=20 loops=1)
-> Table scan on club (cost=1029 rows=10043) (actual time=0.0681..5.9 rows=10001 loops=1)
——
-> Filter: (club.title like '%500%') (cost=1029 rows=1116) (actual time=0.438..7.89 rows=20 loops=1)
-> Table scan on club (cost=1029 rows=10043) (actual time=0.0531..5.99 rows=10001 loops=1)
——
-> Filter: (club.title like '%500%') (cost=1029 rows=1116) (actual time=0.528..7.78 rows=20 loops=1)
-> Table scan on club (cost=1029 rows=10043) (actual time=0.0743..5.98 rows=10001 loops=1)
10번의 쿼리를 날린 결과 Filter 평균 소요 시간 7.3466ms Table Scan on club 평균 소요시간: 5.98165ms
| EXPLAIN |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -> Filter: (match club.title against ('+500' in boolean mode)) (cost=0.35 rows=1) (actual time=0.0192..0.0517 rows=20 loops=1)
-> Full-text index search on club using ft_index (title='+500') (cost=0.35 rows=1) (actual time=0.0181..0.0482 rows=20 loops=1)
|
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> mysql> explain analyze select * from club where match(title) against ('+500' in boolean mode );
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -> Filter: (match club.title against ('+500' in boolean mode)) (cost=0.35 rows=1) (actual time=0.0594..0.183 rows=20 loops=1)
-> Full-text index search on club using ft_index (title='+500') (cost=0.35 rows=1) (actual time=0.0575..0.176 rows=20 loops=1)
|
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> explain analyze select * from club where match(title) against ('+500' in boolean mode );
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -> Filter: (match club.title against ('+500' in boolean mode)) (cost=0.35 rows=1) (actual time=0.036..0.0817 rows=20 loops=1)
-> Full-text index search on club using ft_index (title='+500') (cost=0.35 rows=1) (actual time=0.035..0.0777 rows=20 loops=1)
|
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.01 sec)
mysql> explain analyze select * from club where match(title) against ('+500' in boolean mode );
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -> Filter: (match club.title against ('+500' in boolean mode)) (cost=0.35 rows=1) (actual time=0.0207..0.0634 rows=20 loops=1)
-> Full-text index search on club using ft_index (title='+500') (cost=0.35 rows=1) (actual time=0.0195..0.0593 rows=20 loops=1)
|
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> explain analyze select * from club where match(title) against ('+500' in boolean mode );
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -> Filter: (match club.title against ('+500' in boolean mode)) (cost=0.35 rows=1) (actual time=0.0202..0.0672 rows=20 loops=1)
-> Full-text index search on club using ft_index (title='+500') (cost=0.35 rows=1) (actual time=0.0192..0.0586 rows=20 loops=1)
|
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> explain analyze select * from club where match(title) against ('+500' in boolean mode );
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN |
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -> Filter: (match club.title against ('+500' in boolean mode)) (cost=0.35 rows=1) (actual time=0.021..0.0636 rows=20 loops=1)
-> Full-text index search on club using ft_index (title='+500') (cost=0.35 rows=1) (actual time=0.02..0.0595 rows=20 loops=1)
|
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> explain analyze select * from club where match(title) against ('+500' in boolean mode );
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -> Filter: (match club.title against ('+500' in boolean mode)) (cost=0.35 rows=1) (actual time=0.0216..0.0639 rows=20 loops=1)
-> Full-text index search on club using ft_index (title='+500') (cost=0.35 rows=1) (actual time=0.0202..0.0595 rows=20 loops=1)
|
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> explain analyze select * from club where match(title) against ('+500' in boolean mode );
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -> Filter: (match club.title against ('+500' in boolean mode)) (cost=0.35 rows=1) (actual time=0.0208..0.0634 rows=20 loops=1)
-> Full-text index search on club using ft_index (title='+500') (cost=0.35 rows=1) (actual time=0.0196..0.0592 rows=20 loops=1)
|
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> explain analyze select * from club where match(title) against ('+500' in boolean mode );
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -> Filter: (match club.title against ('+500' in boolean mode)) (cost=0.35 rows=1) (actual time=0.0212..0.0633 rows=20 loops=1)
-> Full-text index search on club using ft_index (title='+500') (cost=0.35 rows=1) (actual time=0.0198..0.0589 rows=20 loops=1)
|
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> explain analyze select * from club where match(title) against ('+500' in boolean mode );
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -> Filter: (match club.title against ('+500' in boolean mode)) (cost=0.35 rows=1) (actual time=0.0376..0.0855 rows=20 loops=1)
-> Full-text index search on club using ft_index (title='+500') (cost=0.35 rows=1) (actual time=0.0364..0.0812 rows=20 loops=1)
|
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> explain analyze select * from club where match(title) against ('+500' in boolean mode );
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN |
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -> Filter: (match club.title against ('+500' in boolean mode)) (cost=0.35 rows=1) (actual time=0.041..0.164 rows=20 loops=1)
-> Full-text index search on club using ft_index (title='+500') (cost=0.35 rows=1) (actual time=0.039..0.156 rows=20 loops=1)
|
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> explain analyze select * from club where match(title) against ('+500' in boolean mode );
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -> Filter: (match club.title against ('+500' in boolean mode)) (cost=0.35 rows=1) (actual time=0.0205..0.0629 rows=20 loops=1)
-> Full-text index search on club using ft_index (title='+500') (cost=0.35 rows=1) (actual time=0.0193..0.0587 rows=20 loops=1)
|
+-------------------------------------------------------------
Filter 평균 소요 시간 : 0.08849ms Full-text index search 평균 소요 시간 0.3593ms
Filter 평균 소요 시간 약 98.79% 단축. 검색 평균 소요시간 약 94.01% 단축
쿼리가 입력되고 결과를 출력할 때까지 소요시간 약 96.643% 단축
Springboot에서 Customfunction 사용하기
이렇게 특정 DB에서 제공하는 기능이 유용하다면 우리의 프로젝트에서 어떻게 사용할 수 있을까?
하이버네이트는 특정 DB에 종속되지 않도록 개발되었다. 그 덕분에 다른 데이터베이스로 변경하려해도 크게 수정되는 부분이 없다. 단순히 다른 드라이버를 선언하고 사용하면 된다.
따라서 특정 DB의 기능을 사용하려면 별도의 함수를 정의해서 사용해야한다.
하이버네이트 5 이전에는 MariadbDialect와 같이 의존성을 추가하고 사용하면 됐지만 하이버네이트 6부터는 이렇게 사용할 수 없게되었다.
버전 6부터는 FunctionContributor인터페이스를 구현하여 사용해야한다.
public class CustomFunctionContributor implements FunctionContributor {
@Override
public void contributeFunctions(final FunctionContributions functionContributions) {
functionContributions.getFunctionRegistry().registerPattern();
}
}
위와 같이 사용한다. registerPattern함수에 인자를 넣어 커스텀 펑션을 작성할 수 있다.
인자는 차례대로 사용할 함수 이름, 쿼리, 리턴 타입으로 함수의 이름은 자바에서 메소드의 이름을 정의하듯이 사용자 지정 이름이다.
쿼리는 match (?1) against (?2 in boolean mode)
와 같이 작성하면 된다.
이 쿼리는 리턴 타입이 Double이다.