392 lines
13 KiB
Markdown
392 lines
13 KiB
Markdown
# **MariaDB 풀 텍스트 검색 (Full-Text Search) 완벽 가이드**
|
|
|
|
**풀 텍스트 검색(Full-Text Search, FTS)**은 일반적인 `LIKE` 연산자보다 강력한 검색 기능을 제공하는 MariaDB의 주요 기능 중 하나입니다.
|
|
대량의 텍스트 데이터를 빠르게 검색할 수 있도록 **인덱스를 활용한 고속 검색, 랭킹(관련성 점수) 기반 정렬, 논리 연산자 지원** 등의 기능을 제공합니다.
|
|
|
|
이 글에서는 **MariaDB의 풀 텍스트 검색 개념, 설정 방법, 사용법, 고급 기능**까지 단계별로 설명하겠습니다.
|
|
|
|
---
|
|
|
|
## **1. 풀 텍스트 검색(Full-Text Search)란?**
|
|
|
|
### **📌 풀 텍스트 검색의 특징**
|
|
- **일반적인 `LIKE` 검색보다 훨씬 빠름** (전문 검색 인덱스를 사용)
|
|
- **단어 단위 검색**이 가능 (`LIKE '%word%'`는 문자열 전체에서 검색하지만, FTS는 단어를 인식)
|
|
- **복잡한 검색 연산 지원** (`MATCH ... AGAINST`를 이용한 자연어 검색, 불리언(Boolean) 검색)
|
|
- **인덱스를 활용하여 검색 성능 최적화** (`FULLTEXT` 인덱스 사용)
|
|
- **정렬 기준을 제공** (관련성 점수를 반환하여 가장 연관성 높은 결과부터 출력 가능)
|
|
|
|
---
|
|
|
|
## **2. 풀 텍스트 검색을 위한 기본 설정**
|
|
|
|
### **✅ 1. 테스트용 테이블 생성**
|
|
MariaDB에서 풀 텍스트 검색을 사용하려면, **TEXT 또는 VARCHAR 컬럼에 `FULLTEXT` 인덱스를 추가해야 합니다.**
|
|
|
|
```sql
|
|
CREATE TABLE articles (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
title VARCHAR(255),
|
|
content TEXT,
|
|
FULLTEXT (title, content) -- 풀 텍스트 인덱스 추가
|
|
) ENGINE=InnoDB;
|
|
```
|
|
✔ `FULLTEXT (title, content)` → **title과 content 컬럼을 대상으로 풀 텍스트 검색 지원**
|
|
✔ `ENGINE=InnoDB` → **MariaDB 10.0.5 이상부터 InnoDB에서도 지원 (기존에는 MyISAM 전용)**
|
|
|
|
---
|
|
|
|
### **✅ 2. 테스트 데이터 삽입**
|
|
|
|
```sql
|
|
INSERT INTO articles (title, content) VALUES
|
|
('MariaDB 풀 텍스트 검색', 'MariaDB의 FULLTEXT 검색은 강력한 검색 기능을 제공합니다.'),
|
|
('MySQL과 MariaDB 비교', 'MariaDB는 MySQL의 오픈소스 버전이며, 여러 기능을 개선했습니다.'),
|
|
('MariaDB 성능 튜닝', 'MariaDB의 성능을 향상시키는 방법에 대해 설명합니다.');
|
|
```
|
|
|
|
---
|
|
|
|
## **3. 기본적인 풀 텍스트 검색 사용법**
|
|
|
|
### **✅ 1. `MATCH ... AGAINST`를 이용한 검색**
|
|
MariaDB에서 풀 텍스트 검색을 수행하려면 **`MATCH(컬럼명) AGAINST ('검색어')`** 구문을 사용합니다.
|
|
|
|
```sql
|
|
SELECT id, title, MATCH(title, content) AGAINST ('MariaDB') AS relevance
|
|
FROM articles
|
|
WHERE MATCH(title, content) AGAINST ('MariaDB');
|
|
```
|
|
✔ `MATCH(title, content) AGAINST ('MariaDB')` → **"MariaDB"를 포함하는 레코드 검색**
|
|
✔ `relevance` (관련성 점수) 반환 → **높은 점수를 가진 데이터가 더 관련성이 높음**
|
|
|
|
**📌 실행 결과 예시:**
|
|
|
|
| id | title | relevance |
|
|
|----|----------------|------------|
|
|
| 1 | MariaDB 풀 텍스트 검색 | 1.2 |
|
|
| 3 | MariaDB 성능 튜닝 | 0.8 |
|
|
|
|
**✅ 장점:**
|
|
- 일반적인 `LIKE '%검색어%'`보다 훨씬 빠른 검색 속도
|
|
- 관련성 점수를 기준으로 정렬 가능
|
|
|
|
---
|
|
|
|
### **✅ 2. 불리언(Boolean) 모드 검색**
|
|
불리언 모드를 사용하면 **논리 연산자(+,-,*,~ 등)를 이용해 복잡한 검색 조건을 적용할 수 있습니다.**
|
|
|
|
```sql
|
|
SELECT id, title
|
|
FROM articles
|
|
WHERE MATCH(title, content) AGAINST ('+MariaDB -MySQL' IN BOOLEAN MODE);
|
|
```
|
|
✔ `+MariaDB` → **"MariaDB"가 반드시 포함된 문서만 검색**
|
|
✔ `-MySQL` → **"MySQL"이 포함된 문서는 제외**
|
|
|
|
**📌 주요 연산자 정리**
|
|
|
|
| 연산자 | 설명 |
|
|
|--------|------|
|
|
| `+` | 반드시 포함 (예: `+MariaDB`) |
|
|
| `-` | 포함되면 제외 (예: `-MySQL`) |
|
|
| `*` | 와일드카드 (예: `Mari*` → Maria, MariaDB 모두 검색) |
|
|
| `~` | 관련성을 낮춤 (예: `~튜닝` → "튜닝" 단어가 있는 경우 가중치를 낮춤) |
|
|
| `""` | 정확한 문구 검색 (예: `"MariaDB 풀 텍스트"`) |
|
|
|
|
---
|
|
|
|
## **4. 고급 검색 기능**
|
|
|
|
### **✅ 1. 자연어(Natural Language) vs 불리언(Boolean) 검색 비교**
|
|
MariaDB에서는 **자연어 검색(Natural Language Mode)**과 **불리언 검색(Boolean Mode)** 두 가지 모드를 지원합니다.
|
|
|
|
#### **📌 자연어 검색 (기본값)**
|
|
```sql
|
|
SELECT * FROM articles
|
|
WHERE MATCH(title, content) AGAINST ('MariaDB');
|
|
```
|
|
✔ **관련성 점수 기반 검색**
|
|
✔ 자동으로 불필요한 단어 제거
|
|
|
|
---
|
|
|
|
#### **📌 불리언 검색**
|
|
```sql
|
|
SELECT * FROM articles
|
|
WHERE MATCH(title, content) AGAINST ('+MariaDB -튜닝' IN BOOLEAN MODE);
|
|
```
|
|
✔ **연산자를 사용하여 논리적으로 검색 조건 적용 가능**
|
|
✔ **관련성 점수를 사용하지 않음**
|
|
|
|
---
|
|
|
|
### **✅ 2. 부분 일치 검색 (NGRAM 인덱스 활용)**
|
|
MariaDB에서 기본적으로 **한 단어(토큰) 단위로 검색**이 이루어지므로, **부분 검색(예: "DB" 검색)이 불가능**합니다.
|
|
이를 해결하기 위해 **ngram 인덱스**를 활용할 수 있습니다.
|
|
|
|
#### **📌 ngram 인덱스 사용법**
|
|
```sql
|
|
ALTER TABLE articles ADD FULLTEXT(title, content) WITH PARSER ngram;
|
|
```
|
|
✔ **기본적으로 2글자 단위로 인덱싱 (bigram 방식)**
|
|
✔ **"DB"와 같은 부분 단어 검색 가능**
|
|
|
|
---
|
|
|
|
### **✅ 3. 정렬 기준 적용 (`ORDER BY`)**
|
|
검색 결과를 **관련성(relevance) 점수를 기준으로 정렬**할 수 있습니다.
|
|
|
|
```sql
|
|
SELECT id, title, MATCH(title, content) AGAINST ('MariaDB') AS relevance
|
|
FROM articles
|
|
WHERE MATCH(title, content) AGAINST ('MariaDB')
|
|
ORDER BY relevance DESC;
|
|
```
|
|
✔ **관련성 점수가 높은 순으로 정렬**
|
|
|
|
---
|
|
|
|
## **5. 정리**
|
|
|
|
### ✅ **MariaDB 풀 텍스트 검색 핵심 요약**
|
|
- `FULLTEXT` 인덱스를 사용하여 **빠른 텍스트 검색 가능**
|
|
- `MATCH() AGAINST()` 구문을 활용하여 검색 수행
|
|
- **불리언 검색(Boolean Mode)**을 사용하면 논리 연산을 적용 가능
|
|
- `NGRAM` 인덱스를 적용하면 **부분 검색 지원**
|
|
|
|
### ✅ **풀 텍스트 검색을 언제 사용할까?**
|
|
- **LIKE 검색이 느려질 때**
|
|
- **대량의 텍스트 데이터를 검색할 때**
|
|
- **검색어의 관련성(점수) 기준으로 정렬이 필요할 때**
|
|
|
|
MariaDB의 풀 텍스트 검색 기능을 활용하면 **강력한 검색 성능과 빠른 응답 속도를 확보할 수 있습니다!** 🚀
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
# **JPA에서 풀 텍스트 검색(Full-Text Search) 활용하기**
|
|
|
|
JPA(Hibernate)를 사용할 때 **풀 텍스트 검색(Full-Text Search)**을 적용하는 방법은 크게 두 가지입니다.
|
|
|
|
1. **데이터베이스 풀 텍스트 검색 기능 활용** (예: MariaDB의 `FULLTEXT` 인덱스)
|
|
2. **Hibernate Search (Elasticsearch, Apache Lucene 등과 연동)**
|
|
|
|
이 글에서는 **MariaDB의 `FULLTEXT` 인덱스를 활용하여 JPA에서 풀 텍스트 검색을 수행하는 방법**을 설명하겠습니다.
|
|
|
|
---
|
|
|
|
## **1. MariaDB의 `FULLTEXT` 검색을 JPA에서 사용하기**
|
|
|
|
### **✅ 1. 엔티티(Entity) 설정**
|
|
|
|
풀 텍스트 검색을 적용하려면 **TEXT 또는 VARCHAR 컬럼에 `FULLTEXT` 인덱스를 추가**해야 합니다.
|
|
|
|
#### **📌 엔티티 클래스 예제**
|
|
```java
|
|
import jakarta.persistence.*;
|
|
import lombok.Getter;
|
|
import lombok.Setter;
|
|
|
|
@Entity
|
|
@Getter
|
|
@Setter
|
|
@Table(name = "articles", indexes = {
|
|
@Index(name = "idx_fulltext_title_content", columnList = "title,content", unique = false)
|
|
})
|
|
public class Article {
|
|
|
|
@Id
|
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
private Long id;
|
|
|
|
@Column(nullable = false)
|
|
private String title;
|
|
|
|
@Column(columnDefinition = "TEXT", nullable = false)
|
|
private String content;
|
|
}
|
|
```
|
|
✔ `@Index(name = "idx_fulltext_title_content", columnList = "title,content")`
|
|
→ **JPA로 FULLTEXT 인덱스를 직접 설정할 수 없지만, 인덱스 지정 가능**
|
|
✔ `@Column(columnDefinition = "TEXT")`
|
|
→ **TEXT 타입 필드(content)를 선언**
|
|
|
|
---
|
|
|
|
### **✅ 2. `FULLTEXT` 인덱스 수동 생성**
|
|
JPA에서는 `FULLTEXT` 인덱스를 자동으로 생성하지 않으므로, **SQL로 직접 인덱스를 추가**해야 합니다.
|
|
|
|
```sql
|
|
CREATE TABLE articles (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
title VARCHAR(255) NOT NULL,
|
|
content TEXT NOT NULL,
|
|
FULLTEXT (title, content) -- FULLTEXT 인덱스 추가
|
|
) ENGINE=InnoDB;
|
|
```
|
|
|
|
---
|
|
|
|
### **✅ 3. JPA에서 풀 텍스트 검색 쿼리 작성**
|
|
|
|
JPA의 `@Query`를 사용하여 **`MATCH() AGAINST()`** 구문을 활용할 수 있습니다.
|
|
|
|
#### **📌 Repository 인터페이스**
|
|
```java
|
|
import org.springframework.data.jpa.repository.JpaRepository;
|
|
import org.springframework.data.jpa.repository.Query;
|
|
import org.springframework.data.repository.query.Param;
|
|
|
|
import java.util.List;
|
|
|
|
public interface ArticleRepository extends JpaRepository<Article, Long> {
|
|
|
|
@Query(value = """
|
|
SELECT * FROM articles
|
|
WHERE MATCH(title, content) AGAINST(:keyword)
|
|
""", nativeQuery = true)
|
|
List<Article> searchByFullText(@Param("keyword") String keyword);
|
|
}
|
|
```
|
|
✔ `MATCH(title, content) AGAINST(:keyword)` → **title과 content에서 `keyword` 검색**
|
|
✔ `nativeQuery = true` → **FULLTEXT 검색은 네이티브 쿼리로 실행해야 함**
|
|
|
|
---
|
|
|
|
### **✅ 4. 서비스(Service) 계층 작성**
|
|
|
|
#### **📌 검색 기능을 수행하는 서비스 클래스**
|
|
```java
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
import java.util.List;
|
|
|
|
@Service
|
|
@Transactional
|
|
public class ArticleService {
|
|
|
|
private final ArticleRepository articleRepository;
|
|
|
|
public ArticleService(ArticleRepository articleRepository) {
|
|
this.articleRepository = articleRepository;
|
|
}
|
|
|
|
public List<Article> searchArticles(String keyword) {
|
|
return articleRepository.searchByFullText(keyword);
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### **✅ 5. 컨트롤러(Controller) 구현**
|
|
|
|
#### **📌 검색 API 컨트롤러**
|
|
```java
|
|
import org.springframework.web.bind.annotation.*;
|
|
|
|
import java.util.List;
|
|
|
|
@RestController
|
|
@RequestMapping("/articles")
|
|
public class ArticleController {
|
|
|
|
private final ArticleService articleService;
|
|
|
|
public ArticleController(ArticleService articleService) {
|
|
this.articleService = articleService;
|
|
}
|
|
|
|
@GetMapping("/search")
|
|
public List<Article> searchArticles(@RequestParam String keyword) {
|
|
return articleService.searchArticles(keyword);
|
|
}
|
|
}
|
|
```
|
|
✔ `/articles/search?keyword=MariaDB` → **MariaDB 관련 문서를 검색하는 API**
|
|
|
|
---
|
|
|
|
## **2. 불리언(Boolean) 검색 적용**
|
|
|
|
MariaDB의 **불리언(Boolean) 검색 모드**를 적용하면 검색 연산자(`+`, `-`, `*`, `" "`)를 사용할 수 있습니다.
|
|
|
|
### **✅ `BOOLEAN MODE` 적용 예제**
|
|
```java
|
|
@Query(value = """
|
|
SELECT * FROM articles
|
|
WHERE MATCH(title, content) AGAINST(:keyword IN BOOLEAN MODE)
|
|
""", nativeQuery = true)
|
|
List<Article> searchByFullTextBoolean(@Param("keyword") String keyword);
|
|
```
|
|
✔ `IN BOOLEAN MODE` → **논리 연산자를 포함한 검색 수행**
|
|
|
|
#### **📌 검색 연산자 예제**
|
|
| 검색어 | 설명 |
|
|
|--------|------|
|
|
| `+MariaDB` | 반드시 "MariaDB" 포함 |
|
|
| `-MySQL` | "MySQL"이 포함된 결과 제외 |
|
|
| `Mari*` | "Maria", "MariaDB" 등 검색 가능 |
|
|
| `"MariaDB 검색"` | 정확한 문구 검색 |
|
|
|
|
---
|
|
|
|
## **3. 정렬(관련성 점수 기반)**
|
|
|
|
풀 텍스트 검색을 수행할 때 **관련성 점수를 기준으로 정렬**할 수 있습니다.
|
|
|
|
### **✅ 정렬을 적용한 Repository**
|
|
```java
|
|
@Query(value = """
|
|
SELECT *, MATCH(title, content) AGAINST(:keyword) AS relevance
|
|
FROM articles
|
|
WHERE MATCH(title, content) AGAINST(:keyword)
|
|
ORDER BY relevance DESC
|
|
""", nativeQuery = true)
|
|
List<Article> searchByRelevance(@Param("keyword") String keyword);
|
|
```
|
|
✔ `MATCH(title, content) AGAINST(:keyword) AS relevance` → **관련성 점수 계산**
|
|
✔ `ORDER BY relevance DESC` → **가장 연관성 높은 결과부터 정렬**
|
|
|
|
---
|
|
|
|
## **4. 페이징 처리 적용**
|
|
|
|
Spring Data JPA의 `Pageable`을 활용하여 검색 결과에 페이징을 적용할 수 있습니다.
|
|
|
|
### **✅ 페이징 지원 Repository**
|
|
```java
|
|
import org.springframework.data.domain.Page;
|
|
import org.springframework.data.domain.Pageable;
|
|
|
|
@Query(value = """
|
|
SELECT * FROM articles
|
|
WHERE MATCH(title, content) AGAINST(:keyword)
|
|
""", nativeQuery = true)
|
|
Page<Article> searchByFullTextPaged(@Param("keyword") String keyword, Pageable pageable);
|
|
```
|
|
✔ `Page<Article>` 반환 → **Spring Data JPA의 페이징 기능을 활용 가능**
|
|
|
|
---
|
|
|
|
## **5. 정리**
|
|
|
|
### **✅ JPA에서 MariaDB 풀 텍스트 검색 활용 방법**
|
|
- `FULLTEXT` 인덱스를 적용하여 빠른 검색 수행
|
|
- `MATCH() AGAINST()`를 활용한 검색 쿼리 작성 (`BOOLEAN MODE` 지원)
|
|
- 관련성 점수 기반 정렬 가능 (`ORDER BY relevance DESC`)
|
|
- `Pageable`을 활용하여 **페이징 처리 가능**
|
|
|
|
### **📌 풀 텍스트 검색이 필요한 경우**
|
|
✔ 데이터가 많아 `LIKE '%keyword%'`로 검색할 경우 성능이 저하될 때
|
|
✔ 연관성 높은 검색 결과를 우선적으로 정렬해야 할 때
|
|
✔ `BOOLEAN MODE`를 활용해 논리 검색(+, -, *)이 필요할 때
|
|
|
|
**MariaDB 풀 텍스트 검색을 활용하면 JPA에서도 강력한 검색 기능을 구현할 수 있습니다! 🚀** |