読者です 読者をやめる 読者になる 読者になる

男女比はカレーと福神漬けと同じくらい

マサカリよろしくお願いします。

RDFをSPARQLを使ってRailsでDBっぽく扱ってみた

RDFをSPARQLで操作するハッカソンがあり、Activerecordを使ってのDB操作と同じようにRDFを扱いたかったのでactiverecordのメソッドっぽくラップしてみた。

まず以下をGemfileに追記する。

# Gemfile
...
gem 'active_attr'
gem 'sparql'

active_attr はテーブルを持たないモデルを少ない記述で作成できるようになるgem。
README読めば大体わかる。

sparql はSPARQLをrubyで叩くためのgem。


 

次にモデルを記述していく。
具体例として尾道市の飲食店のモデルを示す。

class Restaurant
  require "sparql/client"
  include ActiveAttr::TypecastedAttributes
  include ActiveAttr::BlockInitialization

  attribute :name, type: String
  attribute :address, type: String
  attribute :lat, type: Float
  attribute :lng, type: Float
  attribute :image, type: String

  def self.all
    restaurants = []
    results = self.all_query

    results.each do |result|
      restaurant = Restaurant.new do |r|
        r.name = result[:name]
        r.address = result[:address_name]
        r.lat = result[:lat].to_s.gsub('+', '')
        r.lng = result[:lng].to_s.gsub('+', '')
        r.image = result[:img]
      end
      restaurants << restaurant
    end

    restaurants
  end

  private

  def self.all_query
    client = SPARQL::Client.new("https://sparql.odp.jig.jp/api/v1/sparql")
    results = client.query('
      select ?id ?address_name ?img ?name ?lat ?lng {
      ?s <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://purl.org/jrrk#CivicPOI>;
       <http://purl.org/jrrk#address> ?address.
      filter(regex(?address, "広島県尾道市"))
      ?s <http://odp.jig.jp/odp/1.0#genre> <http://odp.jig.jp/res/category/%E9%A3%9F%E3%81%B9%E3%82%8B>.
      ?s <http://purl.org/dc/terms/identifier> ?id.
      ?s ?p ?id.
      ?s <http://imi.ipa.go.jp/ns/core/rdf#住所> ?addresses.
      ?addresses <http://imi.ipa.go.jp/ns/core/rdf#表記> ?address_name.
      ?s <http://schema.org/image> ?img.
      ?s <http://www.w3.org/2000/01/rdf-schema#label> ?name.
      ?s <http://imi.ipa.go.jp/ns/core/rdf#地理座標> ?locate.
      ?locate <http://imi.ipa.go.jp/ns/core/rdf#緯度> ?lat.
      ?locate <http://imi.ipa.go.jp/ns/core/rdf#経度> ?lng.
      filter(lang(?name) = "ja")
    }
    ')

    results
  end
end

実装の流れとしては

  1. 尾道市の飲食店データを得るためのクエリをSPARQLで書く。(ここがメイン)
  2. クエリから結果を返すself.all_queryを書く。
  3. self.all_queryの返り値をマッピングしてモデルのレコードインスタンスを生成するself.allを書く。

これでRestaurant.allを実行すれば尾道市の飲食店が全件取得できるようになる。

SPARQL難しい。