使用Perl的高效语义三元组,没有外部数据库服务器

时间:2014-07-09 21:56:25

标签: 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} = 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}}

  • 我对Porky有什么信息? (keys %{$ijk{Porky}}

  • 列出所有物种(keys %{$jik{species}}

等等。请注意,上面的示例都没有一次列出一个元素。他们都“立即”接受我的回答。换句话说,每个答案都是一个哈希值。当然,答案本身可能是一个列表,但我不会遍历任何列表来获得答案。

然而,定义6个单独的哈希似乎效率很低。在那儿 在不使用外部数据库引擎的情况下更轻松地执行此操作 (对于这个问题,SQLite3算作外部数据库引擎)?

或者我刚刚将一小部分SQL复制到Perl中?

编辑:我想我想说的是:我喜欢关联数组,但它们似乎是这项工作的错误数据结构。这里的数据结构是什么,Perl模块实现了什么?

5 个答案:

答案 0 :(得分:1)

您是否看过使用RDF::Trine?它有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";

RDF :: 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支持SPARQL 1.0和SPARQL 1.1。 RDF :: Trine和RDF :: Query都是由Gregory Williams编写的,他是SPARQL 1.1工作组的成员。 RDF :: Query是首批在SPARQL 1.1 Query测试套件上实现100%的实现之一。 (它甚至可能是第一个?)

答案 1 :(得分:0)

“Efficient”在这里并不是一个正确的词,因为你担心提高的速度来换取内存,这通常是它的工作方式。

唯一真正的选择是将三元组存储为不同的值,然后只有三个“索引”:

$row = [ "Porky", "species", "pig" ];
push @{$subject_index{Porky}}, $row;
push @{$relation_index{species}}, $row;
push @{$target_index{pig}}, $row;

要执行“列出所有猪”之类的操作,您必须找到$relation_index{species}$target_index{pig}的交集。您可以手动执行,也可以使用您喜欢的设置实现。

然后将它全部包装在一个漂亮的对象界面中,你基本上实现了INNER JOIN。 :)

答案 2 :(得分:0)

哈希的单个哈希就足够了:

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

输出:

Porky's species is: pig
All pigs: Porky
Info on Porky: { species => "pig" }
All species: pig

答案 3 :(得分:0)

  

或者我刚刚将一小部分SQL复制到Perl中?

在内存数据库中使用SQLite,开始使用实际的SQL非常容易。

#!/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 => {}}) );

给出:

 [{"object":"pig","predicate":"species","subject":"Porky"},
  {"object":"cat","predicate":"species","subject":"Sylvester"}]

然后,您可以以熟悉的方式查询和索引数据。非常可扩展。

答案 4 :(得分:0)

我认为你正在混合类别和价值观,例如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;

是的,这有一些明显的冗余,因为我们可以很容易地将大多数名称与其他值区分开来。但是第3级哈希键也是顶级哈希键,可用于获取有关某些元素的更多信息。