From a24cd9aaa7d0af5eb0ec8e7a6d0ac5d7cfb5e59e Mon Sep 17 00:00:00 2001 From: Aleksandar Ivanov Date: Tue, 4 Aug 2026 13:55:50 +0200 Subject: [PATCH 1/2] adds pageviews metadata --- v2/Code.gs | 91 ++++++++++++++++++++++++++++++++++++++++++++-------- v2/README.md | 8 +++++ 2 files changed, 85 insertions(+), 14 deletions(-) diff --git a/v2/Code.gs b/v2/Code.gs index e54af06..b559838 100644 --- a/v2/Code.gs +++ b/v2/Code.gs @@ -16,6 +16,15 @@ const DATASETS = { EVENTS: 'events' }; +const METADATA_FIELD_CONFIG = { + pageviews: { + prefix: 'pageview_meta_' + }, + events: { + prefix: 'event_meta_' + } +}; + const QUERY_TYPES = { SCORECARD: 'scorecard', DATE_HISTOGRAM: 'date_histogram', @@ -30,6 +39,12 @@ const FILTER_OPERATORS = { NOT_EQUALS: 'NOT_EQUALS' }; +const METADATA_FILTER_OPERATORS = [ + FILTER_OPERATORS.EQUALS, + FILTER_OPERATORS.IN, + FILTER_OPERATORS.NOT_EQUALS +]; + const FIELD_FILTER_RULES = { path: [FILTER_OPERATORS.EQUALS, FILTER_OPERATORS.IN, FILTER_OPERATORS.CONTAINS, FILTER_OPERATORS.NOT_EQUALS], referrer_hostname: [FILTER_OPERATORS.EQUALS, FILTER_OPERATORS.IN, FILTER_OPERATORS.CONTAINS, FILTER_OPERATORS.NOT_EQUALS], @@ -43,8 +58,7 @@ const FIELD_FILTER_RULES = { }; const EVENT_FIELD_FILTER_RULES = { - event_name: [FILTER_OPERATORS.EQUALS, FILTER_OPERATORS.IN, FILTER_OPERATORS.NOT_EQUALS], - event_meta_plan: [FILTER_OPERATORS.EQUALS, FILTER_OPERATORS.IN, FILTER_OPERATORS.NOT_EQUALS] + event_name: [FILTER_OPERATORS.EQUALS, FILTER_OPERATORS.IN, FILTER_OPERATORS.NOT_EQUALS] }; const FIELD_CATALOG = [ @@ -212,28 +226,34 @@ function getSchema(request) { return { schema: getFieldCatalog(request).map(toSchemaField) }; } -function getFieldCatalog(request) { +function getFieldCatalog(request, includeMetadata) { const dataset = getConfiguredDataset(request); + const staticCatalog = dataset === DATASETS.PAGEVIEWS + ? FIELD_CATALOG + : EVENT_FIELD_CATALOG; - if (dataset === DATASETS.PAGEVIEWS) { - return FIELD_CATALOG; - } - - return EVENT_FIELD_CATALOG.concat(getEventMetadataCatalog(request)); + return includeMetadata === false + ? staticCatalog + : staticCatalog.concat(getMetadataCatalog(request, dataset)); } -function getEventMetadataCatalog(request) { +function getMetadataCatalog(request, dataset) { const configParams = request && request.configParams ? request.configParams : {}; const hostname = normalizeHostname(configParams.hostname); const apiKey = normalizeText(configParams.apiKey); + const timezone = normalizeText(configParams.timezone) || DEFAULT_TIMEZONE; + const metadataConfig = METADATA_FIELD_CONFIG[dataset]; - if (!hostname || !apiKey) { + if (!hostname || !apiKey || !metadataConfig) { return []; } try { const response = UrlFetchApp.fetch( - LOOKER_SCHEMA_ENDPOINT + '?hostname=' + encodeURIComponent(hostname) + '&dataset=' + encodeURIComponent(DATASETS.EVENTS), + LOOKER_SCHEMA_ENDPOINT + + '?hostname=' + encodeURIComponent(hostname) + + '&dataset=' + encodeURIComponent(dataset) + + '&timezone=' + encodeURIComponent(timezone), { method: 'get', headers: { @@ -251,16 +271,57 @@ function getEventMetadataCatalog(request) { const schema = Array.isArray(data.schema) ? data.schema : []; return schema.filter(function(field) { - return field && /^event_meta_/.test(field.name); + return field && isMetadataFieldId(dataset, field.name); }); } catch (error) { return []; } } +function isMetadataFieldId(dataset, fieldId) { + const metadataConfig = METADATA_FIELD_CONFIG[dataset]; + + return Boolean( + metadataConfig && + typeof fieldId === 'string' && + fieldId.indexOf(metadataConfig.prefix) === 0 + ); +} + +function requestUsesMetadataFields(request, dataset) { + const fields = request && Array.isArray(request.fields) ? request.fields : []; + const rawFilters = request && (request.dimensionsFilters || request.dimensionFilters) + ? (request.dimensionsFilters || request.dimensionFilters) + : []; + let fieldReferences = fields.map(function(field) { + return field && field.name; + }); + + if (Array.isArray(rawFilters)) { + rawFilters.forEach(function(rawFilter) { + expandRawFilterParts(rawFilter).forEach(function(filterPart) { + const filterFieldIds = getFilterFieldIds(filterPart); + if (Array.isArray(filterFieldIds)) { + fieldReferences = fieldReferences.concat(filterFieldIds); + } + }); + }); + } + + return fieldReferences.some(function(fieldReference) { + return fieldReference && ( + isMetadataFieldId(dataset, fieldReference) || + isMetadataFieldId(dataset, normalizeFieldKey(fieldReference)) + ); + }); +} + function getData(request) { const config = getValidatedConfig(request); - const fieldCatalog = getFieldCatalog(request); + const fieldCatalog = getFieldCatalog( + request, + requestUsesMetadataFields(request, config.dataset) + ); const fieldCatalogById = buildFieldCatalogById(fieldCatalog); const fieldCatalogByAlias = buildFieldCatalogByAlias(fieldCatalog); const requestedFieldIds = getRequestedFieldIds(request, fieldCatalogById); @@ -575,7 +636,9 @@ function normalizeSingleFilter(rawFilter, dataset, fieldCatalogById, fieldCatalo return filters; } - const allowedOperators = filterRules[fieldId]; + const allowedOperators = filterRules[fieldId] || ( + isMetadataFieldId(dataset, fieldId) ? METADATA_FILTER_OPERATORS : null + ); if (!allowedOperators) { throwUserError('Filtering is not supported for ' + fieldId + '.'); diff --git a/v2/README.md b/v2/README.md index 8c714e7..1c3ce4c 100644 --- a/v2/README.md +++ b/v2/README.md @@ -9,3 +9,11 @@ This connector lets users query data from [Simple Analytics](https://simpleanaly 1. Click "Connect" When using this connector you are subjected to our [general terms and conditions](https://simpleanalytics.com/general-terms-and-conditions) and [privacy policy](https://simpleanalytics.com/privacy). + +## Metadata fields + +The connector discovers recent metadata fields for the configured pageviews or events dataset. Pageview metadata appears as `Pageview Meta: ...` dimensions and event metadata appears as `Event Meta: ...` dimensions. + +Looker Studio caches data-source fields. Refresh the data-source fields when newly collected metadata should become available in an existing report. + +Metadata discovery is limited to 100 safe text dimensions from the last 365 days. URL-parameter metadata is excluded. Metadata dimensions support exact, list, and not-equal filters; contains filters are not supported. From 5f0906f55a79b7e9c7dd22ef8025759915398ca6 Mon Sep 17 00:00:00 2001 From: Aleksandar Ivanov Date: Tue, 4 Aug 2026 16:22:31 +0200 Subject: [PATCH 2/2] removes prefixes for the pageviews and events --- v2/Code.gs | 31 ++++++++++--------------------- v2/README.md | 2 +- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/v2/Code.gs b/v2/Code.gs index b559838..d22a0b5 100644 --- a/v2/Code.gs +++ b/v2/Code.gs @@ -16,14 +16,7 @@ const DATASETS = { EVENTS: 'events' }; -const METADATA_FIELD_CONFIG = { - pageviews: { - prefix: 'pageview_meta_' - }, - events: { - prefix: 'event_meta_' - } -}; +const METADATA_FIELD_PREFIX = 'meta_'; const QUERY_TYPES = { SCORECARD: 'scorecard', @@ -242,9 +235,8 @@ function getMetadataCatalog(request, dataset) { const hostname = normalizeHostname(configParams.hostname); const apiKey = normalizeText(configParams.apiKey); const timezone = normalizeText(configParams.timezone) || DEFAULT_TIMEZONE; - const metadataConfig = METADATA_FIELD_CONFIG[dataset]; - if (!hostname || !apiKey || !metadataConfig) { + if (!hostname || !apiKey) { return []; } @@ -271,24 +263,21 @@ function getMetadataCatalog(request, dataset) { const schema = Array.isArray(data.schema) ? data.schema : []; return schema.filter(function(field) { - return field && isMetadataFieldId(dataset, field.name); + return field && isMetadataFieldId(field.name); }); } catch (error) { return []; } } -function isMetadataFieldId(dataset, fieldId) { - const metadataConfig = METADATA_FIELD_CONFIG[dataset]; - +function isMetadataFieldId(fieldId) { return Boolean( - metadataConfig && typeof fieldId === 'string' && - fieldId.indexOf(metadataConfig.prefix) === 0 + fieldId.indexOf(METADATA_FIELD_PREFIX) === 0 ); } -function requestUsesMetadataFields(request, dataset) { +function requestUsesMetadataFields(request) { const fields = request && Array.isArray(request.fields) ? request.fields : []; const rawFilters = request && (request.dimensionsFilters || request.dimensionFilters) ? (request.dimensionsFilters || request.dimensionFilters) @@ -310,8 +299,8 @@ function requestUsesMetadataFields(request, dataset) { return fieldReferences.some(function(fieldReference) { return fieldReference && ( - isMetadataFieldId(dataset, fieldReference) || - isMetadataFieldId(dataset, normalizeFieldKey(fieldReference)) + isMetadataFieldId(fieldReference) || + isMetadataFieldId(normalizeFieldKey(fieldReference)) ); }); } @@ -320,7 +309,7 @@ function getData(request) { const config = getValidatedConfig(request); const fieldCatalog = getFieldCatalog( request, - requestUsesMetadataFields(request, config.dataset) + requestUsesMetadataFields(request) ); const fieldCatalogById = buildFieldCatalogById(fieldCatalog); const fieldCatalogByAlias = buildFieldCatalogByAlias(fieldCatalog); @@ -637,7 +626,7 @@ function normalizeSingleFilter(rawFilter, dataset, fieldCatalogById, fieldCatalo } const allowedOperators = filterRules[fieldId] || ( - isMetadataFieldId(dataset, fieldId) ? METADATA_FILTER_OPERATORS : null + isMetadataFieldId(fieldId) ? METADATA_FILTER_OPERATORS : null ); if (!allowedOperators) { diff --git a/v2/README.md b/v2/README.md index 1c3ce4c..faa47d0 100644 --- a/v2/README.md +++ b/v2/README.md @@ -12,7 +12,7 @@ When using this connector you are subjected to our [general terms and conditions ## Metadata fields -The connector discovers recent metadata fields for the configured pageviews or events dataset. Pageview metadata appears as `Pageview Meta: ...` dimensions and event metadata appears as `Event Meta: ...` dimensions. +The connector discovers recent metadata fields for the configured pageviews or events dataset. Metadata appears as `Meta: ...` dimensions in both datasets. Looker Studio caches data-source fields. Refresh the data-source fields when newly collected metadata should become available in an existing report.