SPARQLthon55/togo rdfstore

提供:TogoWiki

移動: 案内, 検索

現在のTogoGenomeのRDFストアにはVirtuosoを使用しているが、他のRDFストアへ移行できるかの検討を行った。 AllegroGraphとStar Dogを

目次

AllegroGraph

togostanza(ruby版)

現象

SPARQL中の"?"がillegal characterとしてエラーになり実行できない。

?query=PREFIX%20taxid:%20%3Chttp:%2F%2Fidentifiers.org%2Ftaxonomy%2F%3E%0APREFIX%20stats:%20%3C
http:%2F%2Ftogogenome.org%2Fstats%2F%3E%0A%0ASELECT%20?genome_size%0AFROM%20%3Chttp:%2F%2Ftogogenome.org%2Fgraph%2Fstats%3E%0A
WHERE%0A%7B%0A%20%20taxid:103690%20stats:sequence_length%20?genome_size%20.%0A%7D%0A"\n contains illegal character #\? at position 173.

原因

  • togostanzaが内部で呼び出しているrdfモジュール(ruby-rdf)中でURIを一度デコードしていてエンコードしているがその際に"?"のエンコードが漏れる

ソースコード URI.encode()ではなくCGI.escape()に

  • Virtuosoの場合は"?"が含まれていてもエラーにならないため、AllegroGraph側の問題かもしれない

対処(暫定)

ruby-rdfのエンコード処理部分のコードを改修する

Stanzaコード修正内容

Allegro graphで動作するために以下のようなSPARQL改修を行った

  • Virtuoso独自メソッドの排除
 削除: DEFINE sql:select-option "order" 
//subClassOf*で階層順序を取得するための独自関数
?search_tax rdfs:subClassOf ?tax OPTION (transitive, t_direction 1, t_min(0), t_step("step_no") as ?step) .
  • ASで変数に別名をつける際には括弧で囲む
前: SELECT COUNT(DISTINCT ?gold) AS ?cnt
後: SELECT (COUNT(DISTINCT ?gold) AS ?cnt)

GROUP_CONCATの文法は仕様に準拠する

前: (GROUP_CONCAT(DISTINCT ?exact_synonym, ", ") AS ?exact_synonyms)
後: (GROUP_CONCAT(DISTINCT ?exact_synonym; SEPARATOR = ", ") AS ?exact_synonyms)

暗黙的GROUP BYは効かないのでGROUP BYを記述する

前:
SELECT ?tax_id (COUNT(DISTINCT ?gold) AS ?cnt) {
   (略)
}
後:
SELECT ?type (COUNT(DISTINCT ?gold) AS ?cnt) {
   (略)
} GROUP BY ?tax_id

URIを文字列系関数にかける場合にはSRT()で必ず文字列置換する

前: FILTER(regex(?pdo_uri, "PDO"))
後: FILTER(regex(STR(?pdo_uri), "PDO"))

よく分からないがBINDがエラーになるケースがあるので、その場合は(可能なら)SELECT句に記述する

前:
SELECT ?type {
   (略)
  BIND ("GOLD" AS ?type ).
}
後: SELECT ("GOLD" AS ?type)  {
   (略)
  //BIND削除
}
  • FROMでグラフ名を差すのはVirtuoso独自だと思っていたが、意外と動いている(内容は確認が必要)

結果と感想

  • SPARQLは仕様に則り正しく書きましょう
  • 体感だが実行速度はVirtuosoより遅い。チューニング方法があるかもしれないが、分からない。
    • organismやenvironmentのIDを軸にした軽いクエリは動作するがやや重い
    • gene系(数十億トリプルのGraphを使用)するとタイムアウトするものが多い。メモリが上限に達したというエラーが出ることもある

togostanza(js版)

Ruby版でみられたような"?"で怒られることはなく、クエリを実行できる。速度計測や個々のクエリの検証は未済(Ruby版で検証したので)

SPARQList

Stardog

togostanza(ruby版)

Stanzaコード修正内容

TogoGenomeのスタンザクエリはそのままStardogで動作するものもあったが、いくつかの理由で書き換えが必要になるものもあった。

  • Virtuoso独自メソッドの排除 (Allegro graphと同じ)
  • FROM句を省略した場合Stardogでは結果がヒットしない

ロードデータは全てNamed graphにロードされ、default graphには何も入っていない状態のためだと思われる(仕様通りの挙動)

前:
SELECT *
{
 ?protein a <http://purl.uniprot.org/core/Protein>
} LIMIT 10
=> ノーヒット
後:
SELECT *
FROM <http://togogenome.org/graph/uniprot>
{
 ?protein a <http://purl.uniprot.org/core/Protein>
} LIMIT 10
  • GRAPH句を使っている場合Stardogでは結果がヒットしない

Virtuosoでは速度チューニングのためにわざとGRAPH句で囲っている場合がある。ただしグラフを混ぜたくない場合にも使う事があるためその場合の回避方法が不明(ドキュメント読めば分かるかも?)

前:
GRAPH <http://togogenome.org/graph/uniprot> {
  ?isoform rdf:value ?protein_seq .
}
後:
 ?isoform rdf:value ?protein_seq .
  • OPTIONALのグループグラフ(波カッコ)内で複数パターンを囲む際は遅くなるクエリが遅くなる場合がある

OPTIONAL句内のパターン同士が先にマッピングされているらしいので、外側で?isoformなどの変数が絞り込まれていても反映されず重くなる。
このようなケースではquery explainコマンドでクエリ実行計画を出力して遅くなる部分を特定する。
相談したページ explainコマンド 実行計画の見方

前:
OPTIONAL {
  ?annotation up:substitution ?substitution . 
  ?isoform rdf:value ?seq .
}
=> OPTIONAL句内のパターン同士で先にマッピングされるが変数が被っていないので総当たりになり返ってこない
後:
OPTIONAL {
  ?annotation up:substitution ?substitution . 
}
OPTIONAL {
  ?isoform rdf:value ?seq .
}
  • UNIONのグループグラフ(波カッコ)内で複数パターンを囲む際は遅くなるクエリが遅くなる場合がある

OPTIONALと同様にグループグラフ内を先に解決するのか、外側のVALUES句で指定した条件でトリプルが絞れるにも関わらず返ってこない

前:
{
  VALUES ?gene_id { tg:XXX }
  {
    ?gene_id skos:exactMatch ?gene ;
     ....
  }
  UNION
  {
    ?gene_id skos:exactMatch ?gene ;
     ....
  }
}
後:
{
  {
    VALUES ?gene_id { tg:XXX }
    ?gene_id skos:exactMatch ?gene ;
     ....
  }
  UNION
  {
    VALUES ?gene_id { tg:XXX }
    ?gene_id skos:exactMatch ?gene ;
     ....
  }
}
  • VALUES句を使うと遅くなる場合がある

よく分からないがVALUESで絞ろうとすると遅くなる場合がある。必ずしも遅くなるわけでない(explain実行すれば分かるかも)

前:
VALUES ?feature_type { insdc:Coding_Sequence }
?feature a ?feature_type ;
後:
?feature a insdc:Coding_Sequence ;
  • VALUES句を使わないと遅くなる場合がある

逆にVALUES句を使わないと遅くなるケースもあった。VALUES句を使わないければ遅くて返ってこない(explain実行すれば分かるかも)。

前:
?strand_type rdfs:subClassOf faldo:StrandedPosition
後:
VALUES ?strand_postype { faldo:StrandedPosition }
?strand_type rdfs:subClassOf ?strand_postype ;
  • SELECT句で文字列変換をすると遅くなる場合がある

SELECT句でReplace関数を使用したら返ってこなかったが、BINDで置換を実行したら速度改善した。他のクエリでは大丈夫な場合もあったので不明。

前:
SELECT DISTINCT (REPLACE(STR(?taxonomy),"http://identifiers.org/taxonomy/","") AS ?tax_id)
後:
BIND (REPLACE(STR(?taxonomy),"http://identifiers.org/taxonomy/","") AS ?tax_id)

結果と感想

  • gene系の大量データを検索する際にもVirtuosoと同じぐらいのスピードで返ってくるようになったクエリもある
  • TogoGenomeのページのように同時に重数個のスタンザ表示を行おうとすると、いくつかはタイムアウトで返ってこなくなる(まだ遅いクエリのチューニング、同時接続数を増やす事で可能かも?)
  • メモリ消費が130G程になっていたが、現実的なメモリ量でも動作するかは検証が必要
  • Virtuosoでのパフォーマンスチューニングは「上から順番に解釈させる」というものだったがStardogは同じ方法は取れない。データ量が多い場合、複数のRDFストア間で共通のSPARQLを使い回す事は難しい

クエリ例

gene_attributes

     PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
      PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
      PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
      PREFIX obo:    <http://purl.obolibrary.org/obo/>
      PREFIX insdc: <http://ddbj.nig.ac.jp/ontologies/nucleotide/>
      PREFIX uniprot: <http://purl.uniprot.org/core/>
      PREFIX faldo: <http://biohackathon.org/resource/faldo#>
      SELECT DISTINCT
        ?locus_tag ?gene_type_label ?gene_name
        ?refseq_link ?seq_label ?seq_type_label ?refseq_label ?organism ?tax_link
        ?strand ?insdc_location
      FROM <http://togogenome.org/graph/tgup>
      FROM <http://togogenome.org/graph/uniprot>
      FROM <http://togogenome.org/graph/refseq>
      FROM <http://togogenome.org/graph/so>
      FROM <http://togogenome.org/graph/faldo>
      {
        {
          SELECT ?feature
          {
            {
              <http://togogenome.org/gene/103690:PCC7120DELTA_RS0908> skos:exactMatch ?gene ;
                rdfs:seeAlso/rdfs:seeAlso ?uniprot .
              ?uniprot a uniprot:Protein ;
                uniprot:sequence ?isoform .
              ?isoform rdf:value ?protein_seq .
              ?feature obo:so_part_of ?gene ;
                a insdc:Coding_Sequence ;
                insdc:translation ?translation .
              FILTER (?protein_seq = ?translation)
            }
            UNION
            {
              VALUES ?feature_type { insdc:Transfer_RNA insdc:Ribosomal_RNA insdc:Non_Coding_RNA }
              <http://togogenome.org/gene/103690:PCC7120DELTA_RS0908>  skos:exactMatch ?gene .
              ?feature obo:so_part_of ?gene ;
                insdc:location ?insdc_location ;
                a ?feature_type .
            }
          } LIMIT 1
        }
        #feature info
        VALUES ?feature_type { obo:SO_0000316 obo:SO_0000252 obo:SO_0000253 obo:SO_0000655 } #CDS,rRNA,tRNA,ncRNA
        VALUES ?strand_postype { faldo:StrandedPosition }
        ?feature rdfs:subClassOf ?feature_type ;
          rdfs:label ?gene_label .
        #sequence / organism info
        ?feature obo:so_part_of* ?seq .
        ?seq rdfs:subClassOf ?seq_type .
        ?refseq_link insdc:sequence ?seq ;
          insdc:definition ?seq_label ;
          insdc:sequence_version ?refseq_label ;
          insdc:sequence_version ?refseq_ver ;
          insdc:organism ?organism .
        ?feature obo:RO_0002162 ?tax_link .
        #location info
        ?feature insdc:location  ?insdc_location ;
          faldo:location  ?faldo .
        ?faldo faldo:begin/rdf:type ?strand_type .
        OPTIONAL { ?feature insdc:gene ?gene_name }
        OPTIONAL { ?feature insdc:locus_tag ?locus_tag }
        ?feature_type rdfs:label ?gene_type_label .
        ?seq_type rdfs:label ?seq_type_label .
        ?strand_type rdfs:subClassOf ?strand_postype ;
        rdfs:label ?strand .
      }

gene_length_nano

      PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
      PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
      PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
      PREFIX obo:    <http://purl.obolibrary.org/obo/>
      PREFIX insdc: <http://ddbj.nig.ac.jp/ontologies/nucleotide/>
      PREFIX uniprot: <http://purl.uniprot.org/core/>

      SELECT ?insdc_location
      FROM <http://togogenome.org/graph/tgup>
      FROM <http://togogenome.org/graph/uniprot>
      FROM <http://togogenome.org/graph/refseq>
      {
        {
          SELECT ?feature
          {
            {
              <http://togogenome.org/gene/103690:PCC7120DELTA_RS09085> skos:exactMatch ?gene ;
                rdfs:seeAlso/rdfs:seeAlso ?uniprot .
              ?uniprot a uniprot:Protein ;
                uniprot:sequence ?isoform .
              ?isoform rdf:value ?protein_seq .
              ?feature obo:so_part_of ?gene ;
                a insdc:Coding_Sequence ;
                insdc:translation ?translation .
              FILTER (?protein_seq = ?translation)
            }
            UNION
            {
              VALUES ?feature_type { insdc:Transfer_RNA insdc:Ribosomal_RNA insdc:Non_Coding_RNA }
              <http://togogenome.org/gene/103690:PCC7120DELTA_RS09085> skos:exactMatch ?gene .
              ?feature obo:so_part_of ?gene ;
                insdc:location ?insdc_location ;
                a ?feature_type .
            }
          } LIMIT 1
        }
        ?feature insdc:location ?insdc_location .
      }

genome_jbrowse

      PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
      PREFIX obo: <http://purl.obolibrary.org/obo/>

      SELECT DISTINCT ?tax_id
      FROM <http://togogenome.org/graph/tgup>
      FROM <http://togogenome.org/graph/refseq>
      WHERE
      {
        {
          SELECT ?feature_uri
          {
            <http://togogenome.org/gene/103690:PCC7120DELTA_RS09085> skos:exactMatch ?feature_uri .
          } ORDER BY ?feature_uri LIMIT 1
        }
        ?feature_uri  obo:RO_0002162 ?taxonomy
        BIND (REPLACE(STR(?taxonomy),"http://identifiers.org/taxonomy/","") AS ?tax_id)
      }
      PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
      PREFIX obo: <http://purl.obolibrary.org/obo/>
      PREFIX insdc: <http://ddbj.nig.ac.jp/ontologies/nucleotide/>
      PREFIX faldo: <http://biohackathon.org/resource/faldo#>

      SELECT ?seq_label ?start ?end ?seq_length
      FROM <http://togogenome.org/graph/tgup>
      FROM <http://togogenome.org/graph/refseq>
      WHERE
      {
        {
          SELECT ?feature_uri
          {
            <http://togogenome.org/gene/103690:PCC7120DELTA_RS09085> skos:exactMatch ?feature_uri .
          } ORDER BY ?feature_uri LIMIT 1
        }
        ?feature_uri insdc:location  ?insdc_location ;
          faldo:location  ?faldo .
        ?faldo faldo:begin/faldo:position ?start .
        ?faldo faldo:end/faldo:position ?end .

        ?feature_uri obo:so_part_of ?seq .
        ?refseq insdc:sequence ?seq ;
          insdc:sequence_version ?seq_label .
        ?seq insdc:sequence_length ?seq_length
      }

protein_attributes

      PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
      PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
      PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
      PREFIX up: <http://purl.uniprot.org/core/>

      SELECT DISTINCT ?sequence ?fragment ?precursor ?existence_label
      FROM <http://togogenome.org/graph/uniprot>
      FROM <http://togogenome.org/graph/tgup>
      WHERE {
        {
          SELECT ?gene
          {
            <http://togogenome.org/gene/103690:PCC7120DELTA_RS09085> skos:exactMatch ?gene .
          } ORDER BY ?gene LIMIT 1
        }
        <http://togogenome.org/gene/103690:PCC7120DELTA_RS09085> skos:exactMatch ?gene ;
          rdfs:seeAlso ?id_upid .
        ?id_upid rdfs:seeAlso ?protein .
        ?protein a up:Protein ;
                 up:sequence ?seq .

        # Sequence
        OPTIONAL {
          ?seq rdf:value ?sequence .
        }

        # Sequence status
        OPTIONAL {
          ?seq up:fragment ?fragment .
        }

        # Sequence processing
        OPTIONAL {
          ?seq up:precursor ?precursor .
        }

        # Protein existence
        OPTIONAL {
          ?protein up:existence ?existence .
          ?existence rdfs:label ?existence_label .
        }
      }

protein_general_annotation

    PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
    PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
    PREFIX up: <http://purl.uniprot.org/core/>

    SELECT DISTINCT ?name ?message
    FROM <http://togogenome.org/graph/uniprot>
    FROM <http://togogenome.org/graph/tgup>
    WHERE {
        {
          SELECT ?gene
          {
            <http://togogenome.org/gene/103690:PCC7120DELTA_RS09085> skos:exactMatch ?gene .
          } LIMIT 1
        }
        <http://togogenome.org/gene/103690:PCC7120DELTA_RS09085> skos:exactMatch ?gene ;
          rdfs:seeAlso ?id_upid .
        ?id_upid rdfs:seeAlso ?protein .
        ?protein a up:Protein ;
                 up:annotation ?annotation .

        {
            # type がup:Annotation のアノテーション

            ?annotation a up:Annotation .

            # name, message の取得
            BIND(STR('Miscellaneous') AS ?name) .
            ?annotation rdfs:comment ?message .
        }UNION{
            # subClassOf Annotation で type が up:Subcellular_Location_Annotation のアノテーション

            ?annotation a up:Subcellular_Location_Annotation .

            # name, message の取得
            ?annotation up:locatedIn ?located_in .
            ?located_in up:cellularComponent ?location .
            ?location up:alias ?message .
            BIND(STR("Subcellular Location") AS ?name) .
        }UNION{
            # type が up:Subcellular_Location_Annotation 以外の subClassOf Annotation のアノテーション
 
            ?annotation a ?type .
            ?type rdfs:subClassOf up:Annotation .
            FILTER (?type != up:Subcellular_Location_Annotation)

            # name, message の取得
            ?type rdfs:label ?name .
            ?annotation rdfs:comment ?message .
        }
    }

protein_ontologies

      PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
      PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
      PREFIX up: <http://purl.uniprot.org/core/>
      PREFIX skos: <http://www.w3.org/2004/02/skos/core#>

      SELECT DISTINCT ?root_name ?concept (GROUP_CONCAT(?name; SEPARATOR = ", ") AS ?names)
      FROM <http://togogenome.org/graph/uniprot>
      FROM <http://togogenome.org/graph/tgup>
      WHERE {
        {
          SELECT ?gene
          {
            <http://togogenome.org/gene/103690:PCC7120DELTA_RS09085> skos:exactMatch ?gene .
          } ORDER BY ?gene LIMIT 1
        }
        <http://togogenome.org/gene/103690:PCC7120DELTA_RS09085> skos:exactMatch ?gene ;
          rdfs:seeAlso ?id_upid .
        ?id_upid rdfs:seeAlso ?protein .
        ?protein a up:Protein ;
                 up:classifiedWith ?concept .
        ?concept rdf:type up:Concept .
        FILTER contains(str(?concept), 'keywords') .

        ?concept ?label ?name FILTER (?label = skos:prefLabel || ?label = skos:altLabel).
        ?concept rdfs:subClassOf* ?parents .
        ?parents skos:prefLabel ?root_name .
        FILTER (str(?root_name) IN ('Biological process', 'Cellular component', 'Domain', 'Ligand', 'Molecular function', 'Technical term')) .
      }
      GROUP BY ?root_name ?concept
      ORDER BY ?root_name ?concept ?names
      PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
      PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
      PREFIX up: <http://purl.uniprot.org/core/>
      PREFIX taxonomy: <http://purl.uniprot.org/taxonomy/>

      SELECT DISTINCT ?name ?root_name ?obo_go_uri
      FROM <http://togogenome.org/graph/uniprot>
      FROM <http://togogenome.org/graph/tgup>
      FROM <http://togogenome.org/graph/go>
      WHERE {
        {
          SELECT ?gene
          {
            <http://togogenome.org/gene/103690:PCC7120DELTA_RS09085> skos:exactMatch ?gene .
          } ORDER BY ?gene LIMIT 1
        }
        <http://togogenome.org/gene/103690:PCC7120DELTA_RS09085> skos:exactMatch ?gene ;
          rdfs:seeAlso ?id_upid .
        ?id_upid rdfs:seeAlso ?protein .
        ?protein a up:Protein ;
                 up:classifiedWith ?obo_go_uri .
        ?obo_go_uri rdfs:label ?name .
        ?obo_go_uri rdfs:subClassOf* ?parents .
        ?parents rdfs:label ?root_name .
        FILTER (str(?root_name) IN ('biological_process', 'cellular_component', 'molecular_function')) .
      }

protein_sequence_annotation

      PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
      PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
      PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
      PREFIX up: <http://purl.uniprot.org/core/>
      PREFIX faldo: <http://biohackathon.org/resource/faldo#>

      SELECT DISTINCT ?parent_label ?label ?begin_location ?end_location ?seq_length ?comment (GROUP_CONCAT(?substitution; SEPARATOR = ", ") AS ?substitutions) ?seq_txt ?feature_identifier
      FROM <http://togogenome.org/graph/uniprot>
      FROM <http://togogenome.org/graph/tgup>
      WHERE {
        {
          SELECT ?gene
          {
            <http://togogenome.org/gene/103690:PCC7120DELTA_RS09085> skos:exactMatch ?gene .
          } ORDER BY ?gene LIMIT 1
        }
        <http://togogenome.org/gene/103690:PCC7120DELTA_RS09085> skos:exactMatch ?gene ;
          rdfs:seeAlso ?id_upid .
        ?id_upid rdfs:seeAlso ?protein .
        ?protein a up:Protein ;
                 up:annotation ?annotation .

        ?annotation rdf:type ?type .
        ?type rdfs:label ?label .

        # sequence annotation 直下のtype のラベルを取得(Region, Site, Molecule Processing, Experimental Information)
        ?type rdfs:subClassOf* ?parent_type .
        ?parent_type rdfs:subClassOf up:Sequence_Annotation ;
                     rdfs:label ?parent_label .

        ?annotation up:range ?range .
        OPTIONAL { ?annotation rdfs:comment ?comment . }
        ?range faldo:begin/faldo:position ?begin_location ;
               faldo:end/faldo:position ?end_location .


        # 互いに isoform なUniprotがあるので (e.g. P42166, P42167) 同じIDの isoform で配列のあるものに絞る
        ?protein up:sequence ?isoform .
        BIND( REPLACE( STR(?protein), "http://purl.uniprot.org/uniprot/", "") AS ?up_id)
        FILTER( REGEX(STR(?isoform), ?up_id))
        ?isoform rdf:value ?value .

        # description の一部が取得できるが、内容の表示に必要があるのか
        OPTIONAL {
          ?annotation up:substitution ?substitution . 
        }

        # sequence の長さ取得
        OPTIONAL {
          ?isoform rdf:value ?seq_txt .
          BIND (STRLEN(?seq_txt) AS ?seq_length) .
        }

        OPTIONAL {
          ?annotation rdf:type ?any_type . # Virtuoso 対応
          BIND (STR(?annotation) AS ?feature_identifier) .
          FILTER REGEX(STR(?feature_identifier), "http://purl.uniprot.org/annotation")
        }
      }
      GROUP BY ?parent_label ?label ?begin_location ?end_location ?seq_length ?comment ?seq_txt ?feature_identifier
      ORDER BY ?parent_label ?label ?begin_location ?end_location

protein_sequence

      PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
      PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
      PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
      PREFIX up: <http://purl.uniprot.org/core/>

      SELECT DISTINCT ?up_id ?aminoacid ?mass ?modified ?version ?checksum ?fragment ?precursor ?existence_label
      FROM <http://togogenome.org/graph/uniprot>
      FROM <http://togogenome.org/graph/tgup>
      WHERE {
        {
          SELECT ?gene
          {
            <http://togogenome.org/gene/103690:PCC7120DELTA_RS09085> skos:exactMatch ?gene .
          } ORDER BY ?gene LIMIT 1
        }
        <http://togogenome.org/gene/103690:PCC7120DELTA_RS09085> skos:exactMatch ?gene ;
          rdfs:seeAlso ?id_upid .
        ?id_upid rdfs:seeAlso ?protein .
        ?protein a up:Protein ;
                 up:sequence ?isoform .

        # (P42166 & P42167) x (P42166-1 & P42167-1) => P42166 - P42166-1, P42167 - P42167-1
        BIND( REPLACE( STR(?protein), "http://purl.uniprot.org/uniprot/", "") AS ?up_id)
        FILTER( REGEX(STR(?isoform), ?up_id))

        ?isoform rdf:value ?aminoacid ;
                 up:mass ?mass ;
                 up:modified ?modified ;
                 up:version ?version ;
                 up:crc64Checksum ?checksum .

        # Sequence status
        OPTIONAL {
          ?isoform up:fragment ?fragment .
        }

        # Sequence processing
        OPTIONAL {
          ?isoform up:precursor ?precursor .
        }

        # Protein existence
        OPTIONAL {
          ?protein up:existence ?existence .
          ?existence rdfs:label ?existence_label .
        }
      }