简体   繁体   English

提高 Google App Script 搜索功能的性能

[英]Improve performance in Google App Script Search functions

I have written a script using Google App Script (in javascript) and I am looking for a way to be best optimize function that return objects, based on one or more search fields.我已经使用 Google App Script(在 javascript 中)编写了一个脚本,我正在寻找一种方法来最好地优化 function,它基于一个或多个搜索字段返回对象。 The data is stored in a Google Sheet.数据存储在 Google 表格中。 The UI I have passes parameters to my function, then I iterate over a given sheet to find rows that meet a criteria, and add cells to an object, to be returned.我将参数传递给 function 的 UI,然后我遍历给定的工作表以查找满足条件的行,并将单元格添加到 object,以返回。 The return could be just one object or a list of objects.返回可能只是一个 object 或对象列表。 For the most part, this works fine, but if I have this type of function nested in a loop it can really drag the performance.在大多数情况下,这工作正常,但如果我将这种类型的 function 嵌套在一个循环中,它确实会拖累性能。 Any advise on how to improve performance would be greatly appreciated.任何关于如何提高性能的建议将不胜感激。 Here is an example of my code:这是我的代码示例:

function GetAllReportByOrgID_DataLayer_(org_id, reporting_periods) {
    //get all reporting period for program
    var rows = GetDataRows_(DATA_SPREAD_SHEET_ID, RESPONSE_PAGE);
    var surveys = [];   
    for (var i = 1; i < rows.length; i++) {
        var row = rows[i];
        var found_org_id = row[2];
        var found_is_active = row[13];
        if (found_org_id == org_id && found_is_active == true ) {
            var survey = {};
            survey.indicator_id = row[0];
            survey.program_id = row[1];
            survey.org_guid = row[2];
            survey.survey_response = row[3];
            survey.reporting_period = row[5];
            survey.reporting_period_name = GetReportingPeriodNameById_(row[5], reporting_periods);
            survey.is_final_report = row[6];
            survey.is_submitted = row[7];
            survey.submitted_by = row[8];
            survey.submitted_by_email = row[9];
            survey.date_created = ConvertUnixTimeStampToDateTime_(row[10]);
            survey.date_updated = ConvertUnixTimeStampToDateTime_(row[11]);
            survey.fiscal_year = row[12];
            survey.documents = GetDocumentsById_DataLayer_({
                 program_id: row[13]
            });
            surveys.push(survey);
        }
    }
    surveys.success = true;
    return surveys;
}
function GetDataRows_(Sheet_Id, SheetName) {
    var sheet = GetSheet_(Sheet_Id, SheetName);
    var rows = [];
    if (sheet) {
        rows = sheet.getDataRange().getValues();
    }
    return rows;
}
function GetSheet_(Sheet_Id, SheetName) {  
  var ss = SpreadsheetApp.openById(Sheet_Id);
  var sheet = ss.getSheetByName(SheetName);
  return sheet;
}
function GetReportingPeriodNameById_(id, reporting_periods) {
   if (id) {
       for (var i = 0; i < reporting_periods.length; i++) {
           if (reporting_periods[i].id == id) {
              return reporting_periods[i].value
           }
        }
      return "Reporting Period Not Found"
  } else {
    return "Reporting Period Not Found"
 }
}

function GetDocumentsById_DataLayer_(data) {
    var rows = GetDataRows_(DATA_SPREAD_SHEET_ID, PROGAM_DOCUMENTS_PAGE);
    var documents = [];
    var program_id = data.program_id.trim();

    for (var i = 1; i < rows.length; i++) {
        var row = rows[i];
        var found_program_id = row[1];
        var is_active = row[6];
    if(is_active === true){
      if (found_program_id === program_id) {
        var document = {};
        document.document_id = row[0];
        document.program_id = row[1];
        document.document_name = row[2];
        document.file_id = row[3];
        document.file_name = row[4];
        document.file_url = row[5]
        document.date_created = ConvertUnixTimeStampToDateTime_(row[7]);
        document.date_updated = ConvertUnixTimeStampToDateTime_(row[8]);
        documents.push(document);
      }
    }       
    }
    documents.success = true;
    return documents;
}

function ConvertUnixTimeStampToDateTime_(unix_timestamp) {
    if (!unix_timestamp) {
        return "";
    }
    var a = new Date(unix_timestamp * 1000);
    var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
    var year = a.getFullYear();
    var month = months[a.getMonth()];
    var date = a.getDate();
    var hour = a.getHours();
    var min = a.getMinutes();
    var sec = a.getSeconds();
    var time = a.getMonth() + "/" + date + "/" + year + " " + hour + ":" + min + ":" + sec;
    return time;
}

This code mostly works fine, except when it is in a loop that gets called 100 times or so, then things lag and can take a minute or more to process.这段代码大部分工作正常,除非它在一个被调用 100 次左右的循环中,然后事情会滞后,可能需要一分钟或更长时间来处理。 The sheets aren't that big, less that 200 rows and 15 columns.工作表不是那么大,不到 200 行和 15 列。
thanks谢谢

The reason for performance depreciation is the GetDataRows_ function, because it makes repeated calls to Spreadsheet.openById() , ss.getSheetByName(SheetName);性能下降的原因是GetDataRows_ function,因为它重复调用Spreadsheet.openById()ss.getSheetByName(SheetName); . . You could try using a global map to cache these objects.您可以尝试使用全局map来缓存这些对象。 For example, Use a Spreadsheet map例如,使用Spreadsheet map

const globalSSMap = new Map();//global SS map

Then your getter functions can be rewritten.然后你的 getter 函数可以被重写。 For eg, to get ss , use例如,要获取ss ,请使用

if(!globalSSMap.has(Sheet_id)) globalSSMap.set(Sheet_id,SpreadsheetApp.openById(Sheet_Id));
var ss = globalSSMap.get(Sheet_id);

You can refer to this sample code:你可以参考这个示例代码:

Data_Layer.gs数据层.gs

function GetAllDataByManager_DataLayer_(loggedin_user) {
  var ss = SpreadsheetApp.openById(DATA_SPREAD_SHEET_ID);

  var sheets = ss.getSheets();
  // create sheet objects
  var sheetData = {};
  sheets.forEach(sheet => {
    sheetData[sheet.getName()] = sheet.getDataRange().getValues();
  })

    var program_rows = sheetData[PROGRAM_MANAGERS];
    var ip_ids = [];
    var Company_data = [];
  var runtimeCountStart = new Date();

  //first search to make sure logged in user exists, and is marked as Active == True and is marked as an Admin
  //if they exist, add their ID to the list 
  //Should only see records for the logged in user if
  //they are either listed as the primary manager  (company tab)
  //or they are listed as an additional manager (program managers tab)

    for (var i = 1; i < program_rows.length; i++) {
        var row = program_rows[i];
        var found_admin_email = row[2];
        var found_is_active = row[10];
        if (found_admin_email == loggedin_user && found_is_active == true) {
            ip_ids.push(row[1])
        }
    }

  var partner_rows = sheetData[PARTNER_PAGE];  
  for (var i = 1; i < partner_rows.length; i++) {
    var partner_row = partner_rows[i]
    var found_partner_id = partner_row[0];
    var add_record = false;
    if(ip_ids.includes(found_partner_id)){
      add_record = true;
    }else{
      var found_cor_email = partner_row[5]
      if(loggedin_user.toUpperCase() == found_cor_email.toUpperCase()){
        add_record = true;
      }
    }    
    if(add_record == true){
      var partner = {
                    ip_id: partner_row[0],
                    ip_name: partner_row[1],
                    ip_poc_name: partner_row[2],
                    ip_poc_email: partner_row[3],
                    manager_name: partner_row[4],
                    manager_email: partner_row[5],
                    is_active: partner_row[6],
                    date_created: partner_row[7],
                    created_by: partner_row[8],
                    partner_programs:  GetAllProgramDataByIP_DataLayer_(sheetData, found_partner_id),  
          partner_notes: GetProgramNarrativesByPartnerID_DataLayer_(sheetData, found_partner_id),
          partner_reports: GetAllReportByPartnerID_DataLayer_(sheetData, found_partner_id)       
                };
                Company_data.push(partner)
    }

  }

  stop = new Date();
  newRuntime = Number(stop) - Number(runtimeCountStart);
  newRuntime  = (newRuntime /1000)/60

    return Company_data;

}
function GetAllProgramDataByIP_DataLayer_(sheetData, ip_id) {
    var rows = sheetData[PROGRAM_PAGE];
    var programs = [];
    for (var i = 1; i < rows.length; i++) {
        var row = rows[i];
        var found_partner_id = row[1];
        var program = {}
        if (found_partner_id === ip_id) {      
            program.program_id = row[0].toString();
            program.partner_id = row[1].toString();
            program.program_name = row[2].toString();
            program.program_country = row[3].toString();        
            program.program_background = row[4].toString();
            program.program_objectives = row[5].toString();
            program.program_justification = row[6].toString();
            program.program_start_date = row[7].toString();
      program.program_status = row[8].toString();
            program.program_contract_number = row[9].toString();            
            program.is_active = row[10]
            program.date_created = ConvertUnixTimeStampToDateTime_(row[11].toString());
            program.date_updated = ConvertUnixTimeStampToDateTime_(row[12].toString());
            program.success = true;
            programs.push(program)
        }

    }
    return programs;
}
function GetProgramNarrativesByPartnerID_DataLayer_(sheetData, partner_id) {
    var rows = sheetData[PROGRAM_NARRATIVE_NOTE];
    var programs_notes = [];
    var response = {};
    for (var i = 1; i < rows.length; i++) {
        var row = rows[i];
        var found_partner_id = row[2];
        var is_active = row[7];
    if(is_active === true){
      if (found_partner_id === partner_id) {
        var note = {};
        note.note_id = row[0];
        note.program_id = row[1];
        note.company_guid = row[2];        
        note.note_title = htmlEscape_(row[3]);
        note.note_desc = htmlEscape_(row[4]);
        note.note_reportingPeriod = row[5];
        note.activity_theme = row[6];
        note.date_created = ConvertUnixTimeStampToDateTime_(row[8]);
        note.date_updated = ConvertUnixTimeStampToDateTime_(row[9]);
        programs_notes.push(note);
      }
    }       
    }
    response.success = true;
    response.programs_notes = programs_notes
    return response;
}
function GetAllReportByPartnerID_DataLayer_(sheetData, partner_id) {
    //get all reporting period for program
    var rows = sheetData[RESPONSE_PAGE];  
    var surveys = [];   
    for (var i = 1; i < rows.length; i++) {
        var row = rows[i];
        var found_partner_id = row[2];
        if (found_partner_id == partner_id) {
            var survey = {};
            survey.indicator_id = row[0];
            survey.program_id = row[1];
            survey.company_guid = row[2];
            survey.survey_response = row[3];
            survey.reporting_period = row[5];
            survey.is_final_report = row[6];
            survey.is_submitted = row[7];
            survey.submitted_by = row[8];
            survey.submitted_by_email = row[9];
            survey.date_created = ConvertUnixTimeStampToDateTime_(row[10]);
            survey.date_updated = ConvertUnixTimeStampToDateTime_(row[11]);
            survey.fiscal_year = row[12];
            surveys.push(survey);
        }
    }
    surveys.success = true;
    return surveys;
}

Helper_Functions.gs Helper_Functions.gs

function ConvertUnixTimeStampToDateTime_(unix_timestamp) {
    if (!unix_timestamp) {
        return "";
    }
    var a = new Date(unix_timestamp * 1000);
    var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
    var year = a.getFullYear();
    var month = months[a.getMonth()];
    var date = a.getDate();
    var hour = a.getHours();
    var min = a.getMinutes();
    var sec = a.getSeconds();
    var time = a.getMonth() + "/" + date + "/" + year + " " + hour + ":" + min + ":" + sec;
    return time;
}

function CreateGUID_() {
    return Utilities.getUuid();
}
function htmlEscape_(str) {
  str = str.toString();
    if (str && str.length > 0) {
        return str.replace(/<[^>]+>/g, "")
    } else {
        return "";
    }
}

Note:笔记:

  • I didn't see GetDocumentsById_DataLayer_ and survey.documents in the shared script.我没有在共享脚本中看到GetDocumentsById_DataLayer_survey.documents
  • I just looked for something to improve in your main function GetAllDataByManager_DataLayer_() where nested loop occur when you are checking your partner_rows variable.我只是在您的主要 function GetAllDataByManager_DataLayer_()中寻找可以改进的地方,当您检查partner_rows变量时会发生嵌套循环。
  • I don't think there's any more we can do regarding your nested loop check.我认为关于嵌套循环检查我们无能为力。

Modifications Done:修改完成:

  • Although you prevented repeating the calls for Spreadsheet.openById() , you still make individual calls when you map your sheet objects since you use ss.getSheetByName(SheetName);尽管您阻止了重复调用Spreadsheet.openById() ,但由于您使用ss.getSheetByName(SheetName); map 您的工作表对象时,您仍然会单独调用to get individual sheet object based on the SheetName and even reading the sheet values during every loop for 3 different sheets根据 SheetName 获取单个工作表SheetName ,甚至在每个循环中读取 3 个不同工作表的工作表值
  • What I did is to get all your sheet object available in your spreadsheet using getSheets() , get its data range values then map them based on their sheet name.我所做的是使用getSheets()在电子表格中获取所有工作表 object ,获取其数据范围值,然后根据工作表名称获取 map 。 As what was suggested by @TheMaster in the other answer正如@TheMaster 在其他答案中所建议的那样
  • Remove unnecessary functions in Helper_Functions.gs删除Helper_Functions.gs中不需要的函数

Execution Log:执行日志:

在此处输入图像描述

  • For the code changes, refer to the last 2 recent Web Apps execution logs.代码改动参考最近2个Web Apps执行日志。 Compare it to the first execution将其与第一次执行进行比较

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM