2026/04/25

MariaDB

MariaDB 出垷的原因是因為 MySQL 被賣給了 SUN, 而後 SUN 被 Oracle 併購,所以 MySQL 被 Oracle 所擁有, 這導致一些 MySQL 原本的開發團隊人員對於 MySQL 是否能夠維持開放原始碼產生質疑, 所以複製 MySQL 5.5 原始碼出來維護的分支,並且成為大多數 Linux distribution 的內建選擇。 一開始 Oracle 其實對 MySQL 的管理還不錯,事情變化的源頭為 Oracle 推出 MySQL Heatwave 服務, 並且一些功能只在 MySQL Heatwave 增加,MySQL 社群版或者是企業版都不具備這些在 MySQL Heatwave 增加的功能, 同時 Oracle 也降低了對 MySQL 的開發重視度,這導致了 MySQL 社群版或者是企業版在 MySQL Heatwave 服務推出後其發展開始停滯, 並且遂漸跟不上使用者的需求。

MariaDB 使用與 MySQL 相同的通訊協定,一般而言可以作為 MySQL 的替代,其目標之一就是儘量維持與 MySQL 的相容, 雖然 MariaDB 與 MySQL 隨著二個開發團隊的各自開發而會有小地方的不同,但是目前仍然大多數的 MySQL 工具都可以直接使用。

在 openSUSE 安裝的指令:

sudo zypper in mariadb

使用下列的指令啟動 MariaDB server:

sudo systemctl start mariadb

使用下列的指令查詢 MariaDB server 的狀態:

sudo systemctl status mariadb

使用下列的指令停止 MariaDB server:

sudo systemctl stop mariadb

在安裝後啟動 MariaDB server,再使用下列的指令進行安全性設定:

sudo mysql_secure_installation

使用下列的指令驗證是否安裝正確(如果有設定密碼 password,輸入密碼):

mariadb -u root -p

在 MariaDB 中,若要完整支援 Unicode(包含 Emoji、特殊中日韓字元),應使用 utf8mb4 編碼, 而非舊的 utf8 (即 utf8mb3),因為舊版只支援 3 位元組,而 utf8mb4 支援 4 位元組。

修改 MariaDB 設定檔 /etc/my.cnf

[client]
default-character-set = utf8mb4

[mysql]
default-character-set = utf8mb4

[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
character-set-client-handshake=FALSE

使用下列的指令重新啟動 MariaDB server:

sudo systemctl restart mariadb

重啟 MariaDB 服務後,登入並執行以下指令檢查:

SHOW VARIABLES LIKE 'character_set%';
SHOW VARIABLES LIKE 'collation%';

接下來建立一個新的資料庫以及新的使用者。

CREATE DATABASE `danilo`;

新增一個 MariaDB 資料庫使用者 danilo(Local access only),並將密碼設定為 danilo:

CREATE USER 'danilo'@'localhost' IDENTIFIED BY 'danilo';

給予 danilo 帳號在 danilo 資料庫上面的所有權限,讓 danilo 可以對 danilo 資料庫進行任何操作:

GRANT ALL PRIVILEGES ON danilo.* TO 'danilo'@'localhost';

執行下列的指令,讓修改確定有生效:

FLUSH PRIVILEGES;

檢查是否資料無誤:

SHOW GRANTS FOR 'danilo'@'localhost';

使用 GRANT 設定好帳號的權限後,接著就可以使用新的帳號登入使用:

mariadb -u danilo -p

MariaDB 採用可插拔儲存引擎架構,這意味著同一資料庫中的不同資料表可以使用不同的引擎,每個引擎都針對特定的工作負載進行了最佳化。 預設且最常用的通用引擎是 InnoDB。 MariaDB 可以使用 RocksDB 作為儲存引擎(稱為 MyRocks),只能在 x86_64 下使用,並不支援 32 位元的架構。 要注意的是,如果使用 MyRocks 作為儲存引擎,雖然寫入的速度獲得提升,但是 RocksDB engine 並不支援 FOREIGN KEY

修改 MariaDB 設定檔 /etc/my.cnf

[mariadb]
plugin-load-add = ha_rocksdb

重啟 MariaDB 服務後,登入並執行以下指令檢查:

SHOW ENGINES;

在啟用後,建立資料表時指定儲存引擎就可以使用 RocksDB 作為儲存引擎。 下面是一個例子:

CREATE TABLE sensor_data (
    id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
    timestamp DATETIME NOT NULL,
    sensor_id SMALLINT UNSIGNED NOT NULL,
    value FLOAT NOT NULL
) ENGINE = RocksDB;

也可以先建立資料表,再使用下列的指令指定要使用的儲存引擎

ALTER TABLE sensor_data ENGINE=RocksDB;

如果要讓 RocksDB 成為預設的儲存引擎,需要修改 MariaDB 設定檔 /etc/my.cnf。 注意:在一般的情況下使用預設的 InnoDB 是不錯的主意, 因此需要的時候才在建立資料格的時候設定儲存引擎或者使用 ALTER TABLE 修改是一個不錯的做法。 這裡只是記錄如果要修改預設的儲存引擎應該怎麼做。

[mysqld]
#default-storage-engine=InnoDB
default-storage-engine=rocksdb

重啟 MariaDB 服務後,登入並執行以下指令檢查:

SHOW VARIABLES LIKE 'default_storage_engine';

MariaDB 可以使用 Mroonga 作為儲存引擎, Mroonga 支援包含 Chinese, Japanese, and Korean (CJK) 在內的全文搜尋功能,只能在 x86_64 下使用,並不支援 32 位元的架構。

修改 MariaDB 設定檔 /etc/my.cnf

[mariadb]
plugin-load-add = ha_rocksdb
plugin-load-add = ha_mroonga

也可以使用 SQL 指令啟用:

INSTALL SONAME 'ha_mroonga';

重啟 MariaDB 服務後,登入並執行以下指令檢查:

SHOW ENGINES;

相關連結

2026/04/14

C++ Crow

C++ 並不是網頁開發(Web Development)的首選語言, 但是在對效能十分要求或者是需要在資源略為受限的環境執行時,可以考慮使用 C++。 就目前而言,C++ 較為有名的選擇為效能極高並且支援多項功能的 Drogon, 不用安裝依賴函式庫的 Oat++, 以及設計上較為輕量但是功能也較少的 Crow。 還有一個選擇是 Wt, 但是就我個人而言,我認為 Wt 的策略,也就是使用 C++ 撰寫 UI 然後函式庫再轉譯為 HTML/CSS/JavaScript 的方式並不是一個好的做法。

Crow 是一套 C++ micro web framework,採用 Header Only 的設計,其靈感來自於 Python's Flask, 支援 HTTP 1.1 以及 Websocket,使用 C++ ASIO library 構建,特別適合建立 RESTful API 或 Web 服務。 Crow 的原作者在 2017 年停止維護, 不過之後自由軟體社群有人複製出來一份新的分支接手進行維護,目前仍然持續開發中。

在安裝前需要已經先安裝 ASIO development files,下面是在 openSUSE 的安裝指令:

sudo zypper in asio-devel

我使用 source code 安裝:

git clone https://github.com/CrowCpp/Crow.git
mkdir build; cd build; cmake .. -DCROW_BUILD_EXAMPLES=OFF -DCROW_BUILD_TESTS=OFF
sudo make install

(在不編譯範例以及測試程式的情況下,Crow 只會安裝 CMake 相關檔案以及 header files,所以不用執行 make, 只需要使用 make install 安裝)

Crow 預設的靜態檔案資源放置在 static 目錄,但是可以透過巨集設定目錄。接下來寫一個簡單的靜態網頁伺服器驗證可以正確編譯與使用。

main.cpp

#define CROW_STATIC_DIRECTORY "public"
#define CROW_STATIC_ENDPOINT "/<path>"
#include "crow.h"

int main()
{
    crow::SimpleApp app;

    CROW_ROUTE(app, "/")
    ([](const crow::request&, crow::response& res) {
        res.set_static_file_info("public/index.html");
        res.end();
    });

    app.port(18080).run();
    return 0;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.15)
project(Simple)

set(CMAKE_CXX_STANDARD 17)

# Search for Crow and required dependencies
find_package(Crow REQUIRED)

add_executable(simple main.cpp)

# Link the Crow library to your executable
target_link_libraries(simple PRIVATE Crow::Crow)

Crow 使用 Mustache 作為 template engine language, 接下建立一個簡單的網頁進行測試。 使用 Mustache 撰寫的網頁需要放在 templates 目錄下。

templates/fancypage.html

<!DOCTYPE html>
<html>
  <body>
  <p>Hello {{person}}!</p>
</body>
</html>

main.cpp

#include "crow.h"
// #include "crow_all.h"

int main() {
    crow::SimpleApp app;

    CROW_ROUTE(app, "/<string>")([](std::string name) {
        auto page = crow::mustache::load("fancypage.html");
        crow::mustache::context ctx({{"person", name}});
        return page.render(ctx);
    });

    app.port(18080).multithreaded().run();
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.15)
project(Simple)

set(CMAKE_CXX_STANDARD 17)

# Search for Crow and required dependencies
find_package(Crow REQUIRED)

add_executable(simple main.cpp)

# Link the Crow library to your executable
target_link_libraries(simple PRIVATE Crow::Crow)

編譯成功後執行,使用瀏覽器瀏覽 http://localhost:18080/Bob 進行測試。

參考連結

2026/03/10

魔獸爭霸3

《魔獸爭霸III:混亂之治》(Warcraft III: Reign of Chaos)是暴雪娛樂出品的即時戰略遊戲,《魔獸爭霸》系列第三代作品, 於 2002 年發行。2003 年 5 月暴雪公司又發行了《魔獸爭霸III:寒冰霸權》(Warcraft III: The Frozen Throne), 它是本作的資料片。使用光碟安裝需要 8RTJDU-IH67-BQEL2A-BPSS-NX3197 與 728Y2W-B292-X7PCXZ-PPDX-PKCN72 之類的 CD key。 暴雪娛樂在《魔獸爭霸3》 1.21b 移除了光碟檢查機制,1.27b 則是最後一版可以離線安裝的版本。 《魔獸爭霸III:淬鍊重生》(Warcraft III: Reforged)是由暴雪娛樂開發與發行的即時戰略遊戲,在 2020 年發行, 為 2002 年的《魔獸爭霸3》之復刻版。

可以參考的資料:

使用 winetricks 安裝 lavfilters 解決在使用 Wine 11.3 執行遊戲時部份遊戲動畫黑屏的問題。

winetricks lavfilters

使用 -opengl 參數指定執行遊戲時使用 OpenGL (obsolete as of 1.29) 。


《魔獸爭霸3》使玩家集中注意力於「控制戰鬥單位」而非「生產戰鬥單位」。因此遊戲要求玩家關注於小規模的戰鬥而非宏觀的調兵遣將。 有四個種族在《魔獸爭霸3》中可以選擇,其中兩個在前作《魔獸爭霸2》已經出現,分別是 Human,Orc。 另外兩個則是《魔獸爭霸3》新增的種族:Undead,Night Elf。Naga 在製作《魔獸爭霸III:寒冰霸權》時曾經設想作為第五個可操控的種族, 但在遊戲測試時刪去。而官方解釋是:加上那迦族之後很難保證遊戲的平衡性,因為那迦族的部隊可直接跨過水域。

Gold 是遊戲中主要使用的資源,Lumber 則是第二種需要採集的資源。 Gold 只需要固定的人數採集(五人),更多的人數並不會增加採集速度,這是一個微小但是不錯的改善。

《魔獸爭霸3》加入了角色扮演遊戲的一些要素在即時戰略遊戲中,也就是全新的戰鬥單位英雄 (Hero), 是可以透過獲取經驗值升級並增強能力的超級戰鬥單位。 英雄有 Strength, Agility, Intelligence 三種屬性,其中一種屬性為某個英雄的主要屬性。 其可分為本族英雄和中立英雄。每當己方殺滅一個敵方單位或中立生物時,英雄便可獲取相當的經驗值而累積一定程度時其等級就會上升, 英雄等級最高為 10 級。等級每當上升時,英雄便可通過添加技能點學習或加強一項技能,不同英雄可學習之技能亦有所異。 遊戲過程中,一方最多可以有三個英雄出現(採用三級主堡制,所以需要主堡升級才能夠生產另外一個英雄)。 而如果英雄在戰鬥中陣亡,則可在祭壇中復活並且重生回到戰場上。

中立生物 (creeps) 可視為遊戲中創新之處。即使在多人遊戲中,也有完全由電腦控制的中立生物,以守衛地圖中關鍵的位置及中立的建築物。 它也成為選手間爭奪的資源,擊殺中立生物後便可獲得相當的經驗值、金錢及物品。使這個類似於角色扮演遊戲的玩家更具「攻擊性」而不會過於防衛, 從而提高遊戲的娛樂性及可觀性。

而另一影響遊戲策略的操施就是維護費徵取 ("upkeep" system),此操施源於一種概念——擁有越多戰鬥單位就須要付出越多的資源去滿足部隊的需要, 如士兵需要補給、武器需要打造、盔甲需要鍛煉等。該遊戲則以上稅實行此概念。當玩家部隊的數目到達一定程度時, 就須於每採集十塊黃金上繳其中三塊。當部隊數目抵達更高程度時,每十塊就須上繳六塊。 《魔獸爭霸III:混亂之治》的最大人口數為 90,《魔獸爭霸III:寒冰霸權》略為增加到 100。 也因為有維持費的設計,兵力的增長是有一些限制的,這使得玩家沒有不停的生產士兵以及運送士兵到前線的壓力, 讓玩家更著重兵力的多元化和相剋性。

  • Human
    • Well-rounded units
    • Buildings can be built anywhere
    • Peasants can become Militia at the main Town Hall
    • Multiple workers can be used to construct buildings faster
  • Orc
    • Very beefy and expensive units
    • Burrows coupled with Peons are used as a defense
    • Orc peons are immune while constructing buildings
    • Several orc units can be upgraded to salvage gold and lumber when attacking enemy buildings
  • Night Elf
    • Several buildings (called "Ancients") can uproot to move and attack land units
    • Night elf units have improved vision during nighttime after researching Ultravision
    • Wisps are immune while harvesting gold and can harvest lumber anywhere without needing to return their resources
    • Wisps sacrifice themselves when building Ancients
  • Undead
    • Can use corpses as a resource
    • Buildings must be built upon Blight
    • Acolytes only need to begin a building's summoning
    • Acolytes can harvest gold at Haunted Gold Mines without returning their resources

中立英雄為《魔獸爭霸III:寒冰霸權》加入的新機制,可以在 Tavern 雇用中立英雄。

Race Heroes
Human Alliance Paladin, Archmage, Mountain King, Blood Mage
Orcish Horde Blademaster, Far Seer, Tauren Chieftain, Shadow Hunter
Undead Scourge Death Knight, Lich, Dreadlord, Crypt Lord
Night Elf Sentinels Keeper of the Grove, Priestess of the Moon, Demon Hunter, Warden
Neutral Naga Sea Witch, Dark Ranger, Pandaren Brewmaster, Beastmaster, Pit Lord, Tinker, Firelord

每個種族的工人都是重要的單位,負責採集資源以及建造建築,包含 Human 的 Peasant, Orc 的 Peon,Night Elf 的 Wisp,以及 Undead 的 Acolyte。 增加人口總數的方法為建造特定的建築,有二個種類,主堡 (Main Building) 和糧食供應建築 (Food Supply), 包含 Human 的 Town Hall (+12) 和 Farm (+6), Orc 的 Great Hall (+10, +11 after 1.32.9) 和 Orc Burrow (+10), Night Elf 的 Tree of Life (+10) 和 Moon Well (+10), 以及 Undead 的 Necropolis (+10) 和 Ziggurat (+10)。 其中 Human 的 Town Hall 可以使用 Call to Arms 暫時將 Peasant 轉變成 Militia 進行防禦, Orc Burrow 可以讓工人 Peon 躲進去防守,Night Elf 的 Moon Well 具有補充法力和生命值的能力, Undead 的 Ziggurat 可以升級為 Nerubian Tower 或者是 Spirit Tower。

一般而言,祭壇 (Altar) 會是第一個建造的建築(Human 的 Altar of Kings、 Orc 的 Altar of Storms、Night Elf 的 Altar of Elders),但 Undead 與其它三個種族略有差別。 Undead 使用 Acolyte 收集 Gold,Ghoul 採集 Lumber, 而不像 Human 的工人 Peasant、Orc 的工人 Peon 以及 Night Elf 的工人 Wisp 可以採集 Gold 與 Lumber, 因此 Undead 第一個建築最好是 Crypt,而後才建造 Altar of Darkness。

《魔獸爭霸III:寒冰霸權》加入了各種族的商店,包含 Human 的 Arcane Vault, Orc 的 Voodoo Lounge,Night Elf 的 Ancient of Wonders,Undead 的 Tomb of Relics, 玩家可以在自己的商店購買與販賣物品。

《魔獸爭霸3》有以下 Attack Types 的區別:

  • Normal (most melee units)
    Normal attacks do extra damage against Medium armor, and reduced damage to Fortified armor.
  • Piercing (most ranged attackers)
    Piercing attacks do extra damage to Unarmored units and Light armor, and reduced damage to Fortified, Medium armor, and Heroes.
  • Siege (artillery)
    Siege attacks do extra damage to Fortified armor and Unarmored units, and reduced damage to Medium armor and Heroes.
  • Chaos (A few Creeps, Powerful Summoned Units)
    Chaos attacks do full damage to all armor types.
  • Magic Damage (most spellcasters, air units)
    Magic attacks do extra damage against Light and Heavy armor, and reduced damage to Medium, Fortified armor, and Heroes. Magic attacks do 66% extra damage to ethereal units, and zero damage to magic-immune units.
  • Hero
    Hero attacks do reduced damage to Fortified armor.

《魔獸爭霸3》有以下 Armor Types 的區別:

  • Unarmored (most spellcasters)
    Unarmored takes extra damage from Piercing, and Siege attacks. Some unarmored units such as Huntress, Gargoyle, Hippogryph, and Druid of the Talon in Storm Crow form have armor for balance reasons. Most unarmored units do not have armor.
  • Light (most flying units)
    Light armor takes extra damage from Piercing and Magic attacks.
  • Medium (most ranged attackers and workers)
    Medium armor takes extra damage from Normal attacks, reduces damage from Piercing, Magic, and Siege attacks.
  • Heavy (most low and high tech melee units)
    Heavy armor takes extra damage from Magic attacks.
  • Hero
    Heroes take reduced damage from Piercing, Magic, Spell, and Siege attacks.
  • Fortified (buildings)
    Fortified armor greatly reduces Piercing, Magic, Hero, and Normal attacks, but takes extra damage from Siege attacks.

遊戲中一些技能有自動施法 (Autocast) 的設計。自動施法技能是指可以開啟自動施放或關閉手動施放的技能。 開啟自動施法後,技能會在條件允許時自動施放。

《魔獸爭霸3》中的技能使用可以區分為 Active, Channel, Passive 以及 Aura。 Channel 技能要求使用者在技能啟動期間保持靜止。如果技能被打斷或取消,效果就會結束。 可以中斷的技能包含:

  • Human
    • Sorceress - Polymorph (uncastable on heroes before 1.36.2)
    • Mountain King - Storm Bolt
    • Dragonhawk Rider - Aerial Shackles (air units only)
  • Orc
    • Raider - Ensnare
    • Shadow Hunter - Hex
    • Tauren Chieftain - War Stomp
  • Night Elf
    • Druid of the Talon - Cyclone
    • Keeper of the Grove - Entangling Roots
  • Undead
    • Crypt Lord - Impale
    • Dreadlord - Sleep
    • Frost Wyrm - Freezing Breath (buildings only)
  • Neutral
    • Tinker - Cluster Rockets
    • Firelord - Soul Burn
    • Firelord - Volcano
    • Dark Ranger - Silence
    • Dark Ranger - Charm (uncastable on heroes)
    • Pit Lord - Doom (uncastable on heroes)

Aura(靈氣) 與 Passive 技能類似,沒有冷卻時間或法力消耗, Passive 技能影響的是單位或者是英雄本身,擁有 Aura 技能的單位影響周圍區域內的多個目標。 Aura 技能其種類包含 Archmage 的 Brilliance Aura, Paladin 的 Devotion Aura,Tauren Chieftain 的 Endurance Aura, Kodo Beast 的 War Drums Aura,Keeper of the Grove 的 Thorns Aura, Priestess of the Moon 的 Trueshot Aura,Death Knight 的 Unholy Aura, Dreadlord 的 Vampiric Aura。其中只有 Orc 的 Kodo Beast 並非英雄單位。

2026/02/16

Apache Drill

Apache Drill 使用 Java 撰寫, 它參考了谷歌的 BigQuery 的想法,是一個開放原始碼、可擴展、支援複雜資料的分散式 columnar SQL 查詢引擎, 它可以訪問 Apache Hadoop 的 HDFS,從各種主流文件格式(例如 Parquet、JSON 和 CSV)以及支援的資料庫中讀取數據。 Apache Drill 的優點就是它可以使用 SQL 查詢語言查詢各種不同格式的資料並且與資料互動。

下載程式後解壓縮放在某個目錄,使用 embedded mode 檢查是否可以執行:

bin/drill-embedded

結束程式:

!quit

Distributed Mode 需要 Apache ZooKeeper,並且在 drill-override.conf 設定相關的設定。

drill.exec: {
  cluster-id: "drillbits1",
  zk.connect: "localhost:2181"
}

首先要先啟動 ZooKeeper service。
再來是啟動 Drill server:

bin/drillbit.sh start

停止 server:

bin/drillbit.sh stop

執行 sqlline 驗證可以連線到 Drill server:

bin/sqlline -u jdbc:drill:schema=dfs;zk=localhost

SSL/TLS

使用 keytool 建立 keystroe:

keytool -genkeypair -alias server \
-dname "CN=localhost, OU=IT Department, O=Orange Inc. ,L=Taipei, S=Taiwan,C=TW" \
-ext SAN=DNS:localhost,IP:127.0.0.1 \
-keyalg RSA -keysize 2048 -sigalg SHA256withRSA -storetype PKCS12 \
-validity 3650 \
-keypass password -keystore ./trusted.keystore -storepass password

在 drill-override.conf 設定相關的設定。

drill.exec: {
  cluster-id: "drillbits1",
  zk.connect: "localhost:2181",
  ssl: {
    protocol: "TLSv1.3",
    keyStoreType: "pkcs12",
    keyStorePath: "/home/danilo/Programs/drill/conf/trusted.keystore",
    keyStorePassword: "password",
    trustStoreType: "pkcs12",
    trustStorePath: "/home/danilo/Programs/drill/conf/trusted.keystore",
    trustStorePassword: "password"
  },
  security.user.encryption.ssl: {
    enabled: true,
  },
}

在 drill-override.conf 設定相關的設定,對 WEB UI 允許 SSL/TLS:

drill.exec: {
  cluster-id: "drillbits1",
  zk.connect: "localhost:2181",
  ssl: {
    protocol: "TLSv1.3",
    keyStoreType: "pkcs12",
    keyStorePath: "/home/danilo/Programs/drill/conf/trusted.keystore",
    keyStorePassword: "password",
    trustStoreType: "pkcs12",
    trustStorePath: "/home/danilo/Programs/drill/conf/trusted.keystore",
    trustStorePassword: "password"
  },
  security.user.encryption.ssl: {
    enabled: true,
  },
  http: {
    enabled: true,
    ssl_enabled: true,
  },
}

Plain Security

在 drill-override.conf 設定相關的設定,允許 Plain Security:

drill.exec: {
  cluster-id: "drillbits1",
  zk.connect: "localhost:2181",
  ssl: {
    protocol: "TLSv1.3",
    keyStoreType: "pkcs12",
    keyStorePath: "/home/danilo/Programs/drill/conf/trusted.keystore",
    keyStorePassword: "password",
    trustStoreType: "pkcs12",
    trustStorePath: "/home/danilo/Programs/drill/conf/trusted.keystore",
    trustStorePassword: "password"
  },
  security: {
    auth.mechanisms : ["PLAIN"],
  },
  security.user.auth {
    enabled: true,
    packages += "org.apache.drill.exec.rpc.user.security",
    impl: "pam4j",
    pam_profiles: [ "sudo", "login" ]
  },
  security.user.encryption.ssl: {
    enabled: true,
  },
  http: {
    enabled: true,
    ssl_enabled: true,
    auth: {
      mechanisms: ["FORM"],
    },
  },
}

Apache Drill 支援使用 libpam4j 或者是 jpam 作為 PAM Authenticator。 其中 libpam4j 已被內建,所以只要設定正確就可以使用。

相關連結

2026/02/12

Apache HBase

Apache HBase 是一個開放原始碼、分散式、 列存儲 (Wide column store) 的 NoSQL 資料庫。 它參考了谷歌的 BigTable 論文,實現的程式語言為 Java,建構於 Apache Hadoop HDFS 之上, 其核心架構採用 Master-Slave 模式,主要包含協調服務 ZooKeeper、 管理節點 HMaster、處理資料讀寫的 RegionServers,以及負責儲存資料的 HDFS。 Apache HBase 安裝與 Apache Hadoop 一樣分為三種模式:Standalone, Pseudo-Distributed 與Fully-Distributed。 Fully-Distributed 只能運行在Apache Hadoop 上面。

將下載的 HBase 檔案放到 /home/danilo/Programs/hbase,並且設定好環境變數:

export HBASE_HOME=/home/danilo/Programs/hbase
export PATH=$PATH:$HBASE_HOME/bin

使用 Pseudo-Distributed 與 local file system 安裝模式,修改 hbase-site.xml

<configuration>
  <property>
    <name>hbase.rootdir</name>
    <value>file:///home/danilo/Programs/hbase</value>
  </property>
  <property>
    <name>hbase.tmp.dir</name>
    <value>./tmp</value>
  </property>
  <property>
    <name>hbase.unsafe.stream.capability.enforce</name>
    <value>false</value>
  </property>
  <property>
    <name>hbase.cluster.distributed</name>
    <value>true</value>
  </property>  
  <property>
    <name>hbase.zookeeper.property.dataDir</name>
    <value>/var/lib/zookeeper</value>
  </property>
  <property>
    <name>hbase.zookeeper.quorum</name>
    <value>localhost</value>
  </property>
  <property>
    <name>hbase.zookeeper.property.clientPort</name>
    <value>2181</value>
  </property>  
</configuration>

修改 hbase-env.sh

# Tell HBase whether it should manage it's own instance of ZooKeeper or not.
export HBASE_MANAGES_ZK=false

(HBase 可以設定是否自行管理自己的 ZooKeeper 叢集,這裡選擇使用外部的 ZooKeeper service。)

首先需要啟動 ZooKeeper service。
再來啟動 HBase:

bin/start-hbase.sh

停止 HBase:

bin/stop-hbase.sh

如果要執行 HBase shell:

bin/hbase shell

REST interface

啟動 HBase REST server(前景,使用 -port 指定 port):

bin/hbase rest start -p 8090

啟動 HBase REST server(背景,使用 -port 指定 port):

bin/hbase-daemon.sh start rest -p 8090

停止 HBaseHBase REST server(背景):

bin/hbase-daemon.sh stop rest

SSL/TLS

使用 keytool 建立 keystroe:

keytool -genkeypair -alias server \
-dname "CN=localhost, OU=IT Department, O=Orange Inc. ,L=Taipei, S=Taiwan,C=TW" \
-ext SAN=DNS:localhost,IP:127.0.0.1 \
-keyalg RSA -keysize 2048 -sigalg SHA256withRSA -storetype PKCS12 \
-validity 3650 \
-keypass password -keystore ./trusted.keystore -storepass password

修改 hbase-site.xml

<configuration>
  <property>
    <name>hbase.rootdir</name>
    <value>file:///home/danilo/Programs/hbase</value>
  </property>
  <property>
    <name>hbase.tmp.dir</name>
    <value>./tmp</value>
  </property>
  <property>
    <name>hbase.unsafe.stream.capability.enforce</name>
    <value>false</value>
  </property>
  <property>
    <name>hbase.cluster.distributed</name>
    <value>true</value>
  </property>  
  <property>
    <name>hbase.zookeeper.property.dataDir</name>
    <value>/var/lib/zookeeper</value>
  </property>
  <property>
    <name>hbase.zookeeper.quorum</name>
    <value>localhost</value>
  </property>
  <property>
    <name>hbase.zookeeper.property.clientPort</name>
    <value>2181</value>
  </property>
  <property>
    <name>hbase.rest.ssl.enabled</name>
    <value>true</value>
  </property> 
  <property>
    <name>hbase.rest.ssl.keystore.store</name>
    <value>/home/danilo/Programs/hbase/conf/trusted.keystore</value>
  </property> 
  <property>
    <name>hbase.rest.ssl.keystore.password</name>
    <value>password</value>
  </property> 
  <property>
    <name>hbase.rest.ssl.keystore.type</name>
    <value>pkcs12</value>
  </property>
  <property>
    <name>hbase.rest.ssl.truststore.store</name>
    <value>/home/danilo/Programs/hbase/conf/trusted.keystore</value>
  </property>
  <property>
    <name>hbase.rest.ssl.truststore.password</name>
    <value>password</value>
  </property>
  <property>
    <name>hbase.rest.ssl.truststore.type</name>
    <value>pkcs12</value>
  </property>
</configuration>

再來重新啟動 HBase server 與 HBase REST server 即可。

相關連結

2026/02/08

Apache ZooKeeper

Apache ZooKeeper 是 Apache 軟體基金會的一個軟體專案, 使用 Java 撰寫,它為大型分散式計算提供開源的分散式組態設定服務、同步服務和命名註冊。 ZooKeeper 曾經是 Hadoop 的一個子專案,但現在是一個獨立的頂級專案。

下面為設定 single node (Standalone Operation) 的方式。

將下載的 Apache ZooKeeper 檔案解壓縮以後放到 /opt/zookeeper,並且建立 /var/lib/zookeeper 目錄。

複製 conf/zoo_sample.cfg 為 conf/zoo.cfg,確定有下列的設定:

tickTime=2000
dataDir=/var/lib/zookeeper
clientPort=2181

啟動 server:

sudo bin/zkServer.sh start

如果要停止 server:

sudo bin/zkServer.sh stop

確定可以連線:

bin/zkCli.sh -server localhost:2181

接下來嘗試使用 systedm 管理,在 /usr/lib/systemd/system 目銾下建立 zookeeper.service

[Unit]
Description=ZooKeeper Service
Documentation=http://zookeeper.apache.org
Requires=network.target
After=network.target

[Service]
Type=forking
User=root
Group=root
SuccessExitStatus=143 # For stop service but status code is failed
ExecStart=/opt/zookeeper/bin/zkServer.sh start /opt/zookeeper/conf/zoo.cfg
ExecStop=/opt/zookeeper/bin/zkServer.sh stop /opt/zookeeper/conf/zoo.cfg
ExecReload=/opt/zookeeper/bin/zkServer.sh restart /opt/zookeeper/conf/zoo.cfg
WorkingDirectory=/var/lib/zookeeper
TimeoutSec=30
Restart=on-failure

[Install]
WantedBy=default.target

而後使用 systemd 啟動服務:

sudo systemctl start zookeeper.service

而後使用 systemd 查詢服務狀態:

sudo systemctl status zookeeper.service

而後使用 systemd 停止服務:

sudo systemctl stop zookeeper.service

相關連結

2026/02/04

Apache Geode

簡介

Apache Geode 是一個數據管理平台, 它為廣泛分佈的雲端架構中的資料密集型應用程式提供即時、一致的存取, 一般而言作為 In-Memory Data Grid (IMDG)、快取 (cache) 以及需要即時處理的場合使用。 Apache Geode 提供了 SQL-like 查詢語言,稱為 OQL (Object Query Language), Apache Geode 以 Java object 的方式儲存資料,所以選擇使用 OQL 查詢儲存的資料。

下載 Apache Geode 以後解壓縮放到某個目錄。gfsh 為 Apache Geode 用來管理的 shell tool。

執行 gfsh。下面是在 gfsh 執行的指令,資料來自於 Apache Geode in 15 Minutes or Less 的教學。

Locator 是 Geode 行程 (processes),它告訴新連線的成員正在執行的成員在哪裡,並為伺服器使用提供負載平衡。

start locator --name=locator1

Geode 提供了 web 界面的監控界面,下面是啟動的指令。預設使用者為 admin,密碼為 admin

start pulse

Geode server 是一個行程 (process),它作為叢集中一個長期運行且可配置的成員而存在。 Geode server 主要用於託管長期運行的資料區域,以及運行標準的 Geode 行程,例如用戶端/伺服器配置中的伺服器。

start server --name=server1 --server-port=40411

Regions 是 Geode 叢集的核心建置模組,用於組織資料。在此練習中建立的 Region 採用複製機制在叢集成員之間複製數據, 並利用持久化機制將資料儲存到磁碟。

create region --name=regionA --type=REPLICATE_PERSISTENT

列出目前的 regions:

list regions

列出 Geode 叢集的成員:

list members

描述 Geode region regionA 的資料:

describe region --name=regionA

下面使用 put 新增資料以及使用 query 查詢資料。

put --region=regionA --key="1" --value="one"
put --region=regionA --key="2" --value="two"
query --query="select * from /regionA"

如果你需要刪除一個 region,可以這樣做:

destroy region --name=regionA

如果要停止 server:

stop server --name=server1

關閉系統,包括 locator。

shutdown --include-locators=true

REST

Geode 讓使用者能夠使用 REST 介面存取資料。

啟動一個 locator。

start locator --name=locator1

並且使用以下的設定:

configure pdx --read-serialized=true --disk-store

然後在啟動 Geode server 時加入 --start-rest-api 選項。

start server --name=server1 --server-port=40411 \
--start-rest-api=true \
--http-service-port=8080 --http-service-bind-address=localhost

使用 curl 驗證是否可以使用:

curl -i http://localhost:8080/geode/v1

SSL

使用 keytool 建立 keystroe:

keytool -genkeypair -alias server \
-dname "CN=localhost, OU=IT Department, O=Orange Inc. ,L=Taipei, S=Taiwan,C=TW" \
-ext SAN=DNS:localhost,IP:127.0.0.1 \
-keyalg RSA -keysize 2048 -sigalg SHA256withRSA -storetype PKCS12 \
-validity 3650 \
-keypass password -keystore ./trusted.keystore -storepass password

在 Geode 的 config 目錄下建立一個新的檔案 gfsecurity.properties。 Apache Geode 使用 ssl-enabled-components 設定不同組件間的通訊是否需要使用 SSL/TLS。 all 表示全部都要使用,這裡設定為 web,表示使用在 REST 介面。

ssl-enabled-components=web
ssl-protocols=TLSv1.2,TLSv1.3
ssl-ciphers=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_256_GCM_SHA384
ssl-keystore=/home/danilo/Programs/geode/config/trusted.keystore
ssl-keystore-password=password
ssl-keystore-type=pkcs12
ssl-truststore=/home/danilo/Programs/geode/config/trusted.keystore
ssl-truststore-password=password
ssl-truststore-type=pkcs12

使用 gfsh 啟動一個 locator。

start locator --name=locator1 --port=12345 \
--security-properties-file=/home/danilo/Programs/geode/config/gfsecurity.properties

並且使用以下的設定:

configure pdx --read-serialized=true --disk-store

使用 gfsh 啟動一個 server。

start server --name=server1 --server-port=40411 \
--start-rest-api=true \
--http-service-port=8080 --http-service-bind-address=localhost \
--security-properties-file=/home/danilo/Programs/geode/config/gfsecurity.properties

使用 curl 驗證是否可以使用:

curl -k -i https://localhost:8080/geode/v1

Authentication

以下的方式在 Java 24/25 以後,因為 SecurityManager 被禁止而無法使用。 In Java 17, the Security Manager was deprecated for removal under JEP 411. With JDK 24, its functionality will be effectively disabled. So you could not setup HTTP Basic Authentication support for Apache Geode by using SecurityManager since JDK 24.

將下列的內容儲存為 security.json,並且放到各個 locator 與 server 的目錄下。

{
  "roles": [
    {
      "name": "cluster",
      "operationsAllowed": [
        "CLUSTER:MANAGE",
        "CLUSTER:WRITE",
        "CLUSTER:READ"
      ]
    },
    {
      "name": "data",
      "operationsAllowed": [
        "DATA:MANAGE",
        "DATA:WRITE",
        "DATA:READ"
      ]
    },
    {
      "name": "region1&2Reader",
      "operationsAllowed": [
        "DATA:READ"
      ],
      "regions": ["region1", "region2"]
    }
  ],
  "users": [
    {
      "name": "super-user",
      "password": "1234567",
      "roles": [
        "cluster",
        "data"
      ]
    },
    {
      "name": "joebloggs",
      "password": "1234567",
      "roles": [
        "data"
      ]
    }
  ]
}

使用 gfsh 啟動一個 locator。

start locator --name=locator1 --port=12345 \
--security-properties-file=/home/danilo/Programs/geode/config/gfsecurity.properties \
--J=-Dgemfire.security-manager=org.apache.geode.examples.security.ExampleSecurityManager \
--classpath=.

連線到 locator 需要驗證:

connect --locator=localhost[12345] --user=super-user --password=1234567

並且使用以下的設定:

configure pdx --read-serialized=true --disk-store

使用 gfsh 啟動一個 server。

start server --name=server1 --locators=localhost[12345] --server-port=40411 \
--start-rest-api=true \
--http-service-port=8080 --http-service-bind-address=localhost \
--security-properties-file=/home/danilo/Programs/geode/config/gfsecurity.properties \
--J=-Dgemfire.security-manager=org.apache.geode.examples.security.ExampleSecurityManager \
--classpath=. --user=super-user --password=1234567

Memcached

Apache Geode 提供了 memcached 協議相容的介面。

使用 gfsh 啟動一個 locator。

start locator --name=locator1

並且在 config 新增或者修改 cache.xml,使用下列的設定:

<?xml version="1.0" encoding="UTF-8"?>
<cache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xmlns="http://geode.apache.org/schema/cache" 
       xsi:schemaLocation="http://geode.apache.org/schema/cache http://geode.apache.org/schema/cache/cache-1.0.xsd"
       version="1.0">
  <region name="gemcached">
    <region-attributes refid="PARTITION" />
  </region>
</cache>

使用 gfsh 啟動一個 server。

start server --name=server1 --server-port=40411 \
--memcached-port=11211 --memcached-bind-address=localhost \
--memcached-protocol=BINARY \
--cache-xml-file=/home/danilo/Programs/geode/config/cache.xml

--memcached-protocol 可以設為 ASCII 或者是 BINARY。 ASCII 是 libMemcached 的預設值,如果要使用 BINARY 需要設定。

使用 memcached-for-Tcl 進行驗證。

package require Memcache

memcache server add localhost 11211
memcache behavior MEMCACHED_BEHAVIOR_BINARY_PROTOCOL 1
memcache set moo "cows go moo"
memcache get moo result
puts $result

相關連結

2026/02/02

BaseX database

BaseX 是一個使用 Java 撰寫的 XML 資料庫, 也可以作為 XQuery processor 使用,採用 BSD-3-Clause 授權, 使用 XQuery 作為其查詢語言,實作 XQuery 多項相關標準, 並且設計了自己的 Server Protocol

basexserver 預設的 port 為 1984,初次啟動需要使用 -cPASSWORD 設定 admin 的密碼。

basexserver -cPASSWORD

下面使用 basexclient 連線到 server(-U 為 username,-P 為 password)。

basexclient -Uadmin -Padmin

建立一個新的使用者 danilo,並且給予全域的 CREATE 權限(BaseX 將權限分為 global 與 local, global 有 NONE, READ, WRITE, CREATE, ADMIN 的區別,而 local 則是 NONE, READ, WRITE)。

CREATE USER danilo password
ALTER PASSWORD danilo password
GRANT CREATE TO danilo

其中 CREATE USER 可以在建立使用者的時候同時設定密碼,在之後如果要修改密碼可以使用 ALTER PASSWORD 修改。

如果要移除使用者:

DROP USER danilo

使用 basexclient 建立新的資料庫:

  • Create an empty database
    CREATE DB <database_name>
    
  • To create a database with initial data
    CREATE DB <database_name> <path/to/input>
    
  • To add multiple documents to a newly created empty database
    CREATE DB store
    ADD factbook.xml
    ADD https://files.basex.org/xml/xmark.xml
    

如果使用 XQuery 建立資料庫:

db:create('myDatabaseName', 'path/to/input.xml')

如果要列出目前的資料庫:

LIST

如果使用 XQuery 列出目前的資料庫:

db:list()

使用 basexclient 刪除資料庫:

DROP DB [name]

如果使用 XQuery 刪除資料庫:

db:drop($name)

Tools

BaseX 提供了一些工具可以使用。 basexserver 為 BaseX database server,而 basexclient 則是其 client 的程式。 basexgui 使用 Java Swing 撰寫,是 BaseX 的圖型界面管理工具,可以用來管理其資料庫以及測試一些 XQuery 語句。 basex 可以用來執行 BaseX 或者是 XQuery 的指令。

下面是一個使用 basex 的例子,用來解 1-9位數不重複印出來的練習問題,
使用者輸入1 印1-9
使用者輸入2 印1-98 (11, 22, 33等重複的不印)
使用者輸入3 印1-987 (121, 988, 667等有重複的不印)

basex -bnumber=9 -Qcount.xq

參數 -b 用來設定外部變數。count.xq 的內容如下:

declare variable $number external;
if (xs:integer($number) lt 1 or xs:integer($number) gt 9) then (
    <error>
      <message>Invalid data</message>
    </error>
) else (
    let $max := xs:integer(math:exp10($number) - 1)
    for $i in 1 to $max
    return
    if(not(matches(fn:string($i), '1.*1|2.*2|3.*3|4.*4|5.*5|6.*6|7.*7|8.*8|9.*9|0.*0'))) then (
        $i
    )
)

相關連結

XQuery 學習筆記

XML 簡介

XML stands for Extensible Markup Language. It is a text-based markup language derived from Standard Generalized Markup Language (SGML).

XML is a markup language that defines set of rules for encoding documents in a format that is both human-readable and machine-readable. Following example shows how XML markup looks, when embedded in a piece of text −

<message>
   <text>Hello, world!</text>
</message>

You can notice there are two kinds of information in the above example −

  • Markup (tag)
  • The text, or the character data

XML 文件可以使用樹 (tree) 來表示。一個 XML 樹開始於 root element, 並且從 root element 開始其 child elements 的分支。 XML elements 可以有屬性 (attributes),例如下面的例子:

<note date="2023/04/28">
  <name>Orange</name>
</note> 

The XML document can optionally have an XML declaration. It is written as follows −

<?xml version = "1.0" encoding = "UTF-8"?>

XPath 簡介

XPath (XML Path Language) is an expression language designed to support the query or transformation of XML documents. It was defined by the World Wide Web Consortium (W3C) in 1999.

In XPath, there are seven kinds of nodes: element, attribute, text, namespace, processing-instruction, comment, and root nodes.

The most important kind of expression in XPath is a location path. A location path consists of a sequence of location steps. Each location step has three components:

  • an axis
  • a node test
  • zero or more predicates.
Axis specifiers in XPath
Full syntax Abbreviated syntax Notes
ancestor
ancestor-or-self
attribute @ @abc is short for attribute::abc
child xyz is short for child::xyz
descendant
descendant-or-self // // is short for /descendant-or-self::node()/
following
following-sibling
namespace
parent .. .. is short for parent::node()
preceding
preceding-sibling
self . . is short for self::node()

Node tests may consist of specific node names or more general expressions. In the case of an XML document in which the namespace prefix gs has been defined, //gs:enquiry will find all the enquiry elements in that namespace, and //gs:* will find all elements, regardless of local name, in that namespace.

Other node test formats are:

comment()
finds an XML comment node, e.g. <!-- Comment -->
text()
finds a node of type text excluding any children, e.g. the hello in <k>hello<m> world</m></k>
processing-instruction()
finds XML processing instructions such as <?php echo $a; ?>. In this case, processing-instruction('php') would match.
node()
finds any node at all.

Predicates, written as expressions in square brackets, can be used to filter a node-set according to some condition. For example, a returns a node-set (all the a elements which are children of the context node), and a[@href='help.php'] keeps only those elements having an href attribute with the value help.php.

XQuery 簡介

XQuery 是由 W3C 定義的查詢語言,程式風格為函數式程式設計 (functional programming), 建立在 XPath 的基礎上,使用 Xpath 表達要查詢的路徑資訊, 專門用於在結構化或半結構化 XML 資料中進行搜尋、操作和轉換,類似 SQL 之於關聯式資料庫。 在 XQuery 3.1 增加了對於 JSON 的支援,所以 XQuery 也可以用來處理 JSON 資料(如果你想要這樣做的話)。

目前 XQuery 的版本如下:

  • XQuery 1.0 became a W3C Recommendation on January 23, 2007
  • XQuery 3.0 became a W3C Recommendation on April 8, 2014
  • XQuery 3.1 became a W3C Recommendation on March 21, 2017

XQuery 所有用於執行計算的 XQuery 語句都是表達式 (expressions),其核心是 FLWOR,用於更複雜的查詢:

  • For: 循環存取資料項目。
  • Let: 賦值。
  • Where: 設定篩選條件。
  • Order By: 排序結果。
  • Return: 定義輸出結果。

下面是一個 XQuery 的 Hello World 例子:

let $message := 'Hello World!'
return
<results>
   <message>{$message}</message>
</results>

下面是一個 XML 檔案 books.xml:

<?xml version="1.0" encoding="UTF-8"?>
<books>
   
   <book category="JAVA">
      <title lang="en">Learn Java in 24 Hours</title>
      <author>Robert</author>
      <year>2005</year>
      <price>30.00</price>
   </book>
   
   <book category="DOTNET">
      <title lang="en">Learn .Net in 24 hours</title>
      <author>Peter</author>
      <year>2011</year>
      <price>70.50</price>
   </book>
   
   <book category="XML">
      <title lang="en">Learn XQuery in 24 hours</title>
      <author>Robert</author>
      <author>Peter</author> 
      <year>2013</year>
      <price>50.00</price>
   </book>
   
   <book category="XML">
      <title lang="en">Learn XPath in 24 hours</title>
      <author>Jay Ban</author>
      <year>2010</year>
      <price>16.50</price>
   </book>
   
</books>

XQuery 可以使用 doc() 函數取得 XML 檔案的內容。下面就是一個 XQuery 的例子:

(: XQuery Comment :)
let $books := (doc("books.xml")/books/book)
return <results>
{
   for $x in $books
   where $x/price>30
   order by $x/price
   return $x/title
}
</results>

XQuery 可以使用 for 執行迴圈任務,如下面所示:

for $n in 1 to 10
return
    <result>{$n}</result>

Sequences represent an ordered collection of items where items can be of similar or of different types. Sequences are created using parenthesis with strings inside quotes or double quotes and numbers as such. XML elements can also be used as the items of a sequence.

Viewing items in a sequence

let $sequence := ('a', 'b', 'c', 'd', 'e', 'f')
let $count := count($sequence)
return
   <results>
      <count>{$count}</count>
      <items>
       {
         for $item in $sequence
         return
           <item>{$item}</item>
       }
      </items>
   </results>

XQuery 內建支援 Regular Expressions,下面是一個例子:

let $input := 'TutorialsPoint Simply Easy Learning'
return (
  matches($input, 'Hello') =  true(),
  matches($input, 'T.* S.* E.* L.*') =  true()
)

XQuery 使用 if-then-else 支援條件判斷。

<result>
{
   for $book in doc("books.xml")/books/book
   return
   if ($book/@category = "XML") then (
     $book/title
   )
}
</result>

XQuery 3.0 加入 lambda functions 的支援,下面是一個例子:

let $fn := function($x, $y) { $x + $y }
return $fn(99, 2)

XQuery 3.0 加入 switch 的支援,下面是一個例子:

for $fruit in ("Apple", "Pear", "Peach")
return switch ($fruit)
  case "Apple" return "red"
  case "Pear"  return "green"
  case "Peach" return "pink"
  default      return "unknown"

XQuery 3.0 加入 try catch 的支援,下面是一個例子:

try {
  1 + '2'
} catch * {
  'Error [' || $err:code || ']: ' || $err:description
}

XQuery 3.0 加入 || operator 作為 String Concatenations 使用,其實際上為 concat() 函數的快捷方式。

'Hello' || ' ' || 'Universe'

XQuery 3.0 加入 Simple Map Operator !,用於將第一個表達式的結果應用於第二個表達式,下面是一個例子:

(1 to 10) ! element node { . }

XQuery 3.1 加入 Arrow Operator operator =>,提供了一種方便的替代語法,用於將函數傳遞給值。 運算子前面的表達式將提供作為箭頭後面函數的第一個參數。

'w e l c o m e' => upper-case() => tokenize() => string-join('-')

下面則是沒有 Arrow Operator operator 之前的寫法:

string-join(tokenize(upper-case('w e l c o m e')), '-')

XQuery 3.1 加入了 Map 與 Array 支援對於 JSON 資料格式的處理。 Map 是將一組鍵與值關聯起來的函數,從而產生一組鍵/值對,用來處理 JSON 的 object。 Array 是將一組位置(以正整數表示的鍵)與值關聯起來的功能。Array 中的第一個位置對應整數 1,用來處理 JSON 的 array。

let $map := map { 'foo': 42, 'bar': 'baz', 123: 456 }
return for-each(map:keys($map), $map)
let $array := array { 48 to 52 }
for $i in 1 to array:size($array)
return $array($i)

Lookup operator 提供了一種語法糖,用於存取 Map 或 Array 元素的值。它以問號 (?) 開頭,後面跟著一個說明符。說明符可以是:

  1. A wildcard *,
  2. The name of the key,
  3. The integer offset, or
  4. Any other parenthesized expression.
let $map := map { 'R': 'red', 'G': 'green', 'B': 'blue' }
return (
  $map?*           (: returns all values; same as: map:keys($map) ! $map(.) :),
  $map?R           (: returns the value for key 'R'; same as: $map('R') :),
  $map?('G', 'B')  (: returns the values for key 'G' and 'B' :)
)
let $maps := (
  map { 'name': 'Guðrún', 'city': 'Reykjavík' },
  map { 'name': 'Hildur', 'city': 'Akureyri' }
)
return $maps[?name = 'Hildur'] ?city

XQuery 3.1 提供了 JSON Serialization,下面是一個例子:

declare option output:method 'json';
map { "key": "value" }

XQuery 3.1 使用 fn:parse-json() 執行 JSON deserialization 的工作:

let $json-input := '{ "firstName": "John", "lastName": "Smith", "address": { "city": "New York" }, "phoneNumbers": ["212-732-1234", "646-123-4567"] }'
let $json-data := fn:parse-json($json-input)
return
  $json-data

XQuery 3.1 支援讀取外部的 JSON 文件檔案。

let $json-data := fn:json-doc("/path/to/data.json")
return $json-data

也可以將 JSON 轉換為 XML 文件:

let $json-string := '{ "name": "John", "age": 30, "city": "New York" }'
return fn:json-to-xml($json-string)

參考資料

2025/11/29

Boost.JSON

Boost.JSON 是一個 C++ JSON parser 函式庫, 提供了雖然不是最快但是也足夠快的執行效率、以及雖然不是最方便但是足以滿足使用者需要的便利使用方式, 就綜合條件來說,我認為是十分優秀的 C++ JSON parser 函式庫。 他有二個使用方式,第一種需要連結函式庫:

#include <boost/json.hpp>

第二種是 header-only:

#include <boost/json/src.hpp>

下面是從一個字串分析 JSON 的測試:

#include <boost/json.hpp>
#include <iostream>
#include <string>

namespace json = boost::json;

int main() {
    const std::string json_str = R"(
        {
            "user": "johndoe",
            "id": 12345,
            "active": true,
            "numbers": [1, 2, 3, 4, 5]
        }
    )";

    // Parse the JSON string
    json::value data = json::parse(json_str);

    // Access the values
    std::string username = json::value_to<std::string>(data.at("user"));
    int user_id = json::value_to<int>(data.at("id"));
    bool is_active = json::value_to<bool>(data.at("active"));

    std::cout << "Username: " << username << std::endl;
    std::cout << "ID: " << user_id << std::endl;
    std::cout << "Active: " << (is_active ? "Yes" : "No") << std::endl;

    // For array
    json::array &arr = data.at("numbers").as_array();
    std::vector<int> numbers;
    for (auto const &value : arr) {
        numbers.push_back(json::value_to<int>(value));
    }

    std::cout << "Parsed Numbers: ";
    for (int num : numbers) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

使用 CMake 編譯,CMakeLists.txt 的內容如下:

cmake_minimum_required(VERSION 3.18)

project(parse)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED True)

find_package(Boost 1.89.0 REQUIRED CONFIG COMPONENTS json)

add_executable(parse parse.cpp)
target_link_libraries(parse PRIVATE Boost::json)

如果採用 header-only 的方式,CMakeLists.txt 的內容如下:

cmake_minimum_required(VERSION 3.18)

project(parse)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED True)

find_package(Boost 1.89.0 REQUIRED CONFIG COMPONENTS)

add_executable(parse parse.cpp)
target_link_libraries(parse)

下面是建立 JSON 內容的測試:

#include <boost/json.hpp>
#include <iostream>
#include <string>

namespace json = boost::json;

int main() {
    // Create a JSON object
    json::object obj;
    obj["user"] = "johndoe";
    obj["id"] = 12345;
    obj["active"] = true;

    // Create a JSON array
    json::array numbers;
    numbers.push_back(1);
    numbers.push_back(2);
    numbers.push_back(3);
    numbers.push_back(4);
    numbers.push_back(5);

    obj["numbers"] = numbers;

    // Serialize the object to a string
    std::string serialized_json = json::serialize(obj);

    std::cout << "Generated JSON: " << serialized_json << std::endl;

    return 0;
}

參考連結