Blog Post and URLs Relationship Internal

★★★ advanced

October 09, 2021

本ブログでは、全記事のメタデータを HTML ビルド時に解析し、Cypher クエリに変換した上で Neo4j Aura に保存しています。

今回、記事本文に含まれる外部サイトへのリンク数も解析対象に付与しました。 データモデリングの一例としてご紹介します。

課題

ブログの記事には、Wikipedia やドキュメントへの外部リンクを追加することがよく有ります。

外部リンクの問題は、時々リンク先がリンク切れを起こしており、読者の方がクリックしても本来意図した情報にたどり着くことができない点です。

そこで、ブログ記事に含まれる外部リンクをまずは分析できるようにする必要が有りました。もちろん、「どの記事からリンクされているのか」という関係性もモデルに含む必要 がありました。

また、ブログの価値を向上するために、どの外部リンクへのリンクをよく貼っているのか、どういった記事からどの種類のサイトへのリンクを貼ることが多いのか、分析していく必要が有りました。

解決

以前 "Blog Relevant Tags Internal" の記事でもご紹介したとおり、本ブログは Gatsby を利用して、事前に静的 HTML をビルドしています。今回も、そのビルドプロセスで全記事のメタデータを解析し、Cypher クエリに変換し、Neo4j Aura に Write する形を取りました。

実装

本ブログは、Gatsby のプラグインである gatsby-transformer-remark を利用しています。記事は Markdown ファイルで書いていますが、ビルド時にこのプラグインが remark.js を用いて Abstract Syntax Tree (AST) に変換し、最終的には HTML へと変換して出力しています。

remark.js では、unified.js が策定している一連の API に沿った形で AST を定義しています。allMarkdownRemark が返却するフィールドの中にある htmlAst がそれに該当します。

{
    postsRemark: allMarkdownRemark(
        limit: 100
    ) {
        edges {
            node {
                id
                htmlAst
            }
        }
    }

返却結果のイメージとしては、以下のような AST が JSON で返却されます。

"htmlAst": {
    "type": "root",
    "children": [
    {
        "type": "element",
        "tagName": "h3",
        "properties": {},
        "children": [
            {
                "type": "text",
                "value": "About This Blog"
            }
        ]
    },
    {
        "type": "text",
        "value": "Hello, world!\n"
    },

ここまで手に入ったら、後は AST を歩いて適切な Cypher クエリに変換するコードを書くだけです。

データモデリング

データモデリングとしてはシンプルです。記事を表す (:Post) ノードが、それぞれリンクを示す (:URL) ノードに対して、-[:INCLUDES_LINK_TO] リレーションを含みます。

MERGE (p:Post { slug: '...', title: '...' })
MERGE (u:URL { hostname: $hostname, pathname: $pathname, protocol: $protocol, href: url.href})
MERGE (p)-[:INCLUDES_LINK_TO]->(u)`

また、Hostname ごとの分析を容易にしたかったので、(:Hostname) ノードも作成しました。(:URL) ノードに対して、-[:IS_HOSTED_AT]-> リレーションを作成しています。

MERGE (h:Hostname { value: $hostname })
MERGE (h)<-[:IS_HOSTED_AT]-(u)`

執筆時点における (:Post), (:URL), (:Hostname) の全体像は以下のようになっています。

Post and URLs Relationship

利用例

被リンク数の多い Hostname

以下の Cypher クエリでは、被リンクが 3 つ以上の外部サイトの Hostname を、被リンク数とともにテーブルで表示させます。

MATCH p=(h:Hostname)<-[:IS_HOSTED_AT]-(:URL)<-[:INCLUDES_LINK_TO]-(:Post)
WITH h, count(p) as score
WHERE score >= 3
RETURN h.value as hostname, score
ORDER BY score DESC

例えば、執筆時点では以下のような結果になっています。

hostnamescore
neo4j.com17
en.wikipedia.org4
www.linkedin.com3

被リンク数の多い URL

以下の Cypher クエリでは、被リンク数が 3 つ以上の外部サイトの URL を表示させます。Hostname に正規化させない結果を表示させたい場合は以下を使います。

MATCH p=(u:URL)<-[:INCLUDES_LINK_TO]-(:Post)
WITH u, count(p) as score
WHERE score >= 3
RETURN u.href, score

最後に

以上、本サイトに導入した記事と外部 URL との関連性を分析するためのデータモデリングと、実際の仕様例を幾つか紹介しました。

Recommended Posts

  1. ★★★ advanced
    neo4j-causal-clustering-introduced-routing
    本記事では、Neo4j における Causal Clustering (因果クラスタリング) について説明します。 クラスタリングの目的 データベースを運用する場合、単一のインスタンスを稼働させるか、Causal Clustering を用いてクラスター構成を組むか(クラスタリング)のいずれかを選択することになるでしょう。 Single Instance…
  2. ★★★ advanced
    neo4j-bolt-handshake-protocol-introduced
    Bolt Protocol において、サーバーとクライアントが接続を開始するためのプロトコルとして、Bolt Handshake Protocol Specification の仕様が策定されています。 本記事では、Bolt Handshake Protocol の概要について説明します。 Handshake Workflow…