Postgres 2 MongoDB database link¶
Postgres foreign data wrapper for MongoDB database
This is part 4 of #fdw foreign data wrappers series of posts. Earlier we have introduced wrappers for mysql, oracle and sqlite databases. In this post we will connect to MongoDB database from within Postgres.
mongo_fdw¶
There are no binaries are available on official GitHub repository for this extension.
You have to manually compile and install it.
Compilation procedure is not straightforward. You will have to install a set of packages. Luckily, there is an autogen.sh
script is present which simplifies a lot of things. Nevertheless you still have to install several additional utilities such as make
, cmake
, pkg-config
.
When done with installation you will be able to setup it already within postgres database.
Process is standard: execute CREATE EXTENSION
command from within your postgres database.
Afterwards create foreign server via CREATE SERVER
command which specifies connection parameters to the MongoDB.
Docker image¶
Similarly to all other databases there is a postgres image available with mongo_fdw
extension installed.
You can pick it up on docker hub.
I was unable to manage compilation under Postgres' alpine image and had to use full size image latest
tag.
Compiled library version resulted in +20 Mb to the 322 Mb of postgres uncompressed image size.
Section below describes how to use the image.
Demo¶
In order to run commands below you should download or clone GitHub repo. It also contains readme file with project layout and files descriptions.
Demo infrastructure is spinned up via compose files. For our case we need two instances. One for MongoDB and one for Postgres.
Inside mongo
instance we will create a single table and populate it with 3 rows.
From postgres
instance we will select, insert and delete rows from this table.
After that we will select from this table under mongo
instance again to confirm that the data has been changed accordingly.
Note: If you use docker
then just replace podman
with docker
in all commands below.
Let's start mongo
and postgres
instances.
$ podman-compose -f postgres_mongo_compose.yml up -d
$ podman-compose -f postgres_mongo_compose.yml ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b87c08fce4c6 docker.io/chumaky/postgres_mongo_fdw:latest postgres 4 minutes ago Up 4 minutes ago 0.0.0.0:5432->5432/tcp, 0.0.0.0:27017->27017/tcp postgres
5c504fc23b93 docker.io/library/mongo:latest mongod 4 minutes ago Up 4 minutes ago 0.0.0.0:5432->5432/tcp, 0.0.0.0:27017->27017/tcp mongo
By default mongo starts without authorization. I.e. everyone could connect.
We initialize mongo with root/root
superuser role with admin
authorization database to enable usual user/password authorization approach.
Within postgres
database we automatically create mongo_fdw
extension and map it to the corresponding mongo
host.
We also create foreign table users
with the following structure.
Initial _id
attribute is required for MongoDB specific object id which is automatically assigned for every object.
You could check postgres_mongo_compose.yml
and sql/postgres_mongo_setup.sql
files for details.
Now let's connect to the mongo instance.
$ podman exec -it mongo mongo -u root -p root
MongoDB shell version v4.4.2
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("ba7b798c-ab5e-4de9-826c-69259700cf41") }
MongoDB server version: 4.4.2
Welcome to the MongoDB shell.
...
>
By default user connects to the test
database. We can check that we currently use this database via the following command.
db
is a special object which specifies current database user is connected to.
We already have users
foreign table defined in postgres. In mongo instead of a table we should create a collection.
Database and collection creation in mongo is implemented by "at first usage" approach.
If referenced object doesn't exist it gets created automatically.
Following command will automatically create in the current database new users
collection and insert into it an object.
> db.users.insertOne({id: 1, name: "John"})
{
"acknowledged" : true,
"insertedId" : ObjectId("5fee189c9b775b31e29362ea")
}
> db.users.find()
{ "_id" : ObjectId("5fee189c9b775b31e29362ea"), "id" : 1, "name" : "John" }
We can create many objects in one operation
> db.users.insertMany([{id: 2, name: "Sarah"}, {id: 3, name: "Terminator"}])
{
"acknowledged" : true,
"insertedIds" : [
ObjectId("5fee18e19b775b31e29362eb"),
ObjectId("5fee18e19b775b31e29362ec")
]
}
> db.users.find()
{ "_id" : ObjectId("5fee189c9b775b31e29362ea"), "id" : 1, "name" : "John" }
{ "_id" : ObjectId("5fee18e19b775b31e29362eb"), "id" : 2, "name" : "Sarah" }
{ "_id" : ObjectId("5fee18e19b775b31e29362ec"), "id" : 3, "name" : "Terminator" }
Now let's connect to postgres
instance.
We can check that we already have mongo_fdw
extension installed.
We also have mongo
foreign server and users
foreign table defined.
postgres=> \dx mongo_fdw
List of installed extensions
Name | Version | Schema | Description
-----------+---------+-----------+-----------------------------------------
mongo_fdw | 1.1 | mongo_fdw | foreign data wrapper for MongoDB access
postgres=> \des
List of foreign servers
Name | Owner | Foreign-data wrapper
-------+----------+----------------------
mongo | postgres | mongo_fdw
postgres=> \det
List of foreign tables
Schema | Table | Server
--------+-------+--------
public | users | mongo
postgres=> \d users
Foreign table "public.users"
Column | Type | Collation | Nullable | Default | FDW options
--------+---------+-----------+----------+---------+-------------
_id | name | | | |
id | integer | | | |
name | text | | | |
Server: mongo
Let's check SELECT
and UPDATE
operations against users
table.
postgres=> select * from users;
_id | id | name
--------------------------+----+------------
5fee189c9b775b31e29362ea | 0 | John
5fee18e19b775b31e29362eb | 0 | Sarah
5fee18e19b775b31e29362ec | 0 | Terminator
(3 rows)
id
attribute is displayed as 0
while in mongo it actually has a value.
Probably there is a bug with INTEGER
attributes mapping. Anyway, let's try to update them.
postgres=> update users set id = 1, name = 'John Connor' where name = 'John';
UPDATE 1
postgres=> update users set id = 2, name = 'Sarah Connor' where name = 'Sarah';
UPDATE 1
postgres=> select * from users;
_id | id | name
--------------------------+----+--------------
5fee189c9b775b31e29362ea | 1 | John Connor
5fee18e19b775b31e29362eb | 2 | Sarah Connor
5fee18e19b775b31e29362ec | 0 | Terminator
(3 rows)
We could not only select and change existing data but also INSERT
and DELETE
.
postgres=> delete from users where name = 'Terminator';
DELETE 1
postgres=> insert into users (id, name) values (3, 'James Cameron');
INSERT 0 1
postgres=> select * from users;
_id | id | name
--------------------------+----+---------------
5fee189c9b775b31e29362ea | 1 | John Connor
5fee18e19b775b31e29362eb | 2 | Sarah Connor
5fee1c83abed8c006c124982 | 3 | James Cameron
(3 rows)
Now go back to mongo console and check that the data have been changed accordingly.
> db.users.find()
{ "_id" : ObjectId("5fee189c9b775b31e29362ea"), "id" : 1, "name" : "John Connor" }
{ "_id" : ObjectId("5fee18e19b775b31e29362eb"), "id" : 2, "name" : "Sarah Connor" }
{ "_id" : ObjectId("5fee1c83abed8c006c124982"), "id" : 3, "name" : "James Cameron" }
As you can see we have full SQL
and DML
support over MongoDB right from the Postgres!
Summary¶
mongo_fdw
extension allows SELECT
and DML
operations to be triggered from the Postgres and applied to the MongoDB database.
For quickier setup there is a chumaky/postgres_mongo_fdw
docker image is available on the docker hub.
It contains mongo_fdw
extension pre-installed.
Links¶
- postgres_mongo_fdw docker image with
mongo_fdw
extension installed - repo with docker files
- mongo_fdw extension official repo