{
    "componentChunkName": "component---src-templates-post-js",
    "path": "/rdfrdfsowlturtlenowei-iwozheng-li-site-huairudeli-jie-suruontoroziru-men/",
    "result": {"data":{"ghostPost":{"id":"Ghost__Post__6a74b697781e37000184ede4","title":"RDF・RDFS・OWL・Turtleの違いを整理して、ファイルで理解するオントロジー入門","slug":"rdfrdfsowlturtlenowei-iwozheng-li-site-huairudeli-jie-suruontoroziru-men","featured":false,"feature_image":"https://ghost.tech.anti-pattern.co.jp/content/images/2026/08/ontology-viewer-overview.png","excerpt":"\n「オントロジーを勉強し始めたけれど、RDF、RDFS、OWL、Turtleの違いが分からない」\n\nこれは、たぶんオントロジー初心者が最初につまずきやすいポイントです。少なくとも、ぼくはこれらが出てきたときに思考が止まりました。。。\nなので、これらだけをザックリ解説する記事になっております。\n\n特に混乱しやすいのが、次のような疑問です。\n\n * RDFとOWLは別のファイル形式なのか\n * .ttlファイルはRDFなのかOWLなのか\n * ex:とは何を意味するのか\n * URIはどのように決めればよいのか\n * RDF/XMLでは、なぜURIの書き方が場所によって違うのか\n\nこの記事では、社員と会社を表す小さなサンプルを使いながら、これらの関係を順番に整理します。\n\n最初はつらいですが、がんばって途中まで読んでみてください！なんとなく、わかった気になるはずです。\n\nそして意外だったのは、オブジェクト指向プログラミング言語を触ったことがある方々は、理解しやすいかもですので、その観点でも読んでみていただけますと幸いです。\n\n※途中で出てくるオントロジーのファイル(.ttl)を、グラフとし","custom_excerpt":null,"visibility":"public","created_at_pretty":"06 August, 2026","published_at_pretty":"18 August, 2026","updated_at_pretty":"19 August, 2026","created_at":"2026-08-07T01:30:15.000+09:00","published_at":"2026-08-19T01:58:13.000+09:00","updated_at":"2026-08-19T14:17:53.000+09:00","meta_title":null,"meta_description":null,"og_description":null,"og_image":null,"og_title":null,"twitter_description":null,"twitter_image":null,"twitter_title":null,"authors":[{"name":"Akihiro YAGASAKI","slug":"akihiro","bio":null,"profile_image":"https://ghost.tech.anti-pattern.co.jp/content/images/2022/04/yagasaki--2-.jpeg","twitter":null,"facebook":null,"website":null}],"primary_author":{"name":"Akihiro YAGASAKI","slug":"akihiro","bio":null,"profile_image":"https://ghost.tech.anti-pattern.co.jp/content/images/2022/04/yagasaki--2-.jpeg","twitter":null,"facebook":null,"website":null},"primary_tag":{"name":"Ontology","slug":"ontology","description":null,"feature_image":null,"meta_description":null,"meta_title":null,"visibility":"public"},"tags":[{"name":"Ontology","slug":"ontology","description":null,"feature_image":null,"meta_description":null,"meta_title":null,"visibility":"public"}],"plaintext":"\n「オントロジーを勉強し始めたけれど、RDF、RDFS、OWL、Turtleの違いが分からない」\n\nこれは、たぶんオントロジー初心者が最初につまずきやすいポイントです。少なくとも、ぼくはこれらが出てきたときに思考が止まりました。。。\nなので、これらだけをザックリ解説する記事になっております。\n\n特に混乱しやすいのが、次のような疑問です。\n\n * RDFとOWLは別のファイル形式なのか\n * .ttlファイルはRDFなのかOWLなのか\n * ex:とは何を意味するのか\n * URIはどのように決めればよいのか\n * RDF/XMLでは、なぜURIの書き方が場所によって違うのか\n\nこの記事では、社員と会社を表す小さなサンプルを使いながら、これらの関係を順番に整理します。\n\n最初はつらいですが、がんばって途中まで読んでみてください！なんとなく、わかった気になるはずです。\n\nそして意外だったのは、オブジェクト指向プログラミング言語を触ったことがある方々は、理解しやすいかもですので、その観点でも読んでみていただけますと幸いです。\n\n※途中で出てくるオントロジーのファイル(.ttl)を、グラフとして可視化しそのまま編集できる VSCode Extension\n[https://marketplace.visualstudio.com/items?itemName=AkihiroYAGASAKI.ontology-viewer] \nを公開していますので、こちらも合わせて使っていただくと、よりイメージがわきやすいかもしれませんので、せひ使ってみてください！\n\nOntology Viewer — OWL & RDF Visual Editor - Visual Studio MarketplaceExtension\nfor Visual Studio Code - Visualize and safely edit OWL, RDF, RDFS, and SKOS\nontologies in VS Code with an interactive schema diagram.Visual Studio\nMarketplace\n[https://marketplace.visualstudio.com/items?itemName=AkihiroYAGASAKI.ontology-viewer]\n--------------------------------------------------------------------------------\n\n全体像を理解する\nまず、RDF、RDFS、OWL、Turtleは、すべて同じ種類のものではありません。\n\n一言で整理すると、次のようになります。\n\nRDF     ：情報をグラフとして表すためのデータモデル\nRDFS    ：クラスや継承関係を定義する基本的なスキーマ\nOWL     ：より高度な意味や論理関係を定義するオントロジー言語\nTurtle  ：RDFグラフをテキストで書くための記法\n.ttl    ：Turtleで書かれたファイルの拡張子\n\n\nRDFでは、情報を「主語・述語・目的語」の3要素で表します。これをトリプルと呼びます。\n\nたとえば、\n\n山田はABC社に勤務している\n\n\nという情報は、次のように表せます。\n\n山田 ── worksFor ──> ABC\n\n\nTurtleでは、次のように書きます。\n\n@prefix ex: <https://example.com/company#> .\n\nex:yamada ex:worksFor ex:abc .\n\n\nex:は省略記号です。\n\nex:yamada\n\n\nは、実際には次のIRIを表します。\n\nhttps://example.com/company#yamada\n\n\n同様に、ex:worksForも次のIRIに展開されます。\n\nhttps://example.com/company#worksFor\n\n\nここで重要なのは、Turtleはあくまで「書き方」だという点です。\n\n同じRDFグラフを、RDF/XMLで書くこともできます。\n\n<rdf:Description rdf:about=\"https://example.com/company#yamada\">\n  <ex:worksFor\n      rdf:resource=\"https://example.com/company#abc\"/>\n</rdf:Description>\n\n\nTurtleとRDF/XMLは見た目が異なりますが、どちらも同じRDFトリプルを表せます。\n\nRDFSを使うと、クラスや継承関係を定義できます。\n\nex:Employee rdfs:subClassOf ex:Person .\n\n\nこれは、「EmployeeはPersonの一種である」という意味です。\n\nOWLでは、さらに複雑な意味を表現できます。\n\nex:worksFor owl:inverseOf ex:hasEmployee .\n\n\nこれは、「山田がABCに勤務しているなら、ABCは山田を社員として持つ」という逆方向の関係を定義します。\n\nしたがって、全体の関係は次のようになります。\n\nRDF\n└─ 情報をトリプルとして表す\n\nRDFS\n└─ RDF上でクラスや継承を定義する\n\nOWL\n└─ RDF上でさらに高度な論理を定義する\n\nTurtle\n└─ それらをテキストファイルに書くための構文\n\n\nつまり、.ttlファイルの中には、単純なRDFデータも、RDFSスキーマも、OWLオントロジーも書くことができます。\n\nここまで理解できれば、全体像はほぼつかめています。\n\n\n--------------------------------------------------------------------------------\n\nここから詳しく解説\n1. RDFとは何か\nRDFは、Resource Description Frameworkの略です。\n\n名前だけを見ると難しそうですが、基本的な考え方はシンプルです。\n\nRDFでは、すべての情報を次の3要素で表します。\n\n主語 ── 述語 ──> 目的語\n\n\n英語では、次のように呼びます。\n\nSubject ── Predicate ──> Object\n\n\nたとえば、\n\n山田はABC社に勤務している\n\n\nという情報は、次のトリプルになります。\n\n主語   ：山田\n述語   ：勤務している\n目的語 ：ABC社\n\n\nRDF風に名前を付けると、次のようになります。\n\n山田 ── worksFor ──> ABC\n\n\nさらに、\n\n山田の社員番号はE001である\n\n\nという情報は、次のトリプルになります。\n\n山田 ── employeeId ──> \"E001\"\n\n\nRDFでは、こうしたトリプルをたくさん組み合わせて、情報のネットワークを作ります。\n\n                 ┌── employeeId ──> \"E001\"\n                 │\n山田 ───────────┼── worksFor ────> ABC\n                 │\n                 └── manages ─────> 鈴木\n\n\nこの構造をRDFグラフと呼びます。\n\n\n--------------------------------------------------------------------------------\n\n2. URIやIRIを使う理由\nRDFでは、山田やABC、worksForなどを、単なる文字列ではなくIRIで識別します。\n\nたとえば、次のIRIを使えます。\n\nhttps://example.com/company#yamada\nhttps://example.com/company#abc\nhttps://example.com/company#worksFor\n\n\nIRIは、世界中で名前が衝突しないようにするための識別子です。\n\nたとえば、異なる会社がどちらもEmployeeという言葉を使っていたとしても、IRIが異なれば別の概念として区別できます。\n\nhttps://company-a.example/vocab#Employee\nhttps://company-b.example/vocab#Employee\n\n\nRDFにおけるIRIは、データベースの主キーやプログラムの完全修飾名に近い役割を持ちます。\n\n\n--------------------------------------------------------------------------------\n\n3. Turtleとは何か\nTurtleは、RDFグラフを人間が比較的読みやすい形で記述するための構文です。\n\n次のTurtleを見てみましょう。\n\n@prefix ex: <https://example.com/company#> .\n\nex:yamada\n    ex:worksFor ex:abc ;\n    ex:employeeId \"E001\" ;\n    ex:manages ex:suzuki .\n\n\nこのファイルには、次の3つのトリプルが書かれています。\n\nex:yamada ex:worksFor ex:abc .\nex:yamada ex:employeeId \"E001\" .\nex:yamada ex:manages ex:suzuki .\n\n\nセミコロン;は、同じ主語を繰り返さないための省略記法です。\n\nしたがって、\n\nex:yamada\n    ex:worksFor ex:abc ;\n    ex:employeeId \"E001\" .\n\n\nは、次と同じ意味です。\n\nex:yamada ex:worksFor ex:abc .\nex:yamada ex:employeeId \"E001\" .\n\n\n\n--------------------------------------------------------------------------------\n\n4. @prefixとex:の意味\nTurtleファイルの先頭には、よく次の記述があります。\n\n@prefix ex: <https://example.com/company#> .\n\n\nこれは、\n\nex:\n\n\nを、\n\nhttps://example.com/company#\n\n\nの省略形として使う、という宣言です。\n\nそのため、\n\nex:yamada\n\n\nは次のIRIに展開されます。\n\nhttps://example.com/company#yamada\n\n\nまた、\n\nex:worksFor\n\n\nは次のIRIに展開されます。\n\nhttps://example.com/company#worksFor\n\n\nexという名前自体に特別な意味はありません。\n\n次の3つは、対応する宣言があれば同じIRIを表します。\n\n@prefix ex:      <https://example.com/company#> .\n@prefix company: <https://example.com/company#> .\n@prefix c:       <https://example.com/company#> .\n\n\nex:Employee\ncompany:Employee\nc:Employee\n\n\n重要なのはexという省略名ではなく、展開先のIRIです。\n\n\n--------------------------------------------------------------------------------\n\n5. 名前空間IRIはどのように決めるのか\nサンプルでは、次のIRIを使用しています。\n\nhttps://example.com/company#\n\n\nexample.comは、説明資料やサンプルコードで使用するためのドメインです。\n\n本番環境では、原則として自社や自分が管理しているドメインを使用します。\n\nたとえば、自社のドメインがmycompany.co.jpなら、次のような名前空間を考えられます。\n\n@prefix company:\n    <https://data.mycompany.co.jp/vocab/company#> .\n\n\n実データ用のIRIは、語彙とは分けると管理しやすくなります。\n\n@prefix company:\n    <https://data.mycompany.co.jp/vocab/company#> .\n\n@prefix resource:\n    <https://data.mycompany.co.jp/resource/> .\n\n\n使用例は次のとおりです。\n\nresource:yamada\n    a company:Employee ;\n    company:worksFor resource:abc ;\n    company:employeeId \"E001\" .\n\n\nここでは、\n\ncompany:Employee\n\n\nが「社員という概念」を表し、\n\nresource:yamada\n\n\nが「具体的な山田という人物」を表します。\n\n名前空間を決めるときは、次の点が重要です。\n\n * 自分たちが管理するドメインを使う\n * 長期間変更しなくて済む名前にする\n * 語彙と実データを必要に応じて分離する\n * 一度公開したIRIを安易に変更しない\n * 可能であれば、ブラウザでアクセスした際に説明を返せるようにする\n\nIRIは単なるWebページのURLではなく、概念やリソースの識別子です。\n\nそのため、Webサイトのデザインを変更したからといって、オントロジーのIRIまで変更するべきではありません。\n\n\n--------------------------------------------------------------------------------\n\n6. #と/はどう使い分けるのか\n名前空間には、主にハッシュ形式とスラッシュ形式があります。\n\nハッシュ形式\n@prefix ex: <https://example.com/company#> .\n\n\n展開結果は次のようになります。\n\nhttps://example.com/company#Employee\nhttps://example.com/company#worksFor\n\n\n比較的小規模な語彙を、1つの文書として管理するときに向いています。\n\nスラッシュ形式\n@prefix ex: <https://example.com/company/> .\n\n\n展開結果は次のようになります。\n\nhttps://example.com/company/Employee\nhttps://example.com/company/worksFor\n\n\n各用語を個別のURLとして管理したい場合に向いています。\n\nどちらが絶対に正しいというわけではありません。\n\nそれよりも、一度決めたIRIを安定して使い続けることのほうが重要です。\n\n\n--------------------------------------------------------------------------------\n\n7. TurtleはRDFそのものではない\nここは重要なポイントです。\n\nRDF     ：情報の構造\nTurtle  ：その構造を書く方法\n\n\nたとえば、次のTurtleがあります。\n\nex:yamada ex:worksFor ex:abc .\n\n\nこの内容は、RDF/XMLでも表せます。\n\n<rdf:Description\n    rdf:about=\"https://example.com/company#yamada\">\n\n  <ex:worksFor\n      rdf:resource=\"https://example.com/company#abc\"/>\n\n</rdf:Description>\n\n\n見た目はまったく違いますが、どちらも次のトリプルを表しています。\n\n主語:\nhttps://example.com/company#yamada\n\n述語:\nhttps://example.com/company#worksFor\n\n目的語:\nhttps://example.com/company#abc\n\n\nつまり、TurtleとRDF/XMLは、同じRDFグラフを異なる形式で表現しているだけです。\n\nプログラムでたとえるなら、同じデータをJSONとXMLのどちらで表現するか、という違いに近いでしょう。\n\n\n--------------------------------------------------------------------------------\n\n8. RDF/XMLにおけるex:の意味\nRDF/XMLでは、ファイルの先頭に次のような名前空間宣言があります。\n\n<rdf:RDF\n    xmlns:rdf=\n      \"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\n    xmlns:ex=\n      \"https://example.com/company#\">\n\n\nここで、\n\nxmlns:ex=\"https://example.com/company#\"\n\n\nと宣言しています。\n\nそのため、\n\n<ex:worksFor>\n\n\nは、次のIRIを表します。\n\nhttps://example.com/company#worksFor\n\n\nex:worksForがそのまま文字列として保存されるわけではありません。\n\nXMLの名前空間機能によって、正式なIRIへ展開されます。\n\n\n--------------------------------------------------------------------------------\n\n9. なぜrdf:resourceには完全なIRIを書くのか\n次のRDF/XMLを見てみましょう。\n\n<ex:worksFor\n    rdf:resource=\"https://example.com/company#abc\"/>\n\n\nex:worksForはXML要素名です。\n\nそのため、XML名前空間の仕組みにより、次のIRIへ展開されます。\n\nhttps://example.com/company#worksFor\n\n\n一方、\n\nrdf:resource=\"https://example.com/company#abc\"\n\n\nの引用符の中は、XMLの要素名ではなく、単なる属性値です。\n\nXML名前空間のプレフィックスは、通常、属性値の中までは展開してくれません。\n\nそのため、次のようには書きません。\n\n<ex:worksFor rdf:resource=\"ex:abc\"/>\n\n\n期待するIRIを確実に指定するため、完全なIRIを書きます。\n\n<ex:worksFor\n    rdf:resource=\"https://example.com/company#abc\"/>\n\n\nこれはRDFの都合というより、XML名前空間の仕組みに由来する制約です。\n\nTurtleでは、主語・述語・目的語のすべてに同じプレフィックス記法を使えます。\n\nex:yamada ex:worksFor ex:abc .\n\n\nそのため、人間が直接読み書きする用途では、Turtleのほうが簡潔です。\n\n\n--------------------------------------------------------------------------------\n\n10. rdf:resourceがある場合とない場合\n次の2つを比較してみましょう。\n\n<ex:worksFor\n    rdf:resource=\"https://example.com/company#abc\"/>\n\n\n<ex:employeeId>E001</ex:employeeId>\n\n\n違いは、目的語がリソースなのか値なのかです。\n\nrdf:resourceがある場合\n<ex:worksFor\n    rdf:resource=\"https://example.com/company#abc\"/>\n\n\nこの目的語は、ABCという別のリソースです。\n\n山田 ── worksFor ──> ABC\n\n\nTurtleでは次のようになります。\n\nex:yamada ex:worksFor ex:abc .\n\n\n要素の中に値がある場合\n<ex:employeeId>E001</ex:employeeId>\n\n\nこの目的語は、E001という文字列です。\n\n山田 ── employeeId ──> \"E001\"\n\n\nTurtleでは次のようになります。\n\nex:yamada ex:employeeId \"E001\" .\n\n\nRDFでは、目的語にIRIで識別されるリソースを置くことも、文字列や数値などのリテラルを置くこともできます。\n\n\n--------------------------------------------------------------------------------\n\n11. RDFSとは何か\nRDFだけでもデータは表現できます。\n\nしかし、RDFだけでは次のような構造を十分に説明できません。\n\n * EmployeeはPersonの一種である\n * ManagerはEmployeeの一種である\n * worksForの主語はEmployeeである\n * worksForの目的語はOrganizationである\n\nこうした基本的なスキーマを表現するのがRDFSです。\n\nたとえば、次のようにクラスを定義できます。\n\n@prefix ex:\n    <https://example.com/company#> .\n\n@prefix rdfs:\n    <http://www.w3.org/2000/01/rdf-schema#> .\n\nex:Employee\n    a rdfs:Class ;\n    rdfs:subClassOf ex:Person .\n\nex:Manager\n    a rdfs:Class ;\n    rdfs:subClassOf ex:Employee .\n\n\nクラス階層は次のようになります。\n\nPerson\n└─ Employee\n   └─ Manager\n\n\nManagerがEmployeeのサブクラスで、EmployeeがPersonのサブクラスなら、ManagerはPersonでもあります。\n\n\n--------------------------------------------------------------------------------\n\n12. rdfs:domainとrdfs:range\nRDFSでは、プロパティの主語と目的語の型を表現できます。\n\nex:worksFor\n    rdfs:domain ex:Employee ;\n    rdfs:range ex:Organization .\n\n\nこれは、次の意味です。\n\nworksForの主語はEmployeeである\nworksForの目的語はOrganizationである\n\n\nたとえば、次のデータがあるとします。\n\nex:yamada ex:worksFor ex:abc .\n\n\nRDFSの推論を行うと、次の型を導けます。\n\nex:yamada a ex:Employee .\nex:abc a ex:Organization .\n\n\nただし、domainとrangeを入力チェックと考えてはいけません。\n\nたとえば、次のデータがあったとします。\n\nex:invoice123 ex:worksFor ex:abc .\n\n\n人間から見ると、「請求書が会社に勤務する」という不自然なデータです。\n\nしかしRDFSは、このデータを単純にエラーにするのではなく、次のように推論します。\n\nex:invoice123 a ex:Employee .\n\n\nつまり、RDFSのdomainは、\n\nEmployeeだけがworksForを使用してよい\n\n\nという入力制約ではなく、\n\nworksForの主語になっているものはEmployeeである\n\n\nという意味です。\n\n\n--------------------------------------------------------------------------------\n\n13. OWLとは何か\nOWLは、RDFSより高度な意味や論理関係を表現するためのオントロジー言語です。\n\nRDFSで表現できる主な内容は、次のとおりです。\n\n * クラス\n * サブクラス\n * プロパティ\n * サブプロパティ\n * domain\n * range\n\nOWLでは、さらに次のような内容を表現できます。\n\n * 2つのクラスが同じ意味である\n * 2つのクラスが同時には成立しない\n * 2つのプロパティが逆関係である\n * プロパティが推移的である\n * プロパティが対称的である\n * 個体同士が同一である\n * 特定の条件を満たすものをクラスとして定義する\n * キーを定義する\n * 値の個数に制約を付ける\n\n\n--------------------------------------------------------------------------------\n\n14. OWLによる逆プロパティ\n次のOWL定義を考えてみます。\n\nex:worksFor\n    a owl:ObjectProperty ;\n    owl:inverseOf ex:hasEmployee .\n\n\nこれは、worksForとhasEmployeeが逆方向の関係であることを表します。\n\nデータに次の記述がある場合、\n\nex:yamada ex:worksFor ex:abc .\n\n\nOWL Reasonerは、次の情報を推論できます。\n\nex:abc ex:hasEmployee ex:yamada .\n\n\n人間が両方向を手動で登録しなくても、片方の情報からもう片方を導けるのです。\n\n\n--------------------------------------------------------------------------------\n\n15. OWLによるクラスの条件定義\nOWLでは、クラスを単に名前で宣言するだけでなく、条件によって定義できます。\n\nたとえば、Managerを次のように定義できます。\n\nManagerとは、\nEmployeeであり、\n少なくとも1人のEmployeeを管理しているもの\n\n\nOWLでは、次のように記述できます。\n\nex:Manager\n    owl:equivalentClass [\n        a owl:Class ;\n        owl:intersectionOf (\n            ex:Employee\n            [\n                a owl:Restriction ;\n                owl:onProperty ex:manages ;\n                owl:someValuesFrom ex:Employee\n            ]\n        )\n    ] .\n\n\nデータに次の記述があるとします。\n\nex:yamada ex:manages ex:suzuki .\n\n\n山田がEmployeeで、鈴木もEmployeeであることが分かれば、Reasonerは次を推論できます。\n\nex:yamada a ex:Manager .\n\n\nつまり、Managerという型を明示的に書かなくても、条件から分類できます。\n\n\n--------------------------------------------------------------------------------\n\n16. OWLの排他関係と不整合\nOWLでは、2つのクラスが同時には成立しないことを表現できます。\n\nex:Organization\n    owl:disjointWith ex:Person .\n\n\nこれは、OrganizationとPersonが排他的であることを意味します。\n\nもし、あるリソースに次の両方の型が付いた場合、\n\nex:abc a ex:Organization .\nex:abc a ex:Person .\n\n\nオントロジーは論理的に不整合となります。\n\nOWL Reasonerは、単に1行の入力ミスを探すのではありません。\n\nオントロジー全体について、すべての条件を同時に満たせるかどうかを検証します。\n\n\n--------------------------------------------------------------------------------\n\n17. OWLは入力バリデーションではない\nOWLは強力ですが、データベースの入力チェックとは考え方が異なります。\n\nOWLでは、一般にオープンワールド仮定が使われます。\n\nたとえば、鈴木の勤務先がデータに書かれていない場合、OWLは次のようには判断しません。\n\n鈴木には勤務先が存在しない\n\n\n代わりに、次のように考えます。\n\n鈴木の勤務先は、まだ分かっていない\n\n\nつまり、情報が書かれていないことと、その情報が存在しないことは別です。\n\nそのため、次のような検証にはOWLだけでは向かない場合があります。\n\n * employeeIdは必須\n * employeeIdは必ず1つ\n * 年齢は0以上\n * メールアドレスは特定の形式\n * 必須プロパティが欠けていないか確認する\n\nこうした入力データの検証には、SHACLという技術がよく使われます。\n\n役割を整理すると、次のようになります。\n\nRDFS・OWL\n    データが何を意味するかを定義する\n\nSHACL\n    データが期待する形になっているかを検証する\n\n\n\n--------------------------------------------------------------------------------\n\n18. RDFパーサーとReasonerの違い\nTurtleファイルを読み込んだからといって、自動的にすべての推論が行われるわけではありません。\n\nパーサーとReasonerは別の役割を持ちます。\n\nRDFパーサー\nTurtleやRDF/XMLを読み込み、\n明示的に書かれたRDFトリプルを取り出す\n\n\nReasoner\nRDFSやOWLの意味論を使い、\n明示されていない情報を推論する\n\n\nたとえば、次のデータがあるとします。\n\nex:yamada ex:manages ex:suzuki .\n\n\n単純なRDFパーサーは、このトリプルを読み込むだけです。\n\nしかし、RDFSやOWLの定義があり、Reasonerを使用すると、次のような型が推論される可能性があります。\n\nex:yamada a ex:Manager .\nex:yamada a ex:Employee .\nex:yamada a ex:Person .\n\nex:suzuki a ex:Employee .\nex:suzuki a ex:Person .\n\n\n使用するReasonerや設定によって、どの範囲まで推論されるかは異なります。\n\n\n--------------------------------------------------------------------------------\n\n19. .ttl、.rdf、.owlの違い\nファイル拡張子と、内部で使われる意味論は分けて考える必要があります。\n\n.ttl\nTurtle構文で書かれたRDFファイルです。\n\n中身は、単純なデータの場合もあれば、RDFSやOWLの定義の場合もあります。\n\ndata.ttl\nschema.ttl\nontology.ttl\n\n\nいずれもTurtle形式で記述できます。\n\n.rdf\nRDF/XML形式のファイルであることが多い拡張子です。\n\n.owl\nOWLオントロジーに使用されることが多い拡張子です。\n\nただし、.owlという拡張子だけでは、内部構文が必ずRDF/XMLであるとは限りません。\n\n実際のファイル形式は、内容やContent-Type、使用ツールの設定を確認する必要があります。\n\n\n--------------------------------------------------------------------------------\n\n20. 一つの.ttlファイルにRDF・RDFS・OWLを書ける\n次のようなTurtleファイルを考えてみましょう。\n\n@prefix ex:\n    <https://example.com/company#> .\n\n@prefix rdf:\n    <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n\n@prefix rdfs:\n    <http://www.w3.org/2000/01/rdf-schema#> .\n\n@prefix owl:\n    <http://www.w3.org/2002/07/owl#> .\n\nex:Employee\n    a owl:Class ;\n    rdfs:subClassOf ex:Person .\n\nex:worksFor\n    a owl:ObjectProperty ;\n    rdfs:domain ex:Employee ;\n    rdfs:range ex:Organization ;\n    owl:inverseOf ex:hasEmployee .\n\nex:yamada\n    a ex:Employee ;\n    ex:worksFor ex:abc .\n\n\nこの1つのファイルには、次の内容が混在しています。\n\nRDFデータ\n    ex:yamada ex:worksFor ex:abc\n\nRDFS\n    rdfs:subClassOf\n    rdfs:domain\n    rdfs:range\n\nOWL\n    owl:Class\n    owl:ObjectProperty\n    owl:inverseOf\n\nTurtle\n    ファイル全体の記述構文\n\n\nしたがって、\n\nこのファイルはRDFなのか、RDFSなのか、OWLなのか\n\n\nという二者択一で考える必要はありません。\n\nすべてRDFグラフであり、その中でRDFSやOWLの語彙を使用しています。\n\n\n--------------------------------------------------------------------------------\n\n21. 実務での使い分け\n目的ごとに整理すると、次のようになります。\n\nやりたいこと使用する技術情報をグラフとして表したいRDF人間が読みやすい形式で保存したいTurtleクラスや継承関係を定義したいRDFS\n同値・排他・逆関係・制約を定義したいOWLデータの必須項目や形式を検証したいSHACLRDFデータを検索したいSPARQL\n実務では、次の組み合わせで使うことが多いでしょう。\n\nデータモデル        RDF\nファイル形式        Turtle\n基本スキーマ        RDFS\n高度な意味論        OWL\nデータ検証          SHACL\n検索                SPARQL\n\n\n\n--------------------------------------------------------------------------------\n\nまとめ\n最後に、最も重要な関係をもう一度整理します。\n\nRDF\n    情報を主語・述語・目的語のトリプルとして表す\n\nRDFS\n    RDF上でクラス、継承、domain、rangeを定義する\n\nOWL\n    RDF上で、同値、排他、逆関係、制約などを定義する\n\nTurtle\n    RDFグラフを読みやすいテキストとして書く構文\n\n.ttl\n    Turtle形式のファイルに使われる拡張子\n\n\nそのため、次の表現はすべて正しいものです。\n\nRDFデータをTurtleで書く\nRDFSスキーマをTurtleで書く\nOWLオントロジーをTurtleで書く\n\n\nまた、\n\n@prefix ex: <https://example.com/company#> .\n\n\nのex:は、IRIを短く書くための省略名です。\n\nex:yamada\nex:worksFor\nex:abc\n\n\nは、それぞれ次のIRIに展開されます。\n\nhttps://example.com/company#yamada\nhttps://example.com/company#worksFor\nhttps://example.com/company#abc\n\n\n本番環境で名前空間を設計する場合は、自社が管理するドメインを使用し、長期間変更せずに使えるIRIを選ぶことが重要です。\n\nオントロジーを学ぶときは、最初からOWLの複雑な制約をすべて理解しようとする必要はありません。\n\nまずは、次の順番で学ぶと理解しやすくなります。\n\n1. RDFのトリプルを理解する\n2. Turtleでトリプルを書いてみる\n3. RDFSでクラスと継承を定義する\n4. OWLで逆関係や排他関係を試す\n5. Reasonerによる推論結果を確認する\n6. 必要に応じてSHACLやSPARQLを学ぶ\n\n\nRDF、RDFS、OWL、Turtleは別々の技術に見えますが、すべては「意味を持ったグラフデータを表現し、共有し、推論する」という一つの目的につながっています。\n\nここまで読んだ方はすごいです！！！\n\n改めましてですが、上記オントロジーのファイル(.ttl)を、グラフとして可視化しそのまま編集できる VSCode Extension\n[https://marketplace.visualstudio.com/items?itemName=AkihiroYAGASAKI.ontology-viewer] \nを公開していますので、こちらも合わせて使っていただくと、よりイメージがわきやすいかもしれませんので、せひ使ってみてください！\n\n以上、やがさきからでした。","html":"<h1></h1><p>「オントロジーを勉強し始めたけれど、RDF、RDFS、OWL、Turtleの違いが分からない」</p><p>これは、たぶんオントロジー初心者が最初につまずきやすいポイントです。少なくとも、ぼくはこれらが出てきたときに思考が止まりました。。。 なので、これらだけをザックリ解説する記事になっております。</p><p>特に混乱しやすいのが、次のような疑問です。</p><ul><li>RDFとOWLは別のファイル形式なのか</li><li><code>.ttl</code>ファイルはRDFなのかOWLなのか</li><li><code>ex:</code>とは何を意味するのか</li><li>URIはどのように決めればよいのか</li><li>RDF/XMLでは、なぜURIの書き方が場所によって違うのか</li></ul><p>この記事では、社員と会社を表す小さなサンプルを使いながら、これらの関係を順番に整理します。</p><p>最初はつらいですが、がんばって途中まで読んでみてください！なんとなく、わかった気になるはずです。</p><p>そして意外だったのは、オブジェクト指向プログラミング言語を触ったことがある方々は、理解しやすいかもですので、その観点でも読んでみていただけますと幸いです。</p><p>※途中で出てくる<a href=\"https://marketplace.visualstudio.com/items?itemName=AkihiroYAGASAKI.ontology-viewer\">オントロジーのファイル(.ttl)を、グラフとして可視化しそのまま編集できる VSCode Extension</a> を公開していますので、こちらも合わせて使っていただくと、よりイメージがわきやすいかもしれませんので、せひ使ってみてください！</p><figure class=\"kg-card kg-bookmark-card\"><a class=\"kg-bookmark-container\" href=\"https://marketplace.visualstudio.com/items?itemName&#x3D;AkihiroYAGASAKI.ontology-viewer\"><div class=\"kg-bookmark-content\"><div class=\"kg-bookmark-title\">Ontology Viewer — OWL &amp; RDF Visual Editor - Visual Studio Marketplace</div><div class=\"kg-bookmark-description\">Extension for Visual Studio Code - Visualize and safely edit OWL, RDF, RDFS, and SKOS ontologies in VS Code with an interactive schema diagram.</div><div class=\"kg-bookmark-metadata\"><span class=\"kg-bookmark-author\">Visual Studio Marketplace</span></div></div><div class=\"kg-bookmark-thumbnail\"><img src=\"https://akihiroyagasaki.gallerycdn.vsassets.io/extensions/akihiroyagasaki/ontology-viewer/0.2.1/1787020264218/Microsoft.VisualStudio.Services.Icons.Default\"></div></a></figure><hr><h2 id=\"%E5%85%A8%E4%BD%93%E5%83%8F%E3%82%92%E7%90%86%E8%A7%A3%E3%81%99%E3%82%8B\">全体像を理解する</h2><p>まず、RDF、RDFS、OWL、Turtleは、すべて同じ種類のものではありません。</p><p>一言で整理すると、次のようになります。</p><pre><code class=\"language-text\">RDF     ：情報をグラフとして表すためのデータモデル\nRDFS    ：クラスや継承関係を定義する基本的なスキーマ\nOWL     ：より高度な意味や論理関係を定義するオントロジー言語\nTurtle  ：RDFグラフをテキストで書くための記法\n.ttl    ：Turtleで書かれたファイルの拡張子\n</code></pre><p>RDFでは、情報を「主語・述語・目的語」の3要素で表します。これをトリプルと呼びます。</p><p>たとえば、</p><pre><code class=\"language-text\">山田はABC社に勤務している\n</code></pre><p>という情報は、次のように表せます。</p><pre><code class=\"language-text\">山田 ── worksFor ──&gt; ABC\n</code></pre><p>Turtleでは、次のように書きます。</p><pre><code class=\"language-turtle\">@prefix ex: &lt;https://example.com/company#&gt; .\n\nex:yamada ex:worksFor ex:abc .\n</code></pre><p><code>ex:</code>は省略記号です。</p><pre><code class=\"language-turtle\">ex:yamada\n</code></pre><p>は、実際には次のIRIを表します。</p><pre><code class=\"language-text\">https://example.com/company#yamada\n</code></pre><p>同様に、<code>ex:worksFor</code>も次のIRIに展開されます。</p><pre><code class=\"language-text\">https://example.com/company#worksFor\n</code></pre><p>ここで重要なのは、Turtleはあくまで「書き方」だという点です。</p><p>同じRDFグラフを、RDF/XMLで書くこともできます。</p><pre><code class=\"language-xml\">&lt;rdf:Description rdf:about=\"https://example.com/company#yamada\"&gt;\n  &lt;ex:worksFor\n      rdf:resource=\"https://example.com/company#abc\"/&gt;\n&lt;/rdf:Description&gt;\n</code></pre><p>TurtleとRDF/XMLは見た目が異なりますが、どちらも同じRDFトリプルを表せます。</p><p>RDFSを使うと、クラスや継承関係を定義できます。</p><pre><code class=\"language-turtle\">ex:Employee rdfs:subClassOf ex:Person .\n</code></pre><p>これは、「EmployeeはPersonの一種である」という意味です。</p><p>OWLでは、さらに複雑な意味を表現できます。</p><pre><code class=\"language-turtle\">ex:worksFor owl:inverseOf ex:hasEmployee .\n</code></pre><p>これは、「山田がABCに勤務しているなら、ABCは山田を社員として持つ」という逆方向の関係を定義します。</p><p>したがって、全体の関係は次のようになります。</p><pre><code class=\"language-text\">RDF\n└─ 情報をトリプルとして表す\n\nRDFS\n└─ RDF上でクラスや継承を定義する\n\nOWL\n└─ RDF上でさらに高度な論理を定義する\n\nTurtle\n└─ それらをテキストファイルに書くための構文\n</code></pre><p>つまり、<code>.ttl</code>ファイルの中には、単純なRDFデータも、RDFSスキーマも、OWLオントロジーも書くことができます。</p><p>ここまで理解できれば、全体像はほぼつかめています。</p><hr><h1 id=\"%E3%81%93%E3%81%93%E3%81%8B%E3%82%89%E8%A9%B3%E3%81%97%E3%81%8F%E8%A7%A3%E8%AA%AC\">ここから詳しく解説</h1><h2 id=\"1-rdf%E3%81%A8%E3%81%AF%E4%BD%95%E3%81%8B\">1. RDFとは何か</h2><p>RDFは、Resource Description Frameworkの略です。</p><p>名前だけを見ると難しそうですが、基本的な考え方はシンプルです。</p><p>RDFでは、すべての情報を次の3要素で表します。</p><pre><code class=\"language-text\">主語 ── 述語 ──&gt; 目的語\n</code></pre><p>英語では、次のように呼びます。</p><pre><code class=\"language-text\">Subject ── Predicate ──&gt; Object\n</code></pre><p>たとえば、</p><pre><code class=\"language-text\">山田はABC社に勤務している\n</code></pre><p>という情報は、次のトリプルになります。</p><pre><code class=\"language-text\">主語   ：山田\n述語   ：勤務している\n目的語 ：ABC社\n</code></pre><p>RDF風に名前を付けると、次のようになります。</p><pre><code class=\"language-text\">山田 ── worksFor ──&gt; ABC\n</code></pre><p>さらに、</p><pre><code class=\"language-text\">山田の社員番号はE001である\n</code></pre><p>という情報は、次のトリプルになります。</p><pre><code class=\"language-text\">山田 ── employeeId ──&gt; \"E001\"\n</code></pre><p>RDFでは、こうしたトリプルをたくさん組み合わせて、情報のネットワークを作ります。</p><pre><code class=\"language-text\">                 ┌── employeeId ──&gt; \"E001\"\n                 │\n山田 ───────────┼── worksFor ────&gt; ABC\n                 │\n                 └── manages ─────&gt; 鈴木\n</code></pre><p>この構造をRDFグラフと呼びます。</p><hr><h2 id=\"2-uri%E3%82%84iri%E3%82%92%E4%BD%BF%E3%81%86%E7%90%86%E7%94%B1\">2. URIやIRIを使う理由</h2><p>RDFでは、山田やABC、worksForなどを、単なる文字列ではなくIRIで識別します。</p><p>たとえば、次のIRIを使えます。</p><pre><code class=\"language-text\">https://example.com/company#yamada\nhttps://example.com/company#abc\nhttps://example.com/company#worksFor\n</code></pre><p>IRIは、世界中で名前が衝突しないようにするための識別子です。</p><p>たとえば、異なる会社がどちらも<code>Employee</code>という言葉を使っていたとしても、IRIが異なれば別の概念として区別できます。</p><pre><code class=\"language-text\">https://company-a.example/vocab#Employee\nhttps://company-b.example/vocab#Employee\n</code></pre><p>RDFにおけるIRIは、データベースの主キーやプログラムの完全修飾名に近い役割を持ちます。</p><hr><h2 id=\"3-turtle%E3%81%A8%E3%81%AF%E4%BD%95%E3%81%8B\">3. Turtleとは何か</h2><p>Turtleは、RDFグラフを人間が比較的読みやすい形で記述するための構文です。</p><p>次のTurtleを見てみましょう。</p><pre><code class=\"language-turtle\">@prefix ex: &lt;https://example.com/company#&gt; .\n\nex:yamada\n    ex:worksFor ex:abc ;\n    ex:employeeId \"E001\" ;\n    ex:manages ex:suzuki .\n</code></pre><p>このファイルには、次の3つのトリプルが書かれています。</p><pre><code class=\"language-turtle\">ex:yamada ex:worksFor ex:abc .\nex:yamada ex:employeeId \"E001\" .\nex:yamada ex:manages ex:suzuki .\n</code></pre><p>セミコロン<code>;</code>は、同じ主語を繰り返さないための省略記法です。</p><p>したがって、</p><pre><code class=\"language-turtle\">ex:yamada\n    ex:worksFor ex:abc ;\n    ex:employeeId \"E001\" .\n</code></pre><p>は、次と同じ意味です。</p><pre><code class=\"language-turtle\">ex:yamada ex:worksFor ex:abc .\nex:yamada ex:employeeId \"E001\" .\n</code></pre><hr><h2 id=\"4-prefix%E3%81%A8ex%E3%81%AE%E6%84%8F%E5%91%B3\">4. <code>@prefix</code>と<code>ex:</code>の意味</h2><p>Turtleファイルの先頭には、よく次の記述があります。</p><pre><code class=\"language-turtle\">@prefix ex: &lt;https://example.com/company#&gt; .\n</code></pre><p>これは、</p><pre><code class=\"language-text\">ex:\n</code></pre><p>を、</p><pre><code class=\"language-text\">https://example.com/company#\n</code></pre><p>の省略形として使う、という宣言です。</p><p>そのため、</p><pre><code class=\"language-turtle\">ex:yamada\n</code></pre><p>は次のIRIに展開されます。</p><pre><code class=\"language-text\">https://example.com/company#yamada\n</code></pre><p>また、</p><pre><code class=\"language-turtle\">ex:worksFor\n</code></pre><p>は次のIRIに展開されます。</p><pre><code class=\"language-text\">https://example.com/company#worksFor\n</code></pre><p><code>ex</code>という名前自体に特別な意味はありません。</p><p>次の3つは、対応する宣言があれば同じIRIを表します。</p><pre><code class=\"language-turtle\">@prefix ex:      &lt;https://example.com/company#&gt; .\n@prefix company: &lt;https://example.com/company#&gt; .\n@prefix c:       &lt;https://example.com/company#&gt; .\n</code></pre><pre><code class=\"language-turtle\">ex:Employee\ncompany:Employee\nc:Employee\n</code></pre><p>重要なのは<code>ex</code>という省略名ではなく、展開先のIRIです。</p><hr><h2 id=\"5-%E5%90%8D%E5%89%8D%E7%A9%BA%E9%96%93iri%E3%81%AF%E3%81%A9%E3%81%AE%E3%82%88%E3%81%86%E3%81%AB%E6%B1%BA%E3%82%81%E3%82%8B%E3%81%AE%E3%81%8B\">5. 名前空間IRIはどのように決めるのか</h2><p>サンプルでは、次のIRIを使用しています。</p><pre><code class=\"language-text\">https://example.com/company#\n</code></pre><p><code>example.com</code>は、説明資料やサンプルコードで使用するためのドメインです。</p><p>本番環境では、原則として自社や自分が管理しているドメインを使用します。</p><p>たとえば、自社のドメインが<code>mycompany.co.jp</code>なら、次のような名前空間を考えられます。</p><pre><code class=\"language-turtle\">@prefix company:\n    &lt;https://data.mycompany.co.jp/vocab/company#&gt; .\n</code></pre><p>実データ用のIRIは、語彙とは分けると管理しやすくなります。</p><pre><code class=\"language-turtle\">@prefix company:\n    &lt;https://data.mycompany.co.jp/vocab/company#&gt; .\n\n@prefix resource:\n    &lt;https://data.mycompany.co.jp/resource/&gt; .\n</code></pre><p>使用例は次のとおりです。</p><pre><code class=\"language-turtle\">resource:yamada\n    a company:Employee ;\n    company:worksFor resource:abc ;\n    company:employeeId \"E001\" .\n</code></pre><p>ここでは、</p><pre><code class=\"language-text\">company:Employee\n</code></pre><p>が「社員という概念」を表し、</p><pre><code class=\"language-text\">resource:yamada\n</code></pre><p>が「具体的な山田という人物」を表します。</p><p>名前空間を決めるときは、次の点が重要です。</p><ul><li>自分たちが管理するドメインを使う</li><li>長期間変更しなくて済む名前にする</li><li>語彙と実データを必要に応じて分離する</li><li>一度公開したIRIを安易に変更しない</li><li>可能であれば、ブラウザでアクセスした際に説明を返せるようにする</li></ul><p>IRIは単なるWebページのURLではなく、概念やリソースの識別子です。</p><p>そのため、Webサイトのデザインを変更したからといって、オントロジーのIRIまで変更するべきではありません。</p><hr><h2 id=\"6-%E3%81%A8%E3%81%AF%E3%81%A9%E3%81%86%E4%BD%BF%E3%81%84%E5%88%86%E3%81%91%E3%82%8B%E3%81%AE%E3%81%8B\">6. <code>#</code>と<code>/</code>はどう使い分けるのか</h2><p>名前空間には、主にハッシュ形式とスラッシュ形式があります。</p><h3 id=\"%E3%83%8F%E3%83%83%E3%82%B7%E3%83%A5%E5%BD%A2%E5%BC%8F\">ハッシュ形式</h3><pre><code class=\"language-turtle\">@prefix ex: &lt;https://example.com/company#&gt; .\n</code></pre><p>展開結果は次のようになります。</p><pre><code class=\"language-text\">https://example.com/company#Employee\nhttps://example.com/company#worksFor\n</code></pre><p>比較的小規模な語彙を、1つの文書として管理するときに向いています。</p><h3 id=\"%E3%82%B9%E3%83%A9%E3%83%83%E3%82%B7%E3%83%A5%E5%BD%A2%E5%BC%8F\">スラッシュ形式</h3><pre><code class=\"language-turtle\">@prefix ex: &lt;https://example.com/company/&gt; .\n</code></pre><p>展開結果は次のようになります。</p><pre><code class=\"language-text\">https://example.com/company/Employee\nhttps://example.com/company/worksFor\n</code></pre><p>各用語を個別のURLとして管理したい場合に向いています。</p><p>どちらが絶対に正しいというわけではありません。</p><p>それよりも、一度決めたIRIを安定して使い続けることのほうが重要です。</p><hr><h2 id=\"7-turtle%E3%81%AFrdf%E3%81%9D%E3%81%AE%E3%82%82%E3%81%AE%E3%81%A7%E3%81%AF%E3%81%AA%E3%81%84\">7. TurtleはRDFそのものではない</h2><p>ここは重要なポイントです。</p><pre><code class=\"language-text\">RDF     ：情報の構造\nTurtle  ：その構造を書く方法\n</code></pre><p>たとえば、次のTurtleがあります。</p><pre><code class=\"language-turtle\">ex:yamada ex:worksFor ex:abc .\n</code></pre><p>この内容は、RDF/XMLでも表せます。</p><pre><code class=\"language-xml\">&lt;rdf:Description\n    rdf:about=\"https://example.com/company#yamada\"&gt;\n\n  &lt;ex:worksFor\n      rdf:resource=\"https://example.com/company#abc\"/&gt;\n\n&lt;/rdf:Description&gt;\n</code></pre><p>見た目はまったく違いますが、どちらも次のトリプルを表しています。</p><pre><code class=\"language-text\">主語:\nhttps://example.com/company#yamada\n\n述語:\nhttps://example.com/company#worksFor\n\n目的語:\nhttps://example.com/company#abc\n</code></pre><p>つまり、TurtleとRDF/XMLは、同じRDFグラフを異なる形式で表現しているだけです。</p><p>プログラムでたとえるなら、同じデータをJSONとXMLのどちらで表現するか、という違いに近いでしょう。</p><hr><h2 id=\"8-rdfxml%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8Bex%E3%81%AE%E6%84%8F%E5%91%B3\">8. RDF/XMLにおける<code>ex:</code>の意味</h2><p>RDF/XMLでは、ファイルの先頭に次のような名前空間宣言があります。</p><pre><code class=\"language-xml\">&lt;rdf:RDF\n    xmlns:rdf=\n      \"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\n    xmlns:ex=\n      \"https://example.com/company#\"&gt;\n</code></pre><p>ここで、</p><pre><code class=\"language-xml\">xmlns:ex=\"https://example.com/company#\"\n</code></pre><p>と宣言しています。</p><p>そのため、</p><pre><code class=\"language-xml\">&lt;ex:worksFor&gt;\n</code></pre><p>は、次のIRIを表します。</p><pre><code class=\"language-text\">https://example.com/company#worksFor\n</code></pre><p><code>ex:worksFor</code>がそのまま文字列として保存されるわけではありません。</p><p>XMLの名前空間機能によって、正式なIRIへ展開されます。</p><hr><h2 id=\"9-%E3%81%AA%E3%81%9Crdfresource%E3%81%AB%E3%81%AF%E5%AE%8C%E5%85%A8%E3%81%AAiri%E3%82%92%E6%9B%B8%E3%81%8F%E3%81%AE%E3%81%8B\">9. なぜ<code>rdf:resource</code>には完全なIRIを書くのか</h2><p>次のRDF/XMLを見てみましょう。</p><pre><code class=\"language-xml\">&lt;ex:worksFor\n    rdf:resource=\"https://example.com/company#abc\"/&gt;\n</code></pre><p><code>ex:worksFor</code>はXML要素名です。</p><p>そのため、XML名前空間の仕組みにより、次のIRIへ展開されます。</p><pre><code class=\"language-text\">https://example.com/company#worksFor\n</code></pre><p>一方、</p><pre><code class=\"language-xml\">rdf:resource=\"https://example.com/company#abc\"\n</code></pre><p>の引用符の中は、XMLの要素名ではなく、単なる属性値です。</p><p>XML名前空間のプレフィックスは、通常、属性値の中までは展開してくれません。</p><p>そのため、次のようには書きません。</p><pre><code class=\"language-xml\">&lt;ex:worksFor rdf:resource=\"ex:abc\"/&gt;\n</code></pre><p>期待するIRIを確実に指定するため、完全なIRIを書きます。</p><pre><code class=\"language-xml\">&lt;ex:worksFor\n    rdf:resource=\"https://example.com/company#abc\"/&gt;\n</code></pre><p>これはRDFの都合というより、XML名前空間の仕組みに由来する制約です。</p><p>Turtleでは、主語・述語・目的語のすべてに同じプレフィックス記法を使えます。</p><pre><code class=\"language-turtle\">ex:yamada ex:worksFor ex:abc .\n</code></pre><p>そのため、人間が直接読み書きする用途では、Turtleのほうが簡潔です。</p><hr><h2 id=\"10-rdfresource%E3%81%8C%E3%81%82%E3%82%8B%E5%A0%B4%E5%90%88%E3%81%A8%E3%81%AA%E3%81%84%E5%A0%B4%E5%90%88\">10. <code>rdf:resource</code>がある場合とない場合</h2><p>次の2つを比較してみましょう。</p><pre><code class=\"language-xml\">&lt;ex:worksFor\n    rdf:resource=\"https://example.com/company#abc\"/&gt;\n</code></pre><pre><code class=\"language-xml\">&lt;ex:employeeId&gt;E001&lt;/ex:employeeId&gt;\n</code></pre><p>違いは、目的語がリソースなのか値なのかです。</p><h3 id=\"rdfresource%E3%81%8C%E3%81%82%E3%82%8B%E5%A0%B4%E5%90%88\"><code>rdf:resource</code>がある場合</h3><pre><code class=\"language-xml\">&lt;ex:worksFor\n    rdf:resource=\"https://example.com/company#abc\"/&gt;\n</code></pre><p>この目的語は、ABCという別のリソースです。</p><pre><code class=\"language-text\">山田 ── worksFor ──&gt; ABC\n</code></pre><p>Turtleでは次のようになります。</p><pre><code class=\"language-turtle\">ex:yamada ex:worksFor ex:abc .\n</code></pre><h3 id=\"%E8%A6%81%E7%B4%A0%E3%81%AE%E4%B8%AD%E3%81%AB%E5%80%A4%E3%81%8C%E3%81%82%E3%82%8B%E5%A0%B4%E5%90%88\">要素の中に値がある場合</h3><pre><code class=\"language-xml\">&lt;ex:employeeId&gt;E001&lt;/ex:employeeId&gt;\n</code></pre><p>この目的語は、<code>E001</code>という文字列です。</p><pre><code class=\"language-text\">山田 ── employeeId ──&gt; \"E001\"\n</code></pre><p>Turtleでは次のようになります。</p><pre><code class=\"language-turtle\">ex:yamada ex:employeeId \"E001\" .\n</code></pre><p>RDFでは、目的語にIRIで識別されるリソースを置くことも、文字列や数値などのリテラルを置くこともできます。</p><hr><h2 id=\"11-rdfs%E3%81%A8%E3%81%AF%E4%BD%95%E3%81%8B\">11. RDFSとは何か</h2><p>RDFだけでもデータは表現できます。</p><p>しかし、RDFだけでは次のような構造を十分に説明できません。</p><ul><li>EmployeeはPersonの一種である</li><li>ManagerはEmployeeの一種である</li><li>worksForの主語はEmployeeである</li><li>worksForの目的語はOrganizationである</li></ul><p>こうした基本的なスキーマを表現するのがRDFSです。</p><p>たとえば、次のようにクラスを定義できます。</p><pre><code class=\"language-turtle\">@prefix ex:\n    &lt;https://example.com/company#&gt; .\n\n@prefix rdfs:\n    &lt;http://www.w3.org/2000/01/rdf-schema#&gt; .\n\nex:Employee\n    a rdfs:Class ;\n    rdfs:subClassOf ex:Person .\n\nex:Manager\n    a rdfs:Class ;\n    rdfs:subClassOf ex:Employee .\n</code></pre><p>クラス階層は次のようになります。</p><pre><code class=\"language-text\">Person\n└─ Employee\n   └─ Manager\n</code></pre><p>ManagerがEmployeeのサブクラスで、EmployeeがPersonのサブクラスなら、ManagerはPersonでもあります。</p><hr><h2 id=\"12-rdfsdomain%E3%81%A8rdfsrange\">12. <code>rdfs:domain</code>と<code>rdfs:range</code></h2><p>RDFSでは、プロパティの主語と目的語の型を表現できます。</p><pre><code class=\"language-turtle\">ex:worksFor\n    rdfs:domain ex:Employee ;\n    rdfs:range ex:Organization .\n</code></pre><p>これは、次の意味です。</p><pre><code class=\"language-text\">worksForの主語はEmployeeである\nworksForの目的語はOrganizationである\n</code></pre><p>たとえば、次のデータがあるとします。</p><pre><code class=\"language-turtle\">ex:yamada ex:worksFor ex:abc .\n</code></pre><p>RDFSの推論を行うと、次の型を導けます。</p><pre><code class=\"language-turtle\">ex:yamada a ex:Employee .\nex:abc a ex:Organization .\n</code></pre><p>ただし、<code>domain</code>と<code>range</code>を入力チェックと考えてはいけません。</p><p>たとえば、次のデータがあったとします。</p><pre><code class=\"language-turtle\">ex:invoice123 ex:worksFor ex:abc .\n</code></pre><p>人間から見ると、「請求書が会社に勤務する」という不自然なデータです。</p><p>しかしRDFSは、このデータを単純にエラーにするのではなく、次のように推論します。</p><pre><code class=\"language-turtle\">ex:invoice123 a ex:Employee .\n</code></pre><p>つまり、RDFSの<code>domain</code>は、</p><pre><code class=\"language-text\">EmployeeだけがworksForを使用してよい\n</code></pre><p>という入力制約ではなく、</p><pre><code class=\"language-text\">worksForの主語になっているものはEmployeeである\n</code></pre><p>という意味です。</p><hr><h2 id=\"13-owl%E3%81%A8%E3%81%AF%E4%BD%95%E3%81%8B\">13. OWLとは何か</h2><p>OWLは、RDFSより高度な意味や論理関係を表現するためのオントロジー言語です。</p><p>RDFSで表現できる主な内容は、次のとおりです。</p><ul><li>クラス</li><li>サブクラス</li><li>プロパティ</li><li>サブプロパティ</li><li>domain</li><li>range</li></ul><p>OWLでは、さらに次のような内容を表現できます。</p><ul><li>2つのクラスが同じ意味である</li><li>2つのクラスが同時には成立しない</li><li>2つのプロパティが逆関係である</li><li>プロパティが推移的である</li><li>プロパティが対称的である</li><li>個体同士が同一である</li><li>特定の条件を満たすものをクラスとして定義する</li><li>キーを定義する</li><li>値の個数に制約を付ける</li></ul><hr><h2 id=\"14-owl%E3%81%AB%E3%82%88%E3%82%8B%E9%80%86%E3%83%97%E3%83%AD%E3%83%91%E3%83%86%E3%82%A3\">14. OWLによる逆プロパティ</h2><p>次のOWL定義を考えてみます。</p><pre><code class=\"language-turtle\">ex:worksFor\n    a owl:ObjectProperty ;\n    owl:inverseOf ex:hasEmployee .\n</code></pre><p>これは、<code>worksFor</code>と<code>hasEmployee</code>が逆方向の関係であることを表します。</p><p>データに次の記述がある場合、</p><pre><code class=\"language-turtle\">ex:yamada ex:worksFor ex:abc .\n</code></pre><p>OWL Reasonerは、次の情報を推論できます。</p><pre><code class=\"language-turtle\">ex:abc ex:hasEmployee ex:yamada .\n</code></pre><p>人間が両方向を手動で登録しなくても、片方の情報からもう片方を導けるのです。</p><hr><h2 id=\"15-owl%E3%81%AB%E3%82%88%E3%82%8B%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%AE%E6%9D%A1%E4%BB%B6%E5%AE%9A%E7%BE%A9\">15. OWLによるクラスの条件定義</h2><p>OWLでは、クラスを単に名前で宣言するだけでなく、条件によって定義できます。</p><p>たとえば、Managerを次のように定義できます。</p><pre><code class=\"language-text\">Managerとは、\nEmployeeであり、\n少なくとも1人のEmployeeを管理しているもの\n</code></pre><p>OWLでは、次のように記述できます。</p><pre><code class=\"language-turtle\">ex:Manager\n    owl:equivalentClass [\n        a owl:Class ;\n        owl:intersectionOf (\n            ex:Employee\n            [\n                a owl:Restriction ;\n                owl:onProperty ex:manages ;\n                owl:someValuesFrom ex:Employee\n            ]\n        )\n    ] .\n</code></pre><p>データに次の記述があるとします。</p><pre><code class=\"language-turtle\">ex:yamada ex:manages ex:suzuki .\n</code></pre><p>山田がEmployeeで、鈴木もEmployeeであることが分かれば、Reasonerは次を推論できます。</p><pre><code class=\"language-turtle\">ex:yamada a ex:Manager .\n</code></pre><p>つまり、Managerという型を明示的に書かなくても、条件から分類できます。</p><hr><h2 id=\"16-owl%E3%81%AE%E6%8E%92%E4%BB%96%E9%96%A2%E4%BF%82%E3%81%A8%E4%B8%8D%E6%95%B4%E5%90%88\">16. OWLの排他関係と不整合</h2><p>OWLでは、2つのクラスが同時には成立しないことを表現できます。</p><pre><code class=\"language-turtle\">ex:Organization\n    owl:disjointWith ex:Person .\n</code></pre><p>これは、OrganizationとPersonが排他的であることを意味します。</p><p>もし、あるリソースに次の両方の型が付いた場合、</p><pre><code class=\"language-turtle\">ex:abc a ex:Organization .\nex:abc a ex:Person .\n</code></pre><p>オントロジーは論理的に不整合となります。</p><p>OWL Reasonerは、単に1行の入力ミスを探すのではありません。</p><p>オントロジー全体について、すべての条件を同時に満たせるかどうかを検証します。</p><hr><h2 id=\"17-owl%E3%81%AF%E5%85%A5%E5%8A%9B%E3%83%90%E3%83%AA%E3%83%87%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%A7%E3%81%AF%E3%81%AA%E3%81%84\">17. OWLは入力バリデーションではない</h2><p>OWLは強力ですが、データベースの入力チェックとは考え方が異なります。</p><p>OWLでは、一般にオープンワールド仮定が使われます。</p><p>たとえば、鈴木の勤務先がデータに書かれていない場合、OWLは次のようには判断しません。</p><pre><code class=\"language-text\">鈴木には勤務先が存在しない\n</code></pre><p>代わりに、次のように考えます。</p><pre><code class=\"language-text\">鈴木の勤務先は、まだ分かっていない\n</code></pre><p>つまり、情報が書かれていないことと、その情報が存在しないことは別です。</p><p>そのため、次のような検証にはOWLだけでは向かない場合があります。</p><ul><li>employeeIdは必須</li><li>employeeIdは必ず1つ</li><li>年齢は0以上</li><li>メールアドレスは特定の形式</li><li>必須プロパティが欠けていないか確認する</li></ul><p>こうした入力データの検証には、SHACLという技術がよく使われます。</p><p>役割を整理すると、次のようになります。</p><pre><code class=\"language-text\">RDFS・OWL\n    データが何を意味するかを定義する\n\nSHACL\n    データが期待する形になっているかを検証する\n</code></pre><hr><h2 id=\"18-rdf%E3%83%91%E3%83%BC%E3%82%B5%E3%83%BC%E3%81%A8reasoner%E3%81%AE%E9%81%95%E3%81%84\">18. RDFパーサーとReasonerの違い</h2><p>Turtleファイルを読み込んだからといって、自動的にすべての推論が行われるわけではありません。</p><p>パーサーとReasonerは別の役割を持ちます。</p><h3 id=\"rdf%E3%83%91%E3%83%BC%E3%82%B5%E3%83%BC\">RDFパーサー</h3><pre><code class=\"language-text\">TurtleやRDF/XMLを読み込み、\n明示的に書かれたRDFトリプルを取り出す\n</code></pre><h3 id=\"reasoner\">Reasoner</h3><pre><code class=\"language-text\">RDFSやOWLの意味論を使い、\n明示されていない情報を推論する\n</code></pre><p>たとえば、次のデータがあるとします。</p><pre><code class=\"language-turtle\">ex:yamada ex:manages ex:suzuki .\n</code></pre><p>単純なRDFパーサーは、このトリプルを読み込むだけです。</p><p>しかし、RDFSやOWLの定義があり、Reasonerを使用すると、次のような型が推論される可能性があります。</p><pre><code class=\"language-turtle\">ex:yamada a ex:Manager .\nex:yamada a ex:Employee .\nex:yamada a ex:Person .\n\nex:suzuki a ex:Employee .\nex:suzuki a ex:Person .\n</code></pre><p>使用するReasonerや設定によって、どの範囲まで推論されるかは異なります。</p><hr><h2 id=\"19-ttl%E3%80%81rdf%E3%80%81owl%E3%81%AE%E9%81%95%E3%81%84\">19. <code>.ttl</code>、<code>.rdf</code>、<code>.owl</code>の違い</h2><p>ファイル拡張子と、内部で使われる意味論は分けて考える必要があります。</p><h3 id=\"ttl\"><code>.ttl</code></h3><p>Turtle構文で書かれたRDFファイルです。</p><p>中身は、単純なデータの場合もあれば、RDFSやOWLの定義の場合もあります。</p><pre><code class=\"language-text\">data.ttl\nschema.ttl\nontology.ttl\n</code></pre><p>いずれもTurtle形式で記述できます。</p><h3 id=\"rdf\"><code>.rdf</code></h3><p>RDF/XML形式のファイルであることが多い拡張子です。</p><h3 id=\"owl\"><code>.owl</code></h3><p>OWLオントロジーに使用されることが多い拡張子です。</p><p>ただし、<code>.owl</code>という拡張子だけでは、内部構文が必ずRDF/XMLであるとは限りません。</p><p>実際のファイル形式は、内容やContent-Type、使用ツールの設定を確認する必要があります。</p><hr><h2 id=\"20-%E4%B8%80%E3%81%A4%E3%81%AEttl%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%ABrdf%E3%83%BBrdfs%E3%83%BBowl%E3%82%92%E6%9B%B8%E3%81%91%E3%82%8B\">20. 一つの<code>.ttl</code>ファイルにRDF・RDFS・OWLを書ける</h2><p>次のようなTurtleファイルを考えてみましょう。</p><pre><code class=\"language-turtle\">@prefix ex:\n    &lt;https://example.com/company#&gt; .\n\n@prefix rdf:\n    &lt;http://www.w3.org/1999/02/22-rdf-syntax-ns#&gt; .\n\n@prefix rdfs:\n    &lt;http://www.w3.org/2000/01/rdf-schema#&gt; .\n\n@prefix owl:\n    &lt;http://www.w3.org/2002/07/owl#&gt; .\n\nex:Employee\n    a owl:Class ;\n    rdfs:subClassOf ex:Person .\n\nex:worksFor\n    a owl:ObjectProperty ;\n    rdfs:domain ex:Employee ;\n    rdfs:range ex:Organization ;\n    owl:inverseOf ex:hasEmployee .\n\nex:yamada\n    a ex:Employee ;\n    ex:worksFor ex:abc .\n</code></pre><p>この1つのファイルには、次の内容が混在しています。</p><pre><code class=\"language-text\">RDFデータ\n    ex:yamada ex:worksFor ex:abc\n\nRDFS\n    rdfs:subClassOf\n    rdfs:domain\n    rdfs:range\n\nOWL\n    owl:Class\n    owl:ObjectProperty\n    owl:inverseOf\n\nTurtle\n    ファイル全体の記述構文\n</code></pre><p>したがって、</p><pre><code class=\"language-text\">このファイルはRDFなのか、RDFSなのか、OWLなのか\n</code></pre><p>という二者択一で考える必要はありません。</p><p>すべてRDFグラフであり、その中でRDFSやOWLの語彙を使用しています。</p><hr><h2 id=\"21-%E5%AE%9F%E5%8B%99%E3%81%A7%E3%81%AE%E4%BD%BF%E3%81%84%E5%88%86%E3%81%91\">21. 実務での使い分け</h2><p>目的ごとに整理すると、次のようになります。</p><!--kg-card-begin: html--><table data-line=\"1043\" class=\"code-line\" dir=\"auto\" style=\"border-collapse: collapse; margin-bottom: 0.7em;\"><thead data-line=\"1043\" class=\"code-line\" dir=\"auto\"><tr data-line=\"1043\" class=\"code-line\" dir=\"auto\"><th style=\"text-align: left; border-bottom: 1px solid rgba(255, 255, 255, 0.69); padding: 5px 10px; border-top-color: rgba(255, 255, 255, 0.69); border-right-color: rgba(255, 255, 255, 0.69); border-left-color: rgba(255, 255, 255, 0.69);\">やりたいこと</th><th style=\"text-align: left; border-bottom: 1px solid rgba(255, 255, 255, 0.69); padding: 5px 10px; border-top-color: rgba(255, 255, 255, 0.69); border-right-color: rgba(255, 255, 255, 0.69); border-left-color: rgba(255, 255, 255, 0.69);\">使用する技術</th></tr></thead><tbody data-line=\"1045\" class=\"code-line\" dir=\"auto\"><tr data-line=\"1045\" class=\"code-line\" dir=\"auto\"><td style=\"padding: 5px 10px; border-color: rgba(255, 255, 255, 0.18);\">情報をグラフとして表したい</td><td style=\"padding: 5px 10px; border-color: rgba(255, 255, 255, 0.18);\">RDF</td></tr><tr data-line=\"1046\" class=\"code-line\" dir=\"auto\"><td style=\"padding: 5px 10px; border-top: 1px solid rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-bottom-color: rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18);\">人間が読みやすい形式で保存したい</td><td style=\"padding: 5px 10px; border-top: 1px solid rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-bottom-color: rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18);\">Turtle</td></tr><tr data-line=\"1047\" class=\"code-line\" dir=\"auto\"><td style=\"padding: 5px 10px; border-top: 1px solid rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-bottom-color: rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18);\">クラスや継承関係を定義したい</td><td style=\"padding: 5px 10px; border-top: 1px solid rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-bottom-color: rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18);\">RDFS</td></tr><tr data-line=\"1048\" class=\"code-line\" dir=\"auto\"><td style=\"padding: 5px 10px; border-top: 1px solid rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-bottom-color: rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18);\">同値・排他・逆関係・制約を定義したい</td><td style=\"padding: 5px 10px; border-top: 1px solid rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-bottom-color: rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18);\">OWL</td></tr><tr data-line=\"1049\" class=\"code-line\" dir=\"auto\"><td style=\"padding: 5px 10px; border-top: 1px solid rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-bottom-color: rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18);\">データの必須項目や形式を検証したい</td><td style=\"padding: 5px 10px; border-top: 1px solid rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-bottom-color: rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18);\">SHACL</td></tr><tr data-line=\"1050\" class=\"code-line\" dir=\"auto\"><td style=\"padding: 5px 10px; border-top: 1px solid rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-bottom-color: rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18);\">RDFデータを検索したい</td><td style=\"padding: 5px 10px; border-top: 1px solid rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-bottom-color: rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18);\">SPARQL</td></tr></tbody></table><!--kg-card-end: html--><p>実務では、次の組み合わせで使うことが多いでしょう。</p><pre><code class=\"language-text\">データモデル        RDF\nファイル形式        Turtle\n基本スキーマ        RDFS\n高度な意味論        OWL\nデータ検証          SHACL\n検索                SPARQL\n</code></pre><hr><h1 id=\"%E3%81%BE%E3%81%A8%E3%82%81\">まとめ</h1><p>最後に、最も重要な関係をもう一度整理します。</p><pre><code class=\"language-text\">RDF\n    情報を主語・述語・目的語のトリプルとして表す\n\nRDFS\n    RDF上でクラス、継承、domain、rangeを定義する\n\nOWL\n    RDF上で、同値、排他、逆関係、制約などを定義する\n\nTurtle\n    RDFグラフを読みやすいテキストとして書く構文\n\n.ttl\n    Turtle形式のファイルに使われる拡張子\n</code></pre><p>そのため、次の表現はすべて正しいものです。</p><pre><code class=\"language-text\">RDFデータをTurtleで書く\nRDFSスキーマをTurtleで書く\nOWLオントロジーをTurtleで書く\n</code></pre><p>また、</p><pre><code class=\"language-turtle\">@prefix ex: &lt;https://example.com/company#&gt; .\n</code></pre><p>の<code>ex:</code>は、IRIを短く書くための省略名です。</p><pre><code class=\"language-turtle\">ex:yamada\nex:worksFor\nex:abc\n</code></pre><p>は、それぞれ次のIRIに展開されます。</p><pre><code class=\"language-text\">https://example.com/company#yamada\nhttps://example.com/company#worksFor\nhttps://example.com/company#abc\n</code></pre><p>本番環境で名前空間を設計する場合は、自社が管理するドメインを使用し、長期間変更せずに使えるIRIを選ぶことが重要です。</p><p>オントロジーを学ぶときは、最初からOWLの複雑な制約をすべて理解しようとする必要はありません。</p><p>まずは、次の順番で学ぶと理解しやすくなります。</p><pre><code class=\"language-text\">1. RDFのトリプルを理解する\n2. Turtleでトリプルを書いてみる\n3. RDFSでクラスと継承を定義する\n4. OWLで逆関係や排他関係を試す\n5. Reasonerによる推論結果を確認する\n6. 必要に応じてSHACLやSPARQLを学ぶ\n</code></pre><p>RDF、RDFS、OWL、Turtleは別々の技術に見えますが、すべては「意味を持ったグラフデータを表現し、共有し、推論する」という一つの目的につながっています。</p><p>ここまで読んだ方はすごいです！！！</p><p>改めましてですが、上記<a href=\"https://marketplace.visualstudio.com/items?itemName=AkihiroYAGASAKI.ontology-viewer\">オントロジーのファイル(.ttl)を、グラフとして可視化しそのまま編集できる VSCode Extension</a> を公開していますので、こちらも合わせて使っていただくと、よりイメージがわきやすいかもしれませんので、せひ使ってみてください！</p><p>以上、やがさきからでした。</p>","url":"https://ghost.tech.anti-pattern.co.jp/rdfrdfsowlturtlenowei-iwozheng-li-site-huairudeli-jie-suruontoroziru-men/","canonical_url":null,"uuid":"9a1592e2-7d66-4a53-a054-41eff93296dc","page":null,"codeinjection_foot":null,"codeinjection_head":null,"codeinjection_styles":null,"comment_id":"6a74b697781e37000184ede4","reading_time":13}},"pageContext":{"slug":"rdfrdfsowlturtlenowei-iwozheng-li-site-huairudeli-jie-suruontoroziru-men"}},
    "staticQueryHashes": ["176528973","2358152166","2561578252","2731221146","4145280475"]}