How to Model Customer Surveys in a Graph Database
Want to incorporate customer feedback into a visually appealing graph database? Look no further. See how to do just that here.
Join the DZone community and get the full member experience.
Join For FreeUse-Case
The graph database use-case we are stepping through in this post is the following: In our web application, we have several places where a user is led through a survey, where she decides on details for one of our products. Some of the options within the survey depend on previous decisions and some are independent.
Examples:
- Configure a new car
- Configure a new laptop
- Book extras with your flight (meal, reserve seat etc.)
- Configure a new complete kitchen
- Collect customer feedback via logic-jump surveys
We would like to easily offer a generic page which can be seeded with any decision-tree and just plays the Q&A game with the user. We also want to be able to easily create new and modify existing decision trees in case the products change.
Graph Database Data Model
When speaking of trees, it directly pops into my mind that a graph database probably is a good solution to store the data in, well every tree is a graph. So in this case the data model is actually pretty simple: First, we have question
s we would like to ask the user. These question
s are modeled as vertices
having an attribute query
stating the string that should be displayed to the user. In case of a localized application we will not have one query
attribute, but one for every language we support (e.g. en
, de
, jp
etc.), for simplicity we now assume our shop is only available in English.
Attached to each question
, we have a list of possible answer
s leading the user to the next question
, or to the end of the survey. These answer
s are best realized as the edges
connecting everything together. Each answer
has again a text
attribute that should be presented to the user. Finally, we have product
s which best fit for a user after undergoing the survey.
Let’s now visualize a simple decision tree. We are in an online Avocado Shop and want to give our customer the best Avocado:
Tips and Tricks
If we set up the surveys using a graph all vertices in the graph can be reused in several surveys.
This is especially important if you want to offer the same product in completely different surveys. You do not have to store the product twice, making it easier to update. It is also important if you have overlapping surveys, say for two surveys the starting point is different, but based on some decisions the user will end up in the same dialog for both surveys and can only reach identical products with identical questions.
An example for this is if you are selling and leasing cars to a user. You at first have two sub different dialogs asking the user for selling respectively leasing conditions, but at some point in both dialogs the user finally has to decide on the car. This car selection process is again identical for both surveys.
UI
Sorry, I am no UX designer. In general, I would require only two API endpoints:
showQuestion/:id
Which would enter the survey at any point and return the question with it’s set of possible answers (the number of answers at this point is unknown and my UI widget is able to display an arbitrary number of option boxes).showProduct/:id
Which will show the product because the user is at the end of her survey.
If I want to hold some user state the UI part is responsible for it, it can for instance maintain a list of questions the user has visited and if the user presses “back” it can just resend showQuestion/:id
with the second to last question.
Queries
In the first endpoints, we need to return a query and all it’s possible answers. Using the data model design from above this means we have to select one question by it’s _id
and all its outgoing edges.
In ArangoDB the query to achieve this is the following (including comments):
FOR question IN questions
// Select the question. No worries we use the Primary Index O(1), no looping here ;)
FILTER question._key == @id
// Here we start a subquery to join together all answers into a single array
LET answers = (
// Now we select all outbound edges from question
FOR next, answer IN OUTBOUND question answers
// We make a projection to only return the text and the id of the next object
// If we would use localisation this place has to be adjusted
// We have to select the correct translation
// Also we use the indicator that if next has a query we are not at the end
RETURN {nextId: next._key, text: answer.text, isFinal: next.query == null}
)
// Finally we return the query, and the list of possible answers
RETURN {
query: question.query,
answers: answers
}
For the other endpoint we only have to display the product by its id
.
This can be done via the following even simpler query:
FOR product IN products
// Select the Product. No worries we use the Primary Index O(1), no looping here ;)
FILTER product._key == @id
RETURN product
Creating an API out of it
ArangoDB also offers a nice little framework called Foxx. This framework can easily wrap both queries into nice API endpoints by only adding a couple of lines of JS to it.
In this example, we will use the nice shorthand notations that are offered by ES6.
// First just use our queries:
const questionQuery = `
FOR question IN questions
FILTER question._key == @id
LET answers = (
FOR next, answer IN OUTBOUND question answers
RETURN {nextId: next._key, text: answer.text, isFinal: next.query == null}
)
RETURN {
query: question.query,
answers: answers
}`;
const productQuery = `
FOR product IN products
FILTER product._key == @id
RETURN product
`;
// Now define the routes:
router.get("/showQuestion/:id", (req, res) => {
let cursor = db._query(questionQuery, {id: req.pathParams.id})
res.json(cursor.toArray());
});
router.get("/showProduct/:id", (req, res) => {
let cursor = db._query(productQuery, {id: req.pathParams.id})
res.json(cursor.toArray());
});
And there we go.
See it in Action
Luckily, Foxx can also serve static files, which can contain HTML and JS code. This feature is useful if you want to ship a small administration frontend with you Foxx App.
In this post, I will misuse it to ship my entire survey-demo widget with it. You can simply install it from GitHub using this repository.
Have fun trying it out or even enhance it for your product if you like it. It is all Apache 2 Licence (and so is ArangoDB).
Published at DZone with permission of Michael Hackstein, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments