Приложения Delphi в Linux и доступ к базам данным MongoDB

by May 25, 2017

Для этого примера я буду использовать установленный на моей VirtualBox VM с Ubuntu Server 16.04 отдельный сервер MongoDB v3.4. Как установить и настроить сервер MongoDB в Ubuntu Linux подробно описано в статье Установка InterBase и MongoDB в Linux (Ubuntu server) в этом блоге. Запуск демона MongoDB выполняется командой 

Welcome to Ubuntu 16.04.2 LTS (GNU/Linux 4.4.0-78-generic x86_64)
 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage
Могут быть обновлены 0 пакетов.
0 обновлений касаются безопасности системы.
 
asovtsov@ubuntu16srv:~$ sudo service mongod start
[sudo] пароль для asovtsov:
asovtsov@ubuntu16srv:~$

Кстати, для удобства переноса модулей в Ubuntu я пользуюсь утилитой WinSCP и PuTTY-терминалом для SSH-соединения с Linux.

В этом примере мы будем работать с базой данных из примеров работы с MongoDB в FireDAC , которые поставляются вместе с RAD Studio или Delphi 10.2. Это база данных (database) 'test' и коллекция 'restaurants', которая содержит более 25000 записей о Нью-Йоркских ресторанах, с привязкой по районам. Если ваш MongoDB на Linux еще не содержит такой базы данных, ее можно создать и заполнить прямо из Windows – открыть пример "C:UsersPublicDocumentsEmbarcaderoStudio19.0SamplesObject PascalDatabaseFireDACSamplesDBMS SpecificMongoDBRestaurantsMongo_Restaurants.dproj", собрать и запустить его. Первая кнопка 'LoadData' создаст и заполнит нужную БД.

Для примера ограничимся только двумя запросами, зато интересными:

  • 'Count of restaurants grouped by borough' – подсчет количества ресторанов  в каждом районе
  • 'Count of Brazilian restaurants in Queens grouped by zip-code' – Подсчет количества ресторанов с бразильской кухней в районе Queens, с группировкой по почтовому индексу

Главный модуль приложения выглядит так:

program linuxMongo;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  uLinuxMongoDM in 'uLinuxMongoDM.pas' {fdm: TDataModule};

begin
  try
    { TODO -oUser -cConsole Main : Insert code here }
    AggregateData();
    Readln;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end. 

Вся магия работы с данными заключена в вызове процедуры AggregateData(); которая описана в стандартном TDatamodule в модуле uLinuxMongoDM.

Датамодуль устроен тоже достаточно просто, однако есть специфика работы с MongoDB.

unit uLinuxMongoDM;

interface

uses
  System.SysUtils, System.Classes, FireDAC.Stan.Intf, FireDAC.Stan.Option,
  FireDAC.Stan.Param, FireDAC.Stan.Error, FireDAC.DatS, FireDAC.Phys.Intf,
  FireDAC.DApt.Intf, Data.DB, FireDAC.Comp.DataSet, FireDAC.Comp.Client,
  FireDAC.UI.Intf, FireDAC.Stan.Def, FireDAC.Stan.Pool, FireDAC.Stan.Async,
  FireDAC.Phys, FireDAC.Phys.MongoDB, FireDAC.Phys.MongoDBDef, System.Rtti,
  System.JSON.Types, System.JSON.Readers, System.JSON.BSON,
  System.JSON.Builders, FireDAC.Phys.MongoDBWrapper, FireDAC.ConsoleUI.Wait;

const
  DBsrvAddr = '192.168.56.101';

type
  Tfdm = class(TDataModule)
    FDConnection1: TFDConnection;
    FDPhysMongoDriverLink1: TFDPhysMongoDriverLink;
    procedure DataModuleCreate(Sender: TObject);
  private
    { Private declarations }
    FEnv: TMongoEnv;
    FCon: TMongoConnection;
    procedure DumpCursor(ACrs: IMongoCursor; caption: string);
  public
    { Public declarations }
    procedure DataGetAggregates();
  end;

var
  fdm: Tfdm;

procedure AggregateData();

implementation

{%CLASSGROUP 'System.Classes.TPersistent'}
{$R *.dfm}
uses
  System.Diagnostics,
  FireDAC.Stan.Util;

procedure AggregateData();
var
  DataModule1: Tfdm;
begin
  DataModule1 := Tfdm.Create(nil);
  DataModule1.DataGetAggregates;
  DataModule1.Free;
end;

procedure Tfdm.DataGetAggregates;
var
  oCrs: IMongoCursor;
begin
  oCrs := FCon['test']['restaurants'].Aggregate().Group.Add('_id', '$borough')
    .BeginObject('count').Add('$sum', 1).EndObject.&End;
  DumpCursor(oCrs, 'Count of restaurants grouped by borough');

  oCrs := FCon['test']['restaurants'].Aggregate().Match.Add('borough', 'Queens')
    .Add('cuisine', 'Brazilian').&End.Group.Add('_id', '$address.zipcode')
    .BeginObject('count').Add('$sum', 1).EndObject.&End;
  DumpCursor(oCrs,'Count of Brazilian restaurants in Queens grouped by zip-code');
end;

procedure Tfdm.DataModuleCreate(Sender: TObject);
begin
  with FDConnection1.Params do
  begin
    Clear;
    Add('DriverID=Mongo');
    Add('Server='+DBsrvAddr);
  end;
  FDConnection1.Connected := True;
  FCon := TMongoConnection(FDConnection1.CliObj);
  FEnv := FCon.Env;
end;

procedure Tfdm.DumpCursor(ACrs: IMongoCursor; caption: string);
begin
  Writeln('');
  Writeln(caption);
  while ACrs.Next do
    Writeln(ACrs.Doc.AsJSON);
end;

end.

Вызываемая процедура AggregateData() управляет созданием и уничтожением объекта датамодуля. Обработчик его события OnCreate обеспечивает задание актуальных параметров соединения с сервером MongoDB, создает и инициализирует необходимые объекты (соединение и контекст) для работы с запросами и курсорами Mongodb. Сами запросы задаются и выполняются в процедуре Tfdm.DataGetAggregates, а процедура Tfdm.DumpCursor(ACrs: IMongoCursor; caption: string) обеспечивает выдачу заголовка запроса и полученных в результате его исполнения данных.

За исключением операторов консольной выдачи, исходный текст этого модуля не содержит изменений по сравнению с аналогичным для платформы Windows – фактически, это один и тот же код.

Укажем Linux64, как целевую платформу, и можно компилировать.

Чтобы Delphi автоматически переносил и запускал скомпилированный двоичный модуль, нужно в окне терминала Linux запустить PAServer, необходимый, кроме того, для выполнения отладки программ на Linux.

asovtsov@ubuntu16srv:~$ ~/PAServer-19.0/paserver
Platform Assistant Server  Version 10.0.1.23
Copyright (c) 2009-2017 Embarcadero Technologies, Inc.

Connection Profile password :
Starting Platform Assistant Server on port 64211
Type ? for available commands
>

Если теперь собрать приложение и запустить его в Linux, то получим ошибку! Необходимо установить и настроить клиентские драйвера и библиотеки MongoDB.

В этом месте я совершил небольшую ошибку. Чтобы не повторять ее, рекомендую следовать указаниям документации: http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Connect_to_MongoDB_Database_(FireDAC), раздел Client Software.

Дело в том, что работа FireDAC пока настроена на работу с драйвером mongoc от MongoDB версии 3.0. В состав дистрибутива Ubuntu 16.04 этот драйвер уже был включен. Моя ошибка заключалась в том, что я скачал, собрал и установил более современную версию драйвера 1.6.3. Переход на эту версию драйвера будет сделан в ближайшем обновлении (см. опубликованный RoadMap по RAD Studio). Пришлось "откатываться" на требуемый вариант.

Следуйте указанным в  документации инструкциям и выполните в окне терминала Linux:

sudo apt-get install libmongoc-1.0-0
sudo ln -s /usr/lib/x86_64-linux-gnu/libmongoc-1.0.so.0.0 /usr/lib/x86_64-linux-gnu/libmongoc-1.0.so
sudo ln -s /usr/lib/x86_64-linux-gnu/libbson-1.0.so.0.0 /usr/lib/x86_64-linux-gnu/libbson-1.0.so

Вот теперь можно запускать проект на выполнение!