「卡累利阿开发者」公司 推出 0.3.0「舒亚」更新 适用于 「GIRVAS」内容管理系统。该版本接替代码阶段「沃伊茨」,并以舒亚河命名——这是卡累利阿共和国最大的河流之一,发源于苏奥耶尔维湖,流入奥涅加湖。为 CMS 版本赋予卡累利阿水体名称的传统,强调了产品的区域归属及其融入本土技术开发生态的地位。
本次发布聚焦于内容导航工具、搜索功能和搜索引擎优化工具的开发,同时包含系统核心的重大技术改进。
内置搜索引擎

该版本的关键创新之一是实现了一个全文搜索引擎,无需第三方服务或插件即可运行。
搜索针对条目的标题、描述、主要内容和关键词执行。实现了 AND 搜索逻辑,自动将查询拆分为单个单词并排除标点符号,即使在表述不精确的情况下也能找到相关内容。结果排名基于可配置的权重系数:标题中的匹配优先于文档正文中的匹配。
在管理后台中,搜索功能将在后续更新中扩展:认证用户将能够搜索条目、静态页面和系统用户。
按日期导航的条目归档
0.3.0 版本引入了内置条目归档——一种按时间顺序导航内容的工具。
该功能自动按年份和月份对条目进行分组,显示每个时间间隔的发布数量。导航支持按分类筛选:用户可以查看网站所有条目的通用归档,也可以查看单个分类的归档。面包屑根据所选时间段和分类动态生成,方便在归档层级之间移动。归档页面已针对搜索引擎进行优化——元标签自动生成,归档页面的 URL 已包含在动态站点地图(sitemap)中。
分类层级和改进的导航

该更新实现了对子分类的支持,具有递归树遍历功能。分类页面现在显示考虑所有嵌套子分类的条目,从而提供更全面的内容呈现。
分类和条目页面上的面包屑显示从根分类到当前位置的完整层级链。此改进既优化了用户导航,也优化了网站的搜索引擎内部链接结构。
内置 SEO 内容分析器

管理后台的页面和条目编辑器中集成了一个客户端 SEO 分析器,无需服务器请求即可运行。
该分析器在五个方面进行检查:标题、Meta 描述、关键词、内容和 URL。检查的参数包括 SEO 标签的长度和内容、关键词在标题、首段和正文中的出现情况、标题层级的正确性、图片 alt 文本的存在性、外部链接的 rel="nofollow" 属性、识别「裸」URL 并建议将其格式化为 Markdown 格式。分析结果立即显示,带有颜色编码、进度条和具体的改进建议。

内容块

管理后台新增了「内容块」部分,用于管理嵌入主题区域的专业元素,无需编辑模板。
支持多种块类型:
- 「个性化」 — 带有 Markdown 格式的任意内容;
- 「控制台」 — 用户迷你档案,自动为未认证访客显示登录表单;
- 「分类列表」 — 所有条目分类的层级列表;
- 「空」 — 专供第三方模块集成的类型。
每个块都通过应用于 URL 的正则表达式配置显示规则。当请求的地址与模式匹配时,块将显示在主题的相应区域中。
核心的技术改进
0.3.0 版本包含对 QueryBuilder 组件——内部 SQL 查询构建器的重大改进:
- 增加了对 JOIN(INNER、LEFT、RIGHT、FULL)的支持,并为 MySQL 和 PostgreSQL 提供自适应条件生成;
- 实现了 CaseExpression 类用于构建 CASE 表达式,包括用于 JSON 搜索的专门方法;
- 增加了全面的索引支持:创建和删除时考虑类型
BTREE、HASH、GIST、GIN、SPGIST、BRIN、FULLTEXT,以及UNIQUE、CONCURRENTLY、IF NOT EXISTS和带有 WHERE 条件的部分索引。
在 CMS 安装期间,现在会自动为所有表创建优化索引,包括 JSONB 字段的 GIN 索引(PostgreSQL)和已发布条目的部分索引。索引创建错误会被记录日志,不会中断安装过程。
EntryCategory 类增加了初始化数据的缓存,防止在多次调用 initData() 方法时重复查询数据库。
迁移
文件系统更新
我们强烈建议通过 CMS「GIRVAS」官方仓库获取更新。如果您默认使用它,请在项目根目录中执行控制台命令 git pull,之前先切换到 production 分支(如果您之前使用了其他分支)。
文件系统更新后,请务必 清空缓存:rm -R ./cache/*(路径可能因您在系统中的实际位置而异)。
数据库迁移
成功更新 CMS 文件后,需要进行数据库迁移。为此,您需要根据 DBMS 的类型,对数据库执行多条 SQL 语句(只需复制粘贴即可)。
针对 MySQL
-- 创建内容块表 --
CREATE TABLE IF NOT EXISTS content_blocks (
id INT AUTO_INCREMENT NOT NULL PRIMARY KEY,
name TEXT NOT NULL,
texts JSON,
metadata JSON,
createdUnixTimestamp INTEGER NOT NULL DEFAULT 0,
updatedUnixTimestamp INTEGER NOT NULL DEFAULT 0
);
-- entries 表的索引
CREATE INDEX idx_entries_category ON entries (categoryID);
CREATE INDEX idx_entries_author ON entries (authorID);
CREATE INDEX idx_entries_created ON entries (createdUnixTimestamp DESC);
CREATE INDEX idx_entries_updated ON entries (updatedUnixTimestamp);
CREATE UNIQUE INDEX idx_entries_name_unique ON entries (name);
-- entries_categories 表的索引
CREATE INDEX idx_entries_categories_parent ON entries_categories (parentID);
CREATE UNIQUE INDEX idx_entries_categories_name_unique ON entries_categories (name);
-- entries_comments 表的索引
CREATE INDEX idx_entries_comments_entry ON entries_comments (entryID);
CREATE INDEX idx_entries_comments_author ON entries_comments (authorID);
CREATE INDEX idx_entries_comments_created ON entries_comments (createdUnixTimestamp DESC);
-- pages_static 表的索引
CREATE UNIQUE INDEX idx_pages_static_name_unique ON pages_static (name);
CREATE INDEX idx_pages_static_author ON pages_static (authorID);
CREATE INDEX idx_pages_static_created ON pages_static (createdUnixTimestamp DESC);
-- users 表的索引
CREATE UNIQUE INDEX idx_users_login_unique ON users (login);
CREATE UNIQUE INDEX idx_users_email_unique ON users (email);
-- users_sessions 表的索引
CREATE INDEX idx_users_sessions_user ON users_sessions (userID);
CREATE INDEX idx_users_sessions_token ON users_sessions (token);
-- forms_data 表的索引
CREATE INDEX idx_forms_data_form ON forms_data (formID);
CREATE INDEX idx_forms_data_created ON forms_data (createdUnixTimestamp DESC);
-- web_channels 表的索引
CREATE UNIQUE INDEX idx_web_channels_name_unique ON web_channels (name);
-- metrics 表的索引
CREATE INDEX idx_metrics_date ON metrics (date);
针对 PostgreSQL
-- 创建内容块表 --
CREATE TABLE IF NOT EXISTS content_blocks (
"id" SERIAL NOT NULL PRIMARY KEY,
"name" TEXT NOT NULL,
"texts" JSONB,
"metadata" JSONB,
"createdUnixTimestamp" INTEGER NOT NULL DEFAULT 0,
"updatedUnixTimestamp" INTEGER NOT NULL DEFAULT 0
);
-- entries 表的索引
CREATE INDEX IF NOT EXISTS idx_entries_category ON entries ("categoryID");
CREATE INDEX IF NOT EXISTS idx_entries_author ON entries ("authorID");
CREATE INDEX IF NOT EXISTS idx_entries_created ON entries ("createdUnixTimestamp" DESC);
CREATE INDEX IF NOT EXISTS idx_entries_updated ON entries ("updatedUnixTimestamp");
CREATE UNIQUE INDEX IF NOT EXISTS idx_entries_name_unique ON entries ("name");
CREATE INDEX IF NOT EXISTS idx_entries_texts_gin ON entries USING GIN (texts);
CREATE INDEX IF NOT EXISTS idx_entries_metadata_gin ON entries USING GIN (metadata);
CREATE INDEX IF NOT EXISTS idx_entries_published ON entries ("createdUnixTimestamp" DESC) WHERE (metadata::jsonb->>"isPublished")::boolean = true;
-- entries_categories 表的索引
CREATE INDEX IF NOT EXISTS idx_entries_categories_parent ON entries_categories ("parentID");
CREATE UNIQUE INDEX IF NOT EXISTS idx_entries_categories_name_unique ON entries_categories ("name");
-- entries_comments 表的索引
CREATE INDEX IF NOT EXISTS idx_entries_comments_entry ON entries_comments ("entryID");
CREATE INDEX IF NOT EXISTS idx_entries_comments_author ON entries_comments ("authorID");
CREATE INDEX IF NOT EXISTS idx_entries_comments_created ON entries_comments ("createdUnixTimestamp" DESC);
-- pages_static 表的索引
CREATE UNIQUE INDEX IF NOT EXISTS idx_pages_static_name_unique ON pages_static ("name");
CREATE INDEX IF NOT EXISTS idx_pages_static_author ON pages_static ("authorID");
CREATE INDEX IF NOT EXISTS idx_pages_static_created ON pages_static ("createdUnixTimestamp" DESC);
CREATE INDEX IF NOT EXISTS idx_pages_static_texts_gin ON pages_static USING GIN (texts);
-- users 表的索引
CREATE UNIQUE INDEX IF NOT EXISTS idx_users_login_unique ON users ("login");
CREATE UNIQUE INDEX IF NOT EXISTS idx_users_email_unique ON users ("email");
-- users_sessions 表的索引
CREATE INDEX IF NOT EXISTS idx_users_sessions_user ON users_sessions ("userID");
CREATE INDEX IF NOT EXISTS idx_users_sessions_token ON users_sessions ("token");
-- forms_data 表的索引
CREATE INDEX IF NOT EXISTS idx_forms_data_form ON forms_data ("formID");
CREATE INDEX IF NOT EXISTS idx_forms_data_created ON forms_data ("createdUnixTimestamp" DESC);
-- web_channels 表的索引
CREATE UNIQUE INDEX IF NOT EXISTS idx_web_channels_name_unique ON web_channels ("name");
-- metrics 表的索引
CREATE INDEX IF NOT EXISTS idx_metrics_date ON metrics ("date");
结语
CMS「GIRVAS」0.3.0「舒亚」更新 将用户导航和搜索工具的开发与核心的深度技术现代化相结合。我们建议所有用户执行更新,以体验增强的搜索功能、便捷的内容归档和新的搜索引擎优化工具。
评论