BH16.12/SuperFastRDFSearch

提供:TogoWiki

(版間での差分)
移動: 案内, 検索
(結果例)
67行: 67行:
なお、現時点ではUTF8へ未対応のため、一部文字化けが生じる場合がある。→ 生じていない模様( 12/28 )
なお、現時点ではUTF8へ未対応のため、一部文字化けが生じる場合がある。→ 生じていない模様( 12/28 )
 +
 +
== クライアントプログラム ==
 +
=== Ruby ===
 +
<pre>
 +
require 'xmlrpc/client'
 +
 +
positions = ["S", "P", "O"]
 +
nodetypes = ["Blank", "URI", "tLiteral", "pLiteral"]
 +
buffersize = 64*1024
 +
port = 37201
 +
relmax = 1000
 +
 +
client = XMLRPC::Client.new2("http://localhost:#{port}/")
 +
 +
File.open("allie_rdf_so_unique4sedue.txt") do |file|
 +
  STDIN.each do |line|
 +
    h = Hash.new
 +
    h["query"] = line.chomp
 +
    h["k"] = 0
 +
    h["from"] = 0
 +
    h["to"] = 0
 +
    result = client.call2("flex_query",h)
 +
    if (result[1]["error"] == "occured") then
 +
      print "ERROR: ", result[1]["value"]["faultString"], "\n"
 +
    elsif ( result[1]["total-hits"] > relmax ) then
 +
      print "Too many results: ",  result[1]["total-hits"], " (Max: ", relmax, ")\n"
 +
      exit
 +
    end
 +
 +
    h.delete("from")
 +
    h.delete("to")
 +
    result = client.call2("flex_query",h)
 +
    result[1]["results"].each do |hit|
 +
      pos = hit["position"]
 +
      file.seek(pos)
 +
      af = file.read(buffersize)
 +
      eowpos = af.index("\t")
 +
     
 +
      pb = hit["position"] - buffersize
 +
      if ( pb < 0 ) then pb = 0 end
 +
      file.seek(pb)
 +
      l = pos - pb
 +
      bf = file.read(l)
 +
      tabpos = bf.rindex(/[0-5]\t[0-5]\t/)
 +
      bf = bf[tabpos..-1].sub(/^(\d)\t(\d)/) do |match|
 +
        "#{positions[$1.to_i]}\t#{nodetypes[$2.to_i]}"
 +
      end
 +
      if (tabpos) then
 +
        print hit["part-name"],"\t",bf,af[0..eowpos],"\n"
 +
      else
 +
        puts bf
 +
      end
 +
    end
 +
  end
 +
end
 +
</pre>
 +
 +
=== Perl ===
 +
<pre>
 +
use warnings;
 +
use strict;
 +
use Fatal qw/open/;
 +
use Frontier::Client;
 +
use Getopt::Std;
 +
use FindBin qw/$Bin/;
 +
 +
my %ops;
 +
my $lookbehind = 64*1024;
 +
my $lookahead = 64*1024;
 +
 +
getopt('km', \%ops);
 +
 +
my $sedue_k = $ops{"k"} || 0;
 +
my $max_result = $ops{"m"} || 1000;
 +
my $file = $Bin. "/allie_rdf_so_unique4sedue.txt";
 +
 +
my @positions = qw/S P O/;
 +
my @nodetypes = qw/Blank URI tLiteral pLiteral/;
 +
 +
my $lockvar;
 +
 +
my $port = 37201;
 +
my $client = Frontier::Client->new(url => "http://localhost:$port/");
 +
 +
while(<>) {
 +
    chomp;
 +
    &sdreq(0, $_, $sedue_k, 1, $client, $max_result);
 +
}
 +
 +
exit 0;
 +
 +
sub sdreq {
 +
    my ($idx, $_query, $_sedue_k, $_no_gap, $_req, $_relmax) = @_;
 +
    eval {
 +
        my $r = $_req->call('flex_query',
 +
                            {query    => $_req->string($_query),
 +
                            k        => $_sedue_k,
 +
                            'no-gap' => $_no_gap,
 +
                            'from'  => 0,
 +
                            'to'    => 0,
 +
                        });
 +
        if( $r->{error} eq 'occured' ){
 +
            print "ERROR: ".$r->value->{'faultString'}."\n";
 +
        } elsif( $r->{"total-hits"} > $_relmax ){
 +
            print "Too many results: ", $r->{"total-hits"}, ". (Max: $_relmax)\n";
 +
            return 0;
 +
        }
 +
 +
        $r = $_req->call('flex_query',
 +
                        {query    => $_req->string($_query),
 +
                          k        => $_sedue_k,
 +
                          'no-gap' => $_no_gap,
 +
                      });
 +
        if( $r->{error} eq 'occured' ){
 +
            print "ERROR: ".$r->value->{'faultString'}."\n";
 +
        } else {
 +
            &display( $r->{'results'} );
 +
        }
 +
    };
 +
    if($@){
 +
        warn "SOMETHING WRONG? (Count:$idx) $@";
 +
    }
 +
    return 0;
 +
}
 +
 +
# Handling failure to search for tab hasn't be coded yet.
 +
 +
sub display {
 +
    my $res = shift;
 +
    open F, $file or die "$file:$!\n";
 +
    foreach $a (@$res) {
 +
        my %hash2 = %{$a};
 +
        my $qstr = $hash2{"query-based-string"};
 +
        my $opos = $hash2{"position"};
 +
        my $posforID = $opos - $lookbehind;
 +
 +
        if ($posforID < 0){
 +
            $posforID = 0;
 +
        }
 +
        my ($stc_be, $stc_ah) = ("", "");
 +
        unless (sysseek F, $posforID, 0){
 +
            my $em = "$file>SEEK_ERROR\n";
 +
            syswrite(STDOUT, $em, length($em));
 +
        } else {
 +
            my $part = "";
 +
            if(sysread(F, $part, $opos - $posforID)){
 +
                $part =~ m/[0-5]\t[0-5]\t[^\t]*$/;
 +
                $stc_be = substr($part, $-[0]);
 +
                $stc_be =~ s/^(\d)\t(\d)/$positions[$1]."\t".$nodetypes[$2]/e;
 +
            }else{
 +
                $stc_be = "xxx---xxx";
 +
            }
 +
        }
 +
        unless (sysseek F, $opos, 0){
 +
            my $em = "$file>SEEK_ERROR\n";
 +
            syswrite(STDOUT, $em, length($em));
 +
        } else {
 +
            if(sysread(F, $stc_ah, $lookahead)){
 +
                $stc_ah = substr($stc_ah, 0, index($stc_ah, "\t"));
 +
                $lockvar = join("\t", ($hash2{"part-name"}, $stc_be.$stc_ah))."\n";
 +
                syswrite(STDOUT, $lockvar, length($lockvar));
 +
            }
 +
        }
 +
    }
 +
    close F;
 +
}
 +
 +
__END__
 +
</pre>

2016年12月28日 (水) 16:35時点における版

目次

高速RDF検索サービス

背景

RDFデータの検索にはSPARQLが利用できるが、SPARQLエンドポイントの所在が所与である必要がある。 また、あるURIが与えられたときに、当該URIの示す概念に関する情報を取得するには、Linked Dataの原則に従って情報提供がなされていれば、HTTP GETすることで得られる。 しかし、当該URIを含むデータセットの提供主体以外の第三者によるデータセット内から当該URIが参照されているときに、その情報を取得することはできない。 そこで、URIやリテラルをキーとして、それを含むエンドポイントの一覧が得られるサービスを提供することで、これらの問題に対応する。

準備

以下のリソースを使用する。

  • N-Triple形式のRDFデータセット
  • Perl (RDF::Trineモジュールを利用)
  • Sedueflex (超高速検索エンジン)

結果例

AllieのRDFデータセットを対象として試験的に立ち上げ。

以下のように動作する。

$ echo 'Specific pathogen' | ./search_allieRDF.rb
allieRDF        O       pLiteral        Specific pathogen Free  
allieRDF        O       pLiteral        Specific pathogen free  
allieRDF        O       pLiteral        Specific pathogen-free  
allieRDF        O       pLiteral        Specific pathogene-free 

$ echo 'Specific pathogen' | ./search_allieRDF.pl
allieRDF        O       pLiteral        Specific pathogen Free
allieRDF        O       pLiteral        Specific pathogen free
allieRDF        O       pLiteral        Specific pathogen-free
allieRDF        O       pLiteral        Specific pathogene-free
$ echo '2980434' | ./search_allieRDF.rb 
allieRDF        S       URI     http://purl.org/allie/id/pair/2980434   
allieRDF        S       URI     http://togows.dbcls.jp/entry/ncbi-pubmed/22980434
allieRDF        S       URI     http://togows.dbcls.jp/entry/ncbi-pubmed/2980434
allieRDF        O       URI     http://purl.org/allie/id/pair/2980434   
allieRDF        O       URI     http://togows.dbcls.jp/entry/ncbi-pubmed/22980434
allieRDF        O       URI     http://togows.dbcls.jp/entry/ncbi-pubmed/2980434

$ echo '2980434' | ./search_allieRDF.pl 
allieRDF        S       URI     http://purl.org/allie/id/pair/2980434
allieRDF        S       URI     http://togows.dbcls.jp/entry/ncbi-pubmed/22980434
allieRDF        S       URI     http://togows.dbcls.jp/entry/ncbi-pubmed/2980434
allieRDF        O       URI     http://purl.org/allie/id/pair/2980434
allieRDF        O       URI     http://togows.dbcls.jp/entry/ncbi-pubmed/22980434
allieRDF        O       URI     http://togows.dbcls.jp/entry/ncbi-pubmed/2980434

現時点ではPerlの低レベルファイルIOの問題(utf8周り)?がありそうです。回避方法は他の言語での実装か? → 問題ないことを確認( 12/28 )

$ grep 2980434 allie_rdf_so_unique.txt
        0       1       http://purl.org/allie/id/pair/2980434
        0       1       http://togows.dbcls.jp/entry/ncbi-pubmed/22980434
        0       1       http://togows.dbcls.jp/entry/ncbi-pubmed/2980434
        2       1       http://purl.org/allie/id/pair/2980434
        2       1       http://togows.dbcls.jp/entry/ncbi-pubmed/22980434
        2       1       http://togows.dbcls.jp/entry/ncbi-pubmed/2980434

結果はタブ区切りの行志向で、第一列はエンドポイントを示すラベルで、続いて、トリプルのどの位置であるかを示す文字(S:主語、P:述語、O:目的語、ただし今回は述語は検索対象外)、URIかリテラル(型付きリテラル/プレインリテラルの違いを含む)、そして実際にマッチしたRDFタームの順で表示される。

検索対象のRDFターム数は33,460,464で、「レセプター」をキーとして初めて検索した場合、結果は813あり、timeコマンドで得られる値は以下の通り。

0.235u 0.033s 0:00.28 92.8%     0+0k 0+0io 0pf+0w

なお、現時点ではUTF8へ未対応のため、一部文字化けが生じる場合がある。→ 生じていない模様( 12/28 )

クライアントプログラム

Ruby

require 'xmlrpc/client'

positions = ["S", "P", "O"]
nodetypes = ["Blank", "URI", "tLiteral", "pLiteral"]
buffersize = 64*1024
port = 37201
relmax = 1000

client = XMLRPC::Client.new2("http://localhost:#{port}/")

File.open("allie_rdf_so_unique4sedue.txt") do |file|
  STDIN.each do |line|
    h = Hash.new
    h["query"] = line.chomp
    h["k"] = 0
    h["from"] = 0
    h["to"] = 0
    result = client.call2("flex_query",h)
    if (result[1]["error"] == "occured") then
      print "ERROR: ", result[1]["value"]["faultString"], "\n"
    elsif ( result[1]["total-hits"] > relmax ) then
      print "Too many results: ",  result[1]["total-hits"], " (Max: ", relmax, ")\n"
      exit
    end

    h.delete("from")
    h.delete("to")
    result = client.call2("flex_query",h)
    result[1]["results"].each do |hit|
      pos = hit["position"]
      file.seek(pos)
      af = file.read(buffersize)
      eowpos = af.index("\t")
      
      pb = hit["position"] - buffersize
      if ( pb < 0 ) then pb = 0 end
      file.seek(pb)
      l = pos - pb
      bf = file.read(l)
      tabpos = bf.rindex(/[0-5]\t[0-5]\t/)
      bf = bf[tabpos..-1].sub(/^(\d)\t(\d)/) do |match|
        "#{positions[$1.to_i]}\t#{nodetypes[$2.to_i]}"
      end
      if (tabpos) then
        print hit["part-name"],"\t",bf,af[0..eowpos],"\n"
      else
        puts bf
      end
    end
  end
end

Perl

use warnings;
use strict;
use Fatal qw/open/;
use Frontier::Client;
use Getopt::Std;
use FindBin qw/$Bin/;

my %ops;
my $lookbehind = 64*1024;
my $lookahead = 64*1024;

getopt('km', \%ops);

my $sedue_k = $ops{"k"} || 0;
my $max_result = $ops{"m"} || 1000;
my $file = $Bin. "/allie_rdf_so_unique4sedue.txt";

my @positions = qw/S P O/;
my @nodetypes = qw/Blank URI tLiteral pLiteral/;

my $lockvar;

my $port = 37201;
my $client = Frontier::Client->new(url => "http://localhost:$port/");

while(<>) {
    chomp;
    &sdreq(0, $_, $sedue_k, 1, $client, $max_result);
}

exit 0;

sub sdreq {
    my ($idx, $_query, $_sedue_k, $_no_gap, $_req, $_relmax) = @_;
    eval {
        my $r = $_req->call('flex_query',
                            {query    => $_req->string($_query),
                             k        => $_sedue_k,
                             'no-gap' => $_no_gap,
                             'from'   => 0,
                             'to'     => 0,
                         });
        if( $r->{error} eq 'occured' ){
            print "ERROR: ".$r->value->{'faultString'}."\n";
        } elsif( $r->{"total-hits"} > $_relmax ){
            print "Too many results: ", $r->{"total-hits"}, ". (Max: $_relmax)\n";
            return 0;
        }

        $r = $_req->call('flex_query',
                         {query    => $_req->string($_query),
                          k        => $_sedue_k,
                          'no-gap' => $_no_gap,
                      });
        if( $r->{error} eq 'occured' ){
            print "ERROR: ".$r->value->{'faultString'}."\n";
        } else {
            &display( $r->{'results'} );
        }
    };
    if($@){
        warn "SOMETHING WRONG? (Count:$idx) $@";
    }
    return 0;
}

# Handling failure to search for tab hasn't be coded yet.

sub display {
    my $res = shift;
    open F, $file or die "$file:$!\n";
    foreach $a (@$res) {
        my %hash2 = %{$a};
        my $qstr = $hash2{"query-based-string"};
        my $opos = $hash2{"position"};
        my $posforID = $opos - $lookbehind;

        if ($posforID < 0){
            $posforID = 0;
        }
        my ($stc_be, $stc_ah) = ("", "");
        unless (sysseek F, $posforID, 0){
            my $em = "$file>SEEK_ERROR\n";
            syswrite(STDOUT, $em, length($em));
        } else {
            my $part = "";
            if(sysread(F, $part, $opos - $posforID)){
                $part =~ m/[0-5]\t[0-5]\t[^\t]*$/;
                $stc_be = substr($part, $-[0]);
                $stc_be =~ s/^(\d)\t(\d)/$positions[$1]."\t".$nodetypes[$2]/e;
            }else{
                $stc_be = "xxx---xxx";
            }
        }
        unless (sysseek F, $opos, 0){
            my $em = "$file>SEEK_ERROR\n";
            syswrite(STDOUT, $em, length($em));
        } else {
            if(sysread(F, $stc_ah, $lookahead)){
                $stc_ah = substr($stc_ah, 0, index($stc_ah, "\t"));
                $lockvar = join("\t", ($hash2{"part-name"}, $stc_be.$stc_ah))."\n";
                syswrite(STDOUT, $lockvar, length($lockvar));
            }
        }
    }
    close F;
}

__END__
個人用ツール