From d0706239ea9599c3a88e3332509f2c2af2bd977c Mon Sep 17 00:00:00 2001 From: matevip Date: Sat, 2 May 2026 19:04:06 +0800 Subject: [PATCH] feat(wiki): add mate_wiki_relation cache table for page-to-page edges --- .../mate/wiki/model/WikiRelationEntity.java | 75 +++++++++++++++++++ .../wiki/repository/WikiRelationMapper.java | 22 ++++++ .../migration/h2/V77__wiki_relation_table.sql | 66 ++++++++++++++++ .../mysql/V77__wiki_relation_table.sql | 43 +++++++++++ 4 files changed, 206 insertions(+) create mode 100644 mateclaw-server/src/main/java/vip/mate/wiki/model/WikiRelationEntity.java create mode 100644 mateclaw-server/src/main/java/vip/mate/wiki/repository/WikiRelationMapper.java create mode 100644 mateclaw-server/src/main/resources/db/migration/h2/V77__wiki_relation_table.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/mysql/V77__wiki_relation_table.sql diff --git a/mateclaw-server/src/main/java/vip/mate/wiki/model/WikiRelationEntity.java b/mateclaw-server/src/main/java/vip/mate/wiki/model/WikiRelationEntity.java new file mode 100644 index 00000000..59d98310 --- /dev/null +++ b/mateclaw-server/src/main/java/vip/mate/wiki/model/WikiRelationEntity.java @@ -0,0 +1,75 @@ +package vip.mate.wiki.model; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * Persistent cache of page-to-page multi-signal relations. + * + *

One row per ordered (page_a_id, page_b_id) pair within a knowledge base. + * The row materializes the aggregate score across registered signal strategies + * plus optional taxonomy / confidence / evidence metadata when the planning + * stage of the compile pipeline produced any. + * + *

Distinct from {@link WikiPageCitationEntity}, which models page-to-chunk + * citations. The two tables coexist; nothing in this row supersedes citation + * rows. + * + * @author MateClaw Team + */ +@Data +@TableName("mate_wiki_relation") +public class WikiRelationEntity { + + @TableId(type = IdType.AUTO) + private Long id; + + private Long kbId; + + private Long pageAId; + + private Long pageBId; + + /** Aggregate score (sum of weighted signal contributions). */ + private BigDecimal totalScore; + + /** Per-signal breakdown serialized as JSON, e.g. {@code {"direct_link":2.0,"shared_chunk":5.0}}. */ + private String signalsJson; + + /** Relation taxonomy: {@code mention | cite | supports | contradicts | extends}. */ + private String type; + + /** Confidence taxonomy: {@code EXTRACTED | INFERRED | AMBIGUOUS | UNVERIFIED}. */ + private String confidence; + + /** Verbatim quote or paraphrased rationale supporting the relation; ≤ 500 chars. */ + private String evidence; + + /** When evidence is a quote, the raw material id it was pulled from. */ + private Long evidenceRawId; + + /** Provenance tag: {@code llm-extracted | wikilink-context | manual}. */ + private String source; + + private LocalDateTime computedAt; + + /** Fingerprint of the inputs that produced {@link #totalScore}; used for cache invalidation. */ + private String computedHash; + + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; + + @TableField(fill = FieldFill.INSERT_UPDATE) + private LocalDateTime updateTime; + + @TableLogic + private Integer deleted; +} diff --git a/mateclaw-server/src/main/java/vip/mate/wiki/repository/WikiRelationMapper.java b/mateclaw-server/src/main/java/vip/mate/wiki/repository/WikiRelationMapper.java new file mode 100644 index 00000000..f34b8b81 --- /dev/null +++ b/mateclaw-server/src/main/java/vip/mate/wiki/repository/WikiRelationMapper.java @@ -0,0 +1,22 @@ +package vip.mate.wiki.repository; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import vip.mate.wiki.model.WikiRelationEntity; + +/** + * Mapper for the page-to-page wiki relation cache. + * + *

Read paths fetch top-K rows by ({@code kb_id}, {@code page_a_id}) ordered + * by {@code total_score DESC}; write paths upsert (insert + on-duplicate-key + * update) keyed by ({@code kb_id}, {@code page_a_id}, {@code page_b_id}). + * + *

Cache invalidation paths use soft-delete (set {@code deleted=1}) keyed + * by either {@code page_id} or {@code kb_id}; the consuming services + * provide thin wrappers via custom SQL when needed. + * + * @author MateClaw Team + */ +@Mapper +public interface WikiRelationMapper extends BaseMapper { +} diff --git a/mateclaw-server/src/main/resources/db/migration/h2/V77__wiki_relation_table.sql b/mateclaw-server/src/main/resources/db/migration/h2/V77__wiki_relation_table.sql new file mode 100644 index 00000000..63e0457b --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/h2/V77__wiki_relation_table.sql @@ -0,0 +1,66 @@ +-- mate_wiki_relation: persistent cache of page-to-page multi-signal relations. +-- +-- Distinct from mate_wiki_page_citation, which models page-to-chunk citations. +-- A row here represents one directed (or undirected, see notes below) edge in +-- the wiki page graph for a given knowledge base, materializing: +-- * the aggregate relevance score across registered signal strategies +-- (direct link, shared chunk, shared raw, semantic similarity, ...) +-- * a per-signal breakdown for explainability +-- * an optional taxonomy tag (mention / cite / supports / contradicts / +-- extends) populated by the planning stage of the compile pipeline +-- * confidence + evidence snippets sourced from the same compile output +-- * cache invalidation metadata so readers can decide whether to recompute + +CREATE TABLE IF NOT EXISTS mate_wiki_relation ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + kb_id BIGINT NOT NULL, + page_a_id BIGINT NOT NULL, + page_b_id BIGINT NOT NULL, + + -- Aggregate relevance score (sum of weighted signal contributions). + total_score DECIMAL(8, 4), + + -- Per-signal breakdown, e.g. {"direct_link":2.0,"shared_chunk":5.0}. + signals_json CLOB, + + -- Relation type taxonomy: mention | cite | supports | contradicts | extends. + type VARCHAR(32), + + -- Confidence taxonomy: EXTRACTED | INFERRED | AMBIGUOUS | UNVERIFIED. + confidence VARCHAR(16), + + -- Verbatim or paraphrased justification (≤ 500 chars enforced in Java layer). + evidence CLOB, + + -- When evidence is a quote, the raw material id it was pulled from. + evidence_raw_id BIGINT, + + -- Provenance tag: llm-extracted | wikilink-context | manual. + source VARCHAR(32), + + -- Cache invalidation metadata. + computed_at TIMESTAMP, + computed_hash VARCHAR(64), + + -- Standard rows. + create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted INT NOT NULL DEFAULT 0 +); + +-- Unique edge identity within a KB (deleted column included so soft-deleted +-- rows can coexist with re-inserted ones during re-compute cycles). +CREATE UNIQUE INDEX IF NOT EXISTS uk_wr_pair + ON mate_wiki_relation (kb_id, page_a_id, page_b_id, deleted); + +-- "Top-K related to page X" queries. +CREATE INDEX IF NOT EXISTS idx_wr_page_a + ON mate_wiki_relation (kb_id, page_a_id, total_score DESC); + +-- KB-wide ranking queries (e.g., strongest edges across the graph). +CREATE INDEX IF NOT EXISTS idx_wr_kb_score + ON mate_wiki_relation (kb_id, total_score DESC); + +-- Cache invalidator scans (e.g., "everything older than X"). +CREATE INDEX IF NOT EXISTS idx_wr_computed_at + ON mate_wiki_relation (computed_at); diff --git a/mateclaw-server/src/main/resources/db/migration/mysql/V77__wiki_relation_table.sql b/mateclaw-server/src/main/resources/db/migration/mysql/V77__wiki_relation_table.sql new file mode 100644 index 00000000..d960ac41 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/mysql/V77__wiki_relation_table.sql @@ -0,0 +1,43 @@ +-- mate_wiki_relation: persistent cache of page-to-page multi-signal relations. +-- +-- Distinct from mate_wiki_page_citation, which models page-to-chunk citations. +-- A row here represents one directed (or undirected, see notes below) edge in +-- the wiki page graph for a given knowledge base, materializing: +-- * the aggregate relevance score across registered signal strategies +-- (direct link, shared chunk, shared raw, semantic similarity, ...) +-- * a per-signal breakdown for explainability +-- * an optional taxonomy tag (mention / cite / supports / contradicts / +-- extends) populated by the planning stage of the compile pipeline +-- * confidence + evidence snippets sourced from the same compile output +-- * cache invalidation metadata so readers can decide whether to recompute + +CREATE TABLE IF NOT EXISTS mate_wiki_relation ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + kb_id BIGINT NOT NULL, + page_a_id BIGINT NOT NULL, + page_b_id BIGINT NOT NULL, + + total_score DECIMAL(8, 4), + signals_json JSON, + + type VARCHAR(32), + + confidence VARCHAR(16), + evidence TEXT, + evidence_raw_id BIGINT, + + source VARCHAR(32), + + computed_at DATETIME(3), + computed_hash VARCHAR(64), + + create_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + update_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), + deleted TINYINT NOT NULL DEFAULT 0, + + UNIQUE KEY uk_wr_pair (kb_id, page_a_id, page_b_id, deleted), + KEY idx_wr_page_a (kb_id, page_a_id, total_score DESC), + KEY idx_wr_kb_score (kb_id, total_score DESC), + KEY idx_wr_computed_at (computed_at) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci + COMMENT = 'Page-to-page wiki relation cache (multi-signal score, taxonomy, confidence, provenance).';