How to sort a list on Google-Actions/Dialogflow? - dialogflow-es

I would like to know how you can sort the OptionItems in the list of Google DialogFlow. It seems that they are automatically sorted by the key of their OptionItem. I just want them displayed on the list in the order I push them in the list, so no auto-sorting would be best.
I got following code how I create an OptionItem:
const optionItems:{[key: string]: OptionItem} = {}
optionItems[outlet.outlet_id] = {
synonyms: [
outlet.outlet_id,
],
title: outletOptionItemTitleText,
description: outletOptionItemDescriptionText,
image: {
url: outletOptionItemImageUrlText,
accessibilityText: outletOptionItemImageAccessibilityTextText,
},
}
The optionItems are passed as parameter to the List Object of Google DialogFlow like below:
conv.close(showAvailabilityText,new List({
items: optionItems,
}))
Can I pass another parameter to influence the order of the list? Or anything else?

Related

Storing and querying JSON arrays in Redisjson with nodejs

What I was hoping to do was store an array of objects using RedisJSON very simply and then query that array.
I have something similar to this:
const data = [
{
_id: '63e7d1d85ad7e2f69df8ed6e',
artist: {
genre: 'rock',
},
},
{
_id: '63e7d1d85ad7e2f69df8ed6f',
artist: {
genre: 'metal',
},
},
{
_id: '63e7d1d85ad7e2f69df8ed6g',
artist: {
genre: 'rock',
},
},
]
then I can easily store and retrieve this:
await redisClient.json.set(cacheKey, '$', data)
await redisClient.json.get(cacheKey)
works great. but now I want to also query this data, I've tried creating an index as below:
await redisClient.ft.create(
`idx:gigs`,
{
'$.[0].artist.genre': {
type: SchemaFieldTypes.TEXT,
AS: 'genre',
},
},
{
ON: 'JSON',
PREFIX: 'GIGS',
}
)
and when I try and search this index what I expect is it to return the 2 documents with the correct search filter, but instead it always returns the entire array:
const searchResult = await redisClient.ft.search(`idx:gigs`, '#genre:(rock)')
produces:
{
total: 1,
documents: [
{ id: 'cacheKey', value: [Array] }
]
}
I can't quite work out at which level I'm getting this wrong, but any help would be greatly appreciated.
Is it possible to store an array of objects and then search the nested objects for nested values with RedisJSON?
The Search capability in Redis stack treats each key containing a JSON document as a separate search index entry. I think what you are doing is perhaps storing your whole array of documents in a single Redis key, which means any matches will return the document at that key which contains all of your data.
I would suggest that you store each object in your data array as its own key in Redis. Make sure that these will be indexed by using the GIGS prefix in the key name, for example GIGS:63e7d1d85ad7e2f69df8ed6e and GIGS:63e7d1d85ad7e2f69df8ed6f.
You'd want to change your index definition to account for each document being an object too so it would look something like this:
await redisClient.ft.create(
`idx:gigs`,
{
'$.artist.genre': {
type: SchemaFieldTypes.TEXT,
AS: 'genre',
},
},
{
ON: 'JSON',
PREFIX: 'GIGS:',
}
)
Note I also updated your PREFIX to be GIGS: not GIGS - this isn't strictly necessary, but does stop your index from accidentally looking at other keys in Redis whose name begins GIGS<whatever other characters>.

knex js query many to many

i'm having trouble with node & knex.js
I'm trying to build a mini blog, with posts & adding functionality to add multiple tags to post
I have a POST model with following properties:
id SERIAL PRIMARY KEY NOT NULL,
name TEXT,
Second I have Tags model that is used for storing tags:
id SERIAL PRIMARY KEY NOT NULL,
name TEXT
And I have many to many table: Post Tags that references post & tags:
id SERIAL PRIMARY KEY NOT NULL,
post_id INTEGER NOT NULL REFERENCES posts ON DELETE CASCADE,
tag_id INTEGER NOT NULL REFERENCES tags ON DELETE CASCADE
I have managed to insert tags, and create post with tags,
But when I want to fetch Post data with Tags attached to that post I'm having a trouble
Here is a problem:
const data = await knex.select('posts.name as postName', 'tags.name as tagName'
.from('posts')
.leftJoin('post_tags', 'posts.id', 'post_tags.post_id')
.leftJoin('tags', 'tags.id', 'post_tags.tag_id')
.where('posts.id', id)
Following query returns this result:
[
{
postName: 'Post 1',
tagName: 'Youtube',
},
{
postName: 'Post 1',
tagName: 'Funny',
}
]
But I want the result to be formated & returned like this:
{
postName: 'Post 1',
tagName: ['Youtube', 'Funny'],
}
Is that even possible with query or do I have to manually format data ?
One way of doing this is to use some kind of aggregate function. If you're using PostgreSQL:
const data = await knex.select('posts.name as postName', knex.raw('ARRAY_AGG (tags.name) tags'))
.from('posts')
.innerJoin('post_tags', 'posts.id', 'post_tags.post_id')
.innerJoin('tags', 'tags.id', 'post_tags.tag_id')
.where('posts.id', id)
.groupBy("postName")
.orderBy("postName")
.first();
->
{ postName: 'post1', tags: [ 'tag1', 'tag2', 'tag3' ] }
For MySQL:
const data = await knex.select('posts.name as postName', knex.raw('GROUP_CONCAT (tags.name) as tags'))
.from('posts')
.innerJoin('post_tags', 'posts.id', 'post_tags.post_id')
.innerJoin('tags', 'tags.id', 'post_tags.tag_id')
.where('posts.id', id)
.groupBy("postName")
.orderBy("postName")
.first()
.then(res => Object.assign(res, { tags: res.tags.split(',')}))
There are no arrays in MySQL, and GROUP_CONCAT will just concat all tags into a string, so we need to split them manually.
->
RowDataPacket { postName: 'post1', tags: [ 'tag1', 'tag2', 'tag3' ] }
The result is correct as that is how SQL works - it returns rows of data. SQL has no concept of returning anything other than a table (think CSV data or Excel spreadsheet).
There are some interesting things you can do with SQL that can convert the tags to strings that you concatenate together but that is not really what you want. Either way you will need to add a post-processing step.
With your current query you can simply do something like this:
function formatter (result) {
let set = {};
result.forEach(row => {
if (set[row.postName] === undefined) {
set[row.postName] = row;
set[row.postName].tagName = [set[row.postName].tagName];
}
else {
set[row.postName].tagName.push(row.tagName);
}
});
return Object.values(set);
}
// ...
query.then(formatter);
This shouldn't be slow as you're only looping through the results once.

Add two more keys and values to a requested array of objects with the same values?

I'm using Node.js to make a Axios request to the Vimeo API.
I'm kind of a Noob with Json but I'd like to understand. I made a call to Vimeo with a filter of name, so I get an "array of objects" (I hope that name is correct ) it looks like this below:
[{ name: 'A Personalized Goal for Anthony' },
{ name: 'Flow access & overview' },
{ name: 'Welcome - 2018 Conference' },
{ name: 'Dropout Video' } ] }
This is simply the names of the videos, however I need to add two keys value pairs with the same value as the name value for a dropdown menu, so it looks like this below:
[{ name: 'A Personalized Goal for Anthony',
"text": "A Personalized Goal for Anthony",
"value": "A Personalized Goal for Anthony" },
{ name: 'Flow access & overview',
"text": "Flow access & overview"
"value": "Flow access & overview" },
{ name: 'Welcome - 2018 Conference',
"text": "Welcome - 2018 Conference"
"value": "Welcome - 2018 Conference" },
{ name: 'Dropout Video',
"text": "Welcome - 2018 Conference"
"value": "Welcome - 2018 Conference" }]
My app needs the keys "Text" and "Value" for a dropdown menu in order to process the response from the user.
I'm not sure why { name: 'Dropout Video'}, isn't formatted like this {"name":"Dropout Video"},
Is there a way in Node.js to add or duplicate an array using map or push using the same name value while also doing a foreach to this array which reflects the name but with two additional key value pairs with the same Value as the key Name?
This is a dynamic Object as well so when the request is called again it could have many more than just 4 video names returned.
My goal is to add numerous Videos to a Vimeo Album that I want to make the request to, then my app ( Slack chatbot ) then returns a list of options via a drop down menu for the users consideration so that I'm able to see which Name or title they selected and thus want more information on.
Thank you so much any help here. I wish Vimeo had a Text and Value field so I could just filter that instead but then it wouldn't be fun :)
You could map through your result, so, for example, after you get the result:
const movies = [{ name: 'A Personalized Goal for Anthony' },
{ name: 'Flow access & overview' },
{ name: 'Welcome - 2018 Conference' },
{ name: 'Dropout Video' }];
You could map it to add the other two properties to each one of your objects, the advantage is that you are not mutating your results.
const mappedMovies = movies.map(movie => {
return {
name: movie.name,
text: movie.name,
value: movie.name
}
);
In that way, you will have the list of objects with the format that you want, now if you need it in json format you could do:
const JSONString = JSON.stringify(mappedMovies);
Objects are references, so you could just loop through your objects and add the keys.
const videos = [
{ name: 'A Personalized Goal for Anthony' },
{ name: 'Flow access & overview' },
{ name: 'Welcome - 2018 Conference' },
{ name: 'Dropout Video' }
]
for (video of videos) {
video.text = video.name
video.value = video.name
}
console.log(videos)
Welcome to SO Kevin,
It looks like you almost provided the answer yourself already...
To get the resulting array you can map through the object and add keys as required:
const slackVideos = videos.map(function (item, label) {
return {
...item, //this ensures all original keys are available, using the ES6 destructuring
text: item.name,
value: item.value,
}
});
Assuming videos is the object received from vimeo, slackVideos will now contain your aditional text & value keys; both containing the name key from the original vimeo object.

How to filter Subscribers based on array of tags in Loopabck

I've two models - subscribers and tags
Sample data:
{
subscribers: [
{
name: "User 1",
tags: ["a","b"]
},
{
name: "User 2",
tags: ["c","d"]
}
]
}
I want to filter subscribers based on their tags.
If I give a and b tags, User 1 should list
If I give a and c tags,
both User 1 and User 2 should list
Here is what I tried:
Method 1:
tags is a column in subscribers model with array data type
/subscribers/?filter={"where":{"tags":{"inq":["a","b"]}}} // doesn't work
Method 2:
Created a separate table tags and set subscribers has many tags.
/subscribers/?filter={"where":{"tags":{"inq":["a","b"]}}} // doesn't work
How can I achieve this in Loopback without writing custom methods?
I've Postgresql as the connector
UPDATE
As mentioned in the loopback docs you should use inq not In
The inq operator checks whether the value of the specified property matches any of the values provided in an array. The general syntax is:
{where: { property: { inq: [val1, val2, ...]}}}
From this:
/subscribers/?filter={"where":{"tags":{"In":["a","b"]}}}
To this:
/subscribers/?filter={"where":{"tags":{"inq":["a","b"]}}}
Finally found a hack, using Regex! it's not a performant solution, but it works!!
{ "where": { "tags": { "regexp": "a|b" } } }

Container Builder Slack Notifications

we're testing out CB and part of our requirements is sending messages to Slack.
This tutorial works great, but it'd be helpful if we could specify the source of the build, so we don't have to click in to the message to see what repo/trigger failed/succeeded.
Is there a variable we can pass to the cloud function in the tutorial? I couldn't find helpful documentation.
Ideally, it would be great if CB had an integration/slack GUI that made these options configurable but c'est la vie.
You can add source information to the slack message by adding a new item to the fields list within the createSlackMessage function. You need to make sure title and value are strings.
// createSlackMessage create a message from a build object.
const createSlackMessage = (build) => {
let message = {
text: `Build \`${build.id}\``,
mrkdwn: true,
attachments: [
{
title: 'Build logs',
title_link: build.logUrl,
fields: [{
title: 'Status',
value: build.status
},{
title: 'Source',
value: JSON.stringify(build.source, null, 2)
}]
}
]
};
return message
}
You can find more information on build object here.

Resources