Persistent predicates based on RocksDB

Thanks.

I was actually hoping it was a feature and not a bug. :woozy_face:

Now that I know it is a bug, I suspect that break/0 or something will be needed to keep the RocksDB session alive so that manual queries can be entered at the top level.

Right now, you’d need to check, using rdb_clause/2. But this seems like a reasonable feature, and not difficult to implement, so please create an Issue for it. Issues · JanWielemaker/rocks-predicates · GitHub

That tabling might not be necessary … RocksDB has its own LRU cache of key/value pairs and its use of RAM can be controlled by options.

Again, please open an issue on this … I’ll have to think a bit about the ramifications.

Please file a bug with a reproducible example.
I’m in the middle of some code clean-up, so it’ll be at least a few days before I can work on this.

I have no problem trying to make a reproducible example but now knowing that rdb_close/1 is not working as expected that this might be an artifact of using the system in a way not intended.

@jan @peter.ludemann

I know a lot of work has gone into this and more is needed, so just want to say THANKS!.

That’s still a bug. :wink:
[All systems get used in ways not intended …]

For those wanting to try using rocks-predicates module and need to see just what is needed. These are specific to Ubuntu but should also work with Debian. If you use Windows then Ubuntu can be installed using WSL 2.

Note: This was created as minimal working example for issue 5 but is still useful for others.

Note: Because of Issue 2 the ability to run queries from the top level after rdb_close/1 is run should not occur. In the future if the modules this relies on are changed then the example Prolog code here will probably need to use break/0 or similar.


Installing SWI-Prolog via PPA

Based on: Installing from PPA (Ubuntu Personal Package Archive)

$ sudo apt-get update
$ sudo apt-get upgade
$ sudo apt-get install software-properties-common
$ sudo apt-add-repository ppa:swi-prolog/devel
$ sudo apt-get update
$ sudo apt-get install swi-prolog
$ swipl

After starting SWI-Prolog check the version to make sure it is a recent version.
As of 07/12/2022 it is 8.5.14

?- version.
Welcome to SWI-Prolog (threaded, 64 bits, version 8.5.14)

Installing SWI-Prolog pack RocksDB using Git instead of pack_install/1 so that different commits can be tried.

$ sudo apt install libsnappy-dev liblz4-dev libzstd-dev libgflags-dev
$ mkdir -p  ~/.local/share/swi-prolog/pack
$ cd ~/.local/share/swi-prolog/pack
$ git clone https://github.com/JanWielemaker/rocksdb.git
$ git clone https://github.com/JanWielemaker/rocks-predicates.git
$ cd rocksdb
$ git clone https://github.com/facebook/rocksdb.git

As this only downloaded the source code for the packs they still need to be compiled.

$ swipl
?- pack_rebuild(rocksdb).

To see the checked out commit for a directory

groot@System:~/.local/share/swi-prolog/pack/rocksdb$ git show --oneline -s
e253458 (HEAD -> master, tag: V0.10.0, origin/master, origin/HEAD) ENHANCED: added statistics and logging options

OS: Ubuntu 22.04 LTS
SWI-Prolog: Install via PPA development version 8.5.14
RocksDB pack commit: e253458
RocksDB commit: a9565ccb2
rocks-predicates pack commit: 3071007


Example Prolog code with data (facts) for loading into RocksDB.
Note: This was originally created as a MWE for issue 5 thus the odd name of the module.

:- module('issue_5',
    [
        check/1,
        mars_lander/9
    ]).

% -------------------------------------------------------------------------

% Use the fact dummy and the user:file_search_path/2 to setup the Alias myapp.
dummy.

user:file_search_path(myapp,Dir) :-
    source_file(dummy,File),
    file_directory_name(File,Dir).

user:file_search_path(library, '/home/groot/.local/share/swi-prolog/pack/rocks-predicates').

% ----------------------------------------------------------------------------

:- use_module(library(rocksdb)).
:- use_module(library(rocks_preds)).

% ----------------------------------------------------------------------------

% Source: https://en.wikipedia.org/wiki/List_of_Mars_landers#Mars_landers
fact_01('1','Mars 2MV-3 No.1','04 Nov 1962','25 Nov 1962','890','-','-','Failed','Soviet Union').
fact_01('2','Mars 2','19 May 1971','27 Nov 1971','1210','45°S 47°E','-','Failed','Soviet Union').
fact_01('3','Mars 3','28 May 1971','02 Dec 1971','1210','45°S 202°E','Sirenum Terra','Partial Success','Soviet Union').
fact_01('4','Mars 6','05 Aug 1973','12 Mar 1974','635','23.90°S 19.4°W','Margaritifer Terra','Failed','Soviet Union').
fact_01('5','Mars 7','09 Aug 1973','-','635','-','-','Failed','Soviet Union').
fact_01('6','Viking 1','20 Aug 1975','20 Jul 1976','572','22.27°N 47.95°W','Chryse Planitia','Success','USA').
fact_01('7','Viking 2','09 Sep 1975','03 Sep 1976','572','47.64°N 225.71°W','Utopia Planitia','Success','USA').
fact_01('8','Phobos 1','07 Jul 1988','-','2600','-','-','Failed','Soviet Union').
fact_01('9','Phobos 2','12 Jul 1988','-','2600','-','-','Failed','Soviet Union').
fact_01('10','Mars 96','16 Nov 1996','-','3159','41°31N 153°77 W♦','-','Failed','Russia').
fact_01('11','Mars Pathfinder','04 Dec 1996','04 Jul 1997','361','19°7′48″ N 33°18′12″W','Ares Vallis','Success','USA').
fact_01('12','Mars Polar Lander','03 Jan 1999','03 Dec 1999','583','76°S 195°W','Ultimi Scopuli','Failed','USA').
fact_01('13','Beagle 2','02 Jun 2003','25 Dec 2003','33.2','11.5265°N 90.4295°E','Isidis Planitia','Failed','United Kingdom').
fact_01('14','Spirit rover','10 Jun 2003','4 Jan 2004','174','14.5684°S 175.4726°E','Gusev Crater','Success','USA').
fact_01('15','Opportunity rover','07 Jul 2003','25 Jan 2004','174','1.9462°S 354.4743°E','Meridiani Planum','Success','USA').
fact_01('16','Phoenix lander','04 Aug 2007','5 May 2008','350','68.22°N 125.7°W','Vastitas Borealis','Success','USA').
fact_01('17','Curiosity rover','26 Nov 2011','5 Aug 2012','899','4.5895°S 137.4417°E','Gale Crater','Operational','USA').
fact_01('18','Schiaparelli EDM','14 Mar 2016','19 Oct 2016','577','2.052°S 6.208°W','Meridiani Planum','Failed','European UnionESA/Russia').
fact_01('19','InSight Mars Lander','5 May 2018','26 Nov 2018','727','4.5°N 135.9°E','Elysium Planitia','Operational','USA').
fact_01('20','Perseverance rover','30 Jul 2020','18 Feb 2021','1,025','18.4447°N 77.4508°E','Jezero crater','Operational','USA').
fact_01('21','Tianwen-1','23 July 2020','14 May 2021','240','109.7°E, 25.1°N','Utopia Planitia','Operational','China').

% ----------------------------------------------------------------------------

user:file_search_path(rocksdb,myapp('RocksDB')).

% ----------------------------------------------------------------------------

check(1) :-
    load.

load :-
    absolute_file_name(rocksdb('.'),Rocksdb_directory),
    setup_call_cleanup(
        rdb_open(Rocksdb_directory,RocksDB),
        load_records,
        rdb_close(RocksDB)
    ).

load_records :-
    forall(
        fact_01(A1,A2,A3,A4,A5,A6,A7,A8,A9),
        rdb_assertz(fact_01(A1,A2,A3,A4,A5,A6,A7,A8,A9))
    ).

mars_lander(A1,A2,A3,A4,A5,A6,A7,A8,A9) :-
    rdb_clause(fact_01(A1,A2,A3,A4,A5,A6,A7,A8,A9),true).

Example run.

groot@Galaxy:~$ swipl
Welcome to SWI-Prolog (threaded, 64 bits, version 8.5.14)
...

?- working_directory(_,'/mnt/c/Users/groot/Projects/rocks-predicates_issue_5').
true.

?- [issue_5].
true.

?- check(1).
true.

?- current_blob(Blob,rocksdb).
Blob = <rocksdb>(0x557084c04070) ;
false.

?- rocks_preds:default_db(Dir).
Dir = '/mnt/c/Users/groot/Projects/rocks-predicates_issue_5/RocksDB'.

?- current_table(rocks_preds:H,_),H=rdb_clause_index(_,M:P/I,_).
H = rdb_clause_index(<rocksdb>(0x563e1abac0f0), issue_5:fact_01/9, _),
M = issue_5,
P = fact_01,
I = 9 ;
false.

?- rocks_preds:default_db(Dir),rocks_preds:rdb_predicate_property(Dir,issue_5:fact_01(_,_,_,_,_,_,_,_,_),number_of_clau
ses(N)).
Dir = '/mnt/c/Users/groot/Projects/rocks-predicates_issue_5/RocksDB',
N = 21.

?- issue_5:fact_01(A1,A2,A3,A4,A5,A6,A7,A8,A9).
A1 = '1',
A2 = 'Mars 2MV-3 No.1',
A3 = '04 Nov 1962',
A4 = '25 Nov 1962',
A5 = '890',
A6 = A7, A7 = (-),
A8 = 'Failed',
A9 = 'Soviet Union' ;
A1 = '2',
A2 = 'Mars 2',
A3 = '19 May 1971',
A4 = '27 Nov 1971',
A5 = '1210',
A6 = '45°S 47°E',
A7 = (-),
A8 = 'Failed',
A9 = 'Soviet Union' ;
A1 = '3',
A2 = 'Mars 3',
A3 = '28 May 1971',
A4 = '02 Dec 1971',
A5 = '1210',
A6 = '45°S 202°E',
A7 = 'Sirenum Terra',
A8 = 'Partial Success',
A9 = 'Soviet Union'
...
?-

Note: As this is a MWE for issue 5 this is how to recreate the issue.

?- mars_lander(A1,A2,A3,A4,A5,A6,A6,A8,A9).
A1 = '1',
A2 = 'Mars 2MV-3 No.1',
A3 = '04 Nov 1962',
A4 = '25 Nov 1962',
A5 = '890',
A6 = (-),
A8 = 'Failed',
A9 = 'Soviet Union' ;

Press space bar to see next result.

A1 = '5',
A2 = 'Mars 7',
A3 = '09 Aug 1973',
A4 = A6, A6 = (-),
A5 = '635',
A8 = 'Failed',
A9 = 'Soviet Union' .

Press enter here instead of space bar which causes the error.

ERROR: Arguments are not sufficiently instantiated
ERROR: In:
ERROR:    [5] '$execute_goal2'(user:mars_lander('5','Mars 7','09 Aug 1973',-,'635',-,-,'Failed','Soviet Union'),['A1'='5',...|...],true)
?-

Enjoy.

Debian doesn’t seem to support adding the PPA - it needs some kind of key, which I don’t know how to generate. In the end, I build SWI-Prolog from the github source with this, which puts it in $HOME/.local/bin/swipl:

cd ~/src/swipl-devel && git pull --recurse && \
mkdir -p ~/src/swipl-devel/build && \
 cd ~/src/swipl-devel/build && \
 cmake -DCMAKE_INSTALL_PREFIX=$HOME/.local -G Ninja .. && \
 ninja && \
 ctest -j8 && \
 ninja install
1 Like

@Rscho314

The first trial load of the SemMedDB data completed, took about 14 hours to load.

This line from the RocksDB log is notable.
Note: Split line up into multiple lines to improve readbility.

Cumulative writes: 
   339M writes, 
   339M keys, 
   339M commit groups,
   1.0 writes per commit group,
ingest:
   34.14 GB,
   0.70 MB/s

Since module rocks_preds uses tabling and tabling eats RAM is there some safe guard built in to keep RAM from being exhausted?

Based on my experience with SQL databases, batch loading would give you a significant performance improvement. RocksDB has a batch facility, and the rocksdb interface supports it; but rocks_preds doesn’t use it (yet). It’s probably worth opening an enhancement request for this.

What do you get from the following query after you load your data and do your queries?

?- forall(predicate_property(rocks_preds:rdb_clause_index(_,_,_), P), writeln(P)).

You could, of course, remove the table directive if it turns out to use a lot of memory. It’s not clear to me that it’s needed, although that might require some modification of the rocks_preds or rocksdb code to maintain efficiency.

There’s an open issue on this, but I’m not able to look into it right now.

Thanks.

I have been eyeballing that feature since I first saw it but it requires that the data be presented in key sorted order. (ref). Also that would be great for the initial load of the data but then for regular updates it is back to things as normal.

Another option also of consideration and not on my priority list is the alternative Bulkloading by ingesting external SST files but obviously that requires that the SST files were created, thus one needs the chicken before the egg. If there are many users that follow in loading many of these databases (biological in my case) into RocksDB and don’t mind delays of a few days while the the SST files are updated and compacted for each monthly release then these datasets could be distributed as SST files. Also there is no requirement that there be a single RocksDB, one could put each dataset into a different RocksDB and then instead of loading the data just replace the RocksDB with the fresh SST files as needed. Pointer and handle manipulation is becoming one of my favorite swords of choice.

Also did not check Windows Process Monitor see see if any Virus checker or other such process was hooking file updates that need to be configured correctly for this. (ref)


Does SWI-Prolog really need it?

Since RocksDB is meant to be an embedded DB, the files should be local or at least accessible and if one can access the files then one could just write some simple C++ and do it that way. Also since the code should be pretty much boilerplate with some options I would not be surprised if such code is easily found in a Git repository or such.


What do you get from the following query after you load your data and do your queries?

?- forall(predicate_property(rocks_preds:rdb_clause_index(_,_,_), P), 
   writeln(P)).

EDIT 1 of just this answer

The second attempt almost finished cleanly, seems an RRF file was missing so the code threw an exception entering trace mode and at which point pressing the space bar just completed the commands in the stack, often failing, including failing on rdb_close. Plan to use ldb and other #RocksDB tools to inspect the files.

While your desired query did work this time, the result is not what I think you seek.

?- forall(predicate_property(rocks_preds:rdb_clause_index(_,_,_),P),writeln(P)).
interpreted
visible
static
file(/home/eric/.local/share/swi-prolog/pack/rocks-predicates/rocks_preds.pl)
line_count(348)
number_of_clauses(1)
number_of_rules(1)
last_modified_generation(7084)
defined
tabled
tabled(variant)
size(488)
true.

Obviously something is amiss.

Second attempt clearly loaded more data.

Cumulative writes: 
   671M writes, 671M keys,
   671M commit groups,
   1.0 writes per commit group,
ingest:
   60.01 GB,
   0.50 MB/s

That part of the code is still a mystery to me.

I do know that rocks_preds actually creates two RocksDBs, one for the data and then on in a folder called predicates that I have just to work out the details.

I did find

to try and inspect the RocksDB files but the precompiled version is not free and building for Windows resulted in an error. Might try a Linux build if I am hard pressed for a way to understand the RocksDB files.

See: RocksDB Tools.

Read the issues:
8081 was surprising.


You really are busy. You asked me to open that one. :wink:

Another item of concern.
Journal and/or log files eventually consuming all disk space.

Since RocksDB is like a database in that it keeps a journal and/or logs, over time these files will continue to grow and possibly accumulate. Thus one could run out of disk space. I don’t know how serious this is but something that needs to be understood up front when setting up RocksDB.

Based on reading:
What I Wish Someone Would Have Told Me About Using Rabbitmq Before It Was Too Late

and a few others in the last few days.

Batch loading doesn’t require that the keys be in order, although it might help performance. The reference you mention even says “Bulk loading of data into RocksDB can be modeled as a large parallel sort where the dataset doesn’t fit into memory, …”.

Compaction deletes those journals and/or logs when it’s safe to do so (and the logs with human-readable messages, such as statistics, are rotated). That’s a standard feature of journaling file systems (such as NTFS, ext3, ext4, zfs, etc. and also of databases that use this technique, such as PosgreSQL). Of course, if the database is being written to faster than compaction can happen, then you can run out of disk space, and that wouldn’t happen with a non-journaling system. But I suspect with modern hardware, that would require an incredibly high data rate.

@jan

So far most of the thinking is that for persisting facts (predicates with a body of true) the fact is loaded into RocksDB using rdb_assertsz/1,2. My dubious understanding is that during the process SWI-Prolog creates interoperability structures such as tabled indexes.

Another useful idea would be to take an existing RocksDB and expose the data as facts for use in SWI-Prolog. The thinking is that there are many RocksDB datasets in the real world so just leverage those and make them available to SWI-Prolog by adding the required SWI-Prolog segue structures.

I suspect that there is more that I am missing because of the way the RocksDB keys for predicates are created but I don’t see that as a death knell to this idea.

A similar corollary, for those unfamiliar with this concept, is how existing RDF is accessible to SWI-Prolog, i.e. library(semweb/rdf_db): The RDF database.

Hey, I’ve read all of your notes, thanks for all the work! I’ve been a little overwhelmed at work myself, but this is next on my list.

I have some ideas to use that for clinical guidelines synthesis. It’s going to be very interesting, I think!

You are quite welcome. :slightly_smiling_face:

I am actually glad you asked it gave me the push I needed. This is definitely something that not only Prolog needed but that many Programming languages have been struggling with since I dare say the 1960’s. In the 1990’s when Object Oriented programming took off, it was popularly danced around as the impedance mismatch problem (ref). However one niche language that is not well known in main stream programming that solved this problem for the most part (at least in the 1990’s when it was on my radar) is MUMPS but to put MUMPS more in context with this problem see Persistent programming language.

If this turns out to work as we hope a few us show write a paper, I have never written a paper but Jan W. seems to write a few a year and he would defiantly need to be included if not the lead author.

So far I have not seen anything that says this will not work, Jan’s initial proof of concept showed that the code worked and I am still working on pushing it to 100s of millions of facts based on SemMedDB. It is not fully loaded and working as I think I corrupted the second attempt with an invalid closing of the database because of an error so currently learning how to use the admin tools to investigate. Just par for the course.

Glad to here. As I noted, working with different real world datasets is nice because I often learn something new.

In creating notes for module rocks_preds there is a need to refer to the types of keys. Currently to reference such keys the unique key pattern is used which does not leave a lasting impression nor is it an easy way to search for such in documentation.

Can we give each key a unique meaningful name?

While preliminary these are how I think of the keys

  • Interned predicate key.
    meta\u0001<PI> → true

  • Clause key.
    <PI>\u0001<0-padded hex clause no> → (Head :- Body)

  • RocksDB predicate property key.
    Not to be confused with predicate_property/2.
    <PI>\u0002<Property> → Term

  • Index status key.
    <PI>\u0003<Index> → Status

  • Index reference key.
    <PI>\u0004<Index>\u0002<Hash> → list(ClauseRef)


EDIT

Real data (Click triangle to expand)

Data from: List of Mars landers

% What data is in RocksDB?
"'mars lander':carrier_mars_sublander/3\u00010000000001" - ('mars lander':carrier_mars_sublander(mars_lander,2,1) :- true)
"'mars lander':carrier_mars_sublander/3\u00010000000002" - ('mars lander':carrier_mars_sublander(mars_lander,3,2) :- true)
"'mars lander':carrier_mars_sublander/3\u00010000000003" - ('mars lander':carrier_mars_sublander(mars_lander,10,3) :- true)
"'mars lander':carrier_mars_sublander/3\u00010000000004" - ('mars lander':carrier_mars_sublander(mars_lander,12,4) :- true)
"'mars lander':carrier_mars_sublander/3\u00010000000005" - ('mars lander':carrier_mars_sublander(mars_lander,11,5) :- true)
"'mars lander':carrier_mars_sublander/3\u00010000000006" - ('mars lander':carrier_mars_sublander(mars_lander,20,6) :- true)
"'mars lander':carrier_mars_sublander/3\u00010000000007" - ('mars lander':carrier_mars_sublander(mars_lander,21,7) :- true)
"'mars lander':carrier_mars_sublander/3\u00010000000008" - ('mars lander':carrier_mars_sublander(mars_sublander,7,8) :- true)
"'mars lander':carrier_mars_sublander/3\u0002last_clause"-8
"'mars lander':mars_lander/5\u00010000000001" - ('mars lander':mars_lander(1,'Mars 2MV-3 No.1','04 Nov 1962',890,'Failed') :- true)
"'mars lander':mars_lander/5\u00010000000002" - ('mars lander':mars_lander(2,'Mars 2','19 May 1971',1210,'Failed') :- true)
"'mars lander':mars_lander/5\u00010000000003" - ('mars lander':mars_lander(3,'Mars 3','28 May 1971',1210,'Partial Success') :- true)
"'mars lander':mars_lander/5\u00010000000004" - ('mars lander':mars_lander(4,'Mars 6','05 Aug 1973',635,'Failed') :- true)
"'mars lander':mars_lander/5\u00010000000005" - ('mars lander':mars_lander(5,'Mars 7','09 Aug 1973',635,'Failed') :- true)
"'mars lander':mars_lander/5\u00010000000006" - ('mars lander':mars_lander(6,'Viking 1','20 Aug 1975',572,'Success') :- true)
"'mars lander':mars_lander/5\u00010000000007" - ('mars lander':mars_lander(7,'Viking 2','09 Sep 1975',572,'Success') :- true)
"'mars lander':mars_lander/5\u00010000000008" - ('mars lander':mars_lander(8,'Phobos 1','07 Jul 1988',2600,'Failed') :- true)
"'mars lander':mars_lander/5\u00010000000009" - ('mars lander':mars_lander(9,'Phobos 2','12 Jul 1988',2600,'Failed') :- true)
"'mars lander':mars_lander/5\u0001000000000a" - ('mars lander':mars_lander(10,'Mars 96','16 Nov 1996',3159,'Failed') :- true)
"'mars lander':mars_lander/5\u0001000000000b" - ('mars lander':mars_lander(11,'Mars Pathfinder','04 Dec 1996',361,'Success') :- true)
"'mars lander':mars_lander/5\u0001000000000c" - ('mars lander':mars_lander(12,'Mars Polar Lander','03 Jan 1999',583,'Failed') :- true)
"'mars lander':mars_lander/5\u0001000000000d" - ('mars lander':mars_lander(13,'Beagle 2','02 Jun 2003',33.2,'Failed') :- true)
"'mars lander':mars_lander/5\u0001000000000e" - ('mars lander':mars_lander(14,'Spirit rover','10 Jun 2003',174,'Success') :- true)
"'mars lander':mars_lander/5\u0001000000000f" - ('mars lander':mars_lander(15,'Opportunity rover','07 Jul 2003',174,'Success') :- true)
"'mars lander':mars_lander/5\u00010000000010" - ('mars lander':mars_lander(16,'Phoenix lander','04 Aug 2007',350,'Success') :- true)
"'mars lander':mars_lander/5\u00010000000011" - ('mars lander':mars_lander(17,'Curiosity rover','26 Nov 2011',899,'Operational') :- true)
"'mars lander':mars_lander/5\u00010000000012" - ('mars lander':mars_lander(18,'Schiaparelli EDM','14 Mar 2016',577,'Failed') :- true)
"'mars lander':mars_lander/5\u00010000000013" - ('mars lander':mars_lander(19,'InSight Mars Lander','5 May 2018',727,'Operational') :- true)
"'mars lander':mars_lander/5\u00010000000014" - ('mars lander':mars_lander(20,'Perseverance rover','30 Jul 2020',1025,'Operational') :- true)
"'mars lander':mars_lander/5\u00010000000015" - ('mars lander':mars_lander(21,'Tianwen-1','23 Jul 2020',240,'Operational') :- true)
"'mars lander':mars_lander/5\u0002last_clause"-21
"'mars lander':mars_lander_country_of_origin/2\u00010000000001" - ('mars lander':mars_lander_country_of_origin(1,'Soviet Union') :- true)
"'mars lander':mars_lander_country_of_origin/2\u00010000000002" - ('mars lander':mars_lander_country_of_origin(2,'Soviet Union') :- true)
"'mars lander':mars_lander_country_of_origin/2\u00010000000003" - ('mars lander':mars_lander_country_of_origin(3,'Soviet Union') :- true)
"'mars lander':mars_lander_country_of_origin/2\u00010000000004" - ('mars lander':mars_lander_country_of_origin(4,'Soviet Union') :- true)
"'mars lander':mars_lander_country_of_origin/2\u00010000000005" - ('mars lander':mars_lander_country_of_origin(5,'Soviet Union') :- true)
"'mars lander':mars_lander_country_of_origin/2\u00010000000006" - ('mars lander':mars_lander_country_of_origin(6,'USA') :- true)
"'mars lander':mars_lander_country_of_origin/2\u00010000000007" - ('mars lander':mars_lander_country_of_origin(7,'USA') :- true)
"'mars lander':mars_lander_country_of_origin/2\u00010000000008" - ('mars lander':mars_lander_country_of_origin(8,'Soviet Union') :- true)
"'mars lander':mars_lander_country_of_origin/2\u00010000000009" - ('mars lander':mars_lander_country_of_origin(9,'Soviet Union') :- true)
"'mars lander':mars_lander_country_of_origin/2\u0001000000000a" - ('mars lander':mars_lander_country_of_origin(10,'Russia') :- true)
"'mars lander':mars_lander_country_of_origin/2\u0001000000000b" - ('mars lander':mars_lander_country_of_origin(11,'USA') :- true)
"'mars lander':mars_lander_country_of_origin/2\u0001000000000c" - ('mars lander':mars_lander_country_of_origin(12,'USA') :- true)
"'mars lander':mars_lander_country_of_origin/2\u0001000000000d" - ('mars lander':mars_lander_country_of_origin(13,'United Kingdom') :- true)
"'mars lander':mars_lander_country_of_origin/2\u0001000000000e" - ('mars lander':mars_lander_country_of_origin(14,'USA') :- true)
"'mars lander':mars_lander_country_of_origin/2\u0001000000000f" - ('mars lander':mars_lander_country_of_origin(15,'USA') :- true)
"'mars lander':mars_lander_country_of_origin/2\u00010000000010" - ('mars lander':mars_lander_country_of_origin(16,'USA') :- true)
"'mars lander':mars_lander_country_of_origin/2\u00010000000011" - ('mars lander':mars_lander_country_of_origin(17,'USA') :- true)
"'mars lander':mars_lander_country_of_origin/2\u00010000000012" - ('mars lander':mars_lander_country_of_origin(18,'European Union ESA') :- true)
"'mars lander':mars_lander_country_of_origin/2\u00010000000013" - ('mars lander':mars_lander_country_of_origin(18,'Russia') :- true)
"'mars lander':mars_lander_country_of_origin/2\u00010000000014" - ('mars lander':mars_lander_country_of_origin(19,'USA') :- true)
"'mars lander':mars_lander_country_of_origin/2\u00010000000015" - ('mars lander':mars_lander_country_of_origin(20,'USA') :- true)
"'mars lander':mars_lander_country_of_origin/2\u00010000000016" - ('mars lander':mars_lander_country_of_origin(21,'China') :- true)
"'mars lander':mars_lander_country_of_origin/2\u0002last_clause"-22
"'mars lander':mars_lander_landing_location/4\u00010000000001" - ('mars lander':mars_lander_landing_location(2,'27 Nov 1971','45°S','47°E') :- true)
"'mars lander':mars_lander_landing_location/4\u00010000000002" - ('mars lander':mars_lander_landing_location(3,'02 Dec 1971','45°S','202°E') :- true)
"'mars lander':mars_lander_landing_location/4\u00010000000003" - ('mars lander':mars_lander_landing_location(4,'12 Mar 1974','23.90°S','19.4°W') :- true)
"'mars lander':mars_lander_landing_location/4\u00010000000004" - ('mars lander':mars_lander_landing_location(6,'20 Jul 1976','22.27°N','47.95°W') :- true)
"'mars lander':mars_lander_landing_location/4\u00010000000005" - ('mars lander':mars_lander_landing_location(7,'03 Sep 1976','47.64°N','225.71°W') :- true)
"'mars lander':mars_lander_landing_location/4\u00010000000006" - ('mars lander':mars_lander_landing_location(11,'04 Jul 1997','19.13°N','33.303°W') :- true)
"'mars lander':mars_lander_landing_location/4\u00010000000007" - ('mars lander':mars_lander_landing_location(12,'03 Dec 1999','76°S','195°W') :- true)
"'mars lander':mars_lander_landing_location/4\u00010000000008" - ('mars lander':mars_lander_landing_location(13,'25 Dec 2003','11.5265°N','90.4295°E') :- true)
"'mars lander':mars_lander_landing_location/4\u00010000000009" - ('mars lander':mars_lander_landing_location(14,'4 Jan 2004','14.5684°S','175.4726°E') :- true)
"'mars lander':mars_lander_landing_location/4\u0001000000000a" - ('mars lander':mars_lander_landing_location(15,'25 Jan 2004','1.9462°S','354.4743°E') :- true)
"'mars lander':mars_lander_landing_location/4\u0001000000000b" - ('mars lander':mars_lander_landing_location(16,'5 May 2008','68.22°N','125.7°W') :- true)
"'mars lander':mars_lander_landing_location/4\u0001000000000c" - ('mars lander':mars_lander_landing_location(17,'5 Aug 2012','4.5895°S','137.4417°E') :- true)
"'mars lander':mars_lander_landing_location/4\u0001000000000d" - ('mars lander':mars_lander_landing_location(18,'19 Oct 2016','2.052°S','6.208°W') :- true)
"'mars lander':mars_lander_landing_location/4\u0001000000000e" - ('mars lander':mars_lander_landing_location(19,'26 Nov 2018','4.5°N','135.9°E') :- true)
"'mars lander':mars_lander_landing_location/4\u0001000000000f" - ('mars lander':mars_lander_landing_location(20,'18 Feb 2021','18.4447°N','77.4508°E') :- true)
"'mars lander':mars_lander_landing_location/4\u00010000000010" - ('mars lander':mars_lander_landing_location(21,'14 May 2021','25.1°N','109.7°E') :- true)
"'mars lander':mars_lander_landing_location/4\u0002last_clause"-16
"'mars lander':mars_lander_region/2\u00010000000001" - ('mars lander':mars_lander_region(3,'Sirenum Terra') :- true)
"'mars lander':mars_lander_region/2\u00010000000002" - ('mars lander':mars_lander_region(4,'Margaritifer Terra') :- true)
"'mars lander':mars_lander_region/2\u00010000000003" - ('mars lander':mars_lander_region(6,'Chryse Planitia') :- true)
"'mars lander':mars_lander_region/2\u00010000000004" - ('mars lander':mars_lander_region(7,'Utopia Planitia') :- true)
"'mars lander':mars_lander_region/2\u00010000000005" - ('mars lander':mars_lander_region(11,'Ares Vallis') :- true)
"'mars lander':mars_lander_region/2\u00010000000006" - ('mars lander':mars_lander_region(12,'Ultimi Scopuli') :- true)
"'mars lander':mars_lander_region/2\u00010000000007" - ('mars lander':mars_lander_region(13,'Isidis Planitia') :- true)
"'mars lander':mars_lander_region/2\u00010000000008" - ('mars lander':mars_lander_region(14,'Gusev Crater') :- true)
"'mars lander':mars_lander_region/2\u00010000000009" - ('mars lander':mars_lander_region(15,'Meridiani Planum') :- true)
"'mars lander':mars_lander_region/2\u0001000000000a" - ('mars lander':mars_lander_region(16,'Vastitas Borealis') :- true)
"'mars lander':mars_lander_region/2\u0001000000000b" - ('mars lander':mars_lander_region(17,'Gale Crater') :- true)
"'mars lander':mars_lander_region/2\u0001000000000c" - ('mars lander':mars_lander_region(18,'Meridiani Planum') :- true)
"'mars lander':mars_lander_region/2\u0001000000000d" - ('mars lander':mars_lander_region(19,'Elysium Planitia') :- true)
"'mars lander':mars_lander_region/2\u0001000000000e" - ('mars lander':mars_lander_region(20,'Jezero crater') :- true)
"'mars lander':mars_lander_region/2\u0001000000000f" - ('mars lander':mars_lander_region(21,'Utopia Planitia') :- true)
"'mars lander':mars_lander_region/2\u0002last_clause"-15
"'mars lander':mars_lander_region/2\u00032"-true
"'mars lander':mars_lander_region/2\u00042\u0002165d3c64\u0001'mars lander':mars_lander_region/2\u00010000000001" - 1
"'mars lander':mars_lander_region/2\u00042\u00021aa6aab5\u0001'mars lander':mars_lander_region/2\u00010000000002" - 1
"'mars lander':mars_lander_region/2\u00042\u000239c267d\u0001'mars lander':mars_lander_region/2\u00010000000003" - 1
"'mars lander':mars_lander_region/2\u00042\u000245e1ad78\u0001'mars lander':mars_lander_region/2\u0001000000000b" - 1
"'mars lander':mars_lander_region/2\u00042\u00024be1567f\u0001'mars lander':mars_lander_region/2\u00010000000007" - 1
"'mars lander':mars_lander_region/2\u00042\u0002627c5716\u0001'mars lander':mars_lander_region/2\u0001000000000a" - 1
"'mars lander':mars_lander_region/2\u00042\u00026ebff6ae\u0001'mars lander':mars_lander_region/2\u00010000000009" - 1
"'mars lander':mars_lander_region/2\u00042\u00026ebff6ae\u0001'mars lander':mars_lander_region/2\u0001000000000c" - 1
"'mars lander':mars_lander_region/2\u00042\u000271aa4f08\u0001'mars lander':mars_lander_region/2\u00010000000006" - 1
"'mars lander':mars_lander_region/2\u00042\u00027278a637\u0001'mars lander':mars_lander_region/2\u00010000000008" - 1
"'mars lander':mars_lander_region/2\u00042\u000275b2f341\u0001'mars lander':mars_lander_region/2\u0001000000000d" - 1
"'mars lander':mars_lander_region/2\u00042\u0002762a697a\u0001'mars lander':mars_lander_region/2\u00010000000005" - 1
"'mars lander':mars_lander_region/2\u00042\u000276591783\u0001'mars lander':mars_lander_region/2\u00010000000004" - 1
"'mars lander':mars_lander_region/2\u00042\u000276591783\u0001'mars lander':mars_lander_region/2\u0001000000000f" - 1
"'mars lander':mars_lander_region/2\u00042\u000277a55418\u0001'mars lander':mars_lander_region/2\u0001000000000e" - 1
"'mars lander':mars_sublander/6\u00010000000001" - ('mars lander':mars_sublander(1,'Prop-M','Rover','19 May 1971',4.5,'Failed') :- true)
"'mars lander':mars_sublander/6\u00010000000002" - ('mars lander':mars_sublander(2,'Prop-M','Rover','28 May 1971',4.5,'Not deployed') :- true)
"'mars lander':mars_sublander/6\u00010000000003" - ('mars lander':mars_sublander(3,'Mars 96','Penetrator','16 Nov 1996',88,'Failed') :- true)
"'mars lander':mars_sublander/6\u00010000000004" - ('mars lander':mars_sublander(4,'Deep Space 2','Penetrator','03 Jan 1999',2.4,'Failed') :- true)
"'mars lander':mars_sublander/6\u00010000000005" - ('mars lander':mars_sublander(5,'Sojourner','Rover','04 Dec 1996',11.5,'Success') :- true)
"'mars lander':mars_sublander/6\u00010000000006" - ('mars lander':mars_sublander(6,'Mars helicopter Ingenuity','UAV Helicopter','30 Jul 2021',1.8,'Success') :- true)
"'mars lander':mars_sublander/6\u00010000000007" - ('mars lander':mars_sublander(7,'Zhurong','Rover','23 Jul 2021',240,'Success') :- true)
"'mars lander':mars_sublander/6\u00010000000008" - ('mars lander':mars_sublander(8,'Tianwen-1 Remote camera','Camera','1 June 2021','<1','Success') :- true)
"'mars lander':mars_sublander/6\u0002last_clause"-8
"meta\u0001'mars lander':carrier_mars_sublander/3"-true
"meta\u0001'mars lander':mars_lander/5"-true
"meta\u0001'mars lander':mars_lander_country_of_origin/2"-true
"meta\u0001'mars lander':mars_lander_landing_location/4"-true
"meta\u0001'mars lander':mars_lander_region/2"-true
"meta\u0001'mars lander':mars_sublander/6"-true

Essence of predicate that generated above output:

findall(
	item(Key-Value),
	(
		rocks_preds:default_db(Dir),
		rocks_preds:rdb_open(Dir,DB),
		rocksdb:rocks_enum(DB,Key,Value)
	),
	Items
)

Also, about database - tools, in the current Angular Client implementation ( compile typescript to javascript )

you still cant even start for example with a simple text-tool-database ( it doesnt exist ) ( module ) locally for developping,

so that later on you would have only to change 1 switch to let it work with postgress or any other database, so lets say a database Abstraction layer. As far as i know this still doesnt exist.

I’ll try to document all of this when I return to working on rocks_preds … my first priority right now is fixing SWI-cpp.h, and I’ve been distracted by working on some non-Prolog things. Sorry for the delay.

No rush, really. Thanks for all your effort.

One of the main reasons for asking was to give the community a chance to give input. Also as we know sometimes others have an insight and see things different than we do and come up with a more intuitive name.

As I always note, it is more important to get it correct than fast. :slightly_smiling_face: