如何通过 swi prolog 连接到 MongoDB 服务器?

How to connect to MongoDB server via swi prolog?

我想在 Prolog 和 MongoDB 之间实现一个 API,经过一些研究,第一个障碍是连接到 MongoDB 服务器。我知道已经有 API prolongo 所以我试图理解它,但我没有。我是 Prolog 的新手,所以我的问题是:实际连接到 MongoDB 服务器的位置在哪里?

已编辑: 我已经理解了更多的代码,但我遇到了一个我无法解释的错误:

所以这是 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).

这里是mongo_socket.pl的代码:

:- module(_, [
    new_socket/3,
    free_socket/1,
    send_bytes/2,
    receive_n_bytes/3
]).

:- include(include/common).

%%  new_socket(+Host, +Port, -Socket) is det.
%
%   True if Socket is a new TCP socket connected to Host:Port.
%
%   @throws mongo_error(Description, [SocketException])

new_socket(Host, Port, Socket) :-
    setup_call_catcher_cleanup(
        socket:tcp_socket(SocketId),
        socket:tcp_connect(SocketId, Host:Port, ReadStream, WriteStream),
        exception(SocketException),
        close_socket_and_throw(SocketId, SocketException)),
    Socket = socket(ReadStream,WriteStream).

close_socket_and_throw(SocketId, Exception) :-
    socket:tcp_close_socket(SocketId),
    throw(mongo_error('could not connect to server', [Exception])).

socket_read(Socket, ReadStream) :-
    mongo_util:get_nth1_arg(Socket, 1, ReadStream).

socket_write(Socket, WriteStream) :-
    mongo_util:get_nth1_arg(Socket, 2, WriteStream).

%%  free_socket(+Socket) is det.
%
%   Frees any resources associated with Socket, rendering it unusable.

free_socket(Socket) :-
    socket_read(Socket, ReadStream),
    socket_write(Socket, WriteStream),
    core:close(ReadStream, [force(true)]),
    core:close(WriteStream, [force(true)]).

%%  send_bytes(+Socket, +Bytes) is det.
%
%   True if Bytes are sent (and flushed) over Socket.

send_bytes(Socket, Bytes) :-
    socket_write(Socket, WriteStream),
    send_bytes_and_flush(Bytes, WriteStream).

send_bytes_and_flush(Bytes, WriteStream) :-
    core:format(WriteStream, '~s', [Bytes]),
    core:flush_output(WriteStream).

%%  receive_n_bytes(+Socket, +N, -Bytes) is det.
%
%   True if Bytes is the next N bytes received over Socket.

receive_n_bytes(Socket, N, Bytes) :-
    socket_read(Socket, ReadStream),
    receive_n_bytes_aux(ReadStream, N, Bytes).

receive_n_bytes_aux(_ReadStream, 0, []) :- !.
receive_n_bytes_aux(ReadStream, N, [Byte|Bytes]) :-
    core:get_byte(ReadStream, Byte),
    N1 is N - 1,
    receive_n_bytes_aux(ReadStream, N1, Bytes).

所以用线

socket:tcp_socket(SocketId),
socket:tcp_connect(SocketId, Host:Port, ReadStream, WriteStream),..

Prolog 创建一个 INET-domain stream-socket。我正在使用 MS-Windows,如果套接字库尚未初始化,这也会初始化库。

但是我收到这个错误:

所以这是我的问题:

  1. 我是否尝试与错误的文件建立新连接?
  2. 应该自动加载套接字库,还是我必须将 prolongo 文件放入路径“C:\Program Files\swipl\library”?
  3. 如何解释上面这个错误?

ODBC 是 SWI Prolog 连接到任何数据库的方式。我刚刚再次完成了将 SWI Prolog 链接到 Postgresql 的过程,它可能很繁琐。

首先,您需要在 swipl 命令行检查一下:

use_module(library(odbc)).

如果你得到 错误:source_sink `library(odbc)' 不存在(就像我刚刚在新的 Arch Linux 安装上所做的那样) ,缺少 SWI Prolog 所需的驱动程序。在 Linux 系统上,这意味着您需要安装 unixodbc。我从源代码构建 SWI Prolog,如果系统中有支持的 ODBC 库,ODBC 会自动包含。

下一部分是为您的数据库安装 ODBC 支持(Google 发现 https://www.mongodb.com/blog/post/odbc-driver-for-the-mongodb-connector-for-business-intelligence,但我没有使用 MongoDB 的经验)。

然后您需要配置 ODBC,这在 Linux 上涉及创建一个 ~/.odbc.ini 文件。我不使用 Windows,所以不知道在那里该怎么做。我写了一些关于如何让 Postgresql 和 SWI Prolog 在 https://github.com/roblaing/swipl-webapp-howto/tree/master/unit3

进行通信的笔记