1. 業務要件の分析
企業情報管理システムにおいて、重複企業登録を防止するため、新規登録時に類似企業名を検索する必要がある。入力された企業名(略称の場合あり)と既存データベース内の企業名を比較し、類似度の高い順に表示する。
2. 技術的アプローチ
2.1 処理フロー
- 企業名から不要情報(地域名、括弧、組織形態等)を除去
- IKAnalyzerを使用してキーワード分割
- MySQLの正規表現機能で部分一致検索
- 一致度に基づいて結果をソート
2.2 実装の選択肢
| 方法 | メリット | デメリット |
|---|---|---|
| LIKE句 | シンプル | 部分一致に不向き |
| 全文検索 | 高速 | カスタマイズ性が低い |
| 正規表現 | 柔軟な検索 | パフォーマンス中程度 |
3. 主要な実装コード
3.1 企業名前処理
public String sanitizeCompanyName(String name) {
// 地域情報の除去
String regex = "(?<province>[^省]+自治区|.*?省|.*?行政区|.*?市)" +
"?(?<city>[^市]+自治州|.*?地区|.*?行政単位|.+盟|市辖区|.*?市|.*?県)" +
"?(?<county>[^(区|市|県|旗|島)]+区|.*?市|.*?県|.*?旗|.*?島)" +
"?(?<village>.*)";
Matcher m = Pattern.compile(regex).matcher(name);
while(m.find()) {
name = removeMatchedGroup(name, m.group("province"));
name = removeMatchedGroup(name, m.group("city"));
name = removeMatchedGroup(name, m.group("county"));
}
// 組織形態の除去
return name.replaceAll("[(集团|股份|有限|责任|分公司)]", "");
}
private String removeMatchedGroup(String source, String group) {
return StringUtils.isNotBlank(group) ? source.replace(group, "") : source;
}
3.2 検索クエリ
@Query(value = """
SELECT * FROM company
WHERE isDeleted = '0'
AND companyName REGEXP ?1
ORDER BY LENGTH(REPLACE(companyName, ?2, ''))/LENGTH(companyName)
""", nativeQuery = true)
List<Company> findSimilarCompanies(String keywords, String originalName);
3.3 検索ロジック
public List<String> findSimilarCompanies(String inputName) {
// 前処理
String sanitized = sanitizeCompanyName(inputName)
.replaceAll("[()()]", "");
// 分かち書き
List<String> tokens = new IKAnalyzer().analyze(sanitized);
String searchPattern = String.join("|", tokens);
// データベース検索
return companyRepo.findSimilarCompanies(searchPattern, sanitized)
.stream()
.map(Company::getName)
.collect(Collectors.toList());
}
4. 技術的考慮点
- IKAnalyzerの辞書を業務ドメインに最適化
- 検索パフォーマンスのため適切なインデックス設計
- 大文字小文字、全角半角の統一処理