Práticas recomendadas para o Microsoft Advertising Scripts

Para melhorar o desempenho dos seus scripts e da plataforma, reveja e siga as melhores práticas descritas abaixo.

Trabalhar com seletores

Utilizar filtros

Use os filtros de um seletor em vez de filtrar as entidades. Os seletores permitem-lhe filtrar por IDs e condições. Por exemplo, você pode filtrar pelo desempenho de uma entidade (campanhas de retorno com CPC médio superior a 10), seu status (campanhas que são pausadas), o nome do objeto pai da entidade e muito mais.

Vantagens da utilização de filtros:

  • Limita o número de entidades que o seletor devolve apenas às entidades de que necessita.

  • Permite que o script seja executado mais rapidamente (menos entidades para retornar e processar)

  • Reduz a chance de você se deparar com limites de leitura de entidade (consulte Limites de execução de scripts).

Caminho certo

    var adGroups = AdsApp.adGroups()
        .withCondition('Status = PAUSED')
        .get();

    while (adGroups.hasNext()) {
        var adGroup = adGroups.next();
        // Do something with paused ad group.
    }

Caminho errado

    var adGroups = AdsApp.adGroups().get();

    while (adGroups.hasNext()) {
        var adGroup = adGroups.next();
        
        if (adGroup.isPaused() == true) {
            // Do something with paused ad group.
        }
    }

Não cruzar a hierarquia de entidades

Se quiser obter as entidades subordinadas de uma entidade ou a entidade principal da entidade, não percorra a hierarquia da entidade para as obter.

Para obter entidades subordinadas, utilize a coleção da entidade subordinada no nível pretendido.

Caminho certo

    // Get all ads.
    var ads = AdsApp.ads().get();

    while (ads.hasNext()) {
        var ad = ads.next();
        // Do something with ad.
    }

Ou, se pretender anúncios de uma campanha específica:

    // Get all ads in the campaign, 'mycampaign'.
    var ads = AdsApp.ads()
        .withCondition("CampaignName = 'mycampaign'")
        .get();

    while (ads.hasNext()) {
        var ad = ads.next();
        // Do something with ad.
    }

Ou, obter os anúncios de uma campanha, se você tiver o objeto da campanha:

    // Get all ads in the campaign.
    var ads = campaign.ads().get();

    while (ads.hasNext()) {
        var ad = ads.next();
        // Do something with ad.
    }

Caminho errado

    var campaigns = AdsApp.campaigns().get();

    while (campaigns.hasNext()) {
        var adGroups = campaigns.next().adGroups().get();
        
        while (adGroups.hasNext()) {
            var ads = adGroups.next().ads().get();

            while (ads.hasNext()) {
                var ad = ads.next();
                // Do something with ad.
            }
        }
    }

O mesmo se aplica se você quiser obter o pai de uma entidade. Em vez de percorrer a hierarquia para obter o pai, use o método de acessor pai da entidade filha.

Caminho certo

    // Get all ads.
    var ads = AdsApp.ads()
        .withCondition('Clicks > 5')
        .forDateRange('LAST_7_DAYS')
        .get();

    while (ads.hasNext()) {
        var ad = ads.next();
        
        // Do something with campaign and adGroup.
        var adGroup = ad.adGroup();
        var campaign = ad.campaign();
    }

Caminho errado

    var campaigns = AdsApp.campaigns().get();

    while (campaigns.hasNext()) {
        var campaign = campaigns.next();
        var adGroups = campaign.adGroups().get();
        
        while (adGroups.hasNext()) {
            var adGroup = adGroups.next();
            var ads = adGroup.ads().get();

            while (ads.hasNext()) {
                var ad = ads.next();
                
                if ('<some condition is met>') {
                    // Do something with campaign and adGroup.
                }
            }
        }
    }

Utilizar IDs de entidade sempre que possível

Usar IDs para filtrar entidades oferece o melhor desempenho.

Isto

    var adGroups = AdsApp.adGroups()
        .withIds(["123456"])
        .get();

    while (adGroups.hasNext()) {
        var adGroup = adGroups.next();
        
        // Do something with adGroup.
    }

Proporciona um melhor desempenho do que isto

    var adGroups = AdsApp.adGroups()
        .withCondition("Name = 'myadgroup'")
        .get();

    while (adGroups.hasNext()) {
        var adGroup = adGroups.next();
        
        // Do something with adGroup.
    }

Evite ciclos apertados com seletores e um número desnecessário de obter

Evite loops com solicitações get que obtêm uma única entidade. Por exemplo, digamos que tem um relatório de desempenho de palavras-chave e pretende atualizar as palavras-chave no relatório. Em vez de obter uma linha do relatório, obter a palavra-chave e, em seguida, atualizá-la, deve criar uma lista de IDs de palavras-chave à medida que percorre cada linha do relatório. Em seguida, passe a lista de IDs para o seletor para obter todas as palavras-chave em uma única solicitação get. Em seguida, pode iterar a lista de palavras-chave e atualizá-las.

Caminho certo

    var report = AdsApp.report('<report query goes here>');

    var rows = report.rows();
    var idLists = []; // an array where each element contains an array of IDs.
    var idList = [];  // array of IDs that's limited to maxCount.
    var maxCount = 10000;

    while (rows.hasNext()) {
        var row = rows.next();

        if (idList.length < maxCount) {
            idList.push(row['id']);
        }
        else {
            idLists.push(idList);
            idList = [];
        }
    }

    for (idList of idLists) {
        var keywords = AdsApp.keywords()
            .withIds(idList)
            .get();

        while (keywords.hasNext()) {
            var keyword = keywords.next();
            // update the keyword        
        }
    }

Caminho errado

    var report = AdsApp.report('<report query goes here>');

    var rows = report.rows();

    while (rows.hasNext()) {
        var row = rows.next();

        var keyword = AdsApp.keywords()
            .withIds([row['id']])
            .get()
            .next();

        // update the keyword        
    }

Inclua o método forDateRange somente se você planeja chamar o método getStats da entidade

Chamar o método de forDateRange um seletor faz com que o seletor obtenha os dados de desempenho da entidade. Obter os dados de desempenho de uma entidade é caro, então só os obtenha se você planeja chamar o método da getStats entidade e usar os dados.

O intervalo de datas especificado para uma entidade não se aplica às entidades principais ou subordinadas às quais acede a partir dessa entidade. Por exemplo, se você receber um grupo de anúncios e, em seguida, receber sua campanha pai e tentar acessar as métricas de desempenho da campanha, a chamada falhará.

A campaignStats.getReturnOnAdSpend() chamada no exemplo a seguir falha porque o intervalo de datas se aplica ao grupo de anúncios e não à campanha.

    var myAdGroups = AdsApp.adGroups().
        .withCondition("CampaignName CONTAINS 'gen'")
        .forDateRange("LAST_7_DAYS")
        .get();

    while (myAdGroups.hasNext()) {
        var adGroup = myAdGroups.next();
        var campaign = adGroup.getCampaign();
        var campaignStats = campaign.getStats();
        var campaignROAS = campaignStats.getReturnOnAdSpend();
    }

Para que isso funcione, você precisa criar um seletor para a campanha.

    var myAdGroups = AdsApp.adGroups().
        .withCondition("CampaignName CONTAINS 'gen'")
        .forDateRange("LAST_7_DAYS")
        .get();

    while (myAdGroups.hasNext()) {
        var adGroup = myAdGroups.next();
        var campaign = AdsApp.campaigns()
            .withIds([adGroup.getCampaign().getId()])
            .forDateRange("LAST_7_DAYS")
            .get()
            .next();
        var campaignStats = campaign.getStats();
        var campaignROAS = campaignStats.getReturnOnAdSpend();
    }

Não altere uma propriedade da entidade usada como condição no seletor

Os iteradores reduzem a pressão da memória carregando apenas um único item de cada vez, em vez de todo o conjunto de itens. Por este motivo, alterar uma propriedade que utilizou como condição no seletor pode provocar um comportamento inesperado.

Caminho certo

    var adGroups = []; 

    var iterator = AdsApp.adGroups()
        .withCondition('Status = ENABLED')
        .get();

    while (iterator.hasNext()) {
        adGroups.push(iterator.next());
    }

    for (var adGroup of adGroups) {
        adGroup.pause();
    }

Caminho errado

    var adGroups = AdsApp.adGroups()
        .withCondition('Status = ENABLED')
        .get();

    while (adGroups.hasNext()) {
        var adGroup = adGroups.next();
        adGroup.pause();
    }

Atualizações em lote

Para melhorar o desempenho, o Scripts processa solicitações de construção em lotes. Se você chamar o método de operação de uma solicitação de compilação, isso forçará os scripts a processar as solicitações de compilação na fila imediatamente, negando quaisquer ganhos de desempenho. Se você estiver criando mais de uma entidade, não execute os métodos de operação no mesmo loop que você usa para criar a entidade. Isto leva a um fraco desempenho porque só é processada uma entidade de cada vez. Em vez disso, crie uma matriz das operações e processe-as após o ciclo de compilação.

Caminho certo

    // An array to hold the operations, so you 
    // can process them after all the entities are queued.
    var operations = []; 

    // Create all the new entities.
    for (var i = 0; i < keywords.length; i++) {
        var keywordOperation = AdsApp.adGroups().get().next()
          .newKeywordBuilder()
          .withText(keywords[i])
          .build();
        operations.push(keywordOperation);
    }

    // Now call the operation method so the build requests
    // get processed in batches.
    for (var i = 0; i < operations.length; i++) {
        var newKeyword = operations[i].getResult();
    }

Caminho errado

    for (var i = 0; i < keywords.length; i++) {
        var keywordOperation = AdsApp.adGroups().get().next()  // Get the first ad group
          .newKeywordBuilder()  // Add the keyword to the ad group
          .withText(keywords[i])
          .build();

        // Don't get results in the same loop that creates
        // the entity because Scripts then only processes one
        // entity at a time.
        var newKeyword = keywordOperation.getResult();
    }

O mesmo acontece se atualizar uma entidade e, em seguida, obter a mesma propriedade que atualizou. Não faça o seguinte:

    var bidAmount = 1.2;

    while (keywords.hasNext()) {
        var keyword = keywords.next();

        keyword.bidding().setCpc(bidAmount);

        if (keyword.bidding().getCpc() != bidAmount) {
            Logger.log(`Failed to update bid amount for keyword, ${keyword.getText()} (${keyword.getId()})`);
        }
    }

Use a palavra-chave lucro ao obter grandes conjuntos de entidades

Recuperar um grande número de entidades e carregá-las em uma única lista que você processa em um loop tem algumas desvantagens:

  1. Dependendo do tamanho da solicitação, pode levar n número de solicitações de back-end para buscar todas as entidades antes que o loop comece. Se você não processá-los todos, o tempo e o poder de computação usados para obter as entidades não processadas serão desperdiçados. Por exemplo, se você recuperar palavras-chave 10K e o loop quebrar depois de processar apenas palavras-chave 2K, o tempo e o poder de computação usados para obter as palavras-chave 8K restantes serão desperdiçados.

  2. Criar a lista requer mais memória para manter todas as entidades ao mesmo tempo.

Para resolver esses problemas, use a palavra-chave yield , que permite que seu script busque entidades sob demanda ou, de alguma forma, "preguiçosamente" buscá-las apenas quando forem necessárias. Isso significa que seu script não está fazendo mais chamadas do que precisa no momento e não passa grandes listas de objetos.

Este exemplo inclui o registo para ilustrar o fluxo de controlo ao utilizar a palavra-chave rendimento .

function main() {
    const keywords = getKeywords();

    //@ts-ignore <-- suppresses iterator error
    for (const keyword of keywords) {
        Logger.log("in for loop\n\n");
    }
}

// Note that you must use the yield keyword in a generator function - see the
// '*' at the end of the function keyword.

function* getKeywords() {
    const keywords = AdsApp.keywords()
        .withCondition("Status = ENABLED")
        .withCondition("CombinedApprovalStatus = APPROVED")
        .withLimit(10)
        .get();

    Logger.log(`total keywords in account: ${keywords.totalNumEntities()} \n\n`);

    while (keywords.hasNext()) {
        Logger.log("before next()\n\n");
        yield keywords.next();
        Logger.log("after next\n\n");
    }
}

Padrão de chamadas para evitar limites de entidades

Existe um limite para o número de entidades que os Scripts podem devolver para uma conta. Se a solicitação retornasse mais do que esse limite, Scripts lança um erro com a mensagem, Há muitas entidades. O exemplo a seguir mostra o padrão de chamadas que você deve usar ao obter um grande número de entidades. O exemplo tenta obter todas as palavras-chave no nível da conta primeiro. Se isso falhar, ele tenta fazer várias chamadas para buscar palavras-chave por campanha, já que normalmente há menos entidades no nível da campanha. Geralmente, você pode continuar esse padrão até o nível do grupo de anúncios, se necessário.

Para obter informações sobre como usar a palavra-chave rendimento , consulte Usar a palavra-chave rendimento ao obter grandes conjuntos de entidades.

function* getEntities() {
    const applyConditions = _ => _
        .withCondition('CampaignStatus = ENABLED')
        .withCondition('AdGroupStatus = ENABLED')
        .withCondition('Status = ENABLED')
        .withCondition("CombinedApprovalStatus = DISAPPROVED");

    try {
        // Get the account's keywords. 
        const keywords = applyConditions(AdsApp.keywords()).get();

        while (keywords.hasNext()) {
            yield keywords.next();
        }
    } catch (e) {
        if (!e.message.startsWith('There are too many entities')) {
            throw e;
        }

        // If there are too many keywords at the account level,
        // get keywords by campaigns under the account.
        const campaigns = AdsApp.campaigns().get();

        while (campaigns.hasNext()) {
            const campaign = campaigns.next();

            const keywords = applyConditions(campaign.keywords()).get();

            while (keywords.hasNext()) {
                yield keywords.next();
            }
        }
    }
}