Reputation:
How do we store all user input data in one document per one chat session?
I tried this code:
'use strict';
const functions = require('firebase-functions');
const {WebhookClient} = require('dialogflow-fulfillment');
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
process.env.DEBUG = 'dialogflow:debug';
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
function getAge(agent) {
let age = agent.parameters.age;
db.collection("users").add({age: age});
}
function getLocation(agent) {
let location = agent.parameters.location;
db.collection("users").add({location: location});
}
function getCustomerExperience(agent) {
let customerExperience = agent.query;
db.collection("users").add({customerExperience: customerExperience});
}
let intentMap = new Map();
intentMap.set('age', age);
intentMap.set('location', getLocation);
intentMap.set('customer-experience', getCustomerExperience);
agent.handleRequest(intentMap);
});
but the data were stored in different document IDs:
What I'm trying to achieve is something like this:
If I'm not being clear, please let me know. I'm new to Dialogflow, Firebase, as well as the JS language. Cheers!
Upvotes: 1
Views: 510
Reputation: 50701
You're on the right track! The fundamental problem with your original code is that collection.add()
will create a new document. But you want it to create a new document sometimes, and save it in a previous document other times.
This means that, during the entire Dialogflow session, you'll need some way to know what the document name is or should be. There are a few possible ways to do this.
Use a document based on the session
Dialogflow provides a session identifier that you can get as part of the agent.session
property using the dialogflow-fulfillment library, or in the session
property if you're parsing the JSON request body directly.
However, this string includes forward slash /
characters, which should be avoided in document names. Fortunately, the format of this string is documented to be one of the two formats:
In each case, the Session ID is the last portion of this path, so you can probably use code something like this to get the ID for the session, use it as your document name, and then save an attribute (for example, age) for it:
function documentRef( agent ){
const elements = agent.session.split('/');
const lastElement = elements[elements.length - 1];
return db.collection('users').doc(lastElement);
}
async function getCourier(agent) {
const ref = documentRef( agent );
const age = agent.parameters.age;
return await ref.update({age: age});
}
Note that I have also made getCourier()
an async function, because the function calls that change the database (such as ref.update()
) are async functions and Dialogflow requires you to either make it an async function or explicitly return a Promise. If you wish to return a Promise instead, this would be something more like this:
function getCourier(agent) {
const ref = documentRef( agent );
const age = agent.parameters.age;
return ref.update({age: age});
}
Use the document name generated by Firestore
With this method, you'll store a document name as a Context parameter. When you go to save a value, you'll check if this document name is set. If it is, you'll do an update()
using this document name. If not, you'll do an add()
, get the document name, and save it in the Context parameter.
It might look something like this (untested), again for the age:
async function getCourier( agent ){
const ref = db.collection('users');
const age = agent.parameters.age;
const docContext = agent.context.get('doc');
if( !docContext ){
// We don't previously have a document, so create it
const res = await ref.add({age:age});
// And set a long-lasting context with a "name" parameter
// containing the document id
agent.context.set( 'doc', 99, {
'name': ref.id
} );
} else {
// There is a context with the document name already set
// so get the name
const docName = docContext.parameters['name'];
const docRef = ref.doc(docName);
// And save the data at this location
await docRef.update({age: age});
}
}
Again, this uses an async function. If you'd rather use a Promise, it might be something more like this:
function getCourier( agent ){
const ref = db.collection('users');
const age = agent.parameters.age;
const docContext = agent.context.get('doc');
if( !docContext ){
// We don't previously have a document, so create it
return ref.add({age:age})
.then( ref => {
// And set a long-lasting context with a "name" parameter
// containing the document id
agent.context.set( 'doc', 99, {
'name': ref.id
} );
});
} else {
// There is a context with the document name already set
// so get the name
const docName = docContext.parameters['name'];
const docRef = ref.doc(docName);
// And save the data at this location
return docRef.update({age: age});
}
}
Use a document name you've generated and saved in the context
You don't need to use the session id from the first alternative. If you have some ID or name that makes sense on your own (a username or a timestamp, for example, or some combination), then you can save this in a Context parameter and use this each time as the document name. This is a combination of the first and second approaches above (but probably simpler than the second one, since you don't need to get the document name from creating the document the fist time).
Upvotes: 1