How to connect to MongoDB server via swi prolog?

I’m want to implement a API beetween Prolog and MongoDB and after some research the first hurdle would be connecting to MongoDB server. I know there’s allready the API prolongo so I tried to understand it, but I don’t. I’m a newbie in Prolog so my question is: Where is the actuall connect to the MongoDB server? (I’ve asked this question also on StackOverflow, but nobody seems to know.)

So here’s the code of the mongo_connection.pl:

:- module(_, [
    host/1,
    port/1
]).

:- include(include/common).

%%  host(?Host) is semidet.
%
%   True if Host is the default hostname used by MongoDB.

host(localhost).

%%  port(?Port) is semidet.
%
%   True if Port is the default port used by MongoDB.

port(27017).

/** <module> Connection handling and response parsing.
 */

:- module(_, [
    new_connection/1,
    new_connection/3,
    free_connection/1,
    get_database/3,
    send_to_server/2,
    read_reply/4
]).

:- include(include/common).

%%  new_connection(-Connection) is det.
%%  new_connection(+Host, +Port, -Connection) is det.
%
%   True if Connection represents an opaque handle to a new MongoDB
%   server connection. Default values (see mongo_defaults) are used unless
%   Host and Port are provided.

new_connection(Connection) :-
    mongo_defaults:host(Host),
    mongo_defaults:port(Port),
    new_connection(Host, Port, Connection).

new_connection(Host, Port, Connection) :-
    mongo_socket:new_socket(Host, Port, Socket),
    Connection = connection(Socket).

connection_socket(Connection, Socket) :-
    mongo_util:get_nth1_arg(Connection, 1, Socket).

%%  free_connection(+Connection) is det.
%
%   Frees any resources associated with the Connection handle,
%   rendering it unusable.

free_connection(Connection) :-
    connection_socket(Connection, Socket),
    mongo_socket:free_socket(Socket).

%%  get_database(+Connection, +DatabaseName, -Database) is det.
%
%   True if Database is a handle to the database called DatabaseName
%   on Connection. No communication is performed, so the actual database
%   might or might not already exist.

get_database(Connection, DatabaseName, Database) :-
    mongo_database:new_database(Connection, DatabaseName, Database).

%%  send_to_server(+Connection, +Bytes) is det.
%
%   True if Bytes are sent over Connection.

send_to_server(Connection, Bytes) :-
    connection_socket(Connection, Socket),
    mongo_socket:send_bytes(Socket, Bytes).

%%  read_reply(+Connection, -Header, -Info, -Docs) is det.
%
%   True if Header, Info and Docs together represent the next message
%   received over Connection. Blocks until a message is completely read.
%
%   Header is the structure header(MessageLength,RequestId,ResponseTo,OpCode)
%   where:
%   - MessageLength is the total number of bytes comprising the message
%   - RequestId is the ID of this message (unused)
%   - ResponseTo is the ID of the query that triggered this response (unused)
%   - OpCode is the code signifying that this is a response (always 1)
%
%   Info is the structure info(Flags,CursorId,StartingFrom,NumberReturned)
%   where:
%   - Flags is the bitmask of flags set for this response
%   - CursorId is the cursor ID of this response
%   - StartingFrom is the query offset of the first document in Docs
%   - NumberReturned is the number of documents in Docs

read_reply(Connection, Header, Info, Docs) :-
    connection_socket(Connection, Socket),
    read_response_bytes(Socket, Bytes),
    parse_response(Bytes, Header, Info, Docs).

read_response_bytes(Socket, [B0,B1,B2,B3|Bytes]) :-
    read_message_length(Socket, [B0,B1,B2,B3], TotalLength),
    read_rest_of_message(Socket, TotalLength, Bytes).

read_message_length(Socket, Bytes, Length) :-
    mongo_socket:receive_n_bytes(Socket, 4, Bytes),
    bson_bits:integer_bytes(Length, 4, little, Bytes).

read_rest_of_message(Socket, TotalLength, Bytes) :-
    LengthRest is TotalLength - 4,
    mongo_socket:receive_n_bytes(Socket, LengthRest, Bytes).

parse_response(Bytes, Header, Info, Docs) :-
    % inspect_response_bytes(Bytes), % For debugging.
    phrase(parse_response_meta(Header, Info), Bytes, RestBytes),
    parse_response_docs(RestBytes, Docs).

parse_response_meta(Header, Info) -->
    parse_response_header(Header),
    parse_response_info(Info).

parse_response_header(Header) -->
    { Header = header(MessageLength,RequestId,ResponseTo,OpCode) },
    mongo_bytes:int32(MessageLength),
    mongo_bytes:int32(RequestId),
    mongo_bytes:int32(ResponseTo),
    mongo_bytes:int32(OpCode).

parse_response_info(Info) -->
    { Info = info(Flags,CursorId,StartingFrom,NumberReturned) },
    mongo_bytes:int32(Flags),
    mongo_bytes:int64(CursorId),
    mongo_bytes:int32(StartingFrom),
    mongo_bytes:int32(NumberReturned).

parse_response_docs(Bytes, Docs) :-
    bson:docs_bytes(Docs, Bytes).

ODBC is how SWI Prolog connects to whatever database. I’ve just gone through this process once again for linking SWI Prolog to Postgresql, and it can be fiddly.

Firstly, you need to check this at the swipl command line:

use_module(library(odbc)).

If you get ERROR: source_sink `library(odbc)’ does not exist (as I just did on a fresh Arch Linux installation), the driver needed by SWI Prolog is missing. On a Linux system, that means you need to install unixodbc. I build SWI Prolog from source, and ODBC is automatically included if a supported ODBC library is in the system.

The next part is installing ODBC support for your database (Google found https://www.mongodb.com/blog/post/odbc-driver-for-the-mongodb-connector-for-business-intelligence, but I don’t have experience with MongoDB).

Then you need to configure ODBC, which on Linux involves creating a ~/.odbc.ini file. I don’t use Windows, so don’t know how to do it there. I’ve written some notes on how I got Postgresql and SWI Prolog to communicate at swipl-webapp-howto/unit3 at master · roblaing/swipl-webapp-howto · GitHub

How could ODBC be used for MongoDB? I thought ODBC was heavily geared toward the relational database paradigm. I don’t think this is even possible, but I’d like hear if anyone has tried to make an ODBC driver for MongoDB.

I don’t use plain vanilla SQL to communicate to Postgres via ODBC, and it doesn’t seem to matter. All parsing of the sent query seems to be done by the receiving database, and ODBC just simplifies the palaver of sending and receiving text via a port.

Though I’ve no experience with MongoDB, my guess is as long as the query sent in whatever language it uses is understood on the receiving end, SWI Prolog will receive whatever it sends back. But I’ve no idea if the row(Arg1, Arg2, ...) with some type conversion will work.