-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathbroad-match-keyword-aggregating.js
More file actions
215 lines (182 loc) · 7.56 KB
/
Copy pathbroad-match-keyword-aggregating.js
File metadata and controls
215 lines (182 loc) · 7.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
// ID: 87ea3268e7e6db5e86bc4671c61e65be
/**
*
* Broad-match keyword aggregator script
* This script will group equivalent broad match keywords and label based on performence
*
* Version: 1.1
* Updated 2016-10-11: replaced 'ConvertedClicks' with 'Conversions'
* Google AdWords Script maintained by brainlabsdigital.com
*
*/
function main() {
var ACCOUNT_WIDE = false;
// Defines whether the script looks at campaign-level or account-level broad match duplicate keywords
var METRIC = 'AverageCpc';
// Select the metric which will determine which duplicate keyword will be kept, choose from "Ctr", "QualityScore", "Impressions", "Conversions", "AverageCpc"
var CAMPAIGN_INCLUDE_FILTER = []; // e.g var CAMPAIGN_INCLUDE_FILTER = ["hey", "jude"];
// Campaign filter which will include any campaign with any of the included strings in the campaign name. Case insensitive matching
var CAMPAIGN_EXCLUDE_FILTER = []; // e.g var CAMPAIGN_EXCLUDE_FILTER = ["hey", "jude"];
// Campaign filter which will exclude any campaign with any of the included strings in the campaign name. Case insensitive matching
var DATE_RANGE = 'LAST_30_DAYS';
// Choose one from TODAY, YESTERDAY, LAST_7_DAYS, THIS_WEEK_SUN_TODAY, THIS_WEEK_MON_TODAY, LAST_WEEK, LAST_14_DAYS, LAST_30_DAYS, LAST_BUSINESS_WEEK, LAST_WEEK_SUN_SAT, THIS_MONTH
var KEEP_LABEL = 'DuplicateBroadKeyword_Enable';
// Label one keyword from each duplicate group
var PAUSE_LABEL = 'DuplicateBroadKeyword_Pause';
// Label all keywords which don't have the best statistic from selected
labelDuplicates(ACCOUNT_WIDE, CAMPAIGN_INCLUDE_FILTER, CAMPAIGN_EXCLUDE_FILTER, DATE_RANGE, METRIC, KEEP_LABEL, PAUSE_LABEL);
}
function labelDuplicates(ACCOUNT_WIDE, CAMPAIGN_INCLUDE_FILTER, CAMPAIGN_EXCLUDE_FILTER, DATE_RANGE, METRIC, KEEP_LABEL, PAUSE_LABEL) {
// Create labels
AdWordsApp.createLabel(KEEP_LABEL);
AdWordsApp.createLabel(PAUSE_LABEL);
// Metric data-validation
var allowedMetrics = ['Ctr', 'QualityScore', 'Impressions', 'Conversions', 'AverageCpc'];
var allowedMetrics_lowerCase = allowedMetrics.map(function (str) { return str.toLowerCase(); });
var metricIndex = allowedMetrics_lowerCase.indexOf(METRIC.toLowerCase());
if (metricIndex === -1) {
var error = "Metric '" + METRIC + "' not recognised, please set to one from '" + allowedMetrics.join("', '") + "'.";
Logger.log(error);
throw error;
}
var METRIC = allowedMetrics[metricIndex];
// Generate list of included campaigns
var includeCampaignIds = [];
var campaignIncludes = CAMPAIGN_INCLUDE_FILTER.map(function (str) { return str.toLowerCase(); });
var campaignIterator = AdWordsApp.campaigns()
.withCondition('CampaignStatus = ENABLED')
.get();
while (campaignIterator.hasNext()) {
var campaign = campaignIterator.next();
var campaignId = campaign.getId();
var campaignName = campaign.getName();
var campaignNameLower = campaignName.toLowerCase();
var flag = false;
for (var i = 0; i < campaignIncludes.length; i++) {
if (campaignNameLower.indexOf(campaignIncludes[i]) !== -1) {
flag = true;
break;
}
}
if (flag) {
includeCampaignIds.push(campaignId);
}
}
// Construct AWQL report query
var selectQuery = 'SELECT CampaignName, CampaignId, Id, AdGroupId, Criteria, ' + METRIC + ' ';
var fromQuery = 'FROM KEYWORDS_PERFORMANCE_REPORT ';
var whereQuery = 'WHERE KeywordMatchType = BROAD AND AdNetworkType1 = SEARCH ';
if (includeCampaignIds.length > 0) {
whereQuery += 'AND CampaignId IN [' + includeCampaignIds.join(',') + '] ';
}
for (var i = 0; i < CAMPAIGN_EXCLUDE_FILTER.length; i++) {
whereQuery += "AND CampaignName DOES_NOT_CONTAIN_IGNORE_CASE '" + CAMPAIGN_EXCLUDE_FILTER[i] + "' ";
}
var duringQuery = 'DURING ' + DATE_RANGE;
var query = selectQuery
+ fromQuery
+ whereQuery
+ duringQuery;
// Generate report
var report = AdWordsApp.report(query);
// Poll report rows
var campaignKeywords = {};
var rows = report.rows();
while (rows.hasNext()) {
var row = rows.next();
var keywordId = row.Id;
var adGroupId = row.AdGroupId;
var campaignId = row.CampaignId;
var keywordText = row.Criteria.toLowerCase();
var metricStat = parseFloat(row[METRIC].replace(/,/g, ''));
if (METRIC.toLowerCase() === 'AverageCpc'.toLowerCase()) {
if (metricStat > 0) {
metricStat = 1 / metricStat;
}
}
var stats = { metric: metricStat };
if (ACCOUNT_WIDE) campaignId = 1;
if (typeof (campaignKeywords[campaignId]) === 'undefined') {
campaignKeywords[campaignId] = [];
}
campaignKeywords[campaignId].push(parseKeyword(keywordId, adGroupId, keywordText, stats));
}
// Establish duplicate keyword groups
if (ACCOUNT_WIDE === true) {
var keywordGroups = {};
}
for (var campaignId in campaignKeywords) {
if (ACCOUNT_WIDE === false) {
var keywordGroups = {};
}
var campaignKeywordsList = campaignKeywords[campaignId];
var keywordArray = [];
for (var keyword in campaignKeywordsList) {
keywordArray.push(campaignKeywordsList[keyword].Text);
}
for (var keyword in campaignKeywordsList) {
var keywordText = campaignKeywordsList[keyword].Text;
var firstIndex = keywordArray.indexOf(keywordText);
var lastIndex = keywordArray.lastIndexOf(keywordText);
// push the dupes into dupe groups
if (firstIndex !== lastIndex) {
if (typeof (keywordGroups[keywordText]) === 'undefined') {
keywordGroups[keywordText] = [];
}
keywordGroups[keywordText].push(campaignKeywordsList[keyword]);
}
}
if (ACCOUNT_WIDE === true) {
continue;
}
labelKeywords(keywordGroups, KEEP_LABEL, PAUSE_LABEL);
}
if (ACCOUNT_WIDE === true) {
labelKeywords(keywordGroups, KEEP_LABEL, PAUSE_LABEL);
}
}
function parseKeyword(keywordId, adGroupId, keywordText, stats) {
var keyword = {};
keyword.KeywordId = keywordId;
keyword.AdGroupId = adGroupId;
keyword.Id = [adGroupId, keywordId];
keyword.Text = orderKeyword(keywordText);
keyword.Stats = stats;
return keyword;
}
function orderKeyword(keywordText) {
// Splitting the words
var keywordTextArray = keywordText.trim().split(' ');
// Sort keyword components
var sortedKeywordComponents = keywordTextArray.sort();
// Turn sorted strings into one word
var sortedKeyword = sortedKeywordComponents.join(' ');
return sortedKeyword;
}
function labelKeywords(keywordGroups, KEEP_LABEL, PAUSE_LABEL) {
for (var keywordText in keywordGroups) {
// cycle through each group to pick best of the bunch
var maxMetric = -1;
var bestKeyword = [];
for (var keyword in keywordGroups[keywordText]) {
if (parseFloat(keywordGroups[keywordText][keyword].Stats.metric) > maxMetric) {
maxMetric = keywordGroups[keywordText][keyword].Stats.metric;
bestKeyword[0] = keywordGroups[keywordText][keyword];
}
}
var indexOfBest = keywordGroups[keywordText].indexOf(bestKeyword[0]);
keywordGroups[keywordText].splice(indexOfBest, 1);
// label all groups with pause/unpause labels
var keywordIterator = AdWordsApp.keywords().withIds([bestKeyword[0].Id]).get();
keywordIterator.next().applyLabel(KEEP_LABEL);
var keywordIdArray = [];
for (keyword in keywordGroups[keywordText]) {
keywordIdArray.push(keywordGroups[keywordText][keyword].Id);
}
var keywordIterator = AdWordsApp.keywords().withIds(keywordIdArray).get();
while (keywordIterator.hasNext()) {
var keyword = keywordIterator.next();
keyword.applyLabel(PAUSE_LABEL);
}
}
}