RethinkDB

Let's see the differences for a non-relational database using RethinkDB. If you don't have it, just install it by following the official documentation (https://www.rethinkdb.com/docs/). Let's just start the server:

rethinkdb

This will start the server, which comes with a very nice administration console on port 8080. You can open it in the web browser:

Go to the Tables section on top to see the databases:

Create a database called imagini using the Add Database button. You should now have our database ready. You need nothing else here:

To use our new database, we need to install the rethinkdb dependency. You can remove the MySQL dependency:

npm uninstall mysql --save
npm install rethinkdb -–save

Now, let's change our settings file. This module doesn't accept a connection string, so we'll use a JSON structure:

{
"db": {
"host" : "localhost",
"db" : "imagini"
}
}

To include our dependency, we just need to include the module:

const rethinkdb = require("rethinkdb");

Then, use this to connect to our server:

rethinkdb.connect(settings.db, (err, db) => {
if (err) throw err;

console.log("db: ready");

// ...
// the rest of our service code
// ...

app.listen(3000, () => {
console.log("app: ready");
});
});

After connecting, we can create our table as we did before. This time, we don't need to specify any structure:

rethinkdb.tableCreate("images").run(db);

The rethinkdb object is the one we'll use to manipulate our table, and the db object is a connection object used to reference the connection and to indicate where to run our manipulations.

If you restart our service just like this, you'll see a new table on our previously created database:

If you restart our service again, you'll get an error trying to create the table that already exists. We need to check whether it already exists, and only issue the command if not:

rethinkdb.tableList().run(db, (err, tables) => {
if (err) throw err;

if (!tables.includes("images")) {
rethinkdb.tableCreate("images").run(db);
}
});

Moving on, our upload method should be changed slightly to something like the following:

app.post("/uploads/:name", bodyparser.raw({
limit : "10mb",
type : "image/*"
}), (req, res) => {
rethinkdb.table("images").insert({
name : req.params.name,
size : req.body.length,
data : req.body,
}).run(db, (err) => {
if (err) {
return res.send({ status : "error", code: err.code });
}

res.send({ status : "ok", size: req.body.length });
});
});

If you restart the server just like this, you should be able to upload an image:

We receive the same response, just like with MySQL. We can go to the Data Explorer section in the administration console and get our record to see whether it's there:

Looks good. Notice our record ID is not a number, it's a Universally Unique Identifier (UUID). This is because RethinkDB has support for sharding (our table is sharded by default if there was more than one server) and it's easier to shard unique identifiers than an incremental number.

Moving on to our Express parameter:

app.param("image", (req, res, next, image) => {
if (!image.match(/.(png|jpg)$/i)) {
return res.status(403).end();
}

rethinkdb.table("images").filter({
name : image
}).limit(1).run(db, (err, images) => {
if (err) return res.status(404).end();

images.toArray((err, images) => {
if (err) return res.status(500).end();
if (!images.length) return res.status(404).end();

req.image = images[0];

return next();
});
});
});

With this change, we can now restart our service and see whether our image exists:

We need to change the download just a little bit. We need to remove the previous query to update our usage date and replace it with a new one:

app.get("/uploads/:image", (req, res) => {
let image = sharp(req.image.data);
let width = +req.query.width;
let height = +req.query.height;
let blur = +req.query.blur;
let sharpen = +req.query.sharpen;
let greyscale = [ "y", "yes", "true", "1",
"on"].includes(req.query.greyscale);
let flip = [ "y", "yes", "true", "1",
"on"].includes(req.query.flip);
let flop = [ "y", "yes", "true", "1",
"on"].includes(req.query.flop);

if (width > 0 && height > 0) {
image.ignoreAspectRatio();
}

if (width > 0 || height > 0) {
image.resize(width || null, height || null);
}

if (flip) image.flip();
if (flop) image.flop();
if (blur > 0) image.blur(blur);
if (sharpen > 0) image.sharpen(sharpen);
if (greyscale) image.greyscale();

rethinkdb.table("images").get(req.image.id).update({ date_used :
Date.now() }).run(db);

res.setHeader("Content-Type", "image/" +
path.extname(req.image.name).substr(1));

image.pipe(res);
});

We can now download our image using the web browser:

Next, we need to update our image removal method. It's as easy as our upload:

app.delete("/uploads/:image", (req, res) => {
rethinkdb.table("images").get(req.image.id).delete().run(db, (err)
=> {
return res.status(err ? 500 : 200).end();
});
});

This time, we used the image unique ID to remove it. If we try again using the curl command, we'll receive a code 200:

If we try to get the first record of our table, we'll see there's nothing there:

Finally, there are our two extra features that we added after introducing MySQL: the statistics and removing old unused images.

Our statistics method is not so simple as running an SQL query with aggregations. We must calculate each of our statistics:

app.get("/stats", (req, res) => {
let uptime = process.uptime();

rethinkdb.table("images").count().run(db, (err, total) => {
if (err) return res.status(500).end();

rethinkdb.table("images").sum("size").run(db, (err, size) => {
if (err) return res.status(500).end();

rethinkdb.table("images").max("date_created").run(db, (err,
last_created) => {
if (err) return res.status(500).end();

last_created = (last_created ? new
Date(last_created.date_created) : null);

return res.send({ total, size, last_created, uptime });
});
});
});
});

We should have a similar result to before:

Removing old images is more or less easy; we just need to filter the images we want to remove, and then remove them:

setInterval(() => {
let expiration = Date.now() - (30 * 86400 * 1000);

rethinkdb.table("images").filter((image) => {
return image("date_used").lt(expiration);
}).delete().run(db);
}, 3600 * 1000);

I simplified the previous strategy and am just removing images older than 1 month (30 days, times 86,400 seconds a day, times 1,000 milliseconds).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset