<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body text="#000000" bgcolor="#FFFFFF">
<br>
<div class="moz-cite-prefix">On 25/09/2017 20:53, Jesper Louis
Andersen wrote:<br>
</div>
<blockquote type="cite"
cite="mid:CAGrdgiVqiYwvKm78yALDby1NxrsVg7pU_x9Uz8hic-YJ5d=hhg@mail.gmail.com">
<div dir="ltr">
<div class="gmail_quote">
<div dir="ltr">On Mon, Sep 25, 2017 at 9:16 PM Grzegorz Junka
<<a href="mailto:list1@gjunka.com" moz-do-not-send="true">list1@gjunka.com</a>>
wrote:<br>
</div>
<blockquote class="gmail_quote" style="margin:0 0 0
.8ex;border-left:1px #ccc solid;padding-left:1ex">
<div text="#000000" bgcolor="#FFFFFF"><br>
</div>
<div text="#000000" bgcolor="#FFFFFF"> gb_tree would not
prevent from having to store the key twice (once as the
key for gb_tree and once as the value). Not sure why you
mention gb_tree here?</div>
<div text="#000000" bgcolor="#FFFFFF"><br>
</div>
</blockquote>
<div><br>
</div>
<div>The reason I mention this is because if we have:</div>
<div><br>
</div>
<div>Key = T, % for some arbitrary term T</div>
<div>Id = Horizon, % for some horizon value</div>
<div>Tree2 = gb_trees:enter(Key, Id, Tree1),</div>
<div>Tree3 = gb_trees:enter(Id, Key, Tree2),</div>
<div><br>
</div>
<div>then the `Key` is immutable and thus shared. It will in
practice be of pointer size (4-8 bytes). This only holds if
you are in a gb_trees setting because if we start going to
ETS for this, then we obviously might lose the sharing.
Perhaps it does work if you insert multiple entries at once.</div>
<div><br>
</div>
<blockquote class="gmail_quote" style="margin:0 0 0
.8ex;border-left:1px #ccc solid;padding-left:1ex">
<div text="#000000" bgcolor="#FFFFFF">That's the whole
point. How would you implement an RDF/triple database in
Erlang? :)<br>
<br>
</div>
</blockquote>
<div><br>
</div>
<div>The obvious candidate to beat is Postgres with an
appropriately designed table structure and indexes. If we
can't beat this, we are not even in game. It is also fairly
easy to write, so first, we should use that as a baseline
implementation.</div>
<div><br>
</div>
<div>If our RDF store is fairly small and can fit in memory,
I'd probably just store one ETS table doing a bidrectional
ID<->Key mapping as above. And then, depending on
query needs store those ID's as {{spo, Subject, Predicate},
Object} or such in ETS. This should allow fairly efficient
query, as most of the data resides in memory and lookup is
going to be fast. Some data waste is expected in these
solutions. It might be beneficial to just ignore the ID
mapping for a start and do it naively. This also establishes
a model which can be run in unison with other models via
QuickCheck so we can check for agreement with a simpler
model.</div>
<div><br>
</div>
<div>Going to billions of RDF triples will break the above
scheme. I'd probably do like the Cayley datastore in Go:
store data in eleveldb and steal the Cayley-format. In this
format you store everything in one leveldb database where
different parts of the storage are mapped by different
prefixes (IDs are <<"z", SHA1:160/integer>> for
instance. To run fast SPO, OSP, POS ... indexed hashes are
stored for these mappings as well. In addition a horizon and
history is tracked for each quad so we can answer a query in
the past faithfully if needed. Sharding/Batching might come
in handy here at a later point.</div>
<div><br>
</div>
<div>Leveled trees such as the one leveldb uses tend to be
quite efficient compared to B+-trees in many situations. It
might be in this case too. Because Cayley uses unique SHA1
hashes for data, we obtain that keys with the same subject
will be iterable because they will have the same prefix in
that part of the data store. We essentially pay with writes
up-front to make the lookups faster. Also note that leveldb
will naturally keep often used data in memory if tuned.</div>
<div><br>
</div>
<div>TL;DR: either data fits in memory or it doesn't :P</div>
<div><br>
</div>
</div>
</div>
</blockquote>
<br>
Yeah, there dozens of possible solutions. Bear in mind that if you
implement RDF on top of Postgres then you need to implement SPARQL
on top of SQL. This is no longer an Erlang database. You can
implement Riak or Couch like database on top of Postgres. The
question is, why? The main reason for using Erlang is to be able
distribute the database across multiple cores and nodes. Postgres as
an SQL database scales very well but up to some point. After a
terabyte of data or so it starts breaking down. You can extend the
performance with some tricks but that's it.<br>
<br>
It's not as much about finding a solution that is good enough
(Postgres, ETS) but about designing an algorithm that allows to
store and query triples in a way that can scale into thousands of
nodes. Erlang is perfect for that because you can start with
implementing an algorithm that works with a few thousand of
processes on multiple CPU cores on one computer. If that works then
the same algorithm can transparently be extended to run those
processes across multiple nodes. And if that works, the algorithm
can probably run millions of processes on those nodes.<br>
<br>
You can compare this problem to a choice between a single big and
complex solution (Postgres/SQL or Cray/mainframe) and many small
distributed and cooperating nodes (Riak/NoSQL or a mesh of
interconnected PCs).<br>
<br>
RDF is by design a way of representing data that is most capable of
distributing across nodes (distributing table cells rather than
whole columns or tables). In such an environment the biggest
problem, in my opinion, is to efficiently handle the LIMIT and ORDER
query modifiers. Imagine having to sort a billion of RDF triples
across a thousand of nodes just to return 10 triples with the least
values/objects.<br>
<br>
GrzegorzJ<br>
<br>
<br>
</body>
</html>