漢字字形的處理

Hacker News 上看到「Your Code Displays Japanese Wrong (heistak.github.io)」這篇,原文是「Your Code Displays Japanese Wrong」。

這個算是 CJK 族群的經典問題,主要的問題是有不同的團體都在使用漢字,但雖然都是 U+5203 的「刃」,在不同的地區的「標準寫法」是不一樣的,像是「國家教育研究院 - 教育部國語小字典-刃」這邊就有 SVG 版本的「圖」可以看:

我在 different-lang.html 這邊把他提到的「刃直海角骨入」給放了進去,指定不同的 lang,像是這樣:

<dt>lang="jp"</dt><dd lang="jp">刃直海角骨入</dd>

如果你的電腦裡面有 Noto Sans CJK 的話,應該可以看出不同的字形。

在 HTML 網頁上可以利用 html 內的 lang 資訊告訴瀏覽器去抓取對應的字形,當然,系統有沒有這個字形又是另外一回事了,不過市場上至少有 open source license 的 Noto CJK 系列,算是個低標的答案可以用。

至於要更多樣的話,應該是要分不同語言下去找...

測了 PGroonga,PostgreSQL 上的 fulltext search engine

PostgreSQL 的 news 頁上看到「PGroonga 3.0.0 - Multilingual fast full text search」,想到一直沒有測過 PGroonga,就找台機器測了一下。

PGroonga 是以 Groonga 為引擎提供 PostgreSQL 全文搜尋能力的套件,是個能支援 CJK 語系的全文搜尋套件。

可以先看一下支援的 column type 與對應的語法:「Reference manual | PGroonga」,可以發現基本的 texttext[]varcharvarchar[] 都有支援,比較特別的是有 jsonb,看起來是對裡面的 text 欄位搜尋。

另外一個比較特別的是他會去配合 LIKE '%something%' 這樣的語法,對於無法修改的既有程式也會有幫助。

缺點方面,官方有提到產生出來的 index 會比其他的套件大,但畢竟我們在的環境要支援 CJK,場上的選手已經不多了。

另外一個缺點是目前 AWSRDSGCPCloud SQL 看起來都沒支援,要用的話得自己架 & 自己管,也許可以考慮用老方法,replication 接出來?

接下來就是安裝測試了,我在 x86-64 上的 Ubuntu 22.04 上面測試,就照著「Install on Ubuntu | PGroonga」這頁裡面的「How to install for system PostgreSQL」這段就可以了,裝系統的 PostgreSQL 14 以及 postgresql-14-pgroonga,之後要用 PostgreSQL 官方的新版的話可以參考「How to install for the official PostgreSQL」這段的安裝。

後續再到「Tutorial | PGroonga」頁,針對要搜尋的欄位下 index (這邊裱格式 memos,欄位是 content):

CREATE INDEX ON memos USING pgroonga (content);

官方的教學文件裡是用 SET enable_seqscan = off; 關閉 sequence scan,可以用 EXPLAIN 看到使用了 index:

test=# SELECT * FROM memos WHERE content &@ 'engine';
 id |                                content                                 
----+------------------------------------------------------------------------
  2 | Groonga is a fast full text search engine that supports all languages.
(1 row)

test=# EXPLAIN SELECT * FROM memos WHERE content &@ 'engine';
                                   QUERY PLAN                                    
---------------------------------------------------------------------------------
 Index Scan using memos_content_idx on memos  (cost=0.00..43.18 rows=1 width=36)
   Index Cond: (content &@ 'engine'::text)
(2 rows)

先拔掉 index:

test=# DROP INDEX pgroonga_content_index;
DROP INDEX

接著要塞資料,這邊拿 CQD 生的「中文假文產生器」來用,有 API 可以接比較方便。

test=# SELECT COUNT(*) FROM memos;
 count  
--------
 100000
(1 row)

Time: 15.495 ms

接著多跑幾次測試直接用 LIKE '%台北%' 去找,可以看到大概都在 150ms 以上:

test=# SELECT COUNT(*) FROM memos WHERE content LIKE '%台北%';
 count 
-------
   710
(1 row)

Time: 178.784 ms

接著來建立 index:

test=# CREATE INDEX ON memos USING pgroonga (content);
CREATE INDEX
Time: 17638.124 ms (00:17.638)

再跑幾次同樣的 query,可以看到巨大的改善:

test=# SELECT COUNT(*) FROM memos WHERE content LIKE '%台北%';
 count 
-------
   710
(1 row)

Time: 9.876 ms

Elasticsearch 的 CJK Bigram 設定

Elasticsearch 應該是目前大家搜尋引擎的首選了。而且預設的搜尋法不像以前的搜尋引擎,以前的搜尋引擎會把所有的中文字串當作一個 term,基本上是搜不到東西的。

不過偶而還是會出現一些問題,像是這樣:(這是在求職天眼通搜尋「訊力科技股份有限公司」的結果)

會發現出現了「104人力銀行_一零四資訊科技股份有限公司」,這是因為預設的搜尋演算法把中文字一個一個拆開,後面的「科技股份有限公司」八個字也都有出現,前面的「訊」與「力」也都有出現,於是就被拉出來了...

這種方式被歸類為 unigram 類的方式,像是「波音737 MAX」這一段就會被切成「波」、「音」、「737」與「MAX」。這個切法還算不錯,但有不少機會會遇到問題。

如果限制在 Elasticsearch 內建的功能,其實有更好的設定可以用,也就是對 CJK 文字改用 bigram 方式切:「CJK Bigram Token Filter」。

遇到英文數字還是照原來的切法,但遇到中文字 (更正確的說應該是 CJK) 會用 bigram 的方式切,像是搜尋詞「訊力科技股份有限公司」就會被切成「訊力」、「力科」、「科技」、「技股」、「股份」、「份有」、「有限」、「限公」與「公司」,而本來的「104人力銀行_一零四資訊科技股份有限公司」裡面就不會出現「訊力」、「力科」,於是就不會抓錯...

當然還是有更好的演算法,不過大多就需要另外安裝了,而 Elasticsearch 的升級又很容易跟這些另外裝的套件卡住,所以在考慮維護成本下,CJK Bigram Token Filter 應該是首選...

MySQL 上的全文搜尋引擎:Mroonga

算是無意間翻到的資料,MySQL 上的全文搜尋引擎:「Mroonga」。

看起來後面主要是日本社群?從 2010 年就開始發展了,號稱 CJK 都支援,而且各大作業系統也都有預先包好的版本 (像是 Ubuntu 上有 PPA)。

雖然現在社群音量最大的應該還是 Elasticsearch,但看起來頗有趣的,對於只是想要架個小東西玩的專案,說不定是個有趣的方案?

Adobe 與 Google 合作推出 Noto Serif CJK (思源宋體)

Adobe 的繁體中文公告:「思源宋體」,以及 Google 的公告:「Noto Serif CJK is here!」。

效果看起來還不錯:

而且分別依照各地區的標準設計 (簡中、繁中、日文、韓文):

接下來有機會出其他的字型嗎?像是楷體之類的?

Ubuntu 內建的 Noto Sans CJK...

今天在 Facebook 上才看到 chihchun 貼的資料,在 Ubuntu 14.04 之後都有 Noto Sans CJK 可以用:

sudo apt-get install fonts-noto-cjk #

Posted by 蔡志展 on Sunday, July 26, 2015

這邊提到的是 fonts-noto-cjk,不過我實際測試發現裝 fonts-noto 會比較好:前者預設值很細 (Light),後者包括了各種粗細 (我自己是用 DemiLight)。

這樣又少了一個要自己管理的東西了...

Adobe Typekit 支援 CJK 字型

Adobe Typekit 宣佈支援 CJK 字型:「Announcing East Asian web font support and new font browsing tools for Japanese customers」。中文的公告在「正式公開東亞網頁字體支援以及日文客戶適用的全新字體瀏覽工具」這邊。

這也包括了網頁版的部份。對於 CJK 單一字型檔案過大的問題,與大家的解法也都一樣,取出對應的字組出來給使用者作為 workaround (稱為 Dynamic Subsetting):

會稱為這是 workaround 是因為當網路速度愈來愈之後,就會又變成最單純的直接整包下載...

Anyway,Adobe 這個新功能是一個大邁進,不過他用的 HTTPS (EdgeCast) 還沒支援 SPDY 或是 HTTP/2 啊... :/

MySQL 5.7 的 InnoDB 的全文搜尋

在「InnoDB Full-Text : N-gram Parser」這邊看到對 MySQL 5.7 InnoDB 的全文搜尋功能介紹。開頭就有很重要的說明:

I’m now very happy to say that in MySQL 5.7.6 we’ve made use of the new pluggable full-text parser support in order to provide you with an n-gram parser that can be used with CJK!

這對資料量在中等或是更少的公司相當方便,你可以架 replication server 專門跑 search,而不需要利用 reliable queue 確保更新後推進 SolrElastic (改名了,之前叫 ElasticSearch)。

不過,如果資料量很大的話應該還是得用 Solr 或 Elastic 的方案...

Adobe 與 Google 共同推出 CJK 字型

AdobeGoogle 合作推出了 CJK 字型:「隆重介紹 思源黑體:開放原始碼 Pan-CJK 字型」、「Noto: A CJK Font That is Complete, Beautiful and Right for Your Language and Region」。

這些字體在 Adobe 與 Google 會叫做不同的名稱:

Google will release it as Noto Sans CJK as part of Google's Noto font family. Adobe will release it as Source Han Sans as a part of Adobe's Source family.

字體是由 Adobe 的西塚涼子所開發,而 Adobe 也擁有字型的著作權,並以 Apache License 2.0 放出來。

而 Google 也有說明這些字型參與的過程:

Google contributed significant input into project direction, helped to define requirements, provided in-country testing resources and expertise, and provided funding that made this project possible.

而 Adobe 的部份:

Adobe brought strong design and technical prowess to the table, along with proven in-country type design experience, massive coordination, and automation.

另外還有幾家公司一起參與:

In addition, three leading East Asian type foundries were also brought in to design and draw a bulk of the glyphs—Changzhou SinoType Technology, Iwata Corporation, and Sandoll Communication—due to the sheer size of the project and their local expertise.

字體可以在 Google Noto Fonts 裡直接下載取得。

切過去用以後還是有點不太習慣,不過目前第一印象還算不錯...