SPARQL 사용예

한국관광공사 LOD
관광데이터모델 샘플 질의

목차

1. 소개

이 문서는 한국관광공사의 링크드 오픈데이터 서비스(http://data.visitkorea.or.kr)에서 제공하는
SPARQL Endpoint를 설명합니다.

2. SPARQL Endpoint

한국관광공사의 SPARQL Endpoint 주소는 <http://data.visitkorea.or.kr/sparql>이며, SPARQL1.1을 지원합니다. REST URL를 통해서 접근할 수 있으며, 텍스트 형식의 SPARQL편집기와 GUI형식의 SPARQL Builder를 통해서 SPARQL를 작성할 수 있습니다.

2.1 SPARQL API

SPARQL은 RDF 데이터베이스와 상호작용하기 위한 웹표준 질의언어입니다. SPARQL은 표준화된 질의언어와 질의응답형식(query response formats), HTTP이용한 SPARQL Endpoint와의 상호작용 프로토콜에 대한 명세서로 구성됩니다.한국관광공사에 의해서 링크드 데이터(Linked Data)로 발행된 데이터셋은 SPARQL Endpoint를 통해서 접근할 수 있으며, SPARQL1.1질의언어<http://www.w3.org/TR/sparql11-query/>와 SAPRQL1.1 HTTP 프로토콜<http://www.w3.org/TR/sparql11-protocol/>을 지원합니다.

2.1.1 API파라미터

파라미터 필요여부 설명
query 필수 URL인코딩된 질의언어(SELECT, ASK, DESCRIBE, CONSTRUCT)
format 선택 질의 결과에 대한 포맷(SPARQL1.1<http://www.w3.org/TR/sparql11-protocol/>)

2.1.2 HTTP 요청메소드(Request Methods)

SPARQL Endpoint에 전송되는 SPARQL 질의는 GET과 POST방식을 지원합니다. 일반적으로 클라이언트는 GET방식을 통해서 질의하기를 권고합니다.

2.1.3 응답형식(Response Formats)

SPARQL Endpoint는 SPARQL질의에 대한 응답형식으로 다양한 형태를 지원합니다. 여기서 클라이언트는 다음과 같이 지원되는 포맷에 따라 SPARQL Endpoint에 요청하시기 바랍니다.
◼ SELECT와 ASK는 application/sparql-results+xml 형식으로 반환됨
◼ DESCRIBE와 CONSTRUCT는 RDF graph이므로 application/rdf+xml형식으로 반환됨
응답형식을 선택은 HTTP Accept 헤더를 이용하며, 아래와 같이 다양한 형식의 SPARQL질의와 Accept, 반환 파라미터를 참조하시기 바랍니다.

질의형식 Accept헤더 반환포맷 명세서
ASK, SELECT application/sparql-results+xml xml SPARQL XML Results Format
ASK, SELECT application/sparql-results+json json SPARQL JSON Results Format
SELECT text/csv csv SPARQL CSV Results Format
CONSTRUCT, DESCRIBE text/turtle ttl Turtle
CONSTRUCT, DESCRIBE application/rdf+xml rdf RDF/XML
CONSTRUCT, DESCRIBE application/rdf+json json RDF/JSON

2.1.4 응답 캐싱(Response Caching)

SPARQL Endpoint는 요청에 대한 빠른 응답을 위해 필요시 동일한 질의에 대한 캐싱을 수행합니다. 캐시는 RDF 데이터베이스에 새로운 데이터가 추가/삭제되거나 필요에 의해 RDF데이터베이스를 재시작할 경우 캐시가 초기화될 수 있습니다.

2.2 SPARQL 편집 및 실행

SPARQL 편집기는 한국관광공사의 링크드 데이터에 직접 질의할 수 있는 텍스트 형식의 질의 작성기입니다. SPARQL1.1<http://www.w3.org/TR/sparql11-query/>의 구문을 제공하며, 사용자가 다양한 질의를 작성하여 결과를 얻을 수 있습니다. 질의결과 포맷은 HTML, RDF/XML, N-Triples, Turtle, RDF/JSON, CSV 형식으로 받을 수 있으며, 어플리케이션 활용에 맞게형식을 선택하시면 됩니다.


* 질의결과

2.3 GUI기반 SPARQL 편집기


GUI기반 SPARQL 편집기는 트리플 패턴(Subject, Predicate, Object)의 입력을 쉽게 할 수 있는 클래스와 속성 자동선택기능을 제공합니다. 사용자는 버튼을 클릭하여 새로운 트리플 패턴을 추가/작성할 수 있으며, OFFSET과 LIMIT를 설정한 후 버튼을 클릭하면 SPARQL 구문이 완성됩니다.(아래)

버튼을 클릭하면, SPARQL Endpoint를 실행할 수 있는 페이지로 자동삽입 됩니다. 사용자는 삽입된 질의문을 추가적으로 수정, 작성하여 실행을 하게 되면 원하는 결과를 얻을 수 있습니다.

3. SPARQL 질의 예

3.1 PREFIX 정의

PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX dc: <http://purl.org/dc/elements/1.1/>
PREFIX owl: <http://www.w3.org/2002/07/owl#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
PREFIX vi: <http://www.saltlux.com/transformer/views#>
PREFIX kto: <http://data.visitkorea.or.kr/ontology/>
PREFIX ktop: <http://data.visitkorea.or.kr/property/>
PREFIX ids: <http://data.visitkorea.or.kr/resource/>
PREFIX wgs: <http://www.w3.org/2003/01/geo/wgs84_pos#>
PREFIX f: <http://www.saltlux.com/geo/functions#>
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX geo: <http://www.saltlux.com/geo/property#>

네임스페이스는 리소스(resource)가 가지는 식별주소로써, 질의문 작성 시 선언부에 정의하고 질의 본문에서는 PREFIX명을 통해서 사용할 수 있습니다.

3.2 모든 장소의 이름을 100개만 가져오기

SELECT * 
WHERE {
	?resource a kto:Place ;
	rdfs:label ?name . 
} order by ?name limit 100
				

3.3 모든 장소가 가지는 주소 알아보기

SELECT * 
WHERE {
 ?resource a kto:Place ;
      rdfs:label ?name ;
      ktop:location ?location .
 ?location skos:prefLabel ?location_name .
} limit 100
				

3.4 모든 장소유형(서비스 분류체계) 알아보기

장소유형은 kto:TourismCode 클래스의 인스턴스로 명시되어 있습니다. kto:TourismCode는 skos:Concept의 하위클래스로 정의되어 있습니다.

SELECT * 
WHERE {
 ?resource a kto:Place ;
      rdfs:label ?name ;
      ktop:category ?category . 
 ?category skos:prefLabel ?category_name .
} limit 100
				

3.5 주소코드와 장소유형에 대한 언어코드 매칭하기

SELECT * 
WHERE {
 ?resource a kto:Place ;
      rdfs:label ?name ;
      ktop:category ?category . 
 ?category skos:prefLabel ?category_name . 
 FILTER langMatches( lang(?category_name), "KO" ) 
} limit 100
				

주소코드와 장소유형은 9개국어(중국어는 간체, 번체)로 명시되어 있으며, SPARQL에서 언어코드(language code) iso639-1 표기법에 따라 정의되어 있으며, 언어코드는 FILTER 구문에 “langMatches( lang(?category), "KO" )” 구문을 통해서 매칭이 가능합니다.

국가별 언어코드 테이블

국가 언어코드
대한민국 KO
미국 EN
일본 JA
중국-간체 ZH-HANS
중국-번체 ZH-HANT
독일 DE
프랑스 FR
러시아 RU
스페인 ES

언어태그는 표기는 “@”이며, 데이터는 <http://data.visitkorea.or.kr/resource/125410> <http://data.visitkorea.or.kr/property/address> “경상북도 봉화군 석포면 청옥로 1552-163“@ko . 와 같이 표현됩니다.

3.6 COUNT 함수 사용하기

SELECT (COUNT(*) AS ?CNT) 
WHERE {
 ?resource a kto:Place ;
      rdfs:label ?name ;
      ktop:category ?category . 
 ?category skos:prefLabel ?category_name  . 
 FILTER langMatches( lang(?category_name), "KO" ) 
}
				

한글로 표기된 카테고리(서비스 분류체계)를 가지는 장소의 개수를 구하는 예로써, SPARQL의 COUNT함수를 사용합니다. 가끔 데이터에 중복이 있을 경우는 DISTINCT 함수를 사용하여 중복을 제거합니다.

SELECT (COUNT(distinct *) AS ?CNT) 
WHERE {
 ?resource a kto:Place ;
      rdfs:label ?name ;
      ktop:category ?category . 
 ?category skos:prefLabel ?category_name .
  FILTER langMatches( lang(?category_name), "KO" ) 
} 
				

3.7 서비스 분류체계별 장소가 얼마나 있는 지 알아보기

SELECT (COUNT(distinct *) AS ?CNT) ?category_name
WHERE {
 ?resource a kto:Place ;
      rdfs:label ?name ;
      ktop:category ?category . 
 ?category skos:prefLabel ?category_name . 
 FILTER langMatches( lang(?category_name), "KO" ) 
} GROUP BY ?category ?category_name
				

3.8 지역별로 분포되어 있는 장소 개수 구하기기

SELECT (COUNT(distinct *) AS ?CNT) ?location_name ?upper_name 
WHERE {
 ?resource a kto:Place ;
      rdfs:label ?name ;
      ktop:location ?location . 
 ?location skos:prefLabel ?location_name . 
 FILTER langMatches( lang(?location_name), "KO" ) 
 ?location skos:broader ?upper_loc .
 ?upper_loc skos:prefLabel ?upper_name . 
 FILTER langMatches( lang(?upper_name), "KO" ) 
} GROUP BY ?category ?location_name ?upper_name 
				

3.9 지역별로 분포되어 있는 장소 중 펜션 개수 구하기

SELECT (COUNT(distinct *) AS ?CNT) ?location_name ?upper_name 
WHERE {
 ?resource a kto:Place ;
      rdfs:label ?name ;
      ktop:location ?location . 
 ?location skos:prefLabel ?location_name . 
 FILTER langMatches( lang(?location_name), "KO" ) 
 ?location skos:broader ?upper_loc .
 ?upper_loc skos:prefLabel ?upper_name . 
 FILTER langMatches( lang(?upper_name), "KO" ) 
} GROUP BY ?category ?location_name ?upper_name 
				

* ids:B02010700는 펜션 분류코드임.

3.10 BIND와 CONCAT함수를 이용한 주소코드 합치기

SELECT ?name ?address 
WHERE {
 ?resource a kto:Place ;
      rdfs:label ?name ;
      ktop:location ?location . 
 ?location skos:prefLabel ?location_name . 
 FILTER langMatches( lang(?location_name), "KO" ) 
 ?location skos:broader ?upper_loc .
 ?upper_loc skos:prefLabel ?upper_name . 
 FILTER langMatches( lang(?upper_name), "KO" )  
 BIND (CONCAT(?upper_name, " ", ?location_name) AS ?address)
} LIMIT 100
				

3.11 특정 장소이름 중에서 "대교" 키워드를 포함하고 있는 장소 매칭

SELECT * 
WHERE {
 ?resource a kto:Place ;
      rdfs:label ?name . 
      FILTER (contains(?name, "대교") ) 
} LIMIT 100
				

3.12 특정 장소의 속성 중 ktop:parking 속성을 가지고 있는 않는 곳 찾기(NOT EXISTS)

SELECT ?resource ?name 
WHERE {
 ?resource a kto:Place ;
           rdfs:label ?name . 
           FILTER( NOT EXISTS{?resource ktop:parking  ?parking } ) 
} LIMIT 100
				

NOT EXISTS는 SPARQL를 통해서 관광데이터에 대한 일관성(consistency) 점검할 수 있는 구문이다.

3.13 특정 장소가 가족호텔이거나 펜션인 곳 찾기

SELECT ?resource ?name 
WHERE {
 ?resource a ?type ;
           rdfs:label ?name . 
           FILTER(?type IN (kto:FamilyHotel, kto:Pension))
} LIMIT 100
				

3.14 지역별로 분포되어 있는 장소 중 펜션 개수에 차트표시(이름순 10개)

SELECT (COUNT(distinct *) AS ?CNT) ?location_name ?upper_name 
WHERE {
 ?resource a kto:Place ;
      rdfs:label ?name ;
      ktop:category ids:B02010700 ;
      ktop:location ?location . 
 ?location skos:prefLabel ?location_name . 
 FILTER langMatches( lang(?location_name), "KO" ) 
 ?location skos:broader ?upper_loc .
 ?upper_loc skos:prefLabel ?upper_name . 
 FILTER langMatches( lang(?upper_name), "KO" )  
} GROUP BY ?category ?location_name ?upper_name ORDER BY DESC(?CNT) 
LIMIT 10 
				

SPARQL를 통한 차트결과 표시는 http://yasgui.org/와 같은 툴을 통해서 이용 가능 합니다.

3.15 지역별로 분포되어 있는 장소유형 개수에 차트표시(이름순 100개)

SELECT (COUNT(?resource) AS ?CNT) ?loc_name ?cate_name
WHERE {
 ?resource a kto:Place ;
      rdfs:label ?name ;
      ktop:category ?category ;
      ktop:location ?location . 
 ?category skos:prefLabel ?cate_name . 
 FILTER langMatches( lang(?cate_name), "KO" ) 
 ?location skos:prefLabel ?location_name . 
 FILTER langMatches( lang(?location_name), "KO" ) 
 ?location skos:broader ?upper_loc .
 ?upper_loc skos:prefLabel ?upper_name . 
 FILTER langMatches( lang(?upper_name), "KO" )  
  BIND( concat(?upper_name, " ", ?location_name) as ?loc_name )
} GROUP BY ?loc_name ?cate_name ?location ORDER BY DESC(?name) 
LIMIT 100
				

SPARQL를 통한 차트결과 표시는 http://yasgui.org/와 같은 툴을 통해서 이용 가능 합니다.

3.16 Geo-spatial함수를 이용한 거리구하기

SELECT ?name ?near_name 
	(f:distance(?lat1, ?long1, ?lat2, ?long2) AS ?distance)
WHERE {
 ids:1346828 wgs:lat ?lat1 ;
             wgs:long ?long1 ;
           rdfs:label ?name . 
 ?near wgs:lat ?lat2 ;
       wgs:long ?long2 ;
       rdfs:label ?near_name . 
       FILTER (f:distance(?lat1, ?long1, ?lat2, ?long2) <= 1000) 
} LIMIT 100
				
SELECT ?name ?near_name ?distance 
WHERE {
 ids:1346828 wgs:lat ?lat1 ;
             wgs:long ?long1 ;
           rdfs:label ?name . 
 (?near ?distance) geo:nearby(?lat1 ?long1 "1000") .
 ?near rdfs:label ?near_name .
} LIMIT 100
				

Geo-spatial 함수는 2가지 유형이 있습니다. <http://www.saltlux.com/geo/functions#distance>과 <http://www.saltlux.com/geo/property#nearby>가 있으며 전자는 FILTER function이고 후자는 Property function입니다.

3.17 "치악산" 주변 식당 중 한식이며, 5KM이내에 있는 곳은?

SELECT ?name ?near_name ?distance 
WHERE {
 ids:125587 wgs:lat ?lat1 ;
             wgs:long ?long1 ;
           rdfs:label ?name . 
 (?near ?distance) geo:nearby(?lat1 ?long1 "5000") .
 ?near rdfs:label ?near_name ;
       ktop:category ids:A05020100 . 
} 
				

<http://data.visitkorea.or.kr/resource/A05020100>는 식당유형이 한식입니다.

3.18 주소체계 구하기

SELECT ?top_name ?part1_name ?part2_name 
WHERE {
 ids:AddressScheme skos:hasTopConcept ?top .
 ?top skos:narrower ?part1 ;
      skos:prefLabel ?top_name . 
      FILTER langMatches( lang(?top_name), "KO" ) 
 ?part1 skos:narrowerTransitive ?part2 ;
        skos:prefLabel ?part1_name . 
        FILTER langMatches( lang(?part1_name), "KO" ) 
 ?part2 skos:prefLabel ?part2_name . 
 		FILTER langMatches( lang(?part2_name), "KO" )         
} limit 100
				

3.19 장소에 대한 이미지 구하기

SELECT *
WHERE {
 ids:125587 a kto:Place ;
           foaf:depiction ?depiction .
 ?depiction foaf:depicts ?depicts ;
            foaf:thumbnail ?thumbnail .
}  
				

장소에 대한 이미지는 한국관광공사가 제공하는 이미지입니다.

3.20 송정해수욕장 교통정보를 부산시LOD와 연계하기

SELECT * 
WHERE {
   ids:126080 owl:sameAs ?same .
 SERVICE <http://lod.busan.go.kr/sparql/> {
   ?same busan:bus ?bus ;
         busan:subway ?subway .
 	}
}
				

3.21 대청댐 정보를 한국수자원공사 LOD와 연계하여 정수장 및 수도 공급지역 가져오기

SELECT * 
WHERE {
   ids:127663 owl:sameAs ?same .
 SERVICE <http://opendata.kwater.or.kr/lod/data/sparql/> {
   ?same foaf:name ?name ;
         kwater:supplyTo ?supply .
   ?supply foaf:name ?supply_name ;
           kwater:supplyTo ?supplyTo .
   ?supplyTo foaf:name ?supplyToName .
         
 }
}
				

3.22 국립수목원의 생물정보LOD와 연계하여 치악산의 생물정보 가져오기

SELECT * 
WHERE {
 ids:125587  owl:sameAs ?same .
 SERVICE <http://lod.nature.go.kr/sparql> {
 ?nature  <http://lod.nature.go.kr/ontology/hasInternalDistribution> 
 ?same ;
        rdfs:label ?label ;
       <http://lod.nature.go.kr/ontology/generalDescription> ?desc .
 optional {?nature foaf:depiction ?depiction . }
 }
 
} limit 100
				

3.23 전국도서관 주변 1km내에 있는 음식점 찾기

SELECT  ?name ?lat ?long ?address ?near ?near_name 
        (concat(str(?distance), "m") AS ?dist)
WHERE
  { { SELECT  ?name ?lat ?long 
        (concat(GROUP_CONCAT(?label; SEPARATOR=' '), " ", ?locName) AS ?address)
      WHERE
        { ?resource rdf:type kto:Library .
          ?resource rdfs:label ?name .
          ?resource wgs:lat ?lat .
          ?resource wgs:long ?long .
          ?resource ktop:location ?loc .
          ?loc skos:broaderTransitive ?super_loc .
          ?loc skos:prefLabel ?locName .
          ?super_loc skos:prefLabel ?label
          FILTER langMatches(lang(?label), "KO")
        }
      GROUP BY ?name ?locName ?lat ?long
      LIMIT   10
    }
    { SELECT DISTINCT  *
      WHERE
        { ?near rdf:type kto:Gastro .
          ?near rdfs:label ?near_name .
          ?near wgs:lat ?lat1 .
          ?near wgs:long ?long1
        }
    }
    BIND(f:distance(?lat, ?long, ?lat1, ?long1) AS ?distance)
    FILTER ( ?distance <= 1000 )
  }
ORDER BY ?name ?dist
LIMIT   1000