Building a “Lightweight Knowledge Index” for My Blog (III): Design and Implementation of a Lightweight WordPress Recommendation System Based on Pre-computed Semantic Index
This article was last updated 116 days ago. The information in it may have developed or changed. If anything is outdated, please leave a comment in the comments section.
Article Abstract
Addressing the lightweight requirements of blog recommendation systems, this article proposes an implementation scheme based on pre-computed semantic indexing. By parsing JSON data with a Python script to generate a structured index file, WordPress directly reads this index at runtime to complete the rendering of the recommendation list. The scheme skips the real-time computation phase, solidifying article relationships into an ID mapping table during the offline stage, requiring only simple data lookup and structural mapping at runtime. Ultimately, it achieves a recommendation function without complex algorithms, allowing the recommendation list to be naturally embedded into the article reading flow, which both maintains the lightweight nature of the system and effectively improves content relevance and user reading continuity.
Qwen3-14B · 2026-06-18

1 The Starting Point of Intelligent Blog Recommendation

In the second article of this series (see:Adding a “Semantic Index” Layer to Your Blog (Part 2): Implementation of JSON Structure and Generation Script), finally, we have generated the key semantic-index.json file. And the next, most natural question is:How can we utilize this already organized content relationship to make the articles in the blog truly start to form connections?

After some thought, I believe the function that the blog currently needs most is “the ”recommendation list": It allows readers to continue exploring more related topics along the recommendation list while browsing articles, thereby enhancing the reading experience, and also allowing the content in the blog to be more fully discovered and utilized.

In this current scenario, what I am actually concerned about is not the “recommendation algorithm” itself, but:How to utilize the existing data structure to truly transform the already existing content associations into runnable recommendation capabilities.

The most core premise here is the semantic-index.json file generated in the previous article. Therefore, this article will focus on:How to start from the existing JSON data and further transform the already organized article relationships into a practically usable recommendation system.

The overall goal is not complex: try to avoid introducing additional runtime computation, and while keeping the structure lightweight, implement a recommendation system that can truly reflect the relevance of article content.

Next, this article will focus on the following parts:

  1. How to, from the existing semantic-index.json extract and organize the data structure required by the recommendation system, and ultimately generate the article index file used for runtime queries article-index.json
  2. How to, based on the article-index.json 的 "keywords": ["...", "..."] relationship during WordPress runtime, implement a lightweight mapping from article IDs to display data (title / url), and construct the final recommendation results
  3. How to truly integrate the recommendation list function into WordPress pages and complete the final front-end display

The focus of the entire process is not to recalculate the relationships between articles, but: how to transform the already organized content associations into a truly runnable, displayable, and maintainable lightweight recommendation system.

2 Data Extraction

1 JSON File Overview

In the previous chapter, we clarified the functional goal to be achieved in step 1: ”From the existing semantic-index.json extract and organize the data truly needed by the recommendation system, and generate an article index structure suitable for WordPress calls”. Before we start processing, let's quickly review the semantic-index.json file's basic structure.

Each article in semantic-index.json roughly contains the following fields:

  • id: Unique identifier of the article, used for quick lookup and mapping
  • title: Article title, used for display and generating recommendation lists
  • url: Article link, used for navigation from the recommendation list
  • summary: Article abstract, can be used for a brief display in the recommendation list
  • keywords: Article keywords, can assist in generating semantic relevance
  • "keywords": ["...", "..."]: Recommended related article list (can be empty or partially filled), which is an important basis for generating recommendation lists

After understanding the semantic-index.json basic structure of, the next step isto read the data from the file into the program, so that we can process and generate recommendation lists in subsequent steps.

Depending on the use case, the method for reading JSON varies slightly. Here we provide two common approaches: Python and WordPress/PHP.

2 Data Reading and Processing

2.1 Python Example

If you are using Python on a local machine or server to process JSON files, you can do this:

import json

# 读取 semantic-index.json 结构化数据文件
with open('semantic-index.json', 'r', encoding='utf-8') as f:
    articles = json.load(f)

# 输出前两条记录,用于验证数据结构是否正确
for article in articles[:2]:
    print(article['id'], article['title'], article['url'])

# Read semantic-index.json structured data file with open('semantic-index.json', 'r', encoding='utf-8') as f:

  • articles = json.load(f)
  • # Output the first two records to verify if the data structure is correct
  • for article in articles[:2]:

2.2 WordPress / PHP Example

print(article['id'], article['title'], article['url'])

<?php
json_file = get_template_directory() . '/semantic-index.json';json_data = file_get_contents(json_file);articles = json_decode(json_data, true);i = 0; i<2;i++) {
    echo articles[i]['id'] . ' - ' . articles[i]['title'] . ' - ' . articles[i]['url'] . '
';
  • json.load() will parse the JSON file into lists and dictionaries in Python, with each article corresponding to a dictionary object. The reading operation itself is very straightforward, and its main purposes are:
  • Verifying if the data structure is correct Confirming whether the fields (id / title / url / related) are complete
  • Providing the basic data source for the subsequent index generation phase If you want to read the JSON file directly in WordPress, you can use the following method: file_get_contents()

2.3 Final Method Selection

used to read local file content semantic-index.json json_decode($json_data, true)

will convert JSON into a PHP associative array

Afterwards, you can also organize and process based on

$articles


array fieldsAlthough WordPress / PHP can directly readand complete basic data parsing, from the design goals of the entire recommendation system, the final data processing stage is not suitable to be completed in the runtime environment. Therefore, in the implementation, I chose to hand over the core data processing workflow to Python to complete.

The reason is not inherently complex: for a recommendation system, the truly valuable work is concentrated in the data preprocessing stage, including field extraction, structural reorganization, and article relationship construction. This type of operation essentially belongs to "offline data processing," and Python has clearer and more efficient expressive capabilities in handling JSON data, dictionary mapping, and batch structure conversion.article-index.jsonIn contrast, PHP's positioning in the WordPress ecosystem leans more toward the runtime environment, where its core responsibility is page rendering and content output, rather than undertaking complex data structure construction tasks. Placing data processing logic in the PHP runtime not only increases the burden during page generation but also blurs the boundary between the data layer and the presentation layer.


3 Data Organization and Structuring

Therefore, in the overall implementation scheme of this article, the data processing stage is uniformly undertaken by Python, while WordPress / PHP only serves as the final consumer, responsible for reading the already constructed index data and performing page rendering. semantic-index.json It needs to be specially emphasized that: whether using Python or PHP, the content processed in the current stage 2.2 is essentially just the

raw JSON data input layer (Input Layer)

. The goal of this stage is not to build the final recommendation system structure, but to complete the reading and verification of basic data, ensuring that the data source for subsequent processing is reliable. article-index.json The structured index truly used for WordPress runtime (

), will be uniformly generated in the subsequent construction stage (2.4). This layer will assume the responsibility of the "runtime data entry" and is the data core of the entire recommendation system, no longer belonging to the scope of raw data processing.

After successfully reading id、title、url , we have obtained a complete set of raw article data. However, structurally speaking, this data still remains in the "raw list form," leaning more toward content storage rather than a structured representation oriented toward index systems. "keywords": ["...", "..."] If we directly enter the subsequent index construction stage based on this raw structure, it will bring an implicit problem: although the data is complete, it lacks consistent access semantics. Although the fields of each article exist, they are still "loosely organized" at the structural level.

Before entering

cleaned_articles = []

for article in json_data:
    cleaned_articles.append({
        'id': article['id'],
        'title': article['title'],
        'url': article['url'],
        'related': article.get('related', [])
    })

This logic itself is not complex; all it does is ensure that all articles have a unified structure before entering the next stage.

It needs to be emphasized that the data form at this stage still remains a “list structure”, without introducing any index structure or lookup optimization. In other words, it is still a linear data collection, just becoming cleaner and more consistent at the structural level:

[
    {
        'id': 'article_id_1',
        'title': '文章标题 1',
        'url': '/article-1/',
        'related': ['article_id_5', 'article_id_9']
    },
    {
        'id': 'article_id_2',
        'title': '文章标题 2',
        'url': '/article-2/',
        'related': []
    }
]

The significance of this structure lies not in performance or query efficiency, but in providing a “semantically consistent data starting point” for the subsequent index building stage. Only when all articles have a unified data representation form is it possible to further stably generate the required runtime article-index.json。

Therefore, a more accurate positioning of this stage is not “processing data”, but “constraining data form”—what it does is converge raw data from a loose collection of content into a structurally unified intermediate data layer, providing conditions for subsequent indexing.

4 Building the Runtime Article Index

After completing the data organization in the previous stage, the next thing to do is actually very clear: further solidify the already structured article data into an index file that can be directly used at runtime.

The key point of this stage is not “re-processing data”, but “determining the final form of the data”. That is to say, at this moment, the data begins to transition from an “intermediate structure” to a “runtime asset”, and its purpose also changes from a processing object to a directly consumed index resource.

In the current design, this final form is uniformly defined as article-index.jsonIt no longer carries any data processing logic, nor does it participate in any structural evolution process; it merely exists as a static, stable, and cross-environment readable data carrier.

From the perspective of system roles, the significance of this index file is very clear: it is the only data entry point that WordPress relies on at runtime. Therefore, it must be completely decoupled from the constraints of runtime logic, ensuring that its structure no longer changes after generation.

Under this design, the responsibilities of WordPress are also strictly narrowed down; it is only responsible for reading the index, locating data based on article IDs, and completing the final display on this basis, no longer participating in any form of data construction or relationship calculation.

In the finally generated article-index.json Structurally, it manifests as a mapping table with the article ID as the core key. Each entry fully contains the basic information of the article and its pre-defined relationships, such as the title, link, and the related list:

{
    "article_id_1": {
        "title": "WordPress 缓存优化实践",
        "url": "/wordpress-cache/",
        "related": [
            "article_5",
            "article_9"
        ]
    },

    "article_id_2": {
        "title": "Cloudflare Tunnel 使用记录",
        "url": "/cloudflare-tunnel/",
        "related": [
            "article_1",
            "article_7"
        ]
    }
}

From the structural essence, this file is no longer a “data collection”, but a runtime index map that has already undergone flattening. The relationships between all articles no longer exist in the form of computation, but are solidified in the way of static mapping.

The generation process of the entire index can be understood as a one-way data convergence process: after the raw semantic data is organized, it is compressed and converted into a stable ID mapping structure, and finally output as a JSON file that can be directly consumed at runtime:

semantic-index.json(原始语义数据)
        ↓
结构整理(2.3)
        ↓
索引化转换
        ↓
article-index.json(运行时数据资产)
        ↓
WordPress 只读消费

In this process, the most critical change is not “generating a file”, but the redefinition of system boundaries: the data construction stage ends completely here, and all subsequent runtime behaviors can only unfold on top of the established structure.

Therefore, it can be said that from this moment on, the structure of the entire recommendation system is completely fixed. All subsequent logic will no longer change the data itself, but will only perform reading and mapping based on this index structure.

3 Generation Logic of the Recommendation List

1 Core Idea of Recommendation Logic

In the previous chapter, we completed the construction of the runtime index file article-index.json The next problem to solve becomes very straightforward: how to quickly generate the corresponding recommendation list based on the current article at runtime.

At this stage, the recommendation logic itself is actually no longer complex, because the relationships between articles are not dynamically derived at runtime, but have already been “solidified” into the data structure during the previous index building process. In other words, the recommendation system no longer needs to think about “whether articles are related”; it only needs to accept an established fact—each article's "keywords": ["...", "..."] field has already defined its recommended targets.

Therefore, starting from the current article ID, the core action of the recommendation logic is actually very single: first locate the article-index.json corresponding article node in , and then read the "keywords": ["...", "..."] list within it, then map these IDs one by one back to the complete information of the articles (such as title and url), and finally assemble them into a recommendation result that the frontend can directly use.

If understood in a more intuitive way, this process is essentially not “computation”, but a standard data expansion: starting from a known node, along the pre-defined relationship edges, performing a limited expansion operation:

current_article_id
        ↓
article-index.json lookup
        ↓
获取 related ID 列表
        ↓
ID → article 数据映射
        ↓
生成推荐列表

Precisely because of the existence of this structure, the recommendation system at this layer no longer possesses any “algorithmic complexity”: it does not involve similarity calculations, nor does it involve sorting strategies, and it does not rely on real-time judgments of semantic models. All these factors that might affect recommendation quality have already been moved forward to the data generation stage, while the runtime only retains the most stable and controllable execution path.

From an engineering perspective, this design is very friendly to content systems like WordPress. It avoids complex JOINs at the database level and completely bypasses runtime computational overhead, with the entire process degenerating into pure memory-level data lookup and structural mapping. In other words, the cost of the recommendation system at runtime is almost equivalent to an array read plus a simple traversal.

More importantly, in this model, the upper limit of recommendation quality is no longer determined by the runtime, but depends entirely on how the preceding stage builds the "keywords": ["...", "..."] field. Once "keywords": ["...", "..."] is generated and solidified, all the runtime can do is faithfully “restore” this relationship structure, rather than re-processing or optimizing it.

Therefore, it can also be said that the recommendation system at this stage has undergone a fundamental change: it is no longer a “computing system”, but a typical “data expansion system”, with the core of all logic converging on one action—fully expanding the results from the defined relationships.

2 Generation of the Recommendation List

After clarifying the recommendation logic, generating the recommendation list at runtime becomes very straightforward: since the previous stage has already built the article-index.json, there is no longer any need to process raw data, nor is there a need for structural conversion or relationship calculation in the WordPress execution environment.

Looking at the overall process, there is only one thing the runtime actually does: based on the current article ID, find the corresponding node from the pre-built index, and then read the "keywords": ["...", "..."] list within it, and map these IDs back to specific article information.

In other words, the generation of the recommendation list is no longer a “construction process”, but a “data restoration process”—the system simply unfolds the relationship structure already determined in the offline stage back into content visible to the user.

In WordPress, this logic can typically be encapsulated into a very lightweight function, whose core purpose is not to implement an algorithm, but to complete a standard data mapping:

function get_related_posts(current_article_id,limit = 5)
    json_path = get_template_directory() . '/cache/article-index.json';articles_index = json_decode(json_path),articles_index[current_article_id])) {related_ids = articles_index[current_article_id]['related'] ?? [];

    recommendations = [];related_ids as related_id) {articles_index[related_id])) {article = articles_index[related_id];

        recommendations[] = [article['title'], article['url']recommendations) >= limit) {recommendations;

The final output data structure is also very simple, essentially being a standard article list:

[
    [
        'title' => 'Cloudflare Tunnel 使用记录',
        'url'   => '/cloudflare-tunnel/'
    ],
    [
        'title' => 'WordPress 缓存优化实践',
        'url'   => '/wordpress-cache/'
    ]
]

[

4 Basic Construction and Presentation Design of the Recommendation List System

1 Runtime Environment Preparation

“title” => 'Cloudflare Tunnel Usage Record',

“url” => “/cloudflare-tunnel/”

],

[

'title' => 'WordPress Cache Optimization Practice',

python3 --version

'url' => '/wordpress-cache/'

]

] semantic-index.json The file, which carries the source of semantic relationships between articles and is used as data input for the subsequent index construction phase.

Therefore, before entering the next phase, it is necessary to ensure that this file already exists and meets the following basic conditions:

  • The file has been successfully generated
  • The JSON structure is complete and parsable
  • The encoding format is UTF-8
  • "keywords": ["...", "..."] Field data exists and the structure is correct

The status of this file directly determines whether the subsequent recommendation system as a whole has the basic input required for computation.

It is necessary to confirm that the WordPress theme has basic modification capabilities.

Since the subsequent phase requires integrating the display of the recommendation list at the theme layer, it must be possible to access and modify the relevant files in the theme, such as:

  • functions.php
  • single.php or the corresponding article template file

If a child theme structure is used, it is recommended to complete all modifications in the child theme to avoid overwrite issues caused by main theme updates.

When the above three conditions are met simultaneously, the basic runtime environment of the entire system can be considered ready. The significance of this phase lies in: confirming that the offline data generation capability and the runtime execution capability have been connected, though it does not yet involve any index structure or recommendation logic itself.

2 Generating the Runtime Article Index File

After completing the preliminary semantic data preparation and structural design, the next step is the most critical one in the entire recommendation system: generating the article index file that can be directly used by WordPress at runtime.

The essence of this phase is not “reprocessing data”, but rather solidifying the previously determined structured results into a stable runtime data asset, transforming it from a “processing object” into a “directly consumable data entry point”.

In other words, this step marks the formal transition of the entire system from the “data construction phase” to the “runtime consumption phase”. At the implementation level, this process is typically completed via an independent Python script, such as:

build_article_index.py

The role of this script is very singular: to take the semantic-index.json structured relationships already existing in, reorganize them into an index structure centered on article IDs, and output it as a runtime file.

The core processing logic is as follows:

import json

json_file = 'semantic-index.json'
output_file = 'article-index.json'

with open(json_file, 'r', encoding='utf-8') as f:
    json_data = json.load(f)

articles_index = {}

for article in json_data:
    article_id = article['id']

    articles_index[article_id] = {
        'title': article['title'],
        'url': article['url'],
        'related': [
            item['id'] if isinstance(item, dict) else item
            for item in article.get('related', [])
        ]
    }

with open(output_file, 'w', encoding='utf-8') as f:
    json.dump(articles_index, f, ensure_ascii=False, indent=2)

print("article-index.json 已生成")

Logically speaking, this process itself is not complex; it merely completes a structural transformation:

  • Input: Semantic layer data (semantic-index.json)
  • Output: Runtime index (article-index.json)
  • Intermediate process: Field extraction + ID mapping + structural solidification

At this point, the most core data preparation phase of the entire recommendation system is actually complete. From a system perspective, the completion of this step means:
– Semantic relationship data has been solidified
– The runtime index has been generated
– WordPress only needs to perform data reading and mapping subsequently

3 Integration Position of the Recommendation List in the Article Page

Before actually starting to implement the recommendation list, there is another easily overlooked but actually very important question: where on the article page should the recommendation list be placed?

From a technical implementation perspective, this seems to be just a simple page layout issue, but if from the perspective of reading experience and content organizational structure, this step will actually directly affect the final effect of the entire recommendation system.

For many traditional blogs, related articles are usually placed in: the sidebar, the bottom of the page, after the comment section, or randomly inserted into the body area. However, most of these methods belong to a “traffic distribution” mindset, whose core goal is often to increase click-through rates rather than helping readers establish structural relationships between content.

The recommendation system implemented in this article, however, does not have “popular recommendations” in the traditional sense as its design goal, but rather: providing readers with a reading path of genuine extended value based on the semantic association of the current article. This means that the recommendation list must rely on the context of the current article, so its most reasonable position is not the sidebar, but rather:After the end of the body text。

However, in my current blog, there is actually already a fixed module after the end of the article body—“Content Structure Prompt”:

image.png

The role of this module is to explain the position of the current article from the perspective of the overall knowledge system, as well as the thematic directions it may be associated with. From the perspective of reading logic, it belongs to the “structural cognitive layer”.

The role of the recommendation list is different; it is not responsible for explaining the structure, but rather for transforming the structure into a specific next-step reading path. Therefore, the relationship between the two in the reading chain can be abstracted as:

正文内容
↓
内容结构提示(结构认知层)
↓
相关文章推荐列表(路径延伸层)
↓
评论区

In this sequence, the “Content Structure Prompt” provides knowledge positioning at an abstract level, while the recommendation list provides specific article jump entry points. The key significance of this design is that it prevents the recommendation system from existing as an isolated module, embedding it instead into the entire reading logic chain to form continuous cognitive and behavioral guidance.

At the same time, this sequence also has a very practical advantage: the recommendation list does not interfere with the body reading process, appearing only after the user has finished the current content, thereby avoiding the continuous distraction of attention caused by traditional sidebar recommendations.

From a system design perspective, this layout method is actually defining the “trigger timing” of the recommendation system—it is not an interfering element that persists during the reading process, but an extended module that is activated only after reading is completed.

Once this positional relationship is determined, the subsequent question becomes very clear: how to, at WordPress runtime, take the article-index.json association data in and map it into a recommendation list, embedding it into the article content flow.

4 Theoretical Execution Model of Recommendation List Generation

If we abstract away the previous construction process, what actually happens in the entire recommendation system during the runtime phase is very simple: it is not “calculating recommendations”, but rather “executing a constrained data expansion”.

The key point here is that the recommendation logic does not occur at WordPress runtime, but is solidified in advance during semantic-index.json → article-index.json this offline phase. Therefore, what the runtime faces is not a problem requiring reasoning, but a data problem that has already been structurally defined.

From a model perspective, when an article page loads, WordPress does only three very definite things: identifying the current article, locating the index node, and expanding the associated set. However, these three steps are not an algorithmic process, but are closer to a “mapping chain”.

The role played by the current article ID here is not an “input variable”, but rather more like a “positioning anchor”. Its sole purpose is to find the corresponding node in an already constructed index space. Once this node is successfully located, subsequent behaviors actually no longer possess any uncertainty.

Because in article-index.json In this, the relationship between articles has been pre-defined as a static structure, for example:

article_id → { title, url, related[] }

Therefore, the so-called “recommendation generation” at runtime is essentially just "keywords": ["...", "..."] expanding the set of IDs pointed to by this field and mapping them back to a displayable information structure. This process is more like a “structural restoration” rather than “content computation”.

The truly important change here is that the complexity of the system is completely front-loaded. The runtime no longer participates in any semantic judgment, similarity calculation, or ranking decisions; it merely faithfully executes a pre-defined structural mapping relationship.

Because of this, the core of this model lies not in “recommendation algorithm design”, but in “whether the data has been structured into expandable relationships”. Once this holds true, the runtime logic naturally devolves into a very lightweight reading process, such as simple index access and array traversal in PHP:

$related_ids = $article_index[$current_post_id]['related'] ?? [];

What this line of code expresses is actually no longer “recommendation logic”, but a “relationship expansion entry point”.

Theoretically speaking, the essence of this design is a typicalprecomputed relation modelIt migrates problems that originally belonged to runtime entirely to the build phase, thereby trading for runtime simplicity and determinism.

Therefore, when we look back at the entire recommendation system, we find that the premise for its viability is not the implementation capabilities of WordPress or PHP, but a more fundamental assumption: the relationships between articles can be fully determined in the offline phase and stably saved in a structured format.

Once this assumption holds true, the recommendation system itself is no longer a “system”, but more like an “expander” — it is only responsible for re-presenting the already existing structure.

5 WordPress Runtime Recommendation System Implementation

1 The Essence of This Scheme's Implementation: Just Two Snippets of Code

If we condense all the content of the previous chapters, this recommendation system is actually not as complex at the implementation level as one might imagine. It can even be said that it was deliberately compressed into a very “restrained” form — only two pieces of code are actually working in the entire system.

The first part is the offline build script build_article_index.py, which is responsible for converting semantic-index.json into a runtime-usable article-index.json; the second part is the PHP logic on the WordPress side, which is responsible for reading this index when the article page loads and generating the final recommendation list.

Other than that, there are no database JOINs, no real-time similarity calculations, and no runtime semantic reasoning processes.

From an implementation perspective, this structure is essentially doing a very simple thing: the recommendation system no longer participates in computation, but is only responsible for displaying results.

2 build_article_index.py

The complete code is as follows:

import json

# semantic-index.json 文件路径
# 该文件由前置 Python 脚本生成,包含文章语义关系数据
json_file = 'semantic-index.json'

# 输出运行时索引文件(JSON版本)
# WordPress 在运行时将直接读取该文件(路径需要根据实际环境进行修改)
output_file = '/docker/wordpress/html/wp-content/themes/argon-theme-master/cache/article-index.json'

# 读取原始语义数据
# 输入结构为 list,每一项代表一篇文章的语义信息
with open(json_file, 'r', encoding='utf-8') as f:
    json_data = json.load(f)

# 初始化运行时索引结构
# 最终结构为 dict:{ article_id: article_data }
articles_index = {}

# 构建运行时索引结构(以 article_id 为 key)
for article in json_data:

    # 当前文章唯一标识
    article_id = article['id']

    # 构建标准化文章索引结构
    # 注意:related 字段统一转换为 ID 列表
    articles_index[article_id] = {
        'title': article['title'],
        'url': article['url'],

        # related 兼容两种结构:
        # 1. dict(包含 id 字段)
        # 2. 直接为 string/int id
        # 最终统一转换为 string ID 列表
        'related': [
            item['id'] if isinstance(item, dict) else item
            for item in article.get('related', [])
        ]
    }

# 写入运行时索引文件(JSON)
# ensure_ascii=False:保留中文
# indent=2:提高可读性(便于调试)
with open(output_file, 'w', encoding='utf-8') as f:
    json.dump(
        articles_index,
        f,
        ensure_ascii=False,
        indent=2
    )

# 构建完成提示
print("article-index.json 已生成")

After the script is saved, you can directly execute the generation process:

python3 build_article_index.py

After execution, if everything is normal, the terminal will output a similar execution result:

image.png

At this point, you can see the final generated runtime index file in the target directory:
image.png

After the index file is generated, a quick validation is usually performed on two levels to ensure that the data meets expectations in both “content form” and “structural validity” dimensions.

First, you can confirm whether the index structure has been generated as expected by directly viewing the file content, taking the Argon theme of my blog as an example:

head -20 /docker/wordpress/html/wp-content/themes/argon-theme-master/cache/article-index.json

If the output content clearly shows a structure with the article ID as the key, and contains title、url 和 "keywords": ["...", "..."] and other fields, it indicates that the index data has been correctly generated and is ready for runtime use:

image.png

After confirming that the data structure looks normal, you can also perform a stricter structural validation using a JSON parsing tool, for example:

python3 -m json.tool /docker/wordpress/html/wp-content/themes/argon-theme-master/cache/article-index.json > /dev/null

This step does not output any content; its purpose is to fully parse the entire JSON file. If no error messages are output, it means the file is completely valid at the syntax level and can be read normally by WordPress:

image.png

3 WordPress PHP Code (Reading + Rendering)

Below is the recommendation list implementation code I am currently using in WordPress, which can be directly placed in the ”Code Snippets” plugin or the ”functions.php” of the current theme:

function add_recommend_list_after_content(content) {

    if (!is_single() || !in_the_loop() || !is_main_query()) {
        returncontent;
    }

    post_id = get_the_ID();

    // =========================
    // 地图页 / 结构页过滤
    // =========================map_slugs = [
        '/map/',
        '/cloudflaremap/',
        '/aimap/',
        '/roadmap'
    ];

    current_url =_SERVER['REQUEST_URI'] ?? '';

    foreach (map_slugs asslug) {
        if (strpos(current_url,slug) !== false) {
            return content;
        }
    }

    // =========================
    // 读取 article-index.json
    // =========================article_index_file = get_template_directory() . '/cache/article-index.json';

    if (!file_exists(article_index_file)) {
        returncontent;
    }

    article_index = json_decode(
        file_get_contents(article_index_file),
        true
    );

    if (!isset(article_index[post_id])) {
        return content;
    }current = article_index[post_id];

    // =========================
    // 相关文章
    // =========================
    related_ids =current['related'] ?? [];
    related_list = [];

    foreach (related_ids as related_id) {

        if (!isset(article_index[related_id])) {
            continue;
        }related_list[] = [
            'title' => article_index[related_id]['title'],
            'url'   => article_index[related_id]['url']
        ];
    }

    related_list = array_slice(related_list, 0, 5);

    if (empty(related_list)) {
        returncontent;
    }

    // =========================
    // UI
    // =========================
    html = '<div class="post-recommend-wrapper" style="
    width:60%;
    min-width:360px;
    max-width:600px;

    margin:28px 0;
    padding:16px 18px;

    background:#f0f7ff;
    border-left:4px solid #3b82f6;
    border-radius:6px;

    font-size:14px;
    line-height:1.7;
    color:#444;
">';html .= '<div style="
        font-size:13px;
        color:#666;
        margin-bottom:10px;
        font-weight:600;
    ">
        📎 相关文章
    </div>';

    foreach (related_list asitem) {

        html .= '<div style="margin:6px 0;">
            <a href="' . esc_url(item['url']) . '" target="_blank" style="
                color:#1d4ed8;
                text-decoration:none;
                font-weight:500;
            ">
            ' . esc_html(item['title']) . '
            </a>
        </div>';
    }html .= '</div>';

    return content .html;
}

add_filter('the_content', 'add_recommend_list_after_content', 20);

4 Final Effect Display of the Recommendation System

Up to this point, the entire WordPress recommendation system based on semantic indexing has been fully implemented. From data construction and runtime index generation to reading and rendering on the WordPress side, the entire pipeline can now run stably in a real environment.

On the actual article page, the recommendation list will appear after the “Content Structure Prompt” and before the comment section, serving as a natural extension module after finishing the main text, taking the first article of this series as an example: ”Adding a Layer of “Semantic Index” to the Blog (I): Structural Design and Construction Process“ as an example for the final recommendation list:

image.png

As can be seen, the recommendation list is displayed in an independent block structure, containing related reading content semantically associated with the current article, and providing direct jump links. It does not interfere with the reading process of the main text, but instead provides a natural continuation path after reading is completed.

From the final effect, this recommendation system is no longer just a “functional module”, but a fixed component of the reading pipeline.

6 Summary: A Recommendation System Based on Pre-computed Index

At this point, this lightweight WordPress recommendation system based on semantic indexing is truly and completely up and running.

The entire system is actually not complex: in the preliminary stage, through semantic-index.json organizing the relationships between articles, and then further constructing the runtime-used article-index.json, and finally, WordPress reads the index and generates the recommendation list when the page loads.

Precisely because of this, the runtime structure of this solution is extremely lightweight. It does not rely on complex database queries, does not require real-time calculation of article similarity, and does not involve dynamic semantic analysis. For WordPress, it is more like “consuming” a pre-prepared structured relationship data, rather than participating in recommendation calculations in real time. But looking back, the truly important part of this system is not just “having an extra related articles module at the bottom of the page.” What has truly changed is that, for the first time, the articles in the blog have begun to have a layer of “structural relationships” that can be consumed by programs.

In traditional blogs, articles mostly exist independently of each other. Most of the time, the connections between them can only rely on categories, tags, search, or manual internal links to maintain a relatively loose connection. But when semantic-index.json appeared, the relationships between articles began to be truly preserved in the form of structured data for the first time. From this moment on, articles are no longer just individual pages, but gradually begin to become a kind of “content node”; and the blog itself also begins to possess a layer of semantic structure that can be read, organized, and expanded by programs.

Thus, “related article recommendation” is actually just the first form of expression in which this layer of structure is implemented. Subsequently, whether it is article-level local knowledge graphs, topic aggregation, in-site semantic navigation, or even further blog RAG, they are all essentially built on the same premise: the relationships between articles are no longer just understandings in the author's mind, but have become a truly existing data structure.

So in the end, the problem this series truly wants to solve is not: “how to make a recommendation plugin for WordPress”, but rather: “can the content relationships in a blog be organized and saved in advance, and re-participate in the reading experience of the blog itself?”

At least for me, the answer is yes. And a recommendation system is just the first step in this endeavor.


It needs to be additionally noted that although the entire implementation process of this article revolves around WordPress, from an architectural perspective, this setup based on semantic-index.json 与 article-index.json “s ”pre-computed recommendation model" is actually naturally more suited for static sites. Because its core idea is essentially:to build all semantic relationships offline in advance, and then directly read the static data at runtime.

Static sites themselves do not have traditional database queries, runtime content calculations, and dynamic rendering pressure, so this “pre-generated index + front-end directly consuming JSON” structure is actually highly consistent with the working style of static blogs. In other words, in WordPress, this system is more like “bypassing the runtime”; whereas in static sites, it turns into a very natural default architecture.

For example, in static blog frameworks like Hugo, Hexo, Jekyll, Astro, and Next.js SSG, it is completely possible to directly generate article-index.jsonduring the build stage, and then have front-end JavaScript read the JSON on article pages and render the recommendation list. The whole process doesn't even need PHP anymore, and no server-side calculation behavior will be produced at runtime.

In a sense, the truly suitable scenario for this type of solution is not actually traditional CMS, but rather blog systems where “content is organized during the build phase and only responsible for display at runtime.”

And this is also why, later on, I increasingly felt that: this JSON-based “lightweight semantic structure,” although it doesn't look complex, actually already begins to carry a hint of a “static knowledge system.”


7 Afterword: Why I Didn't Choose an Off-the-shelf ”Related Articles” Plugin

In fact, when I first prepared to add the “related article recommendations” feature to the blog, my first reaction was not to implement it myself, but to research existing WordPress plugin solutions first. After all, “related article recommendations” itself is already a very mature requirement, and plugins like YARPP and Contextual Related Posts have also developed for many years. But after truly researching it later, I found that: although most plugins are called “related article recommendations,” their essence is still making similarity matches at runtime based on tags, categories, keywords, and other information.

This kind of solution is actually already sufficient for ordinary blogs, but for my current content structure which is increasingly leaning towards a “knowledge context” type, a problem will gradually emerge: many truly important article connections cannot necessarily be accurately expressed through keywords. For example, the extension of a certain topic, different stages of the same problem, the repeated appearance of a certain underlying train of thought in different fields... These relationships are essentially closer to “content structure” rather than simple keyword overlap.

This is also why I eventually chose to directly build the recommendation system based on semantic-index.json . Because in this model, “related” is no longer a result dynamically guessed at runtime, but a content relationship that has been determined in advance during the preceding semantic organization stage.

In other words: traditional plugins: deduce “what might be related” at runtime; semantic-index: determine “what is truly related” in advance during the build phase. These two approaches actually have very large differences in result stability and controllability.

At the same time, I am also becoming increasingly cautious about adding heavy plugins to WordPress. Because many plugins are often accompanied by additional database queries, runtime dynamic calculations, and unpredictable performance consumption behind the scenes. In contrast, my current solution has almost no complex calculation during the runtime phase, and is essentially just a single JSON read and array mapping. For a personal blog, this “pre-computation + static index” approach is instead lighter and more stable.

Of course, it is not without its costs: when articles are added, URLs are modified, or semantic relationships are adjusted, the index building process needs to be executed once again. However, for a personal blog with low-frequency updates, this maintenance cost is actually completely acceptable.

In a sense, what truly attracts me to this system is not just that “the recommendation function has been implemented,” but that: the entire recommendation logic has finally begun to become interpretable, controllable, and predictable.

And behind this is actually something I came to value more and more later on—content relationships in a blog should not only exist in human understanding, but should also be able to be structurally preserved.

📚 Series Articles: Building a “Lightweight Knowledge Index” for Blogs (3 / 8)

View Related Categories · 3 Matches
📌 Content Structure Prompt:
This content is part of the following knowledge map, where you can view the complete content path: WordPress Learning Roadmap。
📎 Related Articles
Share this article
The blog content is original, please indicate the source when reposting! The RSS address of the blog is:https://blog.tangwudi.com/feed, welcome to subscribe; if needed, you can join theTelegram Groupto discuss questions together.
No Comments

Send Comment Edit Comment


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗ
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
Kaomoji
Emoji
Little Dinosaur
Flower!
Previous
Next
       

👋 Welcome to “Wudi's Personal Blog”

Here, long-term exploration is mainly carried out around the following directions:

🧱 Personal Digital Infrastructure and Blog System Construction
☁️ Cloudflare and Network Architecture Practice
🧠 AI and Knowledge System Exploration
🛡️ Network Security and Access Optimization
🎵 Music and Sound Cognition
👁️ Cognitive Perspectives and Worldviews