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 descriptionGET /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.