EGF2 Guide - Adding Search

Now that we've got the system operational it is time to add search functionality to our back-end.

We will add search for Posts and for Users. Let's start with separate indexes and then we can do a custom index that works for both Users and Posts simultaneously.

Automatic Indexes

In order to add Post index please add the following text to the sync service config (can be found at /opt/sync/config.json), "elastic"/"indices" section:

...
"post": {
    "object_type": "post",
    "mapping": {
        "id": {"type": "string", "index": "not_analyzed"},
        "description": {"type": "string"},
        "created_at": {"type": "date"}
    }
}
...

Please check out ElasticSearch documentation for supported mapping options.

User index is already partially supported for us. We need to add some fields here though. Here is the way User index should look like:

...
"user": {
    "object_type": "user",
    "mapping": {
        "id": {"type": "string", "index": "not_analyzed"},
        "first_name": {"type": "string", "field_name": "name.given"},
        "last_name": {"type": "string", "field_name": "name.family"},
        "email": {"type": "string", "index": "not_analyzed"}
    }
}
...

As you can see, we have added "first_name" and "last_name" fields to the User index declaration.

Without writing a line of code we have just supported search for Users on name and for Posts on description.

Let's see what we can do with custom index handlers.

Custom Index

Let's add the following text to the config, "elastic"/"indices" section:

...
"post_user": {
    "mapping": {
        "id": {"type": "string", "index": "not_analyzed"},
        "combo": {"type": "string"}
    }
}
...

We need to implement the handler for our new index. Let's start with adding a file "post_user.js" to the "extra" folder of the sync service:

"use strict";

const config = require("../components").config;  
const elastic = require("../components").elasticSearch;

const indexName = "post_user";

function isUser(event) {  
    let obj = event.current || event.previous;
    return obj.object_type === "user";
}

function onPost(event) {  
    return elastic.index({
        index: indexName,
        type: indexName,
        id: event.object,
        body: {
            id: event.current.id,
            combo: isUser(event) ?
                `${event.current.name.given} ${event.current.name.family}` :
                event.current.description
        }
    });
}

function onPut(event) {  
    let combo;
    if (isUser(event)) {
        if (event.current.name.given !== event.previous.name.given ||
            event.current.name.family !== event.previous.name.family) {
            combo = `${event.current.name.given} ${event.current.name.family}`;
        }
    } else if (event.current.description !== event.previous.description) {
        combo = event.current.description;
    }
    if (combo) {
        return elastic.update({
            index: indexName,
            type: indexName,
            id: event.object,
            body: {combo}
        });
    }
    return Promise.resolve();
}

function onDelete(event) {  
    return elastic.delete({
        index: indexName,
        type: indexName,
        id: event.object
    });
}

module.exports = {  
    onPost,
    onPut,
    onDelete
};

This code reacts to events related to User and Post and stores data in a combined ES index.

Last thing we need to do is let the system know about our new handler, please add these lines to the "extra/index.js", somewhere in handleRegistry map:

...
"POST user": require("./post_user").onPost,
"PUT user": require("./post_user").onPut,
"DELETE user": require("./post_user").onDelete,
"POST post": require("./post_user").onPost,
"PUT post": require("./post_user").onPut,
"DELETE post": require("./post_user").onDelete
...

Restart sync. We are all set!

We can now do searches for users, posts and for both as follows:

  • GET /v1/search?q=john&object=user&fields=first_name,last_name to get all users with name or surname "john"
  • GET /v1/search?q=waterfall&object=post&fields=description to get all posts that have "waterfall" in description
  • GET /v1/search?q=helen&object=post_user&fields=combo to get users and posts related to "helen"

In the next post we will add several logic rules.

comments powered by Disqus