# 修正 HAPI FHIR ValueSet 無法正常 Expand 特殊符號的 Code
以下是筆者當時遇到的情境,在 ValueSet 當中的 Code 都是擁有括號的 QW 數值 QW(x),但在進行 Expansion 之後,會變成只有一個 QW 的結果

## 問題的主要原因
- HAPI FHIR 的 free text index 使用 simple query string 進行搜尋,若是文字使用以下符號,將有可能導致 query 不到所想要的結果
### Elastic Search query string syntax
- +, |, -, ", *, (, ), ~

### [Lucene escaping special characters](https://lucene.apache.org/core/2_9_4/queryparsersyntax.html)
- Lucene supports escaping special characters that are part of the query syntax. The current list special characters are
- &, |, !, (, ), {, }, ^, ", ~, *, ?, :, \
### 程式碼來源
- TermReadSvcImpl.java@buildExpansionPredicate

## 更改 hapi-fhir 原始碼
- 筆者在看過 HAPI FHIR 的 Code 發現程式碼其實很單純,所以決定自己手動修改看看
### 下載專案
- 使用 git 下載專案
```bash!=
git clone https://github.com/hapifhir/hapi-fhir.git
```
### 更改 Expand 的邏輯
- 進到`src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java`檔案
- 找到 `buildExpansionPredicate` function
- 更改成以下程式碼
```java!=
/**
* Helper method which builds a predicate for the expansion
*/
private PredicateFinalStep buildExpansionPredicate(List<String> theCodes, SearchPredicateFactory thePredicate) {
assert !theCodes.isEmpty();
List<String> escapedCodes;
if (myHibernatePropertiesProvider.getHibernateSearchBackend().equalsIgnoreCase("lucene")) {
escapedCodes = theCodes.stream().map(this::escapeLucene).collect(Collectors.toList());
} else if (myHibernatePropertiesProvider.getHibernateSearchBackend().equalsIgnoreCase("elasticsearch")) {
escapedCodes = theCodes.stream().map(this::escapeElasticsearch).collect(Collectors.toList());
} else {
escapedCodes = theCodes;
}
return thePredicate.simpleQueryString().field("myCode").matching(String.join(" | ", escapedCodes));
}
private String escapeLucene(String code) {
return code.replaceAll("([+\\-&|!(){}^\"~*?:\\\\])", "\\\\$1");
}
private String escapeElasticsearch(String code) {
return code.replaceAll("([+\\-|\"*()~])", "\\\\\\\\$1");
}
```
> 這段程式碼主要是透過 myHibernatePropertiesProvider檢查 hiebernate 所使用的引擎,並根據 lucene 或 elasticsearch 進行字串的處理,以修正部分代碼擁有特殊符號導致無法正常展開的問題
## 打包成 jar 檔
更改完程式碼後,我們需要進行 jar 檔的打包,讓 hapi fhir jpaserver starter 可以使用我們修改後的 jar 檔
- 在 idea 上對 HAPI FHIR JPA Server 點擊 package

## 使用 local JAR 檔
接下來,我們就要把打包後的 jar 檔丟到 hapi fhir jpserver starter 使用啦!
- 在專案根目錄的 `pom.xml`檔案加入 local repository
```xml!=
<repositories>
<repository>
<id>oss-snapshots</id>
<snapshots>
<enabled>true</enabled>
</snapshots>
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
</repository>
<repository>
<id>localLibs</id>
<name>localLibs</name>
<url>file:///${basedir}/libs</url>
</repository>
</repositories>
```
- 在專案根目錄創建 libs 資料夾
- 根據想要新增的 JAR 檔的 groupId 與 artifactId 創建資料夾
- 在這裡為 `ca/uhn/hapi/fhir/hapi-fhir-jpaserver-base`

- 把更改好的 `hapi-fhir-jpaserver-base-7.4.0.jar` 放入 `libs/ca/uhn/hapi/fhir/hapi-fhir-jpaserver-base` 當中
- 創建 `hapi-fhir-jpaserver-base-7.4.0.pom` 檔案,內容如下
```xml!=
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jpaserver-base</artifactId>
<version>7.4.0</version>
<description>POM was created from install:install-file</description>
</project>
```
- 先進行依賴安裝
```bash!=
mvn -ntp dependency:go-offline
```
- 安裝完畢後,在執行以下指令覆蓋原本的 jar 檔
```bash!=
mvn install:install-file -Dfile=libs/ca/uhn/hapi/fhir/hapi-fhir-jpaserver-base/hapi-fhir-jpaserver-base-7.4.0.jar -DgroupId=ca.uhn.hapi.fhir -DartifactId=hapi-fhir-jpaserver-base -Dversion=7.4.0 -Dpackaging=jar
```
## 測試
上面的步驟都做完後,最大的項目,就是進行測試啦
- 從 idea 直接執行 hapi fhir jpaserver starter 專案
- 透過 postman 把 CodeSystem 與 ValueSet 上傳
- 對 ValueSet 進行展開 ($expand),看到展開的 Code 數量與 IG 上的一樣就代表成功囉!
## 參考資料
- https://chengjhan.github.io/p/%E5%9C%A8-maven-%E5%B0%88%E6%A1%88%E6%96%B0%E5%A2%9E%E7%A7%81%E6%9C%89%E7%9A%84-jar-%E6%AA%94/
- [出錯的 ValueSet](https://twcore.mohw.gov.tw/ig/pas/ValueSet-medication-frequency-hl7-nhi.html)
- 若你還想要優化驗證(validation)的速度,可以看 https://github.com/hapifhir/hapi-fhir/issues/6252
- 此修改已在 hapi-fhir 7.6.0 套用
- 個人修改版的 github: https://github.com/Chinlinlee/hapi-fhir/tree/v7.4.0
## Support Me
文件創作花費了很多心血製作,如果你覺得很有幫助
不妨贊助我一下喝杯咖啡唄,[Support Me](https://portaly.cc/Li070/support)