Script 1 | Search Query Report - Manager Account ( MCC )

USE - With this script you can analyse Search Query Performance report to find undesired search terms and add them as negative (or positive) exact keywords.

=======================================================================
MCC Search Query Report extends Search Query Report to run for multiple accounts under a single MCC. The script uses the Search Query Performance report to find undesired search terms and add them as negative (or positive) exact keywords.

The script reads a spreadsheet for configuration of thresholds. There’s a row for the default configuration, and rows for account-specific configurations (one row for each specified account). When processing each account under the MCC, if the account is found in the account-specific configurations, it will use that specific configuration; otherwise it will use default configuration.

=========================================================================
 Step 1. Replace the Spreadsheet URL to the new one
 Step 2. Set the frequency to Weekly
=======================================================================

// Copyright 2015, Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @name MCC Search Query Report
 *
 * @overview The MCC Search Query Report script uses the Search Query Performance
 *     Report to find undesired search terms in accounts under an MCC account
 *     and add them as negative (or positive) exact keywords. See
 *     https://developers.google.com/adwords/scripts/docs/solutions/mccapp-search-query
 *     for more details.
 *
 * @author AdWords Scripts Team [adwords-scripts@googlegroups.com]
 *
 * @version 1.0.3
 *
 * @changelog
 * - version 1.0.3
 *   - Upgrade to API version v201609.
 * - version 1.0.2
 *   - Added validation for external spreadsheet setup.
 * - version 1.0.1
 *   - Improvements to time zone handling.
 * - version 1.0
 *   - Released initial version.
 */

var SPREADSHEET_URL = 'YOUR_SPREADSHEET_URL';

// Please fix the following variables if you need to reformat the spreadsheet
// column numbers of each config column. Column A in your spreadsheet has
// column number of 1, B has number of 2, etc.
var COLUMN = {
  accountId: 2,
  impressionsThreshold: 3,
  averageCpcThreshold: 4,
  ctrThreshold: 5,
  costPerConvThreshold: 6
};

// Start row/column numbers and total columns of actual config
// (without header and margin)
var CONFIG = {
  startRow: 6,
  startColumn: 2,
  totalColumns: 5
};

// One currency unit is one million micro amount.
var MICRO_AMOUNT_MULTIPLIER = 1000000;

/**
 * Configuration to be used for running reports.
 */
var REPORTING_OPTIONS = {
  // Comment out the following line to default to the latest reporting version.
  apiVersion: 'v201609'
};

function main() {
  // Read config data from the spreadsheet
  Logger.log('Using spreadsheet - %s.', SPREADSHEET_URL);
  var spreadsheet = validateAndGetSpreadsheet(SPREADSHEET_URL);

  // Make sure the spreadsheet is using the account's timezone.
  spreadsheet.setSpreadsheetTimeZone(AdWordsApp.currentAccount().getTimeZone());
  var sheet = spreadsheet.getSheets()[0];

  var endRow = sheet.getLastRow();
  var rows = endRow - CONFIG.startRow + 1;
  var config = [];
  if (rows > 0) {
    config = sheet.getRange(CONFIG.startRow, CONFIG.startColumn,
               rows, CONFIG.totalColumns).getValues();
  }
  else {
    Logger.log('Empty config, abort!');
    return;
  }

  var mccAccount = AdWordsApp.currentAccount();
  sheet.getRange(2, 6).setValue(mccAccount.getCustomerId());

  var accountIterator = MccApp.accounts().get();
  while (accountIterator.hasNext()) {
    var account = accountIterator.next();
    processAccount(account, config);
  }

  // Update "Last execution" timestamp
  var today = new Date();
  sheet.getRange(1, 3).setValue(today);
  MccApp.select(mccAccount);
}

// Core logic for processing each account
function processAccount(account, config) {
  // Swith to current adwords account
  MccApp.select(account);
  var accountId = account.getCustomerId();

  var accountIdCol = COLUMN.accountId - CONFIG.startColumn;
  var impsThrshCol = COLUMN.impressionsThreshold - CONFIG.startColumn;
  var avgCpcThrshCol = COLUMN.averageCpcThreshold - CONFIG.startColumn;
  var ctrThrshCol = COLUMN.ctrThreshold - CONFIG.startColumn;
  var costPerConvThrshCol = COLUMN.costPerConvThreshold - CONFIG.startColumn;

  // Get config for this account, if not found use default entry,
  // if no default entry just skip
  var configIndex = -1;
  var hasDefault = (config[0][accountIdCol].toLowerCase() == 'default');
  var configStartRow = hasDefault ? 1 : 0;
  for (var i = configStartRow; i < config.length; i++) {
    if (config[i][accountIdCol] === accountId) {
      configIndex = i;
      break;
    }
  }

  if (configIndex == -1) {
    if (hasDefault) {
      Logger.log('Processing account %s with default config.', accountId);
      configIndex = 0;
    }
    else {
      Logger.log('Skipping account %s: no config found.', accountId);
      return;
    }
  }
  else {
    Logger.log('Processing account %s with account-specific config.',
               accountId);
  }

  var impsThrsh = config[configIndex][impsThrshCol];
  var avgCpcThrsh = config[configIndex][avgCpcThrshCol];
  var ctrThrsh = config[configIndex][ctrThrshCol];
  var costPerConvThrsh = config[configIndex][costPerConvThrshCol];

  var report = AdWordsApp.report(
      'SELECT Query, Clicks, Cost, Ctr, ConversionRate,' +
      ' CostPerConversion, Conversions, CampaignId, AdGroupId' +
      ' FROM SEARCH_QUERY_PERFORMANCE_REPORT' +
      ' WHERE ' +
          ' Conversions > 0' +
          ' AND Impressions > ' + impsThrsh +
          ' AND AverageCpc > ' + (avgCpcThrsh * MICRO_AMOUNT_MULTIPLIER) +
      ' DURING LAST_7_DAYS', REPORTING_OPTIONS);
  var rows = report.rows();

  var negativeKeywords = {};
  var positiveKeywords = {};
  var allAdGroupIds = {};
  // Iterate through search query and decide whether to add them as positive
  // or negative keywords (or ignore).
  while (rows.hasNext()) {
    var row = rows.next();
    if (parseFloat(row['Ctr']) < ctrThrsh) {
      addToMultiMap(negativeKeywords, row['AdGroupId'], row['Query']);
      allAdGroupIds[row['AdGroupId']] = true;
    } else if (parseFloat(row['CostPerConversion']) < costPerConvThrsh) {
      addToMultiMap(positiveKeywords, row['AdGroupId'], row['Query']);
      allAdGroupIds[row['AdGroupId']] = true;
    }
  }

  // Copy all the adGroupIds from the object into an array.
  var adGroupIdList = [];
  for (var adGroupId in allAdGroupIds) {
    adGroupIdList.push(adGroupId);
  }

  // Add the keywords as negative or positive to the applicable ad groups.
  var adGroups = AdWordsApp.adGroups().withIds(adGroupIdList).get();
  while (adGroups.hasNext()) {
    var adGroup = adGroups.next();
    var adGruopId = adGroup.getId();
    if (negativeKeywords[adGroupId]) {
      for (var i = 0; i < negativeKeywords[adGroupId].length; i++) {
        var curNegativeKeyword = '[' + negativeKeywords[adGroupId][i] + ']';
        adGroup.createNegativeKeyword(curNegativeKeyword);
        Logger.log('Update adGroup "%s": add negative keyword "%s".',
                   adGroupId, curNegativeKeyword);
      }
    }
    if (positiveKeywords[adGroupId]) {
      for (var i = 0; i < positiveKeywords[adGroupId].length; i++) {
        var curPositiveKeyword = '[' + positiveKeywords[adGroupId][i] + ']';
        var keywordOperation = adGroup.newKeywordBuilder()
            .withText(curPositiveKeyword)
            .build();
        Logger.log('Update adGroup "%s": add positive keyword "%s".',
                   adGroupId, curPositiveKeyword);
      }
    }
  }
}

// Helper function that stores queries with AdGroupId
function addToMultiMap(map, key, value) {
  if (!map[key]) {
    map[key] = [];
  }
  map[key].push(value);
}

/**
 * Validates the provided spreadsheet URL to make sure that it's set up
 * properly. Throws a descriptive error message if validation fails.
 *
 * @param {string} spreadsheeturl The URL of the spreadsheet to open.
 * @return {Spreadsheet} The spreadsheet object itself, fetched from the URL.
 * @throws {Error} If the spreadsheet URL hasn't been set
 */
function validateAndGetSpreadsheet(spreadsheeturl) {
  if (spreadsheeturl == 'YOUR_SPREADSHEET_URL') {
    throw new Error('Please specify a valid Spreadsheet URL. You can find' +
        ' a link to a template in the associated guide for this script.');
  }
  return SpreadsheetApp.openByUrl(spreadsheeturl);
}



















// Copyright 2015, Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
 * @name MCC Search Query Report
 *
 * @overview The MCC Search Query Report script uses the Search Query Performance
 *     Report to find undesired search terms in accounts under an MCC account
 *     and add them as negative (or positive) exact keywords. See
 *     https://developers.google.com/adwords/scripts/docs/solutions/mccapp-search-query
 *     for more details.
 *
 * @author AdWords Scripts Team [adwords-scripts@googlegroups.com]
 *
 * @version 1.0.3
 *
 * @changelog
 * - version 1.0.3
 *   - Upgrade to API version v201609.
 * - version 1.0.2
 *   - Added validation for external spreadsheet setup.
 * - version 1.0.1
 *   - Improvements to time zone handling.
 * - version 1.0
 *   - Released initial version.
 */
var SPREADSHEET_URL = 'YOUR_SPREADSHEET_URL';
// Please fix the following variables if you need to reformat the spreadsheet
// column numbers of each config column. Column A in your spreadsheet has
// column number of 1, B has number of 2, etc.
var COLUMN = {
  accountId: 2,
  impressionsThreshold: 3,
  averageCpcThreshold: 4,
  ctrThreshold: 5,
  costPerConvThreshold: 6
};
// Start row/column numbers and total columns of actual config
// (without header and margin)
var CONFIG = {
  startRow: 6,
  startColumn: 2,
  totalColumns: 5
};
// One currency unit is one million micro amount.
var MICRO_AMOUNT_MULTIPLIER = 1000000;
/**
 * Configuration to be used for running reports.
 */
var REPORTING_OPTIONS = {
  // Comment out the following line to default to the latest reporting version.
  apiVersion: 'v201609'
};
function main() {
  // Read config data from the spreadsheet
  Logger.log('Using spreadsheet - %s.', SPREADSHEET_URL);
  var spreadsheet = validateAndGetSpreadsheet(SPREADSHEET_URL);

  // Make sure the spreadsheet is using the account's timezone.
  spreadsheet.setSpreadsheetTimeZone(AdWordsApp.currentAccount().getTimeZone());
  var sheet = spreadsheet.getSheets()[0];

  var endRow = sheet.getLastRow();
  var rows = endRow - CONFIG.startRow + 1;
  var config = [];
  if (rows > 0) {
    config = sheet.getRange(CONFIG.startRow, CONFIG.startColumn,
               rows, CONFIG.totalColumns).getValues();
  }
  else {
    Logger.log('Empty config, abort!');
    return;
  }

  var mccAccount = AdWordsApp.currentAccount();
  sheet.getRange(2, 6).setValue(mccAccount.getCustomerId());

  var accountIterator = MccApp.accounts().get();
  while (accountIterator.hasNext()) {
    var account = accountIterator.next();
    processAccount(account, config);
  }

  // Update "Last execution" timestamp
  var today = new Date();
  sheet.getRange(1, 3).setValue(today);
  MccApp.select(mccAccount);
}
// Core logic for processing each account
function processAccount(account, config) {
  // Swith to current adwords account
  MccApp.select(account);
  var accountId = account.getCustomerId();

  var accountIdCol = COLUMN.accountId - CONFIG.startColumn;
  var impsThrshCol = COLUMN.impressionsThreshold - CONFIG.startColumn;
  var avgCpcThrshCol = COLUMN.averageCpcThreshold - CONFIG.startColumn;
  var ctrThrshCol = COLUMN.ctrThreshold - CONFIG.startColumn;
  var costPerConvThrshCol = COLUMN.costPerConvThreshold - CONFIG.startColumn;

  // Get config for this account, if not found use default entry,
  // if no default entry just skip
  var configIndex = -1;
  var hasDefault = (config[0][accountIdCol].toLowerCase() == 'default');
  var configStartRow = hasDefault ? 1 : 0;
  for (var i = configStartRow; i < config.length; i++) {
    if (config[i][accountIdCol] === accountId) {
      configIndex = i;
      break;
    }
  }

  if (configIndex == -1) {
    if (hasDefault) {
      Logger.log('Processing account %s with default config.', accountId);
      configIndex = 0;
    }
    else {
      Logger.log('Skipping account %s: no config found.', accountId);
      return;
    }
  }
  else {
    Logger.log('Processing account %s with account-specific config.',
               accountId);
  }

  var impsThrsh = config[configIndex][impsThrshCol];
  var avgCpcThrsh = config[configIndex][avgCpcThrshCol];
  var ctrThrsh = config[configIndex][ctrThrshCol];
  var costPerConvThrsh = config[configIndex][costPerConvThrshCol];

  var report = AdWordsApp.report(
      'SELECT Query, Clicks, Cost, Ctr, ConversionRate,' +
      ' CostPerConversion, Conversions, CampaignId, AdGroupId' +
      ' FROM SEARCH_QUERY_PERFORMANCE_REPORT' +
      ' WHERE ' +
          ' Conversions > 0' +
          ' AND Impressions > ' + impsThrsh +
          ' AND AverageCpc > ' + (avgCpcThrsh * MICRO_AMOUNT_MULTIPLIER) +
      ' DURING LAST_7_DAYS', REPORTING_OPTIONS);
  var rows = report.rows();

  var negativeKeywords = {};
  var positiveKeywords = {};
  var allAdGroupIds = {};
  // Iterate through search query and decide whether to add them as positive
  // or negative keywords (or ignore).
  while (rows.hasNext()) {
    var row = rows.next();
    if (parseFloat(row['Ctr']) < ctrThrsh) {
      addToMultiMap(negativeKeywords, row['AdGroupId'], row['Query']);
      allAdGroupIds[row['AdGroupId']] = true;
    } else if (parseFloat(row['CostPerConversion']) < costPerConvThrsh) {
      addToMultiMap(positiveKeywords, row['AdGroupId'], row['Query']);
      allAdGroupIds[row['AdGroupId']] = true;
    }
  }

  // Copy all the adGroupIds from the object into an array.
  var adGroupIdList = [];
  for (var adGroupId in allAdGroupIds) {
    adGroupIdList.push(adGroupId);
  }

  // Add the keywords as negative or positive to the applicable ad groups.
  var adGroups = AdWordsApp.adGroups().withIds(adGroupIdList).get();
  while (adGroups.hasNext()) {
    var adGroup = adGroups.next();
    var adGruopId = adGroup.getId();
    if (negativeKeywords[adGroupId]) {
      for (var i = 0; i < negativeKeywords[adGroupId].length; i++) {
        var curNegativeKeyword = '[' + negativeKeywords[adGroupId][i] + ']';
        adGroup.createNegativeKeyword(curNegativeKeyword);
        Logger.log('Update adGroup "%s": add negative keyword "%s".',
                   adGroupId, curNegativeKeyword);
      }
    }
    if (positiveKeywords[adGroupId]) {
      for (var i = 0; i < positiveKeywords[adGroupId].length; i++) {
        var curPositiveKeyword = '[' + positiveKeywords[adGroupId][i] + ']';
        var keywordOperation = adGroup.newKeywordBuilder()
            .withText(curPositiveKeyword)
            .build();
        Logger.log('Update adGroup "%s": add positive keyword "%s".',
                   adGroupId, curPositiveKeyword);
      }
    }
  }
}
// Helper function that stores queries with AdGroupId
function addToMultiMap(map, key, value) {
  if (!map[key]) {
    map[key] = [];
  }
  map[key].push(value);
}
/**
 * Validates the provided spreadsheet URL to make sure that it's set up
 * properly. Throws a descriptive error message if validation fails.
 *
 * @param {string} spreadsheeturl The URL of the spreadsheet to open.
 * @return {Spreadsheet} The spreadsheet object itself, fetched from the URL.
 * @throws {Error} If the spreadsheet URL hasn't been set
 */
function validateAndGetSpreadsheet(spreadsheeturl) {
  if (spreadsheeturl == 'YOUR_SPREADSHEET_URL') {
    throw new Error('Please specify a valid Spreadsheet URL. You can find' +
        ' a link to a template in the associated guide for this script.');
  }
  return SpreadsheetApp.openByUrl(spreadsheeturl);
}
// Copyright 2015, Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
 * @name MCC Search Query Report
 *
 * @overview The MCC Search Query Report script uses the Search Query Performance
 *     Report to find undesired search terms in accounts under an MCC account
 *     and add them as negative (or positive) exact keywords. See
 *     https://developers.google.com/adwords/scripts/docs/solutions/mccapp-search-query
 *     for more details.
 *
 * @author AdWords Scripts Team [adwords-scripts@googlegroups.com]
 *
 * @version 1.0.3
 *
 * @changelog
 * - version 1.0.3
 *   - Upgrade to API version v201609.
 * - version 1.0.2
 *   - Added validation for external spreadsheet setup.
 * - version 1.0.1
 *   - Improvements to time zone handling.
 * - version 1.0
 *   - Released initial version.
 */
var SPREADSHEET_URL = 'YOUR_SPREADSHEET_URL';
// Please fix the following variables if you need to reformat the spreadsheet
// column numbers of each config column. Column A in your spreadsheet has
// column number of 1, B has number of 2, etc.
var COLUMN = {
  accountId: 2,
  impressionsThreshold: 3,
  averageCpcThreshold: 4,
  ctrThreshold: 5,
  costPerConvThreshold: 6
};
// Start row/column numbers and total columns of actual config
// (without header and margin)
var CONFIG = {
  startRow: 6,
  startColumn: 2,
  totalColumns: 5
};
// One currency unit is one million micro amount.
var MICRO_AMOUNT_MULTIPLIER = 1000000;
/**
 * Configuration to be used for running reports.
 */
var REPORTING_OPTIONS = {
  // Comment out the following line to default to the latest reporting version.
  apiVersion: 'v201609'
};
function main() {
  // Read config data from the spreadsheet
  Logger.log('Using spreadsheet - %s.', SPREADSHEET_URL);
  var spreadsheet = validateAndGetSpreadsheet(SPREADSHEET_URL);

  // Make sure the spreadsheet is using the account's timezone.
  spreadsheet.setSpreadsheetTimeZone(AdWordsApp.currentAccount().getTimeZone());
  var sheet = spreadsheet.getSheets()[0];

  var endRow = sheet.getLastRow();
  var rows = endRow - CONFIG.startRow + 1;
  var config = [];
  if (rows > 0) {
    config = sheet.getRange(CONFIG.startRow, CONFIG.startColumn,
               rows, CONFIG.totalColumns).getValues();
  }
  else {
    Logger.log('Empty config, abort!');
    return;
  }

  var mccAccount = AdWordsApp.currentAccount();
  sheet.getRange(2, 6).setValue(mccAccount.getCustomerId());

  var accountIterator = MccApp.accounts().get();
  while (accountIterator.hasNext()) {
    var account = accountIterator.next();
    processAccount(account, config);
  }

  // Update "Last execution" timestamp
  var today = new Date();
  sheet.getRange(1, 3).setValue(today);
  MccApp.select(mccAccount);
}
// Core logic for processing each account
function processAccount(account, config) {
  // Swith to current adwords account
  MccApp.select(account);
  var accountId = account.getCustomerId();

  var accountIdCol = COLUMN.accountId - CONFIG.startColumn;
  var impsThrshCol = COLUMN.impressionsThreshold - CONFIG.startColumn;
  var avgCpcThrshCol = COLUMN.averageCpcThreshold - CONFIG.startColumn;
  var ctrThrshCol = COLUMN.ctrThreshold - CONFIG.startColumn;
  var costPerConvThrshCol = COLUMN.costPerConvThreshold - CONFIG.startColumn;

  // Get config for this account, if not found use default entry,
  // if no default entry just skip
  var configIndex = -1;
  var hasDefault = (config[0][accountIdCol].toLowerCase() == 'default');
  var configStartRow = hasDefault ? 1 : 0;
  for (var i = configStartRow; i < config.length; i++) {
    if (config[i][accountIdCol] === accountId) {
      configIndex = i;
      break;
    }
  }

  if (configIndex == -1) {
    if (hasDefault) {
      Logger.log('Processing account %s with default config.', accountId);
      configIndex = 0;
    }
    else {
      Logger.log('Skipping account %s: no config found.', accountId);
      return;
    }
  }
  else {
    Logger.log('Processing account %s with account-specific config.',
               accountId);
  }

  var impsThrsh = config[configIndex][impsThrshCol];
  var avgCpcThrsh = config[configIndex][avgCpcThrshCol];
  var ctrThrsh = config[configIndex][ctrThrshCol];
  var costPerConvThrsh = config[configIndex][costPerConvThrshCol];

  var report = AdWordsApp.report(
      'SELECT Query, Clicks, Cost, Ctr, ConversionRate,' +
      ' CostPerConversion, Conversions, CampaignId, AdGroupId' +
      ' FROM SEARCH_QUERY_PERFORMANCE_REPORT' +
      ' WHERE ' +
          ' Conversions > 0' +
          ' AND Impressions > ' + impsThrsh +
          ' AND AverageCpc > ' + (avgCpcThrsh * MICRO_AMOUNT_MULTIPLIER) +
      ' DURING LAST_7_DAYS', REPORTING_OPTIONS);
  var rows = report.rows();

  var negativeKeywords = {};
  var positiveKeywords = {};
  var allAdGroupIds = {};
  // Iterate through search query and decide whether to add them as positive
  // or negative keywords (or ignore).
  while (rows.hasNext()) {
    var row = rows.next();
    if (parseFloat(row['Ctr']) < ctrThrsh) {
      addToMultiMap(negativeKeywords, row['AdGroupId'], row['Query']);
      allAdGroupIds[row['AdGroupId']] = true;
    } else if (parseFloat(row['CostPerConversion']) < costPerConvThrsh) {
      addToMultiMap(positiveKeywords, row['AdGroupId'], row['Query']);
      allAdGroupIds[row['AdGroupId']] = true;
    }
  }

  // Copy all the adGroupIds from the object into an array.
  var adGroupIdList = [];
  for (var adGroupId in allAdGroupIds) {
    adGroupIdList.push(adGroupId);
  }

  // Add the keywords as negative or positive to the applicable ad groups.
  var adGroups = AdWordsApp.adGroups().withIds(adGroupIdList).get();
  while (adGroups.hasNext()) {
    var adGroup = adGroups.next();
    var adGruopId = adGroup.getId();
    if (negativeKeywords[adGroupId]) {
      for (var i = 0; i < negativeKeywords[adGroupId].length; i++) {
        var curNegativeKeyword = '[' + negativeKeywords[adGroupId][i] + ']';
        adGroup.createNegativeKeyword(curNegativeKeyword);
        Logger.log('Update adGroup "%s": add negative keyword "%s".',
                   adGroupId, curNegativeKeyword);
      }
    }
    if (positiveKeywords[adGroupId]) {
      for (var i = 0; i < positiveKeywords[adGroupId].length; i++) {
        var curPositiveKeyword = '[' + positiveKeywords[adGroupId][i] + ']';
        var keywordOperation = adGroup.newKeywordBuilder()
            .withText(curPositiveKeyword)
            .build();
        Logger.log('Update adGroup "%s": add positive keyword "%s".',
                   adGroupId, curPositiveKeyword);
      }
    }
  }
}
// Helper function that stores queries with AdGroupId
function addToMultiMap(map, key, value) {
  if (!map[key]) {
    map[key] = [];
  }
  map[key].push(value);
}
/**
 * Validates the provided spreadsheet URL to make sure that it's set up
 * properly. Throws a descriptive error message if validation fails.
 *
 * @param {string} spreadsheeturl The URL of the spreadsheet to open.
 * @return {Spreadsheet} The spreadsheet object itself, fetched from the URL.
 * @throws {Error} If the spreadsheet URL hasn't been set
 */
function validateAndGetSpreadsheet(spreadsheeturl) {
  if (spreadsheeturl == 'YOUR_SPREADSHEET_URL') {
    throw new Error('Please specify a valid Spreadsheet URL. You can find' +
        ' a link to a template in the associated guide for this script.');
  }
  return SpreadsheetApp.openByUrl(spreadsheeturl);
}

Comments

Popular posts from this blog

Lesson 1 : Google Adwords Introduction

GTM_Workshop _Course_Syllabus