Sql 使用Perl实现高效语义三元组,无需外部db服务器
我有几个语义三元组。一些例子:Sql 使用Perl实现高效语义三元组,无需外部db服务器,sql,perl,Sql,Perl,我有几个语义三元组。一些例子: Porky,species,pig // Porky's species is "pig" Bob,sister,May // Bob's sister is May May,brother,Sam // May's borther is Sam Sam,wife,Jane // Sam's wife is Jane ... and so on ... 我将每个三元组存储在6个不同的散列中。例如: $ijk{Porky}{species}{pig} =
Porky,species,pig // Porky's species is "pig"
Bob,sister,May // Bob's sister is May
May,brother,Sam // May's borther is Sam
Sam,wife,Jane // Sam's wife is Jane
... and so on ...
我将每个三元组存储在6个不同的散列中。例如:
$ijk{Porky}{species}{pig} = 1;
$ikj{Porky}{pig}{species} = 1;
$jik{species}{Porky}{pig} = 1;
$jki{species}{pig}{Porky} = 1;
$kij{pig}{Porky}{species} = 1;
$kji{pig}{species}{Porky} = 1;
这让我能够有效地提出以下问题:
- Porky是什么物种(
)keys%{$ijk{Porky}{species}
- 列出所有猪(
)keys%{$jki{species}{pig}}
- 关于波基我有什么信息?(
)keys%{$ijk{Porky}
- 列出所有物种(
)keys%{$jik{species}}
$row = [ "Porky", "species", "pig" ];
push @{$subject_index{Porky}}, $row;
push @{$relation_index{species}}, $row;
push @{$target_index{pig}}, $row;
要执行类似“列出所有猪”的操作,您必须找到$relationship\u index{species}
和$target\u index{pig}
的交集。您可以手动执行,也可以使用您喜爱的集合实现执行
然后将其封装在一个漂亮的对象界面中,您基本上实现了
内部连接:) 散列的单个散列就足够了:
use strict;
use warnings;
use List::MoreUtils qw(uniq);
use Data::Dump qw(dump);
my %data;
while (<DATA>) {
chomp;
my ($name, $type, $value) = split ',';
$data{$name}{$type} = $value;
}
# What species is Porky?
print "Porky's species is: $data{Porky}{species}\n";
# List all pigs
print "All pigs: " . join(',', grep {defined $data{$_}{species} && $data{$_}{species} eq 'pig'} keys %data) . "\n";
# What information do I have on Porky?
print "Info on Porky: " . dump($data{Porky}) . "\n";
# List all species
print "All species: " . join(',', uniq grep defined, map $_->{species}, values %data) . "\n";
__DATA__
Porky,species,pig
Bob,sister,May
May,brother,Sam
Sam,wife,Jane
你考虑过使用吗?它有DBI支持的存储,但也有内存存储,如果需要持久性,可以用RDF/XML、Turtle、N-Triples等进行解析/序列化
例如:
use strict;
use warnings;
use RDF::Trine qw(statement literal);
my $ns = RDF::Trine::Namespace->new("http://example.com/");
my $data = RDF::Trine::Model->new;
$data->add_statement(statement $ns->Peppa, $ns->species, $ns->Pig);
$data->add_statement(statement $ns->Peppa, $ns->name, literal 'Peppa');
$data->add_statement(statement $ns->George, $ns->species, $ns->Pig);
$data->add_statement(statement $ns->George, $ns->name, literal 'George');
$data->add_statement(statement $ns->Suzy, $ns->species, $ns->Sheep);
$data->add_statement(statement $ns->Suzy, $ns->name, literal 'Suzy');
print "Here are the pigs...\n";
for my $pig ($data->subjects($ns->species, $ns->Pig)) {
my ($name) = $data->objects($pig, $ns->name);
print $name->literal_value, "\n";
}
print "Let's dump all the data...\n";
my $ser = RDF::Trine::Serializer::Turtle->new;
print $ser->serialize_model_to_string($data), "\n";
Trine是一个相当大的框架,因此有一点编译时代价。但在运行时,速度相对较快
如果希望使用SPARQL查询数据,可以将RDF::Trine与RDF::Query结合使用
use RDF::Query;
my $q = RDF::Query->new('
PREFIX : <http://example.com/>
SELECT ?name
WHERE {
?thing :species :Pig ;
:name ?name .
}
');
my $r = $q->execute($data);
print "Here are the pigs...\n";
while (my $row = $r->next) {
print $row->{name}->literal_value, "\n";
}
使用RDF::Query;
我的$q=RDF::查询->新建
前缀:
选择?名称
在哪里{
?事物:种类:猪;
:name?name。
}
');
我的$r=$q->execute($data);
打印“这是猪…\n”;
while(my$row=$r->next){
打印$row->{name}->literal_值“\n”;
}
查询同时支持SPARQL 1.0和SPARQL 1.1。Trine和RDF::Query都是由SPARQL 1.1工作组成员Gregory Williams编写的。查询是在SPARQL 1.1查询测试套件上实现100%的首批实现之一。(这甚至可能是第一次?)
或者我只是将一小部分SQL复制到Perl中
开始使用实际的SQL非常容易,使用SQLite内存数据库
#!/usr/bin/perl
use warnings; use strict;
use DBI;
my $dbh = DBI->connect("dbi:SQLite::memory:", "", "", {
sqlite_use_immediate_transaction => 0,
RaiseError => 1,
});
$dbh->do("CREATE TABLE triple(subject,predicate,object)");
$dbh->do("CREATE INDEX 'triple(subject)' ON triple(subject)");
$dbh->do("CREATE INDEX 'triple(predicate)' ON triple(predicate)");
$dbh->do("CREATE INDEX 'triple(object)' ON triple(object)");
for ([qw<Porky species pig>],
[qw<Porky color pink>],
[qw<Sylvester species cat>]) {
$dbh->do("INSERT INTO triple(subject,predicate,object) VALUES (?, ?, ?)", {}, @$_);
}
use JSON;
print to_json( $dbh->selectall_arrayref('SELECT * from triple WHERE predicate="species"', {Slice => {}}) );
然后,您可以以熟悉的方式查询和索引数据。可扩展性也很好。我认为您正在混合类别和值,例如name=Porky和species=pig
举个例子,我会这样说:
my %hash;
$hash{name}{Porky}{species}{pig} = 1;
$hash{species}{pig}{name}{Porky} = 1;
$hash{name}{Bob}{sister}{May} = 1;
$hash{sister}{May}{name}{Bob} = 1;
$hash{name}{May}{brother}{Sam} = 1;
$hash{brother}{Sam}{name}{May} = 1;
$hash{name}{Sam}{wife}{Jane} = 1;
$hash{wife}{Jane}{name}{Sam} = 1;
是的,这有一些明显的冗余,因为我们可以很容易地将大多数名称与其他值区分开来。但是第三级散列键也是一个顶级散列键,可以用来获取有关某个元素的更多信息。这或多或少就是列上的索引的工作原理,是的。那么,纯Perl中有没有更好的方法来实现这一点呢?使用对象而不是散列。(说到“高效”,我想你指的是作为程序员的高效,而不是CPU时钟周期。)@ThisSuitesBlack你能说得更多吗?物体不会让事情变得更糟吗?我的意思是两方面都有效率。显然,我可以(并且已经)创建一个循环来完成这六个作业。然而,我感觉到我在强迫关联数组成为它们从来就不应该是的东西。使用对象的非常简单的例子(你必须自己实现这个类):my$porky=Pig->new(name=>porky');说$porky->name,$porky->species代码>我仍然认为“应该”有一些Perl实现的数据结构,我只需要输入“Porky,species,pig”一次,其他一切都会“神奇地”发生?就像某种数据库,关系?好的,但是Perl grep不是一次只遍历一个元素吗?我试图用散列来避免类似的事情。好吧,但你为什么要避免类似的事情呢?除非您有一个特定的需要,您试图解决,保持代码尽可能简单。这提高了可读性和可维护性。OP在问题中明确排除了SQLite:“有没有更简单的方法不用使用外部数据库引擎(对于这个问题,SQLite3算作外部数据库引擎)?”是的。我是说SQL可能已经被过早地排除在外了。此解决方案使用内存中的数据库,并展示了此类解决方案的轻量级。
my %hash;
$hash{name}{Porky}{species}{pig} = 1;
$hash{species}{pig}{name}{Porky} = 1;
$hash{name}{Bob}{sister}{May} = 1;
$hash{sister}{May}{name}{Bob} = 1;
$hash{name}{May}{brother}{Sam} = 1;
$hash{brother}{Sam}{name}{May} = 1;
$hash{name}{Sam}{wife}{Jane} = 1;
$hash{wife}{Jane}{name}{Sam} = 1;