ホーム 記事一覧 ブック DH週間トピックス 検索 このサイトについて
English
SPARQL クライアントを Apache Jena Fuseki に対応させるときにハマった 3 つのこと

SPARQL クライアントを Apache Jena Fuseki に対応させるときにハマった 3 つのこと

Virtuoso / Dydra 向けに作られた SPARQL Explorer「Snorql」を Apache Jena Fuseki でも動くようにしました。SPARQL は W3C 標準ですが、エンドポイント実装ごとの挙動差は意外と大きいです。Fuseki 対応で直面した 3 つの問題と、その解決方法を記録します。 開発環境 Docker で Fuseki を起動し、ローカルで検証しました。 # docker-compose.yml services: fuseki: image: stain/jena-fuseki container_name: fuseki ports: - "3030:3030" environment: - ADMIN_PASSWORD=admin - FUSEKI_DATASET_1=test volumes: - fuseki-data:/fuseki volumes: fuseki-data: docker compose up -d # テストデータ投入 curl -X POST 'http://localhost:3030/test/data' \ -H 'Content-Type: text/turtle' \ --data-binary @testdata.ttl 1. DESCRIBE のレスポンス形式が違う 症状 Fuseki に DESCRIBE クエリを投げると、結果が画面に表示されません。コンソールには JSON パースエラーが出ていました。 ...

Snorql — 複数の SPARQL エンドポイントを手軽に探索できるブラウザ UI を公開しました

Snorql — 複数の SPARQL エンドポイントを手軽に探索できるブラウザ UI を公開しました

Snorql — A Browser-Based UI for Exploring Multiple SPARQL Endpoints https://nakamura196.github.io/snorql/ はじめに / Introduction SPARQL エンドポイントを手軽に試せるツールが欲しい ── そう思ったことはありませんか? Have you ever wanted a quick, easy way to try out SPARQL endpoints? SPARQL は Linked Open Data (LOD) を検索するための標準クエリ言語ですが、エンドポイントごとに UI が異なったり、そもそも UI が用意されていなかったりします。そこで、1 つの統一的な UI から複数のエンドポイントを切り替えて使える ツールとして Snorql を公開しました。 SPARQL is the standard query language for searching Linked Open Data (LOD), but each endpoint often has a different UI — or none at all. To solve this, I published Snorql, a tool that lets you switch between multiple endpoints from a single, unified UI. ...

Dydra JSON-LDシリアライゼーションの挙動と回避策

Dydra JSON-LDシリアライゼーションの挙動と回避策

概要 Dydraは優れたクラウドベースのRDFトリプルストアですが、JSON-LDシリアライゼーションにおいて、一部のケースで期待と異なる出力が得られることがあります。このブログでは、その挙動と、我々が実装した回避策について解説します。 確認された挙動 期待される出力 JSON-LD仕様では、URI参照は以下のようにオブジェクト形式で出力されることが一般的です: { "@id": "https://example.com/item/1", "@type": ["prov:Entity"], "prov:wasAttributedTo": { "@id": "https://sepolia.etherscan.io/address/0x1234..." }, "prov:wasGeneratedBy": { "@id": "https://sepolia.etherscan.io/tx/0xabcd..." } } Dydraで確認された出力 DydraのJSON-LDエンドポイントでは、一部のURI参照が単なる文字列として出力されるケースが確認されました: { "@id": "https://example.com/item/1", "@type": ["prov:Entity"], "prov:wasAttributedTo": "https://sepolia.etherscan.io/address/0x1234...", "prov:wasGeneratedBy": "https://sepolia.etherscan.io/tx/0xabcd..." } 注意 : この挙動は全てのプロパティで発生するわけではなく、@contextの定義やプロパティの種類によって異なる場合があります。 挙動の違いによる影響 形式 JSON-LDパーサーの解釈 { "@id": "..." } URI参照(他ノードへのリンク) "..." リテラル文字列 この違いにより、以下の影響が生じる可能性があります: グラフ構造のトラバーサルに影響 一部のSPARQLクエリ結果に影響 JSON-LDフレーミング処理に影響 型付きリテラルについて 同様に、xsd:dateTime などの型付きリテラルでも型情報が省略されるケースがあります。 期待される出力 : { "prov:startedAtTime": { "@value": "2025-01-15T10:30:00Z", "@type": "xsd:dateTime" } } 確認された出力 : { "prov:startedAtTime": "2025-01-15T10:30:00Z" } 回避策 アプローチ:TTL形式で取得してJSON-LDを構築 DydraはTurtle (TTL) 形式では正確にシリアライズするため、以下の戦略を採用しました: [クライアント] │ │ Accept: text/turtle v [Dydra SPARQL Endpoint] │ │ TTL形式で返却 v [n3パーサー] │ │ Quadsに変換 v [JSON-LD構築ロジック] │ │ 正しいJSON-LD v [アプリケーション] 実装 import { Parser } from "n3"; /** * TTLをパースしてJSON-LDに変換 * DydraのJSON-LDシリアライゼーションの挙動を回避 */ function turtleToJsonLd(turtle: string): RDFGraph { const parser = new Parser(); const quads = parser.parse(turtle); // Subject別にトリプルをグループ化 const subjects = new Map<string, Map<string, unknown[]>>(); for (const quad of quads) { const subjectId = quad.subject.value; if (!subjects.has(subjectId)) { subjects.set(subjectId, new Map()); } const predicates = subjects.get(subjectId)!; const predicateId = quad.predicate.value; if (!predicates.has(predicateId)) { predicates.set(predicateId, []); } // オブジェクトの値を型情報付きで構築 let objectValue: unknown; if (quad.object.termType === "NamedNode") { // URI参照: { "@id": "..." } ← ここがポイント objectValue = { "@id": quad.object.value }; } else if (quad.object.termType === "Literal") { const literal = quad.object; if (literal.language) { // 言語タグ付きリテラル objectValue = { "@value": literal.value, "@language": literal.language }; } else if (literal.datatype && literal.datatype.value !== "http://www.w3.org/2001/XMLSchema#string") { // 型付きリテラル(xsd:string以外) objectValue = { "@value": literal.value, "@type": literal.datatype.value }; } else { // プレーンリテラル objectValue = literal.value; } } else if (quad.object.termType === "BlankNode") { objectValue = { "@id": `_:${quad.object.value}` }; } else { objectValue = quad.object.value; } predicates.get(predicateId)!.push(objectValue); } // JSON-LD @graphを構築 const graph: Array<Record<string, unknown>> = []; for (const [subjectId, predicates] of subjects) { const node: Record<string, unknown> = { "@id": subjectId }; for (const [predicateId, objects] of predicates) { if (predicateId === "http://www.w3.org/1999/02/22-rdf-syntax-ns#type") { // @typeは特別扱い node["@type"] = objects.map((o) => { if (typeof o === "object" && o !== null && "@id" in o) { return (o as { "@id": string })["@id"]; } return o; }); } else { // 単一値の場合は配列から取り出す node[predicateId] = objects.length === 1 ? objects[0] : objects; } } graph.push(node); } return { "@context": JSONLD_CONTEXT, "@graph": graph, }; } 使用例 // TTL形式で取得して変換 const response = await fetch(`${DYDRA_ENDPOINT}/sparql`, { method: "POST", headers: { "Accept": "text/turtle", // TTLで取得 }, body: query, }); const turtle = await response.text(); const jsonld = turtleToJsonLd(turtle); // JSON-LDに変換 依存ライブラリ この回避策には n3 ライブラリが必要です: ...

Odeuropa Visualization: SKOS語彙とSPARQLを活用した香りデータの可視化プラットフォーム

Odeuropa Visualization: SKOS語彙とSPARQLを活用した香りデータの可視化プラットフォーム

はじめに Odeuropaは、ヨーロッパの香りの歴史を研究するプロジェクトで、絵画、文学、その他の歴史的資料に描かれた香りの表現を収集・分析しています。本記事では、OdeuropaのSPARQLエンドポイントを活用し、SKOS(Simple Knowledge Organization System)語彙体系に基づいた香りデータの可視化Webアプリケーションの実装について紹介します。 https://odeuropa-seven.vercel.app/ja/ プロジェクト概要 技術スタック フロントエンド : Next.js 15 (App Router) UI : Material-UI v5 国際化 : next-intl データ取得 : SPARQLクエリ (Odeuropa SPARQLエンドポイント) 言語 : TypeScript ホスティング : 静的サイト生成(SSG) 主な機能 1. 香り検索 (/odeuropa-sources) アプリケーションの中核となる機能で、Odeuropaプロジェクトが収集した香りの知覚イベント(smell perception events)を検索・閲覧できます。 主な特徴: 複雑なSPARQLクエリによるデータ取得 香り放出イベント(emission)、香りオブジェクト、ソース(絵画・文学作品など)、テキスト断片を結合 CRMベースのオントロジー(ecrm:P67_refers_to, od:F1_generatedなど)を活用 多軸フィルタリング SKOS語彙による香りの源でフィルタ(?xパラメータ) ソースタイプフィルタ(視覚的アイテム E36_Visual_Item / 言語オブジェクト E33_Linguistic_Object) リッチな情報表示 香りのラベル、ソース情報(タイトル、画像、URI) テキスト断片の引用 嗅覚体験の質的情報(Olfactory Experience) ページネーション - 20件ずつの効率的な表示 SPARQLクエリ例: SELECT DISTINCT ?source ?source_title ?fragment ?fragment_value ?emission ?smell ?smell_label ?source_image WHERE { ?emission od:F3_had_source ?x . ?emission od:F1_generated ?smell . # ソースとの関連(フラグメント経由または直接) { ?fragment ecrm:P67_refers_to ?emission . ?fragment rdf:value ?fragment_value . ?source ecrm:P165_incorporates ?fragment . } UNION { ?source ecrm:P67_refers_to ?emission . } } 2. 香り詳細ページ (/odeuropa-sources/item) 個別の香りに関する詳細情報を表示するページです。 ...

RDFSとSHACLの使い分け:rangeとpropertyShapeの関係を理解する

RDFSとSHACLの使い分け:rangeとpropertyShapeの関係を理解する

はじめに RDF(Resource Description Framework)でデータを扱う際、「RDFS(RDF Schema)」と「SHACL(Shapes Constraint Language)」という2つの仕組みが出てきます。どちらもプロパティやクラスの制約を定義できますが、目的も動作も全く異なります 。 この記事では、特に混乱しやすい以下の疑問に答えます: rdfs:domain / rdfs:range と SHACL の sh:class / sh:datatype は何が違うのか? RDFS の range と異なる SHACL 制約を設定してもいいのか? range がクラス(foaf:Person)なのに SHACL でデータ型(xsd:string)を指定するのは問題ないか? 1. RDFSとSHACLの根本的な違い RDFS:推論(Inference)のため RDFS は**「もしこのプロパティが使われたら、こういう知識が導き出せる」**という宣言です。 # RDFS スキーマ定義 ex:author rdfs:domain ex:Book ; rdfs:range ex:Person . 意味 : ex:author が使われたら、主語(subject)は自動的に ex:Book のメンバーと推論される 目的語(object)は自動的に ex:Person のメンバーと推論される # 元のデータ ex:book1 ex:author ex:john . # 推論エンジンが自動的に導き出す知識 ex:book1 a ex:Book . # domain から推論 ex:john a ex:Person . # range から推論 特徴 : ...

GakuNin RDMとDydraを連携したRDFメタデータ管理システムの開発

GakuNin RDMとDydraを連携したRDFメタデータ管理システムの開発

はじめに 本記事では、GakuNin RDM(Research Data Management)とDydra RDFデータベースを連携させた、研究データのメタデータ管理システムの開発について解説します。このシステムは、研究プロジェクトのファイル管理とDublin Coreメタデータの登録・検索を統合的に扱うことができます。 システム概要 アーキテクチャ ┌─────────────────┐ │ Next.js 14 │ │ (App Router) │ └────────┬────────┘ │ ┌────┴────┐ │ │ ┌───▼───┐ ┌──▼─────┐ │GakuNin│ │ Dydra │ │ RDM │ │ RDF │ │ API │ │ DB │ └───────┘ └────────┘ 主要技術スタック: Next.js 14 (App Router) NextAuth.js (OAuth 2.0認証) Dydra (RDFデータベース) GakuNin RDM API SPARQL (クエリ言語) 1. GakuNin RDMとの連携 1.1 OAuth 2.0認証の実装 GakuNin RDMはOAuth 2.0による認証をサポートしています。NextAuth.jsを使用してこれを実装しました。 ...

Odeuropa Explorer の語彙階層構造を調査する

Odeuropa Explorer の語彙階層構造を調査する

はじめに Odeuropa Explorer は、ヨーロッパの嗅覚遺産をデジタル化した興味深いプロジェクトです。EU の Horizon 2020 研究プログラムの助成を受け、歴史的な匂いの体験を横断的に検索・探索できるプラットフォームを提供しています。 このプロジェクトでは、匂いに関連する情報を以下の3つの主要なカテゴリで分類しています: Smell sources : 匂いを発する物体や物質 Fragrant Spaces : 匂いに関連する場所や空間 Gestures and Allegories : 匂いに関する身振りや寓意的表現 本記事では、これらの語彙がどのような階層構造を持っているのか、Odeuropa vocabularies リポジトリで公開されている SKOS(Simple Knowledge Organization System)形式のデータを調査した結果を報告します。 調査方法 SKOS階層の可視化スクリプト 語彙の階層構造を理解するために、Node.js で SKOS Turtle ファイルを解析するスクリプトを作成しました。 import $rdf from 'rdflib'; import fs from 'fs'; const SKOS = $rdf.Namespace('http://www.w3.org/2004/02/skos/core#'); async function visualizeHierarchy(ttlFile) { const store = $rdf.graph(); const data = fs.readFileSync(ttlFile, 'utf8'); $rdf.parse(data, store, 'http://example.org/', 'text/turtle'); // Get all concepts const concepts = store.match(null, $rdf.sym('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'), SKOS('Concept')); // Build maps for broader/narrower relationships const broaderMap = new Map(); const narrowerMap = new Map(); const conceptLabels = new Map(); for (const concept of concepts) { const subject = concept.subject; // Get prefLabel const labels = store.match(subject, SKOS('prefLabel'), null); if (labels.length > 0) { conceptLabels.set(subject.value, labels[0].object.value); } // Get broader concepts const broaders = store.match(subject, SKOS('broader'), null); if (broaders.length > 0) { const broader = broaders[0].object.value; broaderMap.set(subject.value, broader); if (!narrowerMap.has(broader)) { narrowerMap.set(broader, []); } narrowerMap.get(broader).push(subject.value); } } // Print hierarchy recursively function printHierarchy(conceptUri, indent = '', visited = new Set()) { if (visited.has(conceptUri)) return; visited.add(conceptUri); const label = conceptLabels.get(conceptUri); const children = narrowerMap.get(conceptUri) || []; console.log(`${indent}${label}`); children.forEach((child, index) => { const isLast = index === children.length - 1; const prefix = isLast ? '└── ' : '├── '; printHierarchy(child, indent + prefix, new Set(visited)); }); } // Find and display top-level concepts const topLevelConcepts = []; for (const concept of concepts) { if (!broaderMap.has(concept.subject.value)) { topLevelConcepts.push(concept.subject.value); } } topLevelConcepts.forEach(concept => printHierarchy(concept)); } このスクリプトの主要な処理: ...

DydraへのAPI経由でのRDFデータ登録ガイド

DydraへのAPI経由でのRDFデータ登録ガイド

はじめに Dydraは、クラウドベースのRDFデータベースサービスで、SPARQL endpointとREST APIを提供しています。本記事では、Dydra APIを使用してプログラマティックにRDFデータを登録する方法を解説します。 前提条件 Dydraアカウント APIキー Node.js環境(v16以上推奨、Node.js使用時) 注意: 本記事のコード例では、サンプルとして以下を使用しています: アカウント名: your-account リポジトリ名: your-repository APIキー: your_api_key_here 実際に使用する際は、ご自身のDydraアカウント情報に置き換えてください。 APIの基本情報 エンドポイント構成 ベースURL: https://dydra.com/{account}/{repository} 例: https://dydra.com/your-account/your-repository SPARQL Query: /sparql (GET) SPARQL Update: /sparql (POST) Statements: /statements (POST/GET/DELETE) 認証 DydraではBearerトークン認証を使用します: Authorization: Bearer YOUR_API_KEY 実装方法 Dydra APIへのアクセスは、curl、Python、Node.jsなど様々な方法で実装できます。それぞれの方法を紹介します。 方法1: curlでの実装(最もシンプル) curlを使えば、プログラミング言語なしで即座にデータ登録が可能です。 基本的な認証 # APIキーを環境変数に設定 export DYDRA_API_KEY="your_api_key_here" export DYDRA_BASE_URL="https://dydra.com/your-account/your-repository" Turtle形式でのデータ登録 # data.ttl ファイルから登録 curl -X POST \ -H "Authorization: Bearer ${DYDRA_API_KEY}" \ -H "Content-Type: text/turtle" \ --data-binary @data.ttl \ "${DYDRA_BASE_URL}/statements" data.ttl の例: ...

Odeuropa:歴史的文献から匂いを抽出するLinked Dataの世界

Odeuropa:歴史的文献から匂いを抽出するLinked Dataの世界

はじめに Odeuropa(オデウロパ)は、ヨーロッパの歴史的文献から「匂い」に関する記述を抽出し、Linked Dataとして構造化したユニークなプロジェクトです。本記事では、SPARQLエンドポイントを通じて実際のデータを探索し、その構造と設計思想を明らかにしていきます。 Odeuropaとは プロジェクト名 : Odeuropa(Odeurs d’Europe = ヨーロッパの匂い) データベースURL : https://data.odeuropa.eu/ SPARQLエンドポイント : https://data.odeuropa.eu/repositories/odeuropa Webインターフェース : https://explorer.odeuropa.eu/ データモデルの全体像 OdeuropaはCIDOC-CRM(文化遺産のための概念参照モデル)をベースに、匂いに特化した拡張オントロジーを使用しています。 主要な概念と関係性 Source (文献) ↓ P106_is_composed_of Fragment (テキスト断片) ↓ P67_refers_to ├─ Emission (放出イベント) ←── 中心的なハブ │ ├─ F3_had_source → Object (発生源) │ └─ F1_generated → Smell (匂い) ├─ Smell (匂い) └─ Experience (体験イベント) └─ F2_perceived → Smell (匂い) 重要なポイント: Fragment は直接的にEmission、Smell、Experienceを参照 Object へはEmission経由でアクセス(Fragment → Emission → Object) Emission がObjectとSmellを因果的に接続する中心的な役割 実例で学ぶデータ構造 1810年にドイツで出版された農業書「Grundsätze der rationellen Landwirthschaft」(合理的農業の原理)を例に、データ構造を見ていきましょう。 ...

Omeka SにPROV-Oオントロジーを登録する方法

Omeka SにPROV-Oオントロジーを登録する方法

はじめに Omeka Sでデジタルアーカイブを構築する際、メタデータの記述に標準的な語彙を使用することで、データの相互運用性が向上します。今回は、W3Cが策定したPROV-O(PROV Ontology)をOmeka Sに登録する手順を解説します。 PROV-Oは、データやデジタルオブジェクトの来歴(プロヴェナンス)情報を記述するためのオントロジーで、「誰が」「いつ」「どのように」データを作成・変更したかを構造化して記録できます。 前提条件 Omeka S(バージョン3.0以降)がインストール済み 管理者権限でログイン可能 インターネット接続環境(外部URLからのインポートに必要) 登録手順 1. 語彙管理画面へのアクセス Omeka S管理画面にログイン 左側メニューから「語彙の一覧」をクリック 右上の「新しい語彙を追加」ボタンをクリック 2. 基本情報の入力 語彙の基本情報を以下のように入力します: 項目 入力値 ラベル PROV-Oオントロジー コメント W3C PROV-O (PROV Ontology) - データの来歴情報を記述するための標準オントロジー 名前空間URI http://www.w3.org/ns/prov# 名前空間の接頭語 prov 重要 : 名前空間URIの末尾に#(ハッシュ)が必要です。これを忘れるとプロパティが正しく認識されません。 3. ファイルのインポート設定 Import typeの選択 「URL」を選択します(デフォルトで選択されているはずです)。 File URLの入力 以下のURLを入力します: https://www.w3.org/ns/prov-o このURLは内容交渉(Content Negotiation)に対応しており、Omeka Sが自動的に適切な形式(Turtle)で取得します。 ファイルフォーマット 「Turtle (.ttl)」が自動的に選択されます。変更の必要はありません。 4. 登録の実行 入力内容を確認 「インポート」ボタンをクリック 処理が完了するまで待機(数秒〜数十秒) 5. 登録の確認 登録が完了すると、語彙一覧にPROV-Oオントロジーが表示されます。 クリックして詳細を確認すると、以下のようなクラスとプロパティが登録されているはずです: 主要なクラス: prov:Entity - エンティティ(物や概念) prov:Activity - アクティビティ(プロセスや行為) prov:Agent - エージェント(人、組織、ソフトウェア) 主要なプロパティ: ...

Recogitoを用いたテキストアノテーションを試す

Recogitoを用いたテキストアノテーションを試す

概要 Recogitoを用いたテキストアノテーションを試す機会がありましたので、備忘録です。 Recogitoは以下です。 https://recogito.pelagios.org/ 以下のように説明されています。 Semantic Annotation without the pointy brackets. Recogito is an annotation tool for texts and images - not just for Digital Humanities scholars. (機械翻訳)タグを使わないセマンティックアノテーション。デジタル人文学の研究者だけでなく、誰でも使えるテキストと画像のアノテーションツール サンプルデータ 国立国会図書館が公開する以下を例とします。 https://dl.ndl.go.jp/pid/2585164/1/1 使い方 Recogitoにアクセスし、画面右上の「ログイン」ボタンからログインします。 ログイン後、画面左部の「New」ボタンを押し、「From IIIF manifest」を選択します。 今回、サンプルデータとして使用する以下のマニフェストファイルを入力します。 https://dl.ndl.go.jp/api/iiif/2585164/manifest.json 以下のように、編集画面に遷移します。 その後、矩形を作成し、「Transcribe…」の箇所にテキストを入力します。 ダウンロード 画面上部のダウンロードアイコンから、各種フォーマットでエクスポートすることができます。 JSON-LDフォーマットでダウンロードした結果は以下です。Open Annotationに形式でエクスポートされました。 [ { "@context": "http://www.w3.org/ns/anno.jsonld", "id": "https://recogito.pelagios.org/annotation/82be9a93-332b-4731-a9a4-5df8359eb197", "type": "Annotation", "generator": { "id": "https://recogito.pelagios.org/", "type": "Software", "name": "Recogito", "homepage": "https://recogito.pelagios.org/" }, "generated": "2025-07-23T06:23:59+00:00", "body": [ { "type": "TextualBody", "value": "御座候処貴地御揃奉仕請覚重候時御座候", "creator": "https://recogito.pelagios.org/satoru196", "modified": "2025-07-23T06:15:01+00:00", "purpose": "transcribing" } ], "target": { "source": "https://dl.ndl.go.jp/api/iiif/2585164/R0000005", "type": "Image", "label": "5", "selector": [ { "type": "FragmentSelector", "conformsTo": "http://www.w3.org/TR/media-frags/", "value": "xywh=pixel:4103,883,341,2385" } ] } }, { "@context": "http://www.w3.org/ns/anno.jsonld", "id": "https://recogito.pelagios.org/annotation/caaa671f-79da-4a19-bc93-42a5502b4efa", "type": "Annotation", "generator": { "id": "https://recogito.pelagios.org/", "type": "Software", "name": "Recogito", "homepage": "https://recogito.pelagios.org/" }, "generated": "2025-07-23T06:23:59+00:00", "body": [ { "type": "TextualBody", "value": "一筆啓上仕候寒湿不順之気儘ニ", "creator": "https://recogito.pelagios.org/satoru196", "modified": "2025-07-23T06:12:45+00:00", "purpose": "transcribing" } ], "target": { "source": "https://dl.ndl.go.jp/api/iiif/2585164/R0000005", "type": "Image", "label": "5", "selector": [ { "type": "FragmentSelector", "conformsTo": "http://www.w3.org/TR/media-frags/", "value": "xywh=pixel:4319,901,360,2377" } ] } } ] まとめ Recogitoを用いたテキストアノテーションにあたり、参考になりましたら幸いです。 ...

grlc (git repository linked data API constructor)を試す

grlc (git repository linked data API constructor)を試す

概要 grlcのGitHubリポジトリは以下です。 https://github.com/CLARIAH/grlc 以下のように説明されています。 grlc, the git repository linked data API constructor, automatically builds Web APIs using shared SPARQL queries. (機械翻訳)grlc(git repository linked data API constructor)は、共有されたSPARQLクエリを使用して自動的にWeb APIを構築するツールです。 このツールを試してみましたので、備忘録です。 ジャパンサーチのSPARQL Endpointを対象に作成したAPIエンドポイントは以下です。 https://grlc.io/api-git/nakamura196/grlc-jps 背景 Odeuropaを調査する過程で、以下のページで本ツールについて言及されていることを見つけました。 https://odeuropa.eu/nosebooks/ 使い方 以下が今回のAPI用に作成したGitHubリポジトリです。 https://github.com/nakamura196/grlc-jps 例えば、以下はタイプの一覧を取得するAPI用のSPARQLクエリです。 #+ endpoint: https://jpsearch.go.jp/rdf/sparql/ #+ summary: 利用可能な文化財タイプ一覧 #+ description: Japan Searchで利用可能な文化財タイプの一覧を取得します #+ tags: #+ - タイプ一覧 #+ - メタデータ PREFIX jps: <https://jpsearch.go.jp/term/property#> SELECT ?type (COUNT(?cho) as ?count) WHERE { ?cho a ?type ; jps:sourceInfo ?source . } GROUP BY ?type ORDER BY DESC(?count) デコレータ構文を使用することで、swagger-uiに表示・設定する値を指定できるようでした。 ...

Ontotext GraphDBのデスクトップ版を使用した際の空きディスク容量不足への対処

Ontotext GraphDBのデスクトップ版を使用した際の空きディスク容量不足への対処

概要 Ontotext GraphDBのデスクトップ版を使用した際、 https://www.ontotext.com/products/graphdb/ 空きディスク容量不足により、データのインポートなどができないことがありました。 File not loaded; Insufficient disk space to start a transaction for repository ‘xxx’ due to: The repository ‘abc’ is critically low on free disk space with 4.6% (xxx GB) free left 本記事では、この問題への一時的な対処方法の一例を紹介します。 マニュアル 以下に記載がありました。 https://graphdb.ontotext.com/documentation/11.0/low-disk-space-health-checks.html Claudeによる回答は以下でした。 GraphDB 11.0では新しいヘルスチェック機構が追加されており、これが従来の設定を上書きしています: Fatal状態: 5%または1GB以下で発動 現在の状況: 4.6%なのでFatal状態 結果: 新しいトランザクションが完全に阻止される 対応 以下のようなgraphdb.propertiesを新規に作成して、ヘルスチェック機構を無効化するオプションを追加しました。 # GraphDB 11.0 の新しいヘルスチェック機構を無効化 graphdb.health.minimal.free.storage.enabled=false graphdb.health.minimal.free.storage.asyncCheck=false そして、GraphDBを再起動します。結果、System Informationに設定が反映され、冒頭のエラーが解消しました。 まとめ 空きディスク容量不足に関する根本的な原因を解決すべきかと思いますが、一時的な対応として参考になりましたら幸いです。

AllegroGraphを使ってみる

AllegroGraphを使ってみる

概要 AllegroGraphを使ってみる機会がありましたので、備忘録です。 https://allegrograph.com/ 使い方 いくつかのセットアップ方法があるようですが、以下のクラウド版を利用してみます。 https://allegrograph.cloud/ セットアップ後、以下のように表示されます。 ログイン後、複数のRepositoriesが表示されます。 匿名アクセスを可能にする 例えば、リポジトリ「actors」のSPARQLエンドポイントは以下です。 https://ag1edt2www58hzzy.allegrograph.cloud/repositories/actors/sparql デフォルトでは、Basic認証がかかります。 そこで、anonymousユーザを作成します。ユーザ名を「anonymous」にすることで、パスワードの入力をスキップできました。 公式の説明は以下に記載があります。 https://franz.com/agraph/support/documentation/managing-users.html#anonymous-access そして、「actors」リポジトリへのREADを許可します。 この設定により、指定したリポジトリへ認証なしでアクセスできるようになります。以下は、yasguiでアクセスした例です。 https://yasgui.org/#query=PREFIX+rdf%3A+<http%3A%2F%2Fwww.w3.org%2F1999%2F02%2F22-rdf-syntax-ns%23> PREFIX+rdfs%3A+<http%3A%2F%2Fwww.w3.org%2F2000%2F01%2Frdf-schema%23> SELECT+*+WHERE+{ ++%3Fsub+%3Fpred+%3Fobj+. }+ LIMIT+10&contentTypeConstruct=text%2Fturtle&contentTypeSelect=application%2Fsparql-results%2Bjson&endpoint=https%3A%2F%2Fag1edt2www58hzzy.allegrograph.cloud%2Frepositories%2Factors%2Fsparql&requestMethod=POST&tabTitle=Query&headers={}&outputFormat=table リポジトリを追加してみる 「my_first_repository」というリポジトリを追加しました。RDFファイルを登録してみます。 校異源氏物語テキストDBで使用している以下のRDFファイルを登録してみます。 https://github.com/kouigenjimonogatari/kouigenjimonogatari.github.io/blob/master/files/rdf/item_sets.rdf 登録時に、Graph URIを任意項目として登録することができます。入力例にしたがって、以下のように入力しました。 エクスポートする 「Repository Control」の「Archiving」からエクスポートできます。 「N-Quads」を選択して、エクスポートした結果が以下です。トリプルに加えて、先ほど登録したGraph URIが出力されていることが確認できます。 <https://w3id.org/kouigenjimonogatari/api/item_sets/31.json> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <https://jpsearch.go.jp/term/type/作品> <http://example.org> . <https://w3id.org/kouigenjimonogatari/api/item_sets/31.json> <http://purl.org/dc/terms/relation> <https://w3id.org/kouigenjimonogatari/tei/31.xml> <http://example.org> . <https://w3id.org/kouigenjimonogatari/api/item_sets/31.json> <https://w3id.org/kouigenjimonogatari/api/property/vol> "31"^^<http://www.w3.org/2001/XMLSchema#integer> <http://example.org> . <https://w3id.org/kouigenjimonogatari/api/item_sets/31.json> <http://www.w3.org/2000/01/rdf-schema#label> "まきはしら" <http://example.org> . ... SHACL Shapesを管理する Generate SHACL Shapesボタンを押すと、SHACLファイルが作成されます。 以下のようなJSONが作成されました。 { "@context": { "ns2": "https://jpsearch.go.jp/term/type/", "ns1": "http://www.w3.org/ns/shacl#", "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#" }, "@id": "ns2:WorkShape", "ns1:targetClass": "https://jpsearch.go.jp/term/type/Work", "ns1:property": [ { "ns1:nodeKind": "sh:IRI", "ns1:path": "rdf:type", "ns1:minCount": 1, "ns1:maxCount": 1 }, { "ns1:nodeKind": "sh:IRI", "ns1:path": "dcterms:relation", "ns1:minCount": 1, "ns1:maxCount": 1 }, { "ns1:nodeKind": "sh:IRI", "ns1:path": "rdfs:seeAlso", "ns1:minCount": 1, "ns1:maxCount": 1 }, { "ns1:nodeKind": "sh:IRI", "ns1:path": "https://w3id.org/kouigenjimonogatari/api/property/vol", "ns1:minCount": 1, "ns1:maxCount": 1 }, { "ns1:nodeKind": "sh:IRI", "ns1:path": "dcterms:rights", "ns1:minCount": 1, "ns1:maxCount": 1 }, { "ns1:nodeKind": "sh:literal", "ns1:datatype": "xs:string", "ns1:path": "rdfs:label", "ns1:minCount": 1, "ns1:maxCount": 1 } ], "@type": "ns1:NodeShape" } Queryを投げてみると、SHACLに関するトリプルも登録されていました。 ...

「教科書の中の源氏物語LOD」を使ってみる

「教科書の中の源氏物語LOD」を使ってみる

概要 「教科書の中の源氏物語LOD」を使ってみましたので、備忘録です。 https://linkdata.org/work/rdf1s10294i 以下のように説明されています。 教科書の中の源氏物語LODは、高等学校古典分野の戦後検定教科書における『源氏物語』掲載データをLOD化したものである。 「教科書の中の源氏物語LOD」を作成および公開してくださった関係者の皆様に感謝いたします。 SPARQLエンドポイントの作成 今回はDyDraを使用します。また、以下の記事を参考に、Pythonで登録しました。 DYDRA_ENDPOINT=https://dydra.com/ut-digital-archives/genji_u/sparql DYDRA_API_KEY=xxxxx from dydra_py.api import DydraClient endpoint, api_key = DydraClient.load_env("../.env") client = DydraClient(endpoint, api_key) # genjimaki_listの登録 client.import_by_file("./data/genjimaki_list_ttl.txt", "turtle") # genjitext_listの登録 client.import_by_file("./data/genjitext_list_ttl.txt", "turtle") 注意点として、RDF内のURIについて、http://linkdata.org/resource/rdf1s10294i#とhttps://linkdata.org/resource/rdf1s10294i#が一部混在しておりました。今回は、http://linkdata.org/resource/rdf1s10294i#に統一する置換処理を施したのち、SPARQLエンドポイントに登録しました。 Snorqlによる確認 構築したSPARQLエンドポイントに対して問い合わせを行うSnorqlを作成しました。 https://nakamura196.github.io/snorql_examples/genji/ 例えば以下では、桐壺巻が使用されている教科書がschema:workExampleで関連付けられています。 https://nakamura196.github.io/snorql_examples/genji/?describe=http%3A%2F%2Flinkdata.org%2Fresource%2Frdf1s10294i%23genji_01 また以下では、教科書「高等古文 3」に掲載されている巻がdct:hasPartで関連づけられています。 https://nakamura196.github.io/snorql_examples/genji/?describe=http%3A%2F%2Flinkdata.org%2Fresource%2Frdf1s10294i%23text_001 Yasguiを用いた可視化 さらに、Yasguiを用いた可視化も試みました。Yasguiについては、以下も参考にしてください。 教科書毎の巻数のカウント 詳細 PREFIX dct: <http://purl.org/dc/terms/> SELECT ?textTitle ?publisher (count(?volume) as ?count) ?text WHERE { ?text dct:hasPart ?volume; dct:title ?textTitle; dct:publisher ?publisher } GROUP BY ?text ?textTitle ?publisher ORDER BY desc(?count) 巻毎の教科書数のカウント 詳細 PREFIX dct: <http://purl.org/dc/terms/> PREFIX schema: <http://schema.org/> SELECT ?chapterTitle (count(?text) as ?count) WHERE { ?chapter schema:workExample ?text; dct:title ?chapterTitle } GROUP BY ?chapterTitle ORDER BY desc(?count) 「桐壺」が最も多く含まれていることが分かります。 検定年毎の教科書数 詳細 PREFIX jp-textbook: <https://w3id.org/jp-textbook/> SELECT (str(?year) as ?year) (count(?year) as ?count) WHERE { ?text jp-textbook:authorizedYear ?year . } GROUP BY ?year ORDER BY asc(?year) ...

Peripleoを試す

Peripleoを試す

概要 「Peripleo」を使う方法を調べましたので、備忘録です。「Peripleo」は以下のように説明されています。 Peripleo is a browser-based tool for the mapping of things related to place. https://github.com/britishlibrary/peripleo 今回は以下の記事で紹介した「れきちず」と組み合わせて、使用する方法について紹介します。 成果物 以下のURLでお試しいただけます。 https://nakamura196.github.io/peripleo/ リポジトリは以下です。 https://github.com/nakamura196/peripleo 本ブログでは、以下の『東京帝國大學本部構内及農學部建物鳥瞰圖』(東京大学農学生命科学図書館所蔵)をサンプルデータとして使用します。 https://iiif.dl.itc.u-tokyo.ac.jp/repo/s/agriculture/document/187cc82d-11e6-9912-9dd4-b4cca9b10970 背景 以下の会議に参加し、「Peripleo」について教えていただきました。「Peripleo」を開発してくださっている関係者の皆様、会議を開催してくださった皆様、また使用方法を教えてくださったGethin Rees氏に感謝いたします。 http://codh.rois.ac.jp/conference/linked-pasts-10/ 基本的な使い方 以下に記載があります。 https://github.com/britishlibrary/peripleo?tab=readme-ov-file#installation-guide 今回は、『東京帝國大學本部構内及農學部建物鳥瞰圖』のデータを利用するにあたり、カスタマイズした点について紹介します。 データの準備 以下のようなスプレッドシートを用意します。 https://docs.google.com/spreadsheets/d/1ZZJZL0K4cBOc0EgMHNV9NQ56C_fcZm0eceBg_OPmxe4/edit?usp=sharing 灰色のセルは不要な列です。 データの準備ができたら、CSV形式でダウンロードします。 JSON形式への変換 Locolligoというツールを用いて、CSVのデータをJSON形式のデータに変換します。 https://github.com/docuracy/Locolligo まず、以下にアクセスします。 https://docuracy.github.io/Locolligo/ CSVファイルをアップロード後、「Assign CSV Columns」を押すと、以下が表示されます。 予約語をCSVのヘッダーに使用しておくと、手動でマッピングする必要がないようでした。うまくマッピングされなかった場合には、手動で設定します。 なお、予約語は以下で確認できました。 https://github.com/docuracy/Locolligo/blob/main/js/data-converter.js function assign(){ $('#assign').removeClass('throb'); var assignmentOptions = [ ['(ignore)'], ['@id','identifier|uuid|id|@id'], ['properties.title','title|name|label'], ['properties.%%%'], ['geometry.coordinates','coordinates|coords|OSGB'], ['geometry.coordinates[0]','longitude|long|lng|easting|westing|X'], ['geometry.coordinates[1]','latitude|lat|northing|southing|Y'], ['names[0].toponym','toponym'], ['links[0].type'], ['links[0].identifier'], ['depictions[0].@id'], ['depictions[0].title'], ['descriptions[0].@id'], ['descriptions[0].value'], ['types[0].identifier'], ['types[0].label'], ['{custom}'] ]; 結果をダウンロードすると、featuresに以下のようなアイテムが格納されたデータを取得できます。 ...

ShExCとShExJの違い

ShExCとShExJの違い

概要 ShExC(ShEx Compact Syntax)とShExJ(ShEx JSON Syntax)の違いについて、ChatGPTによる回答です。誤っている点もあるかもしれませんが、参考になりましたら幸いです。 回答 ShExC(ShEx Compact Syntax)とShExJ(ShEx JSON Syntax)は、どちらもShEx(Shape Expressions)スキーマの表現形式ですが、表記形式や用途が異なります。以下にその違いを説明します。 1. 表記形式 ShExC(ShEx Compact Syntax) : テキストベース のシンタックスです。 人間が読み書きしやすいように設計されており、シンプルかつ簡潔にRDFデータの構造や制約を記述することができます。 主に手動で記述 したり、コード内に直接埋め込む ことを想定して設計されています。 例 : <PersonShape> { foaf:name xsd:string ; foaf:age xsd:integer ? } * これは「Person」という形状が、`foaf:name` という必須の文字列プロパティと、`foaf:age` というオプションの整数プロパティを持つことを示しています。 ShExJ(ShEx JSON Syntax) : JSONベース のシンタックスです。 機械可読性を重視した形式で、ShExスキーマをプログラムやAPIで扱うために設計されています。 ツールやライブラリで処理しやすい ため、特にプログラム間でのデータ交換やAPIレスポンスで使用されることが多いです。 例 : { "type": "Schema", "shapes": [ { "id": "PersonShape", "type": "Shape", "expression": { "type": "EachOf", "expressions": [ { "type": "TripleConstraint", "predicate": "foaf:name", "valueExpr": { "type": "NodeConstraint", "datatype": "xsd:string" } }, { "type": "TripleConstraint", "predicate": "foaf:age", "valueExpr": { "type": "NodeConstraint", "datatype": "xsd:integer" }, "min": 0, "max": 1 } ] } } ] } 2. 用途 ShExC : ...

ShExとSHACLの違い

ShExとSHACLの違い

概要 ShEx(Shape Expressions)Schema と SHACL(Shapes Constraint Language)の違いに関するChatGPTによる回答です。間違っている点もあるかもしれませんが、参考になりましたら幸いです。 回答 ShEx(Shape Expressions)Schema と SHACL(Shapes Constraint Language)は、どちらもRDFデータに対する検証 や制約 を定義するための言語です。両者は同じ目的を持ちながらも、構文 やアプローチ に違いがあります。以下にその違いを説明します。 1. 目的と用途 ShEx(Shape Expressions) : ShExは、RDFデータが特定の形状(Shape)に従っているかどうかを 簡潔に定義し、検証するための言語です。 主に、柔軟で簡単なスキーマ定義 を目的として設計されており、特定の形状にデータが適合するかどうかの判定がしやすいです。 スキーマ検証 が主な用途で、特定のシェイプにデータが従っているかどうかをすばやく確認できます。 SHACL(Shapes Constraint Language) : SHACLは、RDFデータに対して複雑な制約 やルール を定義するために設計されたW3C標準の言語です。 より柔軟で強力な検証機能を持ち、スキーマ定義だけでなく、制約の論理的な定義 や高次の推論 を行うことができます。 例えば、条件付き制約や、プロパティの型、データの依存関係など、より複雑なルールを記述できます。 2. 構文と表現力 ShEx : シンプルでコンパクトな構文 を持ち、RDFデータの形状を定義する際に、簡単に読み書きできるよう設計されています。 直感的で短い記述 でシェイプを定義できるため、学習コストが低く、データ検証を行う際に適しています。 例(ShExC形式): <PersonShape> { foaf:name xsd:string ; foaf:age xsd:integer ? } * これは、「`PersonShape`は、`foaf:name`として文字列型、`foaf:age`として整数型のオプションプロパティを持つ」という意味です。 SHACL : SHACLはRDFグラフ上で定義 され、検証ロジックをRDFのトリプル形式 で記述します。 SPARQLクエリを使って検証を行うことができるため、より複雑なルールや推論が可能です。 例(Turtle形式): ex:PersonShape a sh:NodeShape ; sh:targetClass foaf:Person ; sh:property [ sh:path foaf:name ; sh:datatype xsd:string ; ] ; sh:property [ sh:path foaf:age ; sh:datatype xsd:integer ; sh:maxCount 1 ; ] . * これは「`PersonShape`は、`foaf:name`として文字列型、`foaf:age`として整数型で1つまでのプロパティを持つ」という意味です。 3. 検証方法 ShEx : ...

rico-converterを試す

rico-converterを試す

概要 rico-converterを試す機会がありましたので、備忘録です。 https://github.com/ArchivesNationalesFR/rico-converter 以下のように説明されています。 A tool to convert EAC-CPF and EAD 2002 XML files to RDF datasets conforming to Records in Contexts Ontology (RiC-O) (機会翻訳)EAC-CPFやEAD 2002のXMLファイルを、Records in Contexts Ontology(RiC-O)に準拠したRDFデータセットに変換するためのツール 変換する 以下に説明があります。 https://archivesnationalesfr.github.io/rico-converter/en/GettingStarted.html まず、以下から最新のzipファイルをダウンロードして、展開します。 https://github.com/ArchivesNationalesFR/rico-converter/releases/latest サンプルデータとして、input-eacとinput-eadがあるので、これらをRDFに変換します。 input-eac 本フォルダ内にあるinput-eac/FRAN_NP_051151.xmlについて、ChatGPTによる解説は以下です。 ! このXMLファイルは、EAC-CPF(Encoded Archival Context for Corporate Bodies, Persons, and Families)という形式で記述されており、フランスの文化省(Ministère de la Culture)などの組織に関する情報を体系的に整理したものです。主な要素は次の通りです: • Controlセクション: レコードに関するメタデータを含んでいます。これには、レコードID、言語の宣言、更新履歴、使用した資料の出典などが含まれます。 • Identityセクション: 法人(ここではフランスの文化省)に関する基本的な情報が記載されています。文化省の名前が変遷してきたことを示す複数の名称や、各名称が使われていた期間も詳細に記述されています。 • Descriptionセクション: 文化省の存在期間、法的地位、主要な機能、任務、さらにその歴史的な変遷に関する詳細な説明が含まれています。たとえば、1959年に設立され、法令に基づいてどのように運営されてきたか、組織の構造がどのように変化したかが説明されています。 • Relationsセクション: 他の組織や人物との関係を示しています。ここには、フランス国内の関連機関や教育機関、さらには歴代の文化相との関係が詳細に記載されており、外部のリソース(アーカイブやオンラインの情報)へのリンクも含まれています。 このXMLは、アーカイブや組織に関する情報を標準化された形式で記述し、他のデータベースやシステムと連携することを目的としています。特に、組織の歴史や関係性を追跡する際に有用です。 以下を実行します。 sh ricoconverter.sh デフォルトのまま実行すると、output-eac-20241005といったフォルダが作成され、その中にRDFファイルが出力されます。 input-ead 本フォルダ内にあるinput-ead/FRAN_IR_003500.xmlについて、ChatGPTによる解説は以下です。 ...

Pythonを使ってRDFデータをDydraに登録する

Pythonを使ってRDFデータをDydraに登録する

概要 Pythonを使ってRDFデータをDydraに登録するライブラリを作成しました。 https://github.com/nakamura196/dydra-py 中途半端な実装が含まれますが、お役に立つ場面があれば幸いです。 工夫 インポートは以下で行なっています。 https://github.com/nakamura196/dydra-py/blob/main/dydra_py/api.py#L55 以下のように、SPARQLのINSERT DATA オペレーションを使用しています。 def import_by_file(self, file_path, format, graph_uri=None, verbose=False): """ Imports RDF data from a file into the Dydra store. Args: file_path (str): The path to the RDF file to import. format (str): The format of the RDF file (e.g., 'xml', 'nt'). graph_uri (str, optional): URI of the graph where data will be inserted. Defaults to None. """ headers = { "Authorization": f"Bearer {self.api_key}", "Content-Type": "application/sparql-update" } files = self._chunk_rdf_file(file_path, format=format) print("Number of chunks: ", len(files)) for file in tqdm(files): # RDFファイルの読み込み graph = rdflib.Graph() graph.parse(file, format=format) # フォーマットはファイルに応じて変更 nt_data = graph.serialize(format='nt') if graph_uri is None: query = f""" INSERT DATA {{ {nt_data} }} """ else: query = f""" INSERT DATA {{ GRAPH <{graph_uri}> {{ {nt_data} }} }} """ if verbose: print(query) response = requests.post(self.endpoint, data=query, headers=headers) if response.status_code == 200: print("Data successfully inserted.") else: print(f"Error: {response.status_code} {response.text}") 工夫 工夫した点として、サイズが大きいRDFファイルを一度にアップロードした際、プロセスが途中で止まってしまうケースがありました。 ...