Query variable arrays - google-apps-script

How do I use an array variable as a query argument instead of the literal array itself?
For example, the documentation mentions the following:
var result = db.query({name: db.anyOf(['fred', 'barney', 'mark']});
But instead, I wish to do this:
var myTeam = ["fred","barney","mark"];
var result = db.query({name: db.anyOf(myTeam)});
So far, I have not been successful.
What am I missing?

Nothing. Your code works fine for me (besides the missing parenthesis typo).
function scriptdbTest() {
var db = ScriptDb.getMyDb();
db.save({name:'fred', age:40}); //just to get one result on my test script
var myTeam = ["fred","barney","mark"];
var result = db.query({name: db.anyOf(myTeam)});
while( result.hasNext() )
Logger.log(result.next().toJson());
}

Related

XmlService.parse() not able to handle HTML tables

I am looking for help from this community regarding the below issue.
// I am searching my Gmail inbox for a specific email
function getWeeklyEmail() {
var emailFilter = 'newer_than:7d AND label:inbox AND "Report: Launchpad filter"';
var threads = GmailApp.search(emailFilter, 0, 5);
var messages=[];
threads.forEach(function(threads)
{
messages.push(threads.getMessages()[0]);
});
return messages;
}
// Trying to parse the HTML table contained within the email
function getParsedMsg() {
var messages = getWeeklyEmail();
var msgbody = messages[0].getBody();
var doc = XmlService.parse(msgbody);
var html = doc.getRootElement();
var tables = doc.getDescendants();
var templ = HtmlService.createTemplateFromFile('Messages1');
templ.tables = [];
return templ.evaluate();
}
The debugger crashes when I try to step over the XmlService.parse function. The msgbody of the email contains both text and HTML formatted table. I am getting the following error: TypeError: Cannot read property 'getBody' of undefined (line 19, file "Code")
If I remove the getParsedMsg function and instead just display the content of the email, I get the email body along with the element tags etc in html format.
Workaround
Hi ! The issue you are experiencing is due to (as you previously mentioned) XmlService only recognising canonical XML rather than HTML. One possible workaround to solve this issue is to search in the string you are obtaining with getBody() for your desired tags.
In your case your main issue is var doc = XmlService.parse(msgbody);. To solve it you could iterate through the whole string looking for the table tags you need using Javascript search method. Here is an example piece of code retrieving an email with a single table:
function getWeeklyEmail() {
var emailFilter = 'newer_than:7d AND label:inbox AND "Report: Launchpad filter"';
var threads = GmailApp.search(emailFilter, 0, 5);
var messages=[];
threads.forEach(function(threads)
{
messages.push(threads.getMessages()[0]);
});
return messages;
}
// Trying to parse the HTML table contained within the email
function getParsedMsg() {
var messages = getWeeklyEmail();
var msgbody = messages[0].getBody();
var indexOrigin = msgbody.search('<table');
var indexEnd = msgbody.search('</table');
// Get what is in between those indexes of the string.
// I am adding 8 as it indexEnd only gets the first index of </table
// i.e the one before <
var Table = msgbody.substring(indexOrigin,indexEnd+8);
Logger.log(Table);
}
If you are looking for more than one table in your message, you can change getParsedMsg to the following:
function getParsedMsg() {
// If you are not sure about how many you would be expecting, use an approximate number
var totalTables = 2;
var messages = getWeeklyEmail();
var msgbody = messages[0].getBody();
var indexOrigin = msgbody.indexOf('<table');
var indexEnd = msgbody.indexOf('</table');
var Table = []
for(i=0;i<totalTables;i++){
// go over each stable and store their strings in elements of an array
var start = msgbody.indexOf('<table', (indexOrigin + i))
var end = msgbody.indexOf('</table', (indexEnd + i))
Table.push(msgbody.substring(start,end+8));
}
Logger.log(Table);
}
This will let you store each table in an element of an array. If you want to use these you would just need to retrieve the elements of this array and use them accordingly (for exaple to use them as HTML tables.
I hope this has helped you. Let me know if you need anything else or if you did not understood something. :)

How to create document from data array

I have FlexTable with chekBoxes in first cell of each row, when checkBox is true data from FlexTable's row is collected in variable. Now I need to create document with table that contains table with data from variable. I tried to store string's value in Hidden but it doesn't work and can't figure out how to realise it.
All my (although the code is not really my, code is almost half #Sergeinsas's) code is avaliable here: http://pastebin.com/aYmyA7N2, thankyou in advance.
There are a few errors in your code... widgets like hidden can only have string values and they can only return string values when you retrieve their values.
One possible and easy way to convert arrays to string (and back) is to use a combination of join() and split() , here is the modified code (relevant part only) that works.
// Storing checked rows
function check(e) {
var checkedArray = [];
var data = sh.getRange(1,1,lastrow,lastcol).getValues();
for(var n=0; n < data.length;++n){
if(e.parameter['check'+n]=='true'){
checkedArray.push(data[n].join(','));// convert data row array to string with comma separator
}
}
var hidden = app.getElementById('hidden');
hidden.setValue(checkedArray.join('|'));// convert array to string with | separator
return app;
}
function click(e) {
var hiddenVal = e.parameter.hidden.split('|');// e.parameter.hidden is a string, split back in an array of strings, each string should be splitted too to get the original array of arrays
var d = new Date();
var time = d.toLocaleTimeString();
var table = []
for(var n in hiddenVal){
table.push(hiddenVal[n].split(','));// reconstruction of a 2D array
}
DocumentApp.create('doc '+time).getBody().appendTable(table);// the table is in the document
}
Full code available here
EDIT : suggestion : if you put your headers in your spreadsheet you could retrieve them in your final table quite easily like this :
function check(e) {
var checkedArray = [];
var data = sh.getRange(1,1,lastrow,lastcol).getValues();
checkedArray.push(data[0].join(','));// if you have headers in your spreadsheet, you could add headers by default
for(var n=0; n < data.length;++n){
if(e.parameter['check'+n]=='true'){
checkedArray.push(data[n].join(','));
}
}
You could also use data[0] in the doGet function to build the header of your UI, I think this would make your code more easy to maintain without hardcoding of data.... but this is only a suggestion ;-)

Error when trying to store an array in ScriptDb

I have an array of objects that is created by my script and I am trying to copy that array into a new array and then store it in scriptDb using the following function:
function copyAndStore (currentArray) {
var db = ScriptDb.getMyDb();
var copyArray = [];
for (var i in currentArray) {
copyArray.push(currentArray[i]);
}
var id = db.save(copyArray);
return id;
}
It copies everything properly but when it gets to var id = db.save(copyArray); I get the error: Invalid argument. Expected a javascript map object.
Does ScriptDb have issues with storing arrays? Thanks in advance for the help.
As #Thomas said, you can save an array in a map object.
You don't need to perform a copy operation before putting an object into the ScriptDB, either. You could save your array by simply db.save({myArray}), and remember the ID.
Here's some minimalist code to demonstrate. I'm showing two ways to retrieve your saved array - one by ID, which seems to be the way you were planning to, but also a second way using a "key" value for a query. If you expect to retrieve the contents of ScriptDB in a later run of your code, this approach eliminates the need to somehow remember the ID of the stored array.
function saveArray (currentArray) {
var db = ScriptDb.getMyDb();
return db.save({type: "savedArray", data:currentArray}).getId();
}
function loadArrayById (id) {
var db = ScriptDb.getMyDb();
return db.load(id).data;
}
function loadArrayByType () {
var db = ScriptDb.getMyDb();
var result = db.query({type: "savedArray"});
if (result.hasNext()) {
return result.next().data;
}
else {
return [];
}
}
function test() {
var arr = ['this','is','a','test'];
var savedId = saveArray( arr );
var loaded1 = loadArrayById( savedId );
var loaded2 = loadArrayByType();
debugger; // pause if running debugger
}
Here's what you'll see at the debugger pause:
Note that by using the map tag data to pull the array from the saved object, both loaded1 and loaded2 are identical to the source array arr.
ScriptDb only stores map objects. You could however store a map that contains an array!
You can use arrays to save several objects in a single call using db.saveBatch.

Converting GAS array (from .gs) to javascript array (in .html)

Is there an "easy" way of converting a GAS string array (in .gs file) to a javascript array that exists in an HTML file? I'm trying to use jquery to do stuff, and it requires (from what I can tell) values to be in a javascript array. The first piece of code is the function that gets email addresses from contacts app and returns an array of strings. The rest are samples of HTML with a link break to separate their cajoled result (at least I'm pretty sure their cajoled result). Also, this is basically what I'm using the availableTags variable for you'll see below: http://jqueryui.com/autocomplete/
Script File, returns a string array of email addresses
function getAllContacts(){
var contacts = ContactsApp.getContactsByGroup(ContactsApp.getContactGroup("ContactsAppTest"));
var email = new Array();
for(var i=0;i<contacts.length;i++){
if(contacts[i].getPrimaryEmail() != ""){
email.push(contacts[i].getPrimaryEmail());
}
}
return email;
}
Try 1, create an empty js array, and manually populate from getAllContacts function. This works, but thinking this is not very efficient.
var availableTags = [];
<?
var temp = getAllContacts();
for (var i=0; i<temp.length; i++) { ?>
availableTags.push(<?= temp[i] ?>);
<?} ?>
availableTags.push_m___?availableTags.push('email1#test.com'):availableTags.m___('push',['email1#test.com']);availableTags.push_m___?availableTags.push('email2#test2.com'):availableTags.m___('push',['email2#test2.com']);availableTags.push_m___?availableTags.push('email3#test3.com'):availableTags.m___('push',['email3#test3.com']);
Try 2, set availableTags = getAllContacts function
var availableTags = <?=getAllContacts()?>;
availableTags='email1#test.com,email2#test2.com,email3#test3.com'
Normal js array
var availableTags = ["email1#test.com","email2#test2.com","email3#test3.com"];
availableTags=['email1#test.com','email2#test2.com','email3#test3.com'];
Try something like this out:
<? var temp = getAllContacts(); ?>
var availableTags = <?= temp ? "[" + temp.toString() + "]" : "null" ?>;
That should initialize the array on the client side all-at-once. (I wouldn't worry too much about efficiency anyway, unless you're dealing with hundred or thousands of these contacts. Myself, I'd shoot for what I found most readable.)
UPDATE: as noted in comment, this won't work, as the result appears to be enquoted.
Other efforts to use new Function() or eval() on that string fail, likely due to the Caja sanitizer GAS uses on output. This should work, instead:
<? var temp = getAllContacts(); ?>
var array = null;
var str = <?= temp ? temp.toString() : "" ?>;
if(str) array = str.split(",");

How do you query ScriptDb for partial matches?

I tried using RegEx and it did not return any results:
function findRecord() {
var db = ScriptDb.getMyDb();
var toFind = /Quality/i;
var results = db.query({companyName: toFind});
while (results.hasNext()) {
var result = results.next();
Logger.log(Utilities.jsonStringify(result));
}
}
From what I can see, ScriptDb's query() will only return exact matches for strings.
The only way I can see is to return the entire database and then iterate through it. I really hope there is a way to query partial matches.
Try iterating over the results using the match method
function testQuery() {
var db = ScriptDb.getMyDb();
var results = db.query({});
var start = new Date();
while (results.hasNext()) {
var result = results.next();
if (result.companyName.match(/qual.*/i)){
Logger.log(Utilities.jsonStringify(result));
}
}
var endTime = new Date();
Logger.log("time is " + (endTime.getTime() - start.getTime()) + "ms");
}
ScriptDb currently doesn't support partial matches in strings. Depending on the data you may be able to use the anyOf method:
var results = db.query({
companyName: db.anyOf(['Quality', 'quality'])
});
I don't think that is possible. You may open an "enhancement request" on the issue tracker.
But depending on your usage, it may be possible to achieve your goal if you structured your database differently, probably creating some kind of "tag" category properties for your objects, that you set beforehand, i.e. when adding the object to the database, so you can query on it later.