2021年12月7日火曜日

メディア芸術DBクイズ のSPARQLクエリ解説

メディア芸術DBクイズ のSPARQLクエリについて解説します。 

まず「マンガクイズ」のためのクエリについて。
マンガクイズが最も単純な仕組みで動作しています。

1 まず以下のクエリを最初に1回だけリクエストして、対象となるデータの主語(?s)のレコード数を取得します。この結果をメモリに格納しておいて、それを最大値とした乱数をクイズ問題を作成するつど生成します。(この仕組みはアニメもゲームも同じ)
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX schema: <https://schema.org/>
SELECT (count(?s) as ?count)
WHERE {
?s schema:genre "マンガ単行本シリーズ" ;
   rdfs:label ?name ;
   schema:creator  ?creator ;
   schema:datePublished  ?time ;
   schema:publisher  ?publisher ;
   schema:brand  ?brand ;
   schema:numberOfItems  ?numberOfItems ;
   schema:inLanguage  "日本語" .
  FILTER regex(?creator, "")
  FILTER regex(?brand, "")
  FILTER (xsd:integer(?numberOfItems) >= 5)
}

2 次に以下のクエリで、実際にクイズに使うデータを取得します。
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX schema: <https://schema.org/>
SELECT *
WHERE {
?s schema:genre "マンガ単行本シリーズ" ;
   rdfs:label ?name ;
   schema:creator  ?creator ;
   schema:datePublished  ?time ;
   schema:publisher  ?publisher ;
   schema:brand  ?brand ;
   schema:numberOfItems  ?numberOfItems ;
   schema:inLanguage  "日本語" .
  FILTER regex(?creator, "")
  FILTER regex(?brand, "")
  FILTER (xsd:integer(?numberOfItems) >= 5)
} OFFSET 250
LIMIT 20

OFFSETの数字には、上記で生成した乱数をセットしてランダムにデータを取得するようにします。LIMITを20件にしている理由は、例えばマンガの作者を答える問題を出題した際、3択問題ですのでLIMIT 3、ということにすると、同じ作者がときどきかぶってしまうことがあります。LIMIT20にしておけば、万が一作者がかぶったとしても、そのときは20件のうちの別の作者に変えたらいいので、そういった保険的な意味合いで20件にしています。

■ データを取得する必須項目
?name  (作品名)
?creator  (著者)
?time  (出版年月)
?publisher  (出版者(社))
?brand  (出版ブランド名)
?numberOfItems  (発刊した巻数)

■ 絞り込み項目
?s schema:genre "マンガ単行本シリーズ" 
 ⇒「マンガ単行本シリーズ」データセットをターゲットとする
schema:inLanguage  "日本語"
 ⇒ 日本語のマンガだけをターゲットとする
FILTER regex(?creator, "") 
 ⇒ 言語指定されている「著者」を除外する
(※enを除外し単純リテラルのみ抽出)
FILTER regex(?brand, "") 
 ⇒ 言語指定されている「出版ブランド名」を除外する
(※enを除外し単純リテラルのみ抽出)
FILTER (xsd:integer(?numberOfItems) >= 5)
 ⇒ 単行本が5巻以上発刊されているマンガのみをターゲットにする
(※5巻以上の有名な作品のみにして問題を易しくするのが狙い)

あとは上記で取得した必須項目データを、乱数を組み込んだプログラムにより組み合わせを変化させてクイズを生成します。問題種別の出題割合は以下のとおりにしています。
・作品名を回答する問題 50%
・著者名を回答する問題 30%
・出版社を回答する問題 20%

アニメクイズもマンガと同じように最初に1回だけ最大値を取得し、あとはクイズのつど、クイズデータを取得します。以下はアニメデータを取得するクエリです。
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX schema: <https://schema.org/>
PREFIX ma: <https://mediaarts-db.bunka.go.jp/data/property/>
SELECT *
WHERE {
?s schema:genre "アニメテレビレギュラーシリーズ" ;
   rdfs:label ?name ;
   schema:productionCompany ?productionCompany  ;
   schema:actor ?actor  ;
   schema:contributor ?contributor  ;
   schema:startDate ?startDate  ;
   schema:endDate ?endDate .
 OPTIONAL{?s schema:publisher ?publisher.}
 OPTIONAL{?s ma:programDuration ?programDuration .}
 OPTIONAL{?s ma:originalWorkCreator ?originalWorkCreator .}
 OPTIONAL{?s ma:numberOfPrograms ?numberOfPrograms  .}
 OPTIONAL{?s ma:periodDisplayed ?periodDisplayed  .}
 OPTIONAL{?s schema:track ?track  .}           
}
ORDER BY ?startDate
OFFSET 400
LIMIT 20

クイズを作成する理屈は上述のマンガとほぼ同じですが、マンガと異なる点は、必須でない項目をOPTIONAL句で取得しているところ。例えば
 OPTIONAL{?s schema:track ?track  .}  
でアニメの主題歌を取得しています。
OPTIONAL句にしないと「データ不存在」の分の結果セットが減ってしまうので、データ不存在が多い項目についてはOPTIONALにしているということです。
OPTIONAL句の項目は、それが無くても問題として成立するけどヒント的な感じで出題文に入れ込む、というパターンが多いです。

ゲームクイズもだいたいマンガやアニメと同じ理屈で出題していますが、やや複雑な構造になっていますので詳しい説明は割愛します。以下のクエリでほぼ近いデータを得られます。
PREFIX schema: <https://schema.org/> 
PREFIX dcterms: <http://purl.org/dc/terms/> 
SELECT ?s ?gameName ?time ?gameGenre ?creator ?creatorName ?location ?character ?productionCompany ?productionCompanyName
WHERE { 
 ?s schema:name  ?gameName ;
    schema:datePublished  ?time ;
    schema:keywords  ?gameGenre . 
 FILTER regex(?gameName, "")  
 OPTIONAL {
   ?s dcterms:creator  ?creator . 
   ?creator schema:name  ?creatorName.
 } 
 OPTIONAL { ?s schema:contentLocation  ?location .} 
 OPTIONAL { ?s schema:character  ?character . } 
 OPTIONAL { 
   ?s schema:productionCompany ?productionCompany . 
   ?productionCompany schema:name  ?productionCompanyName.
 }
}