Commit e202048b authored by platyhouse's avatar platyhouse

# MySQL 컬럼 comment 기반 Elasticsearch 매핑 설정 기능 추가

## ptyElasticUploadFromMysql.php

### 새로운 기능: elastic.* 옵션 파싱
- parseElasticOptions() 메소드 추가: MySQL 컬럼 comment에서 elastic.* 설정 파싱
  - elastic.register=0: 해당 컬럼을 인덱스에서 제외
  - elastic.type=text|keyword: Elasticsearch 필드 타입 지정
  - elastic.analyzer=분석기명: analyzer 설정

### 테이블 구조 조회 개선
- DESCRIBE 대신 SHOW FULL COLUMNS 사용하여 컬럼 comment 정보 획득
- elastic.register=0인 컬럼을 매핑에서 제외하는 로직 추가
- elastic.type 지정 시 자동 변환 대신 해당 타입 사용
- elastic.analyzer 설정 시 해당 analyzer를 매핑에 추가

### 데이터 업로드 시 제외 컬럼 처리
- excludedColumns 멤버 변수 추가: elastic.register=0인 컬럼 목록 저장
- 벌크 업로드 시 excludedColumns에 포함된 컬럼 데이터 제거
- 기존 인덱스에 데이터만 업로드할 때도 getTableStructure() 호출하여 제외 컬럼 정보 획득

### 도움말 개선
- MySQL 컬럼 comment 설정 옵션 설명 추가
- 사용 예시 추가
parent 748c0e17
...@@ -127,6 +127,8 @@ class MySQLToElastic ...@@ -127,6 +127,8 @@ class MySQLToElastic
private $mysqlConfig; private $mysqlConfig;
private $elasticConfig; private $elasticConfig;
private $sampleDocIds = []; private $sampleDocIds = [];
private $excludedColumns = []; // elastic.register=0인 컬럼들
private $usedAnalyzers = []; // 사용된 analyzer들
public function __construct($tableName, $indexName, $batchSize = 1000, $mysqlConfig = null, $elasticConfig = null) public function __construct($tableName, $indexName, $batchSize = 1000, $mysqlConfig = null, $elasticConfig = null)
{ {
...@@ -193,6 +195,49 @@ class MySQLToElastic ...@@ -193,6 +195,49 @@ class MySQLToElastic
$this->log->success("Elasticsearch 연결 성공: {$this->elasticConfig['host']} ({$authMethod})"); $this->log->success("Elasticsearch 연결 성공: {$this->elasticConfig['host']} ({$authMethod})");
} }
/**
* MySQL 컬럼 comment에서 elastic.* 설정 파싱
*
* 지원하는 설정:
* - elastic.register=0 : 해당 컬럼을 인덱스에서 제외
* - elastic.type=text|keyword : 타입 지정
* - elastic.analyzer=분석기명 : analyzer 설정
*
* 예시 comment: "법령.기본정보.법령명_한글, elastic.type=text, elastic.analyzer=lw_nori_analyzer"
*
* @param string $comment MySQL 컬럼 comment
* @return array ['register' => bool, 'type' => string|null, 'analyzer' => string|null]
*/
private function parseElasticOptions($comment)
{
$options = [
'register' => true, // 기본값: 등록
'type' => null, // null이면 자동 변환
'analyzer' => null,
];
if (empty($comment)) {
return $options;
}
// elastic.register=0 체크
if (preg_match('/elastic\.register\s*=\s*0/', $comment)) {
$options['register'] = false;
}
// elastic.type=text|keyword 체크
if (preg_match('/elastic\.type\s*=\s*(text|keyword)/i', $comment, $matches)) {
$options['type'] = strtolower($matches[1]);
}
// elastic.analyzer=분석기명 체크
if (preg_match('/elastic\.analyzer\s*=\s*([a-zA-Z0-9_-]+)/', $comment, $matches)) {
$options['analyzer'] = $matches[1];
}
return $options;
}
/** /**
* 테이블 구조를 읽어서 Elasticsearch 매핑 생성 * 테이블 구조를 읽어서 Elasticsearch 매핑 생성
*/ */
...@@ -200,26 +245,48 @@ class MySQLToElastic ...@@ -200,26 +245,48 @@ class MySQLToElastic
{ {
$this->log->info("테이블 구조 조회 중..."); $this->log->info("테이블 구조 조회 중...");
$stmt = $this->pdo->query("DESCRIBE `{$this->tableName}`"); // SHOW FULL COLUMNS를 사용하여 컬럼 comment도 가져옴
$stmt = $this->pdo->query("SHOW FULL COLUMNS FROM `{$this->tableName}`");
$columns = $stmt->fetchAll(); $columns = $stmt->fetchAll();
$mapping = []; $mapping = [];
$skippedColumns = [];
foreach ($columns as $column) { foreach ($columns as $column) {
$field = $column['Field']; $field = $column['Field'];
$type = $column['Type']; $type = $column['Type'];
$key = $column['Key']; $key = $column['Key'];
$comment = $column['Comment'] ?? '';
// Primary Key 저장 // Primary Key 저장
if ($key === 'PRI') { if ($key === 'PRI') {
$this->primaryKey = $field; $this->primaryKey = $field;
} }
// MySQL 타입을 Elasticsearch 타입으로 변환 // 컬럼 comment에서 elastic.* 설정 파싱
$esType = $this->convertMySQLTypeToElastic($type); $elasticOptions = $this->parseElasticOptions($comment);
// elastic.register=0이면 이 컬럼은 인덱스에서 제외
if (!$elasticOptions['register']) {
$skippedColumns[] = $field;
continue;
}
// elastic.type이 지정되었으면 해당 타입 사용, 아니면 자동 변환
if ($elasticOptions['type'] !== null) {
$esType = $elasticOptions['type'];
} else {
// MySQL 타입을 Elasticsearch 타입으로 변환
$esType = $this->convertMySQLTypeToElastic($type);
}
$mapping[$field] = ['type' => $esType]; $mapping[$field] = ['type' => $esType];
// elastic.analyzer가 지정되었으면 analyzer 설정
if ($elasticOptions['analyzer'] !== null) {
$mapping[$field]['analyzer'] = $elasticOptions['analyzer'];
}
// text 타입인 경우 keyword 필드도 추가 (정렬, 집계용) // text 타입인 경우 keyword 필드도 추가 (정렬, 집계용)
if ($esType === 'text') { if ($esType === 'text') {
$mapping[$field]['fields'] = [ $mapping[$field]['fields'] = [
...@@ -231,7 +298,14 @@ class MySQLToElastic ...@@ -231,7 +298,14 @@ class MySQLToElastic
} }
} }
$this->log->success("테이블 구조 조회 완료 (컬럼 수: " . count($columns) . ")"); $this->log->success("테이블 구조 조회 완료 (컬럼 수: " . count($columns) . ", 매핑 수: " . count($mapping) . ")");
// 제외된 컬럼 저장 (데이터 업로드 시에도 제외하기 위해)
$this->excludedColumns = $skippedColumns;
if (!empty($skippedColumns)) {
$this->log->info("제외된 컬럼 (elastic.register=0): " . implode(', ', $skippedColumns));
}
return $mapping; return $mapping;
} }
...@@ -313,6 +387,8 @@ class MySQLToElastic ...@@ -313,6 +387,8 @@ class MySQLToElastic
$this->log->success("기존 인덱스 삭제 완료"); $this->log->success("기존 인덱스 삭제 완료");
} else { } else {
$this->log->warning("인덱스가 이미 존재합니다. 매핑 업데이트를 시도합니다."); $this->log->warning("인덱스가 이미 존재합니다. 매핑 업데이트를 시도합니다.");
// 인덱스를 생성하지 않더라도 제외할 컬럼 목록은 필요
$this->getTableStructure();
return; return;
} }
} }
...@@ -494,6 +570,11 @@ class MySQLToElastic ...@@ -494,6 +570,11 @@ class MySQLToElastic
} }
} }
// elastic.register=0인 컬럼 제거
foreach ($this->excludedColumns as $excludedColumn) {
unset($row[$excludedColumn]);
}
// index 액션 // index 액션
$action = [ $action = [
'index' => [ 'index' => [
...@@ -672,6 +753,17 @@ if (!$tableName || !$indexName || isset($options['help'])) { ...@@ -672,6 +753,17 @@ if (!$tableName || !$indexName || isset($options['help'])) {
echo " ./ptyElasticUploadFromMysql users users --mysql=production --elastic=production\n"; echo " ./ptyElasticUploadFromMysql users users --mysql=production --elastic=production\n";
echo " ./ptyElasticUploadFromMysql new_law_items law_items --batch=500 --recreate\n"; echo " ./ptyElasticUploadFromMysql new_law_items law_items --batch=500 --recreate\n";
echo "\n"; echo "\n";
echo "MySQL 컬럼 comment에서 elastic.* 설정:\n";
echo " 인덱스 생성/재생성 시 MySQL 컬럼 comment에서 다음 설정을 인식합니다.\n";
echo "\n";
echo " elastic.register=0 해당 컬럼을 인덱스에서 제외\n";
echo " elastic.type=text|keyword Elasticsearch 필드 타입 지정\n";
echo " elastic.analyzer=분석기명 analyzer 설정 (예: lw_nori_analyzer)\n";
echo "\n";
echo " 예시 comment:\n";
echo " \"법령.법령명_한글, elastic.type=text, elastic.analyzer=lw_nori_analyzer\"\n";
echo " \"내부데이터, elastic.register=0\"\n";
echo "\n";
exit(isset($options['help']) ? 0 : 1); exit(isset($options['help']) ? 0 : 1);
} }
$batchSize = isset($options['batch']) ? (int)$options['batch'] : 100; $batchSize = isset($options['batch']) ? (int)$options['batch'] : 100;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment