Assigned
Status Update
Comments
lo...@immobiliare.it <lo...@immobiliare.it> #2
Or is there a bug in Google Chrome version 133.0.6905.0 (Official version) dev 64-bit, on Fedora Rawhide Linux: 6.13.0-0.rc5.42.fc42.x86_64
In the console (from the developer tools, DevTools), empty parentheses after "Symbol" return an error, whereas without empty parentheses, it returns what is stated in the information "URL to the relevant page onweb.dev "
16:11:31.288 Symbol( "String" ) === Symbol().for( "String" );
16:11:31.291 VM590:1 Uncaught TypeError: Symbol(...).for is not a function at <anonymous>:1:36 (anonymous) @ VM590:1
16:11:52.488 Symbol().for( "String" ) === Symbol().for( "String" );
16:11:52.490 VM594:1 Uncaught TypeError: Symbol(...).for is not a function at <anonymous>:1:13 (anonymous) @ VM594:1
16:12:02.226 Symbol( "String" ) === Symbol( "String" );
16:12:02.239 false
From the Gemini AI support in DevTools itself (once enabled in the DevTools settings), with some simplifications made by me:
Explanation
The error "Uncaught TypeError: Symbol(...).for is not a function" indicates that the for method you're trying to call is not available on a Symbol object.
Here's the breakdown of why this happens and how to fix it:
Understanding the Issue
Symbol() creates unique, anonymous symbols: When you call Symbol(), you get a brand new, unique symbol. These symbols are distinct even if the description is the same (e.g., Symbol("String") !== Symbol("String")). They are meant for internal use within your code and are not shared or registered.
Symbol.for() creates and retrieves global symbols: Symbol.for(key) is a static method on the Symbol constructor, not on a specific instance of a symbol. It's used to create or retrieve a symbol from a global registry using the given key (a string). If a symbol with that key already exists, it returns the existing symbol. Otherwise, it creates a new one and registers it.
The Error in your Code
In your code:
Code (Use code snippets with caution)
Symbol().for( "String" ) === Symbol().for( "String" );
You're mistakenly trying to call for as a method on the result of Symbol() which is a unique anonymous symbol object.
The Correct Usage
The correct way to use Symbol.for() is as a static method of the Symbol constructor:
Code (Use code snippets with caution)
Symbol.for( "String" ) === Symbol.for( "String" ); // This is correct
Explanation of the Correct Code
Symbol.for("String") (First Instance):
The JavaScript engine checks if a symbol with the key "String" exists in the global symbol registry.
If it doesn't exist, a new symbol is created, registered with the key "String", and returned.
If a symbol with the key "String" already exists, it's retrieved from the registry and returned.
Symbol.for("String") (Second Instance):
The same process repeats. Since "String" was already used as a key, the engine looks for an existing symbol with this key.
It finds the symbol created in the previous step and returns it (the same object!).
=== (Strict Equality):
Because both instances of Symbol.for("String") return the same object, the strict equality comparison (===) evaluates to true.
Summary
The error Symbol(...).for is not a function arises from incorrectly trying to call for as a method on a unique, anonymous symbol instance. The correct usage is Symbol.for(key), where for is a static method on the Symbol constructor, and key is the string used to register and retrieve the globally registered symbol.
Revised Code (Correct)
Code (Use code snippets with caution)
console.log(Symbol.for( "String" ) === Symbol.for( "String" ));
This code will now work correctly and output true.
In the console (from the developer tools, DevTools), empty parentheses after "Symbol" return an error, whereas without empty parentheses, it returns what is stated in the information "URL to the relevant page on
16:11:31.288 Symbol( "String" ) === Symbol().for( "String" );
16:11:31.291 VM590:1 Uncaught TypeError: Symbol(...).for is not a function at <anonymous>:1:36 (anonymous) @ VM590:1
16:11:52.488 Symbol().for( "String" ) === Symbol().for( "String" );
16:11:52.490 VM594:1 Uncaught TypeError: Symbol(...).for is not a function at <anonymous>:1:13 (anonymous) @ VM594:1
16:12:02.226 Symbol( "String" ) === Symbol( "String" );
16:12:02.239 false
From the Gemini AI support in DevTools itself (once enabled in the DevTools settings), with some simplifications made by me:
Explanation
The error "Uncaught TypeError: Symbol(...).for is not a function" indicates that the for method you're trying to call is not available on a Symbol object.
Here's the breakdown of why this happens and how to fix it:
Understanding the Issue
Symbol() creates unique, anonymous symbols: When you call Symbol(), you get a brand new, unique symbol. These symbols are distinct even if the description is the same (e.g., Symbol("String") !== Symbol("String")). They are meant for internal use within your code and are not shared or registered.
Symbol.for() creates and retrieves global symbols: Symbol.for(key) is a static method on the Symbol constructor, not on a specific instance of a symbol. It's used to create or retrieve a symbol from a global registry using the given key (a string). If a symbol with that key already exists, it returns the existing symbol. Otherwise, it creates a new one and registers it.
The Error in your Code
In your code:
Code (Use code snippets with caution)
Symbol().for( "String" ) === Symbol().for( "String" );
You're mistakenly trying to call for as a method on the result of Symbol() which is a unique anonymous symbol object.
The Correct Usage
The correct way to use Symbol.for() is as a static method of the Symbol constructor:
Code (Use code snippets with caution)
Symbol.for( "String" ) === Symbol.for( "String" ); // This is correct
Explanation of the Correct Code
Symbol.for("String") (First Instance):
The JavaScript engine checks if a symbol with the key "String" exists in the global symbol registry.
If it doesn't exist, a new symbol is created, registered with the key "String", and returned.
If a symbol with the key "String" already exists, it's retrieved from the registry and returned.
Symbol.for("String") (Second Instance):
The same process repeats. Since "String" was already used as a key, the engine looks for an existing symbol with this key.
It finds the symbol created in the previous step and returns it (the same object!).
=== (Strict Equality):
Because both instances of Symbol.for("String") return the same object, the strict equality comparison (===) evaluates to true.
Summary
The error Symbol(...).for is not a function arises from incorrectly trying to call for as a method on a unique, anonymous symbol instance. The correct usage is Symbol.for(key), where for is a static method on the Symbol constructor, and key is the string used to register and retrieve the globally registered symbol.
Revised Code (Correct)
Code (Use code snippets with caution)
console.log(Symbol.for( "String" ) === Symbol.for( "String" ));
This code will now work correctly and output true.
ra...@google.com <ra...@google.com>
lo...@immobiliare.it <lo...@immobiliare.it> #3
An update on my proposed query:
# Materialize Web Vitals metrics from GA4 event export data
# Replace target table name
CREATE OR REPLACE TABLE bigquery_project_id.ga4_demo_dev.web_vitals_summary
PARTITION BY DATE(event_timestamp)
CLUSTER BY metric_name
AS
SELECT
session_id,
IF(
is_first_visit > 0,
'New user',
'Returning user') AS user_type,
IF(
(SELECT MAX(session_engaged) FROM UNNEST(events)) > 0, 'Engaged', 'Not engaged')
AS session_engagement,
evt.* EXCEPT (session_engaged, event_name),
event_name AS metric_name,
FORMAT_TIMESTAMP('%Y%m%d', event_timestamp) AS event_date
FROM
(
SELECT
session_id,
MAX(is_first_visit) as is_first_visit,
ARRAY_AGG(custom_event) AS events
FROM
(
SELECT
session_id,
is_first_visit,
STRUCT(
country,
device_category,
device_os,
traffic_medium,
traffic_name,
traffic_source,
page_path,
debug_target,
event_timestamp,
event_name,
metric_id,
IF(event_name = 'LCP', metric_value / 1000, metric_value) AS metric_value,
user_pseudo_id,
session_engaged,
session_revenue) AS custom_event
FROM
(
SELECT
CONCAT( user_pseudo_id, (SELECT value.int_value FROM UNNEST(event_params) WHERE key = 'ga_session_id'))
AS session_id, #Truly unique session identifier across the GA4 Dataset
(SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'metric_id')
AS metric_id,
ANY_VALUE(device.category) AS device_category,
ANY_VALUE(device.operating_system) AS device_os,
ANY_VALUE(traffic_source.medium) AS traffic_medium,
ANY_VALUE(traffic_source.name) AS traffic_name,
ANY_VALUE(traffic_source.source) AS traffic_source,
ANY_VALUE(
REGEXP_SUBSTR(
(SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_location'),
r'^[^?]+')) AS page_path,
ANY_VALUE(
(SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'debug_target'))
AS debug_target,
ANY_VALUE(user_pseudo_id) AS user_pseudo_id,
ANY_VALUE(geo.country) AS country,
MAX(IF(event_name in ('LCP', 'FID', 'CLS'), event_name, null)) AS event_name, #Avoiding picking non web vitals event names randomly
MAX(IF(event_name = 'first_visit', 1, null)) as is_first_visit, #New method of counting New/Returning
SUM(ecommerce.purchase_revenue) AS session_revenue,
MAX(
(
SELECT
COALESCE(
value.double_value, value.int_value, CAST(value.string_value AS NUMERIC))
FROM UNNEST(event_params)
WHERE key = 'session_engaged'
)) AS session_engaged,
TIMESTAMP_MICROS(MAX(event_timestamp)) AS event_timestamp,
MAX(
(
SELECT COALESCE(value.double_value, value.int_value)
FROM UNNEST(event_params)
WHERE key = 'metric_value'
)) AS metric_value,
FROM
# Replace source table name
`bigquery_project_id.analytics_XXXXX.events_*`
WHERE
event_name IN ('LCP', 'FID', 'CLS', 'first_visit', 'purchase')
GROUP BY
1, 2
)
)
WHERE
session_id IS NOT NULL
GROUP BY session_id
)
CROSS JOIN UNNEST(events) AS evt
WHERE evt.event_name NOT IN ('first_visit', 'purchase');
I have added a new way of counting New/Returining users as a workaround after the exclusion of fist_visit from the event_names in the inner layer
ph...@google.com <ph...@google.com> #4
Assigning to mkazi@ for comment, since he was the one who originally wrote this query.
Also cc: rviscomi@ and barrypollard@ who I believe have some experience with this this part of the workflow.
mk...@google.com <mk...@google.com> #5
Thanks for identifying the issue Lorenzo. I'll take a look a this later this in early March.
lo...@immobiliare.it <lo...@immobiliare.it> #6
Any updates on this?
Description
2.The proposed query athttps://web.dev/articles/vitals-ga4#materialize_web_vitals_data seems to yield non-deterministic results during testing. For instance, it generates varying total metric values and user attributes when executed on the same dataset.
After thorough examination, I've identified two potential factors contributing to this behavior:
Combination of ga_session_id and ANY_VALUE(): As per documentation provided athttps://developers.google.com/analytics/blog/2022/hll#sessions , ga_session_id does not serve as a unique session identifier across a GA4 dataset. It's possible for multiple users to share the same ga_session_id, which essentially functions more like a timestamp indicating the session's initiation. Consequently, using ANY_VALUE() while grouping by session_id and metric_id leads to different outcomes with each execution of the query.
Combination of event name and ANY_VALUE(): As mentioned at the top of the article, multiple events can be received for the same metric. In this particular case it's possible that two events are sent with the same metric_id, for example "first_visit" can share the same metric_id with on of the web vitals events (FID).
My proposed solution is the following:
I concatenated ga_session_id and user_pseudo_id to create a truly unique session identifier across the dataset and I added a filtering method using MAX(IF()) to the selection of event name to avoid selecting event names outside the web vitals one.