Skip to content

ExpressJS and OrientDB

Giraldo Rosales edited this page Jan 21, 2014 · 1 revision

In this tutorial we assume that you already have an understanding about how web sites works, about how javascript works and that you are interested about what and how OrientDB works with Node.js.

Prerequisites

In order to complete this tutorial, you need to have installed and working:

  • Node.js: check it with the command node -v (on my computer it prints v0.8.16). In order to install Node.js on your computer, follow the instructions.
  • expressjs: check it with the command express -V (on my computer it prints 3.0.5). In order to install express, type npm install -g express (may required root access).
  • OrientDB: we assume that it's running on your computer. In order to install it, follow the instuctions. This tutorial uses version 1.3.0.

The basics

First, let's make a bare minimum express js app. Open up a console/terminal and type

express --sessions tutorial

Then

cd tutorial
npm install -d
node app.js

Express will report

Express server listening on port 3000

head over here and see express working as expected.

Introducing OrientDB

Create an blog database on OrientDB: you can do that using OrientDB Studio or using the OrientDB console with the command

create database local:/home/federico/materiale/works_My/orientdb-graphed-1.3.0/databases/blog admin admin local graph

(don't forget to adjust the path)

Open the package.json file and add the Node.js OrientDB driver to the list of dependencies:

"orientdb": "*"

Now require the driver in app.js:

var orientdb = require("orientdb");

Right after it, add the following code

//Previous code omitted

var dbConfig = {
    username:"admin",
    password:"admin",
    database:"test",
    host: "localhost",
    port: 2424,
    database_type: "document", //Optional. Default: document.
    storage_type: "local" //Optional. Default: local.
};

var server = new orientdb.Server(serverConfig);
var db     = new orientdb.GraphDb("blog", server, dbConfig);

db.open(function(error) {
    if (error) {
        throw error;
    }
    console.log("Successfully connected to OrientDB");
});

//Subsequent code omitted

Go back to the terminal, interrupt the running node process with CTRL+C, run npm install -d again to download OrientDB Node.js driver, then restart the app with node app.js. You'll now see:

Express server listening on port 3000
Successfully connected to OrientDB

Well done!

Making the routes use OrientDB

Right below the console.log statement in db.open callback, add this snipped

routes.init(db, function(error) {
    if (error) {
        throw error;
    }
});

Then open index.js in routes folder. At its top, add the line module.db = null;. Then put the following function at the end of the file

exports.init = function(orient, callback) {
    module.db = orient;
};

This way, our routes module now has a private db property that allows us to communicate with OrientDB.

Preparing the schema

We are going to programmatically add some classes to our blog database, but only IF they are missing.

Open file index.js in routes folder and add the following function

function ensureSchemaIsSetup(callback) {
    if (module.db.getClassByName("Post") === null) {
        module.db.createClass("Post", "OGraphVertex", callback);
    }
    if (module.db.getClassByName("Tag") === null) {
        module.db.createClass("Tag", "OGraphVertex", callback);
    }
}

Now get exports.init function and, below module.db assignement, call ensureSchemaIsSetup so that it now looks like this:

exports.init = function(orient, callback) {
    module.db = orient;
    
    ensureSchemaIsSetup(callback);
};

Listing blog posts

Open file index.js in routes folder and modify the index function so that it looks like:

exports.index = function(req, res) {
    module.db.query("SELECT FROM Post ORDER BY creation_date DESC", function(error, results) {
        res.render("index", {
            title: "Express + OrientDB blog",
            posts: results
        });
    });
};

The important code here is the usage of the command function: first argument is the query to execute, an optional callback is the second argument.

Then open file index.jade in views folder and modify it so that it looks like:

extends layout

block content
  h1= title
  p Welcome to #{title}
  if posts.length > 0
    each post in posts
      p 
        b= post.title
      p= post.text
      p= post.creation_date.toLocaleDateString()
  else
    h3 There are no posts in this blog!

Restart the app to see our blog complaining for having no blog posts to show. Well done for now: it's time make some new posts!

Inserting blog posts

Open file index.jade and append the following line

a(href="/new_post") Insert new blog post

Refreshing the index, you should see a link pointing to http://localhost:3000/new_post.

Create a new file called new_post.jade in views folder. Type in the following code:

extends layout

block content
  h1= title
  form(action="new_post",method="post")
    p
      label Title
      input(name="title",type="text")
    p
      label Text
      textarea(name="text",rows="5",cols="40")
    input(type="submit")

Now open file index.js in routes folder and add two new functions:

exports.new_post_form = function(req, res) {
    res.render("new_post", { title: "New post" });
};

exports.new_post = function(req, res, next) {
    var post = {
        title: req.body.title,
        text: req.body.text,
        creation_date: new Date()
    };
    module.db.createVertex(post, { "class": "Post" }, function(err) {
        if (err) return next(err);
        res.redirect("/");
    });
};

The important code here is the createVertex function: first argument if the vertex data, then an optional hash of options (if you are using a specific class, you need to specify it here) and an optional callback.

Wire them to the router: open file app.js in the root folder and below line app.get('/users', user.list); add:

app.get('/new_post', routes.new_post_form);
app.post('/new_post', routes.new_post);

Now restart the application and try inserting some new posts. Is it working? Are posts sorted in reverse chronological order? Well done!

Tagging blog posts

Let's link our blog posts to some tags.

First add the tag field to new blog post form: open file new_post.jade in views folder and add the following code above the submit button

    p
      label Tags(comma separated)
      input(name="tags",type="text")

Then we need to handle this new field. We are going to check if a submitted tag already has an associated vertex: if the vertex doesn't exist, create it, if it exists, load it and put it into a list of tag vertexes that will then be linked to the submitted post.

Modify exports.new_post in file index.js in folder routes so that it looks like:

exports.new_post = function(req, res, next) {

    function fetchOrCreateTag(tagName, callback) {
        tagName = tagName.trim();
        
        module.db.command("select from Tag where name = '" + tagName + "'", function(err, results) {
            if (err) return callback(err);

            if (results.length === 0) {
                module.db.createVertex({ name: tagName }, { "class": "Tag" }, function(err, tagVertex) {
                    callback(null, tagVertex);
                });
            } else {
                callback(null, results[0]);
            }
        });
    }

    function fetchTagVertexes(callback) {
        if (!req.body.tags || req.body.tags.trim() === "") {
            callback(null, []);
            return;
        }
        var tagStrings = req.body.tags.trim().split(",");
        var tagsAccumulator = [];
        for (var idx = 0; idx < tagStrings.length; idx++) {
            fetchOrCreateTag(tagStrings[idx], function(err, tagVertex) {
                if (err) return callback(err);

                tagsAccumulator.push(tagVertex);

                if (tagStrings.length === tagsAccumulator.length) {
                    callback(null, tagsAccumulator);
                }
            });
        }
    }

    fetchTagVertexes(function(err, tags) {
        if (err) return next(err);

        var post = {
            title: req.body.title,
            text: req.body.text,
            creation_date: new Date()
        };

        module.db.createVertex(post, { "class": "Post" }, function(err, post) {
            if (err) return next(err);

            if (tags.length === 0) {
                res.redirect("/");
                return;
            }

            for (var idx = 0; idx < tags.length; idx++) {
                module.db.createEdge(post, tags[idx], { label: "tag" }, function(err, edge) {
                    if (err) return next(err);

                    tags.pop();

                    if (tags.length === 0) {
                        res.redirect("/");
                    }
                });
            }
        });
    });
};

The first two functions, fetchOrCreateTag and fetchTagVertexes, do what they say: the former check if the given tag already exists and creates it if not.

Then we wrap the previous function body so that it is a callback of fetchTagVertexes (this way we are sure all the given tags are already there, ready to be linked to out blog post): we create the post as before and then we link it to each tag, consuming the previously created array. When the last tag has been linked, we redirect to the homepage as we did before.

The important piece of code here is the createEdge function, which links two vertexes: the first two arguments are the vertexes involved or their RIDs, the third is an optional hash of data, the forth is the callback.

To show the tags in the posts list, we need to modify the query so that it fetches also linked vertexes: we'll use a fetch plan. Go to function exports.index and modify it so that it looks like:

exports.index = function(req, res) {
    module.db.query("SELECT FROM Post ORDER BY creation_date DESC", { fetchPlan: "*:2" }, function(error, results) {
        res.render('index', {
            title: 'Express + OrientDB blog',
            posts: results
        });
    });
};

The edit file index.jade in folder views and below the code p= post.creation_date.toLocaleDateString() add the following snippet:

      if post.out && post.out.length > 0
        p Tags: 
          each toTagEdge in post.out
            span= toTagEdge.in.name + "(" + toTagEdge.in.in.length + ") "

The important piece of code here is the new second argument of the command function. The { fetchPlan: "*:2" } hash tells OrientDB to load the vertexes of type Post and their linked vertexes/edges until the second level in the hierarchy.

Therefore, given the structure of our graph, we expect, for each post, a hierarchy like: POST(vertex type, position 0) ==>> edge labeled "tag"(edge type, position 1) ==>> TAG(vertex type, position 2).

Time to run it: restart the app, insert a new post, type some tags separated by a comma and submit the form.

Like it? :)

Don't forget to give us feedback: is this tutorial unclear? would you like an error corrected? Open an issue and tell us.

Thank you for your attention.

Acknowledgements

Thank you Federico Fissore for this great tutorial.