-
Notifications
You must be signed in to change notification settings - Fork 7
/
lambda.js
247 lines (223 loc) · 7.91 KB
/
lambda.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
"use strict";
var os = require("os");
var MongoClient = require("mongodb").MongoClient;
var uri = "mongodb://localhost:27017/IMDB";
var moviesCollection = "movies";
const allGenres = "All";
let cachedDb = null;
// --------------- Main handler -----------------------
// Route the incoming request based on intent.
// The JSON body of the request is provided in the event slot.
exports.handler = (event, context, callback) => {
try {
if (event.bot.name !== "SearchMoviesBot") {
callback("Invalid Bot Name");
process.exit(1);
}
var jsonContents = JSON.parse(JSON.stringify(event));
//handling API Gateway input where the event is embedded into the 'body' element
if (event.body !== null && event.body !== undefined) {
console.log("retrieving payload from event.body");
jsonContents = JSON.parse(event.body);
}
dispatch(context, jsonContents, response => {
callback(null, response);
});
} catch (err) {
callback(err);
}
};
// --------------- Events -----------------------
function dispatch(context, intentRequest, callback) {
var slots = intentRequest.currentIntent.slots;
var jsonSlots = JSON.stringify(slots);
console.log(`request received for userId=${intentRequest.userId}, intentName=${intentRequest.currentIntent.name} with slots=${jsonSlots}`);
if (slots.genre !== undefined && slots.castMember !== undefined) {
context.callbackWaitsForEmptyEventLoop = false;
var mongoDBUri = process.env['MONGODB_URI'];
try {
//testing if the database connection exists and is connected to Atlas so we can try to re-use it
if (cachedDb && cachedDb.serverConfig.isConnected()) {
query(cachedDb, intentRequest, callback);
} else {
//some performance penalty might be incurred when running that database connection initialization code
//console.log(`=> connecting to database ${mongoDBUri}`);
MongoClient.connect(mongoDBUri, (err, client) => {
if (err) {
console.log(`the error is ${err}.`, err);
process.exit(1);
}
cachedDb = client.db('moviesDB');
return query(cachedDb, intentRequest, callback);
});
}
} catch (err) {
console.error("an error occurred", err);
}
}
}
function query(db, intentRequest, callback) {
const sessionAttributes = intentRequest.sessionAttributes;
const slots = intentRequest.currentIntent.slots;
const castMember = slots.castMember;
const genre = slots.genre;
var year = parseInt(slots.year.replace(/[^0-9]/g, ""), 10);
console.log(`Searching for ${genre} movies with ${castMember} in ${year}`);
var castMemberMovies = "";
var msgGenre = allGenres;
var msgYear;
var castArray = [castMember];
//castArray = [castMember, "Angelina Jolie"];
var matchQuery = {
Cast: { $in: castArray },
Genres: { $not: { $in: ["Documentary", "News", ""] } },
Type: "movie"
};
if (genre !== undefined && genre !== allGenres) {
matchQuery.Genres = { $in: [genre] };
msgGenre = genre.toLowerCase();
}
if ((year !== undefined && isNaN(year)) || year > 1895) {
matchQuery.Year = year;
msgYear = year;
}
console.log(`query is ${JSON.stringify(matchQuery)}`);
var resMessage;
if (msgGenre === undefined && msgYear === undefined) {
resMessage = `Sorry, I couldn't find any movie for ${castMember}.`;
}
if (msgGenre !== undefined && msgYear === undefined) {
resMessage = `Sorry, I couldn't find any ${msgGenre} movie for ${castMember}.`;
}
if (msgGenre === undefined && msgYear !== undefined) {
resMessage = `Sorry, I couldn't find any movie for ${castMember} in ${msgYear}.`;
}
if (msgGenre !== undefined && msgYear !== undefined) {
resMessage = `Sorry, ${castMember} starred in no ${msgGenre} movie in ${msgYear}.`;
}
var aggregationFramework = true;
var unwindStage = { $unwind: "$Cast"};
var castFilterStage = { $match: {Cast: { $in: castArray } } };
var collation = { locale: "en", strength: 1 };
var moviesCount = 0;
var yearSpan = 0;
var cursor;
if (aggregationFramework) {
cursor = db.collection(moviesCollection).aggregate(
[
{ $match: matchQuery },
{ $sort: { Year: 1 } },
unwindStage,
castFilterStage,
{ $group: {
_id: "$Cast",
allMoviesArray: {$push: {$concat: ["$Title", " (", { $substr: ["$Year", 0, 4] }, ")"] } },
moviesCount: { $sum: 1 },
maxYear: { $last: "$Year" },
minYear: { $first: "$Year" }
}
},
{
$project: {
moviesCount: 1,
timeSpan: { $subtract: ["$maxYear", "$minYear"] },
allMovies: {
$reduce: {
input: "$allMoviesArray",
initialValue: "",
in: {
$concat: [
"$$value",
{
$cond: {
if: { $eq: ["$$value", ""] },
then: "",
else: ", "
}
},
"$$this"
]
}
}
}
}
}
],
{collation: collation} // cf. https://docs.mongodb.com/manual/reference/method/db.collection.aggregate/#specify-a-collation
);
} else {
cursor = db.collection(moviesCollection)
.find(matchQuery, { _id: 0, Title: 1, Year: 1 })
.collation(collation) //cf. https://docs.mongodb.com/manual/reference/method/db.collection.find/#specify-collation
.sort({ Year: 1 });
}
cursor.toArray(function(err, results) {
if (err) {
console.log(`Error querying the db: ${err}.`, err);
process.exit(1);
}
if (results.length > 0) {
console.log(`Raw results: ${JSON.stringify(results)}`);
if (aggregationFramework) {
for (var i = 0, len = results.length; i < len; i++) {
castMemberMovies = results[i].allMovies;
moviesCount = results[i].moviesCount;
yearSpan = results[i].timeSpan;
}
}
else {
for (var i = 0, len = results.length; i < len; i++) {
castMemberMovies += `${results[i].Title} (${results[i].Year}), `;//${os.EOL}`;
}
//removing the last comma and space
castMemberMovies = castMemberMovies.substring(0, castMemberMovies.length - 2);
moviesCount = results.length;
var minYear, maxYear;
minYear = results[0].Year;
maxYear = results[results.length-1].Year;
yearSpan = maxYear - minYear;
}
if (msgGenre != allGenres) {
resMessage = `${toTitleCase(castMember)} starred in the following ${moviesCount>1?moviesCount+" ":""}${msgGenre.toLowerCase()} movie(s)${yearSpan>0?" over " + yearSpan +" years":""}: ${castMemberMovies}`;
} else {
resMessage = `${toTitleCase(castMember)} starred in the following ${moviesCount>1?moviesCount+" ":""}movie(s)${yearSpan>0?" over " + yearSpan +" years":""}: ${castMemberMovies}`;
}
if (msgYear !== undefined) {
resMessage = `In ${msgYear}, ` + resMessage;
}
}
console.log(`Response message: ${resMessage}`);
//db.close();
callback(
close(sessionAttributes, "Fulfilled", {
contentType: "PlainText",
content: resMessage
})
);
});
}
function toTitleCase(str) {
return str.replace(/\w\S*/g, function(txt) {
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
});
}
// Close dialog with the bot user, reporting fulfillmentState of Failed or Fulfilled ("In 2005, Angeline Jolie played in Mr. and Mrs. Smith")
function close(sessionAttributes, fulfillmentState, message) {
return {
sessionAttributes,
dialogAction: {
type: "Close",
fulfillmentState,
message
}
};
}
function delegate(sessionAttributes, slots) {
return {
sessionAttributes,
dialogAction: {
type: "Delegate",
slots
}
};
}