跪拜 Guibai
← Back to the summary

Modeling a Classical Chinese Novel as a Knowledge Graph: Data, Ontology, and One 4.4 MB HTML

Modeling a Classical Chinese Novel as a Knowledge Graph: Data, Ontology, and Single-File HTML

Recently I made a side project: the "Dream of the Red Chamber: Character, Event, and Geography Atlas", which organizes the first 80 chapters of the novel into an interactive web page — character profiles, day-by-day events, maps, poetry search, and a knowledge graph. It is free and open source.

This article does not discuss Redology; it mainly addresses several engineering problems: how the data is organized, how the ontology is designed, how ancient time markers and place names are "translated" into something a program can handle, and why the final deliverable is a single HTML file.

1. Data Source

The base text is the "Jiaxu Edition Collated with Zhiyanzhai Commentary" (甲戌本 system); chapter titles, quoted passages, and Zhiyanzhai comments all follow this edition. I extracted the full text and comments of the first 80 chapters from the layout and placed them in data/chapters.json.

Summing the n_chars field per chapter, the body text totals 623,645 characters; summing n_comments, there are 5,634 comments (some are chapter-title markers, not all are comment text). Chapters 17 and 18 are not split in the collated edition, so there are still 80 chapter records.

Character and plot illustrations come from the Qing dynasty works by 改琦 ("Dream of the Red Chamber Illustrations") and 孙温 ("Illustrated Complete Dream of the Red Chamber"), embedded in the page as base64 (255 images in img), each with an imgsrc / imgcap recording the source and caption.

2. Data Structures

There are four files under data/ in the repository.

1. honglou_data.json: all structured data used by the page. Top-level keys:

people, places, events, relations, quotes, years, eras, maps,
chapters, gems, days, onto, china, img, imgsrc, imgcap

Counts: people 115, places 54, events 51, relations 163, quotes (Zhiyanzhai comments) 14, eras 7, gems (poems, lyrics, verdicts, etc.) 117, days 71. A sample person record (excerpt):

{
  "id": "jiajing", "name": "贾敬", "fam": "贾",
  "role": "宁府老爷,袭爵后弃家好道",
  "nick": ["炼丹翁"], "place": "玄真观",
  "birth": -25, "death": "红楼十七年四月廿六夜", "death_ch": 63,
  "mentions": 20, "first": 2,
  "chapters": [2, 10, 13, 18, 53, 63, 64], "cls": "zhuzi"
}

Person relations use the simplest four-tuple: [subject, object, relation, note].

["jiamu", "baoyu", "祖孙", "隔代最疼"]

Fine-grained relation words (grandparent-grandchild, uncle-nephew, father-in-law/son-in-law) are mapped through onto.rel2prop to coarse-grained ontology properties, e.g. "祖孙": "qinshu" (blood kin). This way the page shows terms close to the original text, while the graph can aggregate by broader categories.

2. chapters.json: 80 entries, each with fields num, title, start_pdf, end_pdf, body, comments:

{ "num": 1, "title": "甄士隐梦幻识通灵贾雨村风尘怀闺秀",
  "start_pdf": 12, "end_pdf": 28,
  "body": "甄士隐梦幻识通灵 贾雨村风尘怀闺秀①    列位看官,你道此书从何而来?……" }

start_pdf / end_pdf retain the page numbers in the original PDF for easy cross-reference.

3. mentions.json: has a single key first_seen, recording appearance statistics for 159 names/titles:

"贾母": { "chapter": 3, "count_at_first": 41, "total": 1717,
          "chapters": [3, 4, 5, 6, …, 80] }

Note that the "mention count" in the character profile does not come directly from this number — the same person appears under full names, abbreviations, and nicknames, which need separate merging.

4. ontology.ttl: the same knowledge serialized in OWL/Turtle, expanded in the next section.

3. Ontology Design

The ontology has two layers: the model layer (TBox) defines classes and properties, and the instance layer (ABox) holds concrete people, events, and places. The prefix is http://example.org/honglou#.

Classes: 7 top-level classes — 人物 (person), 事件 (event), 地点 (place), 家族 (family), 纪年 (calendar era), 脂批 (Zhiyanzhai commentary), 作品 (work); with 22 subclasses. People are divided into 7 categories by status (master/affinal kin, official/imperial, servant/maid, performer/actor, physician/diviner/retainer, monk/Taoist, commoner), events into 9 categories by nature, places into 6 categories by real/virtual status.

:人物 a owl:Class .
:事件 a owl:Class .
:地点 a owl:Class .
:主子姻眷 rdfs:subClassOf :人物 .
:诗社雅集 rdfs:subClassOf :事件 .
:园林院苑 rdfs:subClassOf :地点 .

Object properties: 21 in total. Ten are person-person relations: 血亲眷属 (blood kin), 夫妻妾室 (spouse/concubine), 主仆役使 (master-servant), 朋友知交 (friend/acquaintance), 情缘私恋 (romantic attachment), 仇怨对立 (enmity/opposition), 师恩庇护 (teacher/patron), 交易勾结 (transaction/collusion), 事缘交往 (event-based acquaintance), 泛关联 (general association); the rest connect different types, such as 参与 (participates, person→event), 发生于 (occurs at, event→place), 居于 (resides), 撰作 (authors), 所咏 (sings about), 题于 (inscribed on), 位于 (located at).

:血亲眷属 a owl:ObjectProperty ; rdfs:domain :人物 ; rdfs:range :人物 .
:参与     a owl:ObjectProperty ; rdfs:domain :人物 ; rdfs:range :事件 .
:发生于   a owl:ObjectProperty ; rdfs:domain :事件 ; rdfs:range :地点 .

The instance layer references directly:

:jiamu :血亲眷属 :baoyu . # 祖孙:隔代最疼
:e3 a :身世遭际 ; :hasTitle "元宵失英莲·葫芦庙火" ;
    :hasSeason "红楼元年·元宵/三月十五" ; :inChapter 1 ;
    :inEra :era1 ; :occursAt :gusu .

I parsed the actual data with rdflib:

import rdflib
g = rdflib.Graph(); g.parse("data/ontology.ttl")
len(g)  # 3428

The entire .ttl file parses into 3,428 RDF triples (including TBox declarations, type assertions, and data properties). The "3,311 triples" shown in the page's graph is the count from the onto.triples field, a different accounting; I state this plainly. The instance layer uses 9 person-relation properties (泛关联 is declared but has no instances), with the most being 参与 289, 血亲眷属 54, 主仆役使 28.

4. Time Modeling

The novel's time is "Red Chamber calendar year + lunar calendar + time of day", which a program cannot sort directly. My approach:

  1. Use the year of Baoyu's birth as "Red Chamber Year 1"; years and eras (7 segments) give the chapter ranges covered by each year.
  2. days holds 71 day-by-day records, each with y (Red Chamber year), m, d, mo (numeric month, used for sorting).
  3. Where the original text does not state a date and it is inferred from plot, set est: true, displayed on the page as "about / inferred" — 42 of the 71 records are inferred.
  4. For explicitly stated "shichen" (double-hour) times, the tm_m field carries a modern clock time; there are 29 such records.

For example, Chapter 14 "Fengjie manages the Ningguo Mansion":

{ "id": "d015", "y": 12, "m": "9月", "d": "约26日", "ch": 14,
  "title": "凤姐协理宁国府", "ev": "e13", "pl": "ningfu",
  "tm": "卯正二刻点卯·戌初查夜",
  "tm_m": "卯正二刻(6:30)点卯 · 巳正(10:00)早饭 · 午初刻(11:15)领牌回事 · 戌初(19:00)查夜" }

Each shichen lasts two hours, divided into "early" and "main"; each ke is 15 minutes: 卯正 (Mao main) is 6:00, plus two ke is 6:30.

Note: the 51 events are plot units, the 71 days are a dated timeline; they reference each other through the ev field, they are not the same list.

5. Geography Modeling

maps has three layers: city (general situation), fu (Rong and Ning mansions), yuan (Grand View Garden). The outermost layer's base map uses today's provincial administrative divisions (Aliyun DataV GeoAtlas). Place markers follow Zhou Ruchang's "Beijing-Nanjing dual city" theory: the capital is today's Beijing, Jinling is today's Nanjing, while Gusu and Yangzhou are recorded as-is.

{ "id": "gusu", "name": "姑苏", "kind": "city",
  "region": "江南·今江苏苏州", "cls": "zhoufu" }

Disputed locations are not hard-coded as conclusions: conjectured locations like Daruzhou, Ping'an Zhou, Heishan Village, and Dahuang Mountain are marked on the map with dashed lines and labeled "doubtful" or "one person's theory"; the Illusory Land of Great Void is a dream and does not enter geography. The floor plans of the Rong and Ning mansions and the Grand View Garden are approximated from Daiyu's entry route and the garden tour order, not surveyed.

6. Single-File Delivery + GitHub Pages

The final product is an index.html of about 4.4 MB, with data, images, and province boundaries all embedded; the page does not reference any external URL. Practical benefits:

The JSON and TTL files under data/ are for people who want to reuse the data; the page itself does not depend on them.

7. Open Source and Contribution

The project is free and open source; code and data are in the repository:

https://github.com/wangweilong334/hongloumeng

If there are errors in calendar years, place names, character relations, or quotations, please open an Issue or PR. When changing inferred dates or disputed locations, please keep the "about", "inferred", "one person's theory" annotations — the project's principle is not to present speculation as established fact.