DriveApp.searchFiles not giving all matching files - google-apps-script

I am trying to create a Google Sheet where all instances of Sheets with the same string prefix in the title are gathered, in particular their file IDs. Out of 110 files with a perfect match, the output is only 24 files, always the same files. I am testing this to not actually alert clients in a different outputtest sheet, for which I have written the code underneath:
function onOpen(e) {
SpreadsheetApp.getUi()
.createMenu('testMenu')
.addItem('mainFunction', 'main')
.addToUi();
};
function main() {
var sheetName = 'Contacts';
var q = '(title contains "2022 Client Data ")'; //all titles start with this string
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.getSheetByName(sheetName);
var data = sheet.getDataRange().getValues();
//loadSettings(spreadsheet, data);
var clientNames = getClientNames(data);
var clientFiles = getClientFiles(q);
//var eDict = matchClientWithFile(clientNames, clientFiles);
outputTest(spreadsheet, clientFiles);
};
function getClientFiles(q) {
var files = DriveApp.searchFiles(q);
var clientFiles = {};
while (files.hasNext()) {
var clientFile = files.next();
clientFiles[clientFile.getName()] = clientFile.getId();
};
return clientFiles;
};
function outputTest(spreadsheet, dict) {
var sheet = spreadsheet.getSheetByName('outputtest');
if (!sheet) {
var sheet = spreadsheet.insertSheet('outputtest');
}
for (const [key, value] of Object.entries(dict)) {
sheet.appendRow([key, value]);
};
};
The text in the q-string is the only requirement for a file match, as all file titles start with "2022 Client Data" which is really the only identifier. All files are stored in a shared directory.
I have looked up previous StackOverflow questions as well as documentation to see whether everything was in place, as well as making the q-string less specific and cleaning up the code, with the same result.
Drive searchbar with the type:spreadsheet title:"2022 client data" shows the amount of matches I should be getting in the outputtest sheet. I am hoping to put all 110 currently existing files together.
EDIT: when accessing it from a different google account in the shared Drive, the amount of files changes. It is highly likely this has to do with the DriveApp.searchFiles function only accessing the files that have been created by the account running the script. Is there a way to change this?

Related

List Google drive folder contents to google sheets with only new files

Looking to learn how to improve my use of loops. Currently I need to list the names and URLS from a google drive Folder to a sheet and this is the code that I have:
Existing Code
function wthFolderContents() {
var folder_id = 'myFolderID';
var folders = DriveApp.getFolderById(folder_id)
var contents = folders.getFiles();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("SheetName");
sheet.clearContents()
sheet.appendRow( ['name', 'link'] );
var file;
var name;
var link;
var row;
while(contents.hasNext()) {
file = contents.next();
name = file.getName();
link = file.getUrl();
sheet.appendRow ( [name, link] );
with this code everytime the script is run the contents are cleared and then relisted. I am looking at a way of doing this dynamically / only update the new files so the script runs more effeciently.
Ive tried the following
New Code
function wthFolderContents2() {
var folder_id = '1vBzucZsb0SMOoHSWGtkUF-5QLQr5Fh1C';
var folders = DriveApp.getFolderById(folder_id)
var contents = folders.getFiles();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("WHTCert");
var lastRow = sheet.getLastRow()
var existing = sheet.getRange(1,1,lastRow,1).getValues()
for(i=1;i<lastRow;i++) {
var existingFilename = existing [i][0]
Logger.log(existingFilename)
while(contents.hasNext()) {
var file;
var name;
var link;
file = contents.next();
name = file.getName();
link = file.getUrl();
if (!name == existingFilename) {
sheet.appendRow ( [name, link] );
}
}
}
I cant get this to work, not sure what exactly where I have gone wrong. Hope someone can point me int he right direction!
Cheers
I believe your goal is as follows.
You want to reduce the process cost of your script.
Modification points:
In your script, appendRow is used. In this case, the process cost will become high. Ref
The search for files is run in a loop. In this case, the process cost will become high.
In your situation, it seems that you want to retrieve the file list just under the specific folder. In this case, I thought that when Drive API is used, the process cost can be reduced. In this answer, I would like to propose using Drive API in your script. When this is reflected in your script, it becomes as follows.
When Drive API is used, all values can be retrieved. So, I thought that your 1st process might be able to be used.
Modified script:
Before you use this script, please enable Drive API at Advanced Google services.
function wthFolderContents2() {
var folder_id = '1vBzucZsb0SMOoHSWGtkUF-5QLQr5Fh1C';
// Retrieve file list.
var q = `'${folder_id}' in parents and trashed = false and mimeType != '${MimeType.FOLDER}'`;
var fileList = [['name', 'link']];
var pageToken = "";
do {
var obj = Drive.Files.list({ q, maxResults: 1000, pageToken, fields: "nextPageToken,items(id,title)", corpora: "allDrives", supportsAllDrives: true, includeItemsFromAllDrives: true });
if (obj.items.length > 0) {
fileList = [...fileList, ...obj.items.map(({ id, title }) => [title, `https://docs.google.com/presentation/d/${id}/edit?usp=drivesdk`])];
}
pageToken = obj.nextPageToken;
} while (pageToken);
// Put the values to Spreadsheet.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("WHTCert");
sheet.clearContents();
sheet.getRange(1, 1, fileList.length, fileList[0].length).setValues(fileList);
}
When this script is run, the file list (filename and URL) is retrieved from the specific folder. And, the retrieved values to the "WHTCert" sheet.
Reference:
Files: list
Please convert this 2 script for scan folder only one subfolder and file, show folder name and link.
function wthFolderContents()
function wthFolderContents2()

Using Logger.Log to log different value

I was wondering: is it even possible to use Logger.Log in Google Apps Script to log different string to be posted to a spreadsheet?
I have the following code:
var ss = SpreadsheetApp.openByUrl("spreadsheet url");
var sheet = ss.getSheetByName("spreadsheet sheet");
var DocNumber = e.parameter.DocNumber;
var folderId = "Folder ID 1";
var lastFileUrl = getLatestFile(folderId); // just a function that retrieves url of latest file in the folder
Logger.log(lastFileUrl);
var addUrl = sheet.getRange(1,2,sheet.getLastRow(),1);
var fileURL = "https://drive.google.com/uc?export=view&id="+lastFileUrl;
var folderId2 = "Folder ID 2";
var lastFileUrl2 = getLatestFile(folderId2); // same as above
Logger.log(lastFileUrl2);
var addUrl2 = sheet.getRange(1,3,sheet.getLastRow(),1);
var fileURL2 = "https://drive.google.com/uc?export=view&id="+lastFileUrl2;
sheet.appendRow([DocNumber,fileURL,fileURL2]);
}
When this get posted to the spreadsheet, it only posts the second url (fileURL2) - I assume because the last value in the log is this. But I was hoping to post both URL into the spreadsheet.
I tried setting it as a var first as well:
var URL2 = Logger.log(lastFileURL2);
but then the posted value will be https://drive.google.com/uc?export=view&id=Logger
I also tried using appendRow before the second URL logging but it still only takes the second url and disregard the first url.
Therefore, I was curios whether this is even possible at all?
And if not, what's the best way to achieve this without using Logger.log?
Spreadsheet output:
URL1 and URL2 is the URL from Google Drive folder.
Also, forgot to mention, I'm using the script as a Web App, used by an android app. Posting files into the Drive folder is okay, the only problem is fetching the links of the files in different folders.
These are the codes I used to get the latest file url from my folders:
function getLatestFile(folderId) {
var files = DriveApp.getFolderById("Folder_1_ID").getFiles();
var fileObj = [];
while (files.hasNext()) {
var file = files.next();
fileObj.push({id: file.getId(), date: file.getDateCreated()});
}
fileObj.sort(function(a, b) {return new Date(b.date) - new Date(a.date)});
return fileObj[0].id;
}
function getLatestFile(folderId2) {
var files2 = DriveApp.getFolderById("Folder_2_ID").getFiles();
var fileObj2 = [];
while (files2.hasNext()) {
var file2 = files2.next();
fileObj2.push({id: file2.getId(), date: file2.getDateCreated()});
}
fileObj2.sort(function(a, b) {return new Date(b.date) - new Date(a.date)});
return fileObj2[0].id;
}
Problem
Having two functions declared under the same name
Solution
Step by step:
Remove one of the functions (they are identical in terms in usage)
Make the remaining one use the parameter passed in it:
function getLatestFile(folderId) {
var files = DriveApp.getFolderById(folderId).getFiles();
var fileObj = [];
while (files.hasNext()) {
var file = files.next();
fileObj.push({id: file.getId(), date: file.getDateCreated()});
}
fileObj.sort(function(a, b) {return new Date(b.date) - new Date(a.date)});
return fileObj[0].id;
}
Change Logger to console - as of recently, all logs are sent to Stackdriver service, and thus there is no benefit in using Logger (besides by using console you make script more portable).
Commentary
What happens when you declare two or more functions under same name? Normally, the last one declared gets executed (basically, second declaration overwrites the first):
function clone(original) {
return `I am the clone of ${original}`;
}
function clone(cloned) {
return `I am a clone of ${cloned}'s clone`;
}
const elem = document.querySelector("#cloned");
elem.textContent = clone("Gary");
<h2 id="cloned"></h2>

Trying to create tree-view of google drive folders

Many thanks for the comments and response. That code was a little too advanced for me and I ended up finding a very inelegant solution that I used because I ran out of time. I was able to get the code to list Folder and first level of subFolders with links, but I have not yet been able to get it to iterate through all levels of folders, mostly because I just need to back up and learn a lot of the basics. I was also able to get all folders to list using some code I found to create a tree, but I couldn't get it to format in a way that you could actually see the structure, or add links. I'm going to continue to try, and will post if I sort it out. Here is what I used, which was fine for our purposes because our shared drive is fairly limited.
For reference, this was the code I used to start with:
https://superuser.com/questions/1095578/list-of-subfolder-names-and-file-links-in-google-sheets-script
function listFolders(foldername) {
var ss = SpreadsheetApp.openById(ID);
var sheet = ss.getSheetByName("sheet name");
sheet.appendRow("Parent Folder", "Name", "Link" ]);
//change the folder ID below to reflect your folder's ID (look in the
URL when you're in your folder)
var folders = DriveApp.getFolderById(ID);
var contents = folders.getFolders();
var cnt = 0;
var folderD;
while (contents.hasNext()) {
var folderD = contents.next();
cnt++;
data = [
folders.getName(),
folderD.getName(),
folderD.getUrl(),
];
sheet.appendRow(data);
};
};
Original Post:
I am a beginner using script in google sheets and I am trying to create a list of folders in a google drive with many subfolders. Ideally it would be a tree form but I'd settle for a list at this point. I don't need to list all the files, just the folders. I have been trying to get the code below to work but it keeps hanging up at calling up the spreadsheet. Can anyone help?
I have tried calling up both the folders and the spreadsheet by both name and ID but it always tells me it can't execute the getactivespreadsheet command. I have also tried to modify the code referred to in another another question but I can't get that to work either: https://ctrlq.org/code/19923-google-drive-files-list
function generateFolderIndex(myfoldername) {
var folder = DriveApp.getFolderById('0B8vOJQUb-IIVTHdudlZSVkdtdE0');
var subFolders = folder.getFolders();
var childFolders = subFolders
var ss = SpreadsheetApp.getActiveSpreadsheet('1Trv9OtJFnD4AdSHrZKFfsSu6JMV9f78H6wwZNhF2_M4');
var sheet = ss.getSheetByName('Directory');
sheet.clear(directory);
sheet.appendRow([name, link]);
while (subFolders.hasNext())
{
var childFolder = childFolders.next();
var foldername = childFolder.getname();
var name = childFolder.getName()
var link = childFolder.getUrl()
var date = childFolder.getDateCreated()
data = [name, link]
sheet.appendRow(data);
}
};
I am trying to get a sheet that lists folders and subfolders with URL links. I am currently receiving the following error message:
[19-05-31 15:32:20:911 EDT] Execution failed: Cannot find method getActiveSpreadsheet(string). (line 5, file "Code") [0.432 seconds total runtime]
Or.. the easy way...
Use DRIVE or FS DRIVE APP for desktop in PC. Usea A CMD (windows)... AND THE FUNCTION
TREE >a.txt
The generated file a.txt will display all the tree.
IT SAVES HOURS OF RESEARCH.
SpreadsheetApp.getActiveSpreadsheet() doesn't have any parameters.
However
SpreadsheetApp.openById('ssid') does require and id. I think perhaps you meant to be using openById();
openById
getActiveSpreadsheet
This is a script that I'm currently working on but it generates a list of Spreadsheets and you can exclude folders by id and files by id.
function getAllSpreadsheets() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('FilesAndFolders');
if(sh.getLastRow()>0) {
sh.getRange(1,1,sh.getLastRow(),2).clear().clearDataValidations();
}
getFnF();
SpreadsheetApp.getUi().alert('Process Complete')
}
var level=0;
function getFnF(folder) {
var folder= folder || DriveApp.getRootFolder();
//var folder=DriveApp.getRootFolder();
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('FilesAndFolders');
var files=folder.getFilesByType(MimeType.GOOGLE_SHEETS)
while(files.hasNext()) {
var file=files.next();
if(isExcluded(file.getId(),'file')){continue;}
var firg=sh.getRange(sh.getLastRow() + 1,level + 1);
firg.setValue(Utilities.formatString('=HYPERLINK("%s","%s")',file.getUrl(),'FILE: ' + file.getName()));
firg.offset(0,1).insertCheckboxes('Exclude','Include');
}
var subfolders=folder.getFolders()
while(subfolders.hasNext()) {
var subfolder=subfolders.next();
if(isExcluded(subfolder.getId(),'folder')){continue;}
var forg=sh.getRange(sh.getLastRow() + 1,level + 1);
forg.setValue(Utilities.formatString('=HYPERLINK("%s","%s")',subfolder.getUrl(),'FOLDER: ' + subfolder.getName()));
//forg.offset(0,1).insertCheckboxes('Exclude','Include');
//level++;
getFnF(subfolder);
}
//level--;
}
function isExcluded(id,type) {//type: file or folder
var type=type||'Missing Input';
var xFldrIdA=['Excluded folder ids'];
var xFileIdA=['Excluded file ids'];
var type=type.toLowerCase();
switch(type) {
case 'file':
return xFileIdA.indexOf(id)>-1;
break;
case 'folder':
return xFldrIdA.indexOf(id)>-1;
break;
default:
throw(Utilities.formatString('Error: Invalid Type: %s in isExcluded.',type));
return true;//assume excluded
break;
}
}
Your welcome to use it, perhaps it will help.

How to display websites containing searched keyword on Google Spreadsheet using Google Scripts

Currently the Google Script code I have creates a button that prompts the user for a keyword to search for. It then is supposed to return the results on a spreadsheet by retrieving their URL/Name/Title and displaying them in their respective columns on Google Sheets. I want to be able to have the search, search through a particular site that hosts links to subpages of the site and return the sites that contain the searched term anywhere on that site and paste the results onto a spreadsheet.
EDIT
The problem I get when running the code is that after searching the keyword, the spreadsheet returns a dialog box indicating that the "resource you requested could not be located" but the website I am searching through does in fact have the keyword on multiple pages.
This is the code I need help with:
function websearch() {
// Prompt the user for a search term
var websearchTerm = Browser.inputBox("Enter the keyword to search for:");
// Get the active spreadsheet and the active sheet
var wss = SpreadsheetApp.getActiveSpreadsheet();
var wsheet = wss.getActiveSheet();
// Set up the spreadsheet to display the results
var theaders = [["Name", "Title", "Web Address"]];
wsheet.clear();
wsheet.getRange("A1:C1").setValues(theaders);
// Search the webpages associated with the given URL
var site = SitesApp.getSite("https://sites.google.com/a/umich.edu/hb-ua/");
//var matches = site.search(websearchTerm);
var matches = site.search("fullText contains '"+websearchTerm.replace("'","\'")+"'");
var woutput = [];
// Loop through the results and get the file name, file title, and URL
while (site.hasNext()) {
var wfile = site.next();
var wname = wfile.getName();
var wtitle = wfile.getTitle();
var wurl = wfile.getUrl();
// push the file details to our output array (essentially pushing a row of data)
woutput.push([wname, wtitle, wurl]);
}
// write data to the sheet
wsheet.getRange(2, 1, woutput.length, 3).setValues(woutput);
}
Then this code I know for sure works, it does what I am asking help for, but searches through google drive instead of google sites/specific parent websites:
function search(Phrase, FolderID) {
// Prompt the user for a search term
var searchTerm = Browser.inputBox("Enter the string to search for:");
// Get the active spreadsheet and the active sheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
// Set up the spreadsheet to display the results
var headers = [["File Name", "File Type", "URL"]];
sheet.clear();
sheet.getRange("A1:C1").setValues(headers);
// Search the files in the user's Google Drive for the search term based on if the word is included in thefile or name
// Search Reference Guide: https://developers.google.com/apps-script/reference/drive/drive-app#searchFiles(String)
var files = DriveApp.searchFiles("fullText contains '"+searchTerm.replace("'","\'")+"'");
//var SearchString = 'fullText contains "' + Phrase + '" and "' + FolderID + '" in parents';
//var files = DriveApp.searchFiles(SearchString);
// create an array to store our data to be written to the sheet
var output = [];
// Loop through the results and get the file name, file type, and URL
while (files.hasNext()) {
var file = files.next();
var name = file.getName();
var type = file.getMimeType();
var url = file.getUrl();
// push the file details to our output array (essentially pushing a row of data)
output.push([name, type, url]);
}
// write data to the sheet
sheet.getRange(2, 1, output.length, 3).setValues(output);
}
This is the search dialog prompt code:
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var searchMenuEntries = [ {name: "Search in all files", functionName: "search"}];
var websearchMenuEntries = [ {name: "Search in all files", functionName: "websearch"}];
ss.addMenu("Search Google Drive", searchMenuEntries);
ss.addMenu("Search UMich", websearchMenuEntries);
}
You didn't describe what is wrong, or what error is shown.
But your analogy script transformed from Drive's script is looking good except one thing:
method DriveApp.searchFiles() returns FileIterator, but
method Site.search() return just Array of Pages.
Try to use for(), foreach() or .forEach() instead of .next() to iteration.

returning document owner for large list of Google Drive doc IDs

I am trying to identify the owner of a long list (almost 1000 items) of documents within Google Drive. The owners may vary between the docs. I have the unique doc ID for each item.
I'm the admin for Google Apps at my domain, and have a utility that lets me punch in a doc ID, and see the ownership of that particular item. However, I have to do this through a web interface, one at a time.
Is there any way to leverage Google's API, say in a spreadsheet, that can return a document's owner when given the doc ID? Or Google scripts perhaps?
It can be done in Apps Script.
Class File
A file in Google Drive. Files can be accessed or created from DriveApp.
Code Sample
var files = DriveApp.getId('DOC_ID');
while (files.hasNext()) {
var file = files.next();
var owner = file.getOwner()
}
getOwner()
Gets the owner of the File.
Return
User — a User object representing the owner
Hope this helps.
Worked with a colleague of mine to generate the following Google Apps script that works great.
Usage: With a list of doc ID's in column D, highlight the rows you want to check (any cells are fine, just have something highlighted in the desired rows) and execute using the custom "Automation" menu.
Result: Owner is then returned in column E.
function getOwnerFromValue(val){
var doc = DriveApp.getFileById(val);
var user = doc.getOwner();
return user;
}
function scanRange() {
var range = SpreadsheetApp.getActiveRange();
var start = range.getRow();
var stop = range.getLastRow();
var range = SpreadsheetApp.getActive().getRange("D"+start+":D"+stop)
var resultRange = SpreadsheetApp.getActive().getRange("E"+start+":E"+stop);
var result = [];
range.getValues().forEach(function(obj){
obj = obj[0];
if(obj.trim() != "")
result.push([getOwnerFromValue(obj).getEmail()]);
});
resultRange.setValues(result);
}
function onOpen(){
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var entries = [{
name : "Get Owners' User",
functionName : "scanRange"
}];
sheet.addMenu("Automation",entries);
}
function onAdd(e){
var editted = e.range;
if(editted.getRow() != 1 && editted.getColumn() == 4) {
Logger.log(getOwnerFromValue(editted.getValue()));
}
}