From 237fafa7c32d7082db9781e8a5e40b29400e90da Mon Sep 17 00:00:00 2001 From: Lin Yu Date: Tue, 20 May 2025 09:55:01 +0800 Subject: [PATCH 01/55] generate assets for import solution on graph studio --- .gitignore | 1 + scripts/readme.md | 2 +- scripts/script.js | 62 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 63 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 84245149..f6395895 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ node_modules/ **/model/*.csv **/model/*.json **/concepts/*.json +import_solution_metadata/ \ No newline at end of file diff --git a/scripts/readme.md b/scripts/readme.md index 86501b7d..ee9356a0 100644 --- a/scripts/readme.md +++ b/scripts/readme.md @@ -65,7 +65,7 @@ queries: 3. The algorithms list all the graph algorithms used in the solution. -4. The `first` and `last` sections are used to control the order of the queries. The queries listed under `first` will be executed first, and the queries listed under `last` will be executed last. +4. The `first` and `last` sections are used to control the order of the queries. The queries listed under `first` will be executed first, and the queries listed under `last` will be executed last. We assume the queries listed under `last` are the queries that need to run after all the data is loaded to process the data. 5. `production` is default to true, if you want to hide the solution from production environment(`tgcloud.io`), set this to `false` diff --git a/scripts/script.js b/scripts/script.js index d36b49fa..ffa5b187 100644 --- a/scripts/script.js +++ b/scripts/script.js @@ -399,7 +399,7 @@ async function main() { await syncFolder(`global/udfs`); } -main(); +// main(); // Debug code // function decodeSolution(path) { @@ -411,3 +411,63 @@ main(); // console.log(sampleLoadingJob); // console.log(reset); // } + +async function generateFileForImportSolution() { + const dir = "import_solution_metadata"; + fs.mkdirSync(dir); + + const solutions = getAllSolutions(); + const metadataList = []; + for (let solution of solutions) { + const { metadata, queries: { first, last } = {} } = await getSolution(solution); + + if (metadata.production === false) { + console.log("hide solution", metadata.name); + continue; + } + + const solutionDetails = await getSolutionDetail( + solution, + first || [], + last || [], + metadata.graph, + metadata.is_library + ); + + // strip aws s3 url prefix + metadata.icon = '/studio/assets/' + metadata.icon.slice(metadata.icon.indexOf(solution)); + if (metadata.images) { + metadata.images = metadata.images.map((image) => { + return '/studio/assets/' + image.slice(image.indexOf(solution)); + }); + } + + metadataList.push({ + ...metadata, + path: solution, + initQuery: solutionDetails.lastQuery + }); + + // copy images and icon + fs.mkdirSync(`${dir}/${solution}/meta`, { recursive: true }); + if (fs.existsSync(`${solution}/meta/icon.png`)) { + fs.copyFileSync(`${solution}/meta/icon.png`, `${dir}/${solution}/meta/icon.png`); + } else if (fs.existsSync(`${solution}/meta/icon.jpg`)) { + fs.copyFileSync(`${solution}/meta/icon.jpg`, `${dir}/${solution}/meta/icon.jpg`); + } else if (fs.existsSync(`${solution}/meta/icon.svg`)) { + fs.copyFileSync(`${solution}/meta/icon.svg`, `${dir}/${solution}/meta/icon.svg`); + } + if (fs.existsSync(`${solution}/meta/images`)) { + fs.mkdirSync(`${dir}/${solution}/meta/images`); + const images = globSync([`${solution}/meta/images/*`]); + for (let image of images) { + const fileName = path.basename(image); + fs.copyFileSync(image, `${dir}/${solution}/meta/images/${fileName}`); + } + } + } + + fs.writeFileSync(`${dir}/solution_list.json`, JSON.stringify(metadataList, null, 2)); +} + +generateFileForImportSolution(); \ No newline at end of file From bc48c8feae6625be925319c61f65c9b896bb7af3 Mon Sep 17 00:00:00 2001 From: Lin Yu Date: Tue, 20 May 2025 09:58:20 +0800 Subject: [PATCH 02/55] fix --- scripts/script.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/script.js b/scripts/script.js index ffa5b187..737a8b12 100644 --- a/scripts/script.js +++ b/scripts/script.js @@ -435,7 +435,9 @@ async function generateFileForImportSolution() { ); // strip aws s3 url prefix - metadata.icon = '/studio/assets/' + metadata.icon.slice(metadata.icon.indexOf(solution)); + if (metadata.icon) { + metadata.icon = '/studio/assets/' + metadata.icon.slice(metadata.icon.indexOf(solution)); + } if (metadata.images) { metadata.images = metadata.images.map((image) => { return '/studio/assets/' + image.slice(image.indexOf(solution)); From 732dd57386cb4aef6af748789f878e6e482429c9 Mon Sep 17 00:00:00 2001 From: JingYang11 Date: Wed, 18 Jun 2025 11:08:19 +0800 Subject: [PATCH 03/55] TCE-6719 remove update description block for all queries --- ...e_failure_impact_radius_visualization.gsql | 3 -- ..._visualization_with_subgraph_topology.gsql | 4 --- ...nstream_device_topology_visualization.gsql | 5 --- .../explore_topology_from_all_router.gsql | 2 -- ...xplore_topology_from_multiple_routers.gsql | 3 -- .../explore_topology_from_one_router.gsql | 6 +--- ...nts_by_impacted_device_and_time_range.gsql | 7 ---- ...d_events_by_time_range_and_event_type.gsql | 6 ---- ...tial_incident_source_of_event_by_time.gsql | 6 ---- ..._related_events_from_incident_by_time.gsql | 6 ---- .../find_unsecured_servers_visualization.gsql | 4 --- .../incident_impact_by_max_radius.gsql | 5 --- .../top_k_devices_with_most_alerts.gsql | 4 --- .../top_k_devices_with_most_incidents.gsql | 4 --- .../Application_Engagement_Individuals.gsql | 2 -- .../queries/Application_Starts.gsql | 2 -- .../queries/Application_Submissions.gsql | 2 -- .../queries/Customers_No_Engagement.gsql | 2 -- .../queries/Customers_With_Product.gsql | 3 -- .../queries/Customers_With_Sessions.gsql | 2 -- .../queries/Email_Engagement_Accounts.gsql | 2 -- .../queries/Email_Engagement_By_Product.gsql | 2 -- .../queries/Individual_WebSearch.gsql | 2 -- .../Individuals_K_Most_Engagement.gsql | 3 -- .../queries/Individuals_No_Application.gsql | 2 -- .../queries/Individuals_Product_Browse.gsql | 2 -- .../queries/delete_all_cc_connections.gsql | 6 ---- .../queries/delete_unused_cc_nodes.gsql | 5 --- .../find_shared_piis_of_two_entities.gsql | 6 ---- .../queries/match_entities.gsql | 34 ------------------ .../queries/output_entity_cc_to_file.gsql | 4 --- .../queries/unify_entities.gsql | 2 -- .../queries/queries.gsql | 36 ------------------- .../batch_application_cc_features.gsql | 6 ---- .../batch_application_distance_and_path.gsql | 6 ---- ...delete_all_application_cc_connections.gsql | 6 ---- .../queries/delete_unused_cc_nodes.gsql | 6 ---- ...istance_and_path_to_fraud_application.gsql | 6 ---- ...nce_and_path_to_fraud_application_vis.gsql | 8 ----- .../find_shared_piis_of_two_applications.gsql | 6 ---- .../queries/get_application_cc_features.gsql | 6 ---- .../queries/get_application_fraud_status.gsql | 4 --- .../get_num_applications_by_app_status.gsql | 2 -- .../get_num_applications_by_fraud_status.gsql | 2 -- ...nected_components_by_num_applications.gsql | 4 --- ...et_top_k_products_by_num_applications.gsql | 6 ---- ...oducts_by_num_applications_with_other.gsql | 6 ---- .../incremental_application_match.gsql | 28 --------------- .../incremental_application_unify.gsql | 4 --- .../queries/match_application_entities.gsql | 36 ------------------- .../output_application_cc_to_file.gsql | 5 --- .../queries/set_application_fraud_status.gsql | 6 ---- .../queries/unify_application_entities.gsql | 2 -- .../queries/batch_party_cc_features.gsql | 6 ---- .../batch_party_distance_and_path.gsql | 6 ---- .../delete_all_party_cc_connections.gsql | 6 ---- .../queries/delete_unused_cc_nodes.gsql | 6 ---- .../distance_and_path_to_fraud_party.gsql | 6 ---- .../find_shared_piis_of_two_parties.gsql | 6 ---- .../queries/get_party_cc_features.gsql | 6 ---- .../queries/get_party_fraud_status.gsql | 4 --- .../queries/incremental_party_match.gsql | 28 --------------- .../queries/incremental_party_unify.gsql | 4 --- .../queries/match_party_entities.gsql | 36 ------------------- .../queries/output_party_cc_to_file.gsql | 4 --- .../queries/set_party_fraud_status.gsql | 6 ---- .../queries/unify_party_entities.gsql | 2 -- .../attributes_to_party_traversal.gsql | 5 --- .../queries/party_full_address.gsql | 4 --- .../queries/single_Party_PII.gsql | 4 --- .../attributes_to_party_traversal.gsql | 5 --- .../card_has_frequent_transactions.gsql | 6 ---- .../queries/card_has_large_total_amount.gsql | 6 ---- .../queries/card_transactions_stats.gsql | 3 -- .../card_with_single_large_transaction.gsql | 6 ---- .../merchant_category_transactions_stats.gsql | 3 -- .../merchant_has_frequent_transactions.gsql | 6 ---- .../merchant_has_large_total_amount.gsql | 5 --- .../queries/merchant_transactions_stats.gsql | 3 -- ...erchant_with_single_large_transaction.gsql | 6 ---- .../queries/party_full_address.gsql | 3 -- .../queries/single_Party_PII.gsql | 3 -- .../queries/single_card_lookup.gsql | 3 -- .../queries/single_merchant_lookup.gsql | 3 -- .../queries/single_transaction_lookup.gsql | 2 -- 85 files changed, 1 insertion(+), 540 deletions(-) diff --git a/agile_operations/network_infrastructure/queries/device_failure_impact_radius_visualization.gsql b/agile_operations/network_infrastructure/queries/device_failure_impact_radius_visualization.gsql index c22df26b..5370ed0b 100644 --- a/agile_operations/network_infrastructure/queries/device_failure_impact_radius_visualization.gsql +++ b/agile_operations/network_infrastructure/queries/device_failure_impact_radius_visualization.gsql @@ -48,6 +48,3 @@ CREATE OR REPLACE QUERY device_failure_impact_radius_visualization ( } -UPDATE DESCRIPTION OF QUERY device_failure_impact_radius_visualization "This query finds and visualizes the devices that will fail if the provided input device fails." - -UPDATE DESCRIPTION OF QUERY_PARAM device_failure_impact_radius_visualization.device "The input device (accepts devices of all types)." diff --git a/agile_operations/network_infrastructure/queries/device_failure_impact_radius_visualization_with_subgraph_topology.gsql b/agile_operations/network_infrastructure/queries/device_failure_impact_radius_visualization_with_subgraph_topology.gsql index 207200de..32e87c45 100644 --- a/agile_operations/network_infrastructure/queries/device_failure_impact_radius_visualization_with_subgraph_topology.gsql +++ b/agile_operations/network_infrastructure/queries/device_failure_impact_radius_visualization_with_subgraph_topology.gsql @@ -69,7 +69,3 @@ CREATE OR REPLACE QUERY device_failure_impact_radius_visualization_with_subgraph ; } - -UPDATE DESCRIPTION OF QUERY device_failure_impact_radius_visualization_with_subgraph_topology "This query finds and visualizes the devices that will fail if the provided input device fails along with the subgraph that the input device was in. Use only for visualization purposes." - -UPDATE DESCRIPTION OF QUERY_PARAM device_failure_impact_radius_visualization_with_subgraph_topology.device "The input device (accepts devices of all types)." \ No newline at end of file diff --git a/agile_operations/network_infrastructure/queries/downstream_device_topology_visualization.gsql b/agile_operations/network_infrastructure/queries/downstream_device_topology_visualization.gsql index d39e7c98..9d784bc1 100644 --- a/agile_operations/network_infrastructure/queries/downstream_device_topology_visualization.gsql +++ b/agile_operations/network_infrastructure/queries/downstream_device_topology_visualization.gsql @@ -106,8 +106,3 @@ CREATE OR REPLACE QUERY downstream_device_topology_visualization ( } -UPDATE DESCRIPTION OF QUERY downstream_device_topology_visualization "This query visualizes the downstream device topology of a provided input device." - -UPDATE DESCRIPTION OF QUERY_PARAM downstream_device_topology_visualization.device "The input device (accepts devices of all types)." - -UPDATE DESCRIPTION OF QUERY_PARAM downstream_device_topology_visualization.k_hop_switch_limit "The amount of additional hops to branching Switch devices. It dtermines the maximum depth when iterating from one Switch to another. Defaults to 3." diff --git a/agile_operations/network_infrastructure/queries/explore_topology_from_all_router.gsql b/agile_operations/network_infrastructure/queries/explore_topology_from_all_router.gsql index 4f8862e5..e3922c7a 100644 --- a/agile_operations/network_infrastructure/queries/explore_topology_from_all_router.gsql +++ b/agile_operations/network_infrastructure/queries/explore_topology_from_all_router.gsql @@ -12,5 +12,3 @@ CREATE OR REPLACE QUERY explore_topology_from_all_router () { PRINT all_devices_with_connections; PRINT @@edges_to_display AS edges_to_display; } - -UPDATE DESCRIPTION OF QUERY explore_topology_from_all_router "This query visualizes the network topology of all devices in the database. It shows the downstream connections from all routers." \ No newline at end of file diff --git a/agile_operations/network_infrastructure/queries/explore_topology_from_multiple_routers.gsql b/agile_operations/network_infrastructure/queries/explore_topology_from_multiple_routers.gsql index 2f7f345a..c8163b5b 100644 --- a/agile_operations/network_infrastructure/queries/explore_topology_from_multiple_routers.gsql +++ b/agile_operations/network_infrastructure/queries/explore_topology_from_multiple_routers.gsql @@ -83,6 +83,3 @@ CREATE OR REPLACE QUERY explore_topology_from_multiple_routers (SET starter all_visited_servers ; PRINT @@edges_to_display AS edges_to_display; -} - -UPDATE DESCRIPTION OF QUERY explore_topology_from_one_router "This query visualizes the network topology starting from the router 'starter_router'." - -UPDATE DESCRIPTION OF QUERY_PARAM explore_topology_from_one_router.starter_router "The input router device to explore the network topology." \ No newline at end of file +} \ No newline at end of file diff --git a/agile_operations/network_infrastructure/queries/find_events_by_impacted_device_and_time_range.gsql b/agile_operations/network_infrastructure/queries/find_events_by_impacted_device_and_time_range.gsql index 924cfcf9..3c4b28b3 100644 --- a/agile_operations/network_infrastructure/queries/find_events_by_impacted_device_and_time_range.gsql +++ b/agile_operations/network_infrastructure/queries/find_events_by_impacted_device_and_time_range.gsql @@ -92,10 +92,3 @@ CREATE OR REPLACE QUERY find_events_by_impacted_device_and_time_range ( PRINT @@edges_to_display; } - -UPDATE DESCRIPTION OF QUERY find_events_by_impacted_device_and_time_range "This query finds the events based on an input device and time range. It also visualizes the events and optionally can visualize the event type." - -UPDATE DESCRIPTION OF QUERY_PARAM find_events_by_impacted_device_and_time_range.input_device "The input device (accepts devices of all types)." -UPDATE DESCRIPTION OF QUERY_PARAM find_events_by_impacted_device_and_time_range.start_time "The start time filter to search for events - min time an event can have." -UPDATE DESCRIPTION OF QUERY_PARAM find_events_by_impacted_device_and_time_range.end_time "The end time to search for events - max time an event can have." -UPDATE DESCRIPTION OF QUERY_PARAM find_events_by_impacted_device_and_time_range.show_event_types_vis "Whether or not to display vertices that relate to the type of the event. Defaults to False." diff --git a/agile_operations/network_infrastructure/queries/find_events_by_time_range_and_event_type.gsql b/agile_operations/network_infrastructure/queries/find_events_by_time_range_and_event_type.gsql index 99d6ade8..831ee4c7 100644 --- a/agile_operations/network_infrastructure/queries/find_events_by_time_range_and_event_type.gsql +++ b/agile_operations/network_infrastructure/queries/find_events_by_time_range_and_event_type.gsql @@ -111,9 +111,3 @@ CREATE OR REPLACE QUERY find_events_by_time_range_and_event_type ( selected_events_with_info.@event_impacted_device_list AS impacted_devices_list ]; } - -UPDATE DESCRIPTION OF QUERY find_events_by_time_range_and_event_type "This query finds the events based on a time range and optionally on event type." - -UPDATE DESCRIPTION OF QUERY_PARAM find_events_by_time_range_and_event_type.start_time "The start time filter to search for events - min time an event can have." -UPDATE DESCRIPTION OF QUERY_PARAM find_events_by_time_range_and_event_type.end_time "The end time to search for events - max time an event can have." -UPDATE DESCRIPTION OF QUERY_PARAM find_events_by_time_range_and_event_type.input_event_type_filter "The event type that can be used to search for events. Defaults to an empty string for no filter on event type." diff --git a/agile_operations/network_infrastructure/queries/find_potential_incident_source_of_event_by_time.gsql b/agile_operations/network_infrastructure/queries/find_potential_incident_source_of_event_by_time.gsql index c88da0ca..ecc15180 100644 --- a/agile_operations/network_infrastructure/queries/find_potential_incident_source_of_event_by_time.gsql +++ b/agile_operations/network_infrastructure/queries/find_potential_incident_source_of_event_by_time.gsql @@ -124,9 +124,3 @@ CREATE OR REPLACE QUERY find_potential_incident_source_of_event_by_time ( PRINT @@edges_to_display; } - -UPDATE DESCRIPTION OF QUERY find_potential_incident_source_of_event_by_time "This query finds the potential source or cause of an event based on the 'input_event' event that happened 'num_seconds_before_event_start' seconds before the event and at most 'max_radius' hops away from the connected device." - -UPDATE DESCRIPTION OF QUERY_PARAM find_potential_incident_source_of_event_by_time.input_event "The input events to find potential source incidents to" -UPDATE DESCRIPTION OF QUERY_PARAM find_potential_incident_source_of_event_by_time.max_radius "The maximum number of hops extending from the device(s) the input event has an impact on. Defaults to 3." -UPDATE DESCRIPTION OF QUERY_PARAM find_potential_incident_source_of_event_by_time.num_seconds_before_event_start "The number of seconds before an event starts to search for incidents. Defaults to 3600 seconds." \ No newline at end of file diff --git a/agile_operations/network_infrastructure/queries/find_potential_related_events_from_incident_by_time.gsql b/agile_operations/network_infrastructure/queries/find_potential_related_events_from_incident_by_time.gsql index e27fbee7..311d97b2 100644 --- a/agile_operations/network_infrastructure/queries/find_potential_related_events_from_incident_by_time.gsql +++ b/agile_operations/network_infrastructure/queries/find_potential_related_events_from_incident_by_time.gsql @@ -130,9 +130,3 @@ CREATE OR REPLACE QUERY find_potential_related_events_from_incident_by_time ( ; PRINT @@edges_to_display; } - -UPDATE DESCRIPTION OF QUERY find_potential_related_events_from_incident_by_time "This query finds the events that can be potentially related to the incident within 'num_seconds_from_incident_start' seconds after the input incident. It goes over devices that is up to 'max_radius' hops away from devies the incident impacts." - -UPDATE DESCRIPTION OF QUERY_PARAM find_potential_related_events_from_incident_by_time.input_incident "The input incident to find related events to it." -UPDATE DESCRIPTION OF QUERY_PARAM find_potential_related_events_from_incident_by_time.max_radius "The maximum number of hops extending from the device(s) the input incident has an impact on. Defaults to 3." -UPDATE DESCRIPTION OF QUERY_PARAM find_potential_related_events_from_incident_by_time.num_seconds_from_incident_start "The number of seconds after an incident starts to search for related events. Defaults to 3600 seconds." diff --git a/agile_operations/network_infrastructure/queries/find_unsecured_servers_visualization.gsql b/agile_operations/network_infrastructure/queries/find_unsecured_servers_visualization.gsql index be31a400..9feff355 100644 --- a/agile_operations/network_infrastructure/queries/find_unsecured_servers_visualization.gsql +++ b/agile_operations/network_infrastructure/queries/find_unsecured_servers_visualization.gsql @@ -69,7 +69,3 @@ CREATE OR REPLACE QUERY find_unsecured_servers_visualization (UINT k_hop_switch_ ; } - -UPDATE DESCRIPTION OF QUERY find_unsecured_servers_visualization "This query visualizes the unsecured paths from Router devices to Server devices that do not go through firewalls." - -UPDATE DESCRIPTION OF QUERY_PARAM find_unsecured_servers_visualization.k_hop_switch_limit "The amount of additional hops to branching Switch devices. It determines the maximum depth when iterating from one Switch to another. Defaults to 3." diff --git a/agile_operations/network_infrastructure/queries/incident_impact_by_max_radius.gsql b/agile_operations/network_infrastructure/queries/incident_impact_by_max_radius.gsql index ad6e3a9d..39c5f966 100644 --- a/agile_operations/network_infrastructure/queries/incident_impact_by_max_radius.gsql +++ b/agile_operations/network_infrastructure/queries/incident_impact_by_max_radius.gsql @@ -57,8 +57,3 @@ CREATE OR REPLACE QUERY incident_impact_by_max_radius ( ; PRINT @@edges_to_display; } - -UPDATE DESCRIPTION OF QUERY incident_impact_by_max_radius "This query finds the devices that can be impacted by an incident within 'max_radius' hops away from the devices directly impacted by the event." - -UPDATE DESCRIPTION OF QUERY_PARAM incident_impact_by_max_radius.input_incident "The input incident to find devices that can be impacted by it." -UPDATE DESCRIPTION OF QUERY_PARAM incident_impact_by_max_radius.max_radius "The maximum number of hops extending from the device(s) the input incident has an impact on. Defaults to 4." \ No newline at end of file diff --git a/agile_operations/network_infrastructure/queries/top_k_devices_with_most_alerts.gsql b/agile_operations/network_infrastructure/queries/top_k_devices_with_most_alerts.gsql index 16b2a08f..de48d6b8 100644 --- a/agile_operations/network_infrastructure/queries/top_k_devices_with_most_alerts.gsql +++ b/agile_operations/network_infrastructure/queries/top_k_devices_with_most_alerts.gsql @@ -22,7 +22,3 @@ CREATE OR REPLACE QUERY top_k_devices_with_most_alerts (INT k) { PRINT devices; } - -UPDATE DESCRIPTION OF QUERY top_k_devices_with_most_alerts "This query finds the top k devices with the most alerts in the system." - -UPDATE DESCRIPTION OF QUERY_PARAM top_k_devices_with_most_alerts.k "Determines the amount of top devices to find." diff --git a/agile_operations/network_infrastructure/queries/top_k_devices_with_most_incidents.gsql b/agile_operations/network_infrastructure/queries/top_k_devices_with_most_incidents.gsql index 80721d69..e0dd8fe4 100644 --- a/agile_operations/network_infrastructure/queries/top_k_devices_with_most_incidents.gsql +++ b/agile_operations/network_infrastructure/queries/top_k_devices_with_most_incidents.gsql @@ -21,7 +21,3 @@ CREATE OR REPLACE QUERY top_k_devices_with_most_incidents (INT k) { PRINT devices; } - -UPDATE DESCRIPTION OF QUERY top_k_devices_with_most_incidents "This query finds the top k devices with the most incidents in the system." - -UPDATE DESCRIPTION OF QUERY_PARAM top_k_devices_with_most_incidents.k "Determines the amount of top devices to find." diff --git a/connected_customer/customer_360/queries/Application_Engagement_Individuals.gsql b/connected_customer/customer_360/queries/Application_Engagement_Individuals.gsql index 69cc8338..1fe386ad 100644 --- a/connected_customer/customer_360/queries/Application_Engagement_Individuals.gsql +++ b/connected_customer/customer_360/queries/Application_Engagement_Individuals.gsql @@ -6,5 +6,3 @@ CREATE DISTRIBUTED QUERY Application_Engagement_Individuals(/* Parameters here * PRINT individuals; } - -UPDATE DESCRIPTION OF QUERY Application_Engagement_Individuals "This query returns all individuals that have had engagement with a product application" diff --git a/connected_customer/customer_360/queries/Application_Starts.gsql b/connected_customer/customer_360/queries/Application_Starts.gsql index 9485e119..b60f7b71 100644 --- a/connected_customer/customer_360/queries/Application_Starts.gsql +++ b/connected_customer/customer_360/queries/Application_Starts.gsql @@ -18,5 +18,3 @@ CREATE DISTRIBUTED QUERY Application_Starts(/* Parameters here */) FOR GRAPH Cus PRINT unfinished_individuals; } - -UPDATE DESCRIPTION OF QUERY Application_Starts "This query returns all individuals who started an application but did not finish the application" diff --git a/connected_customer/customer_360/queries/Application_Submissions.gsql b/connected_customer/customer_360/queries/Application_Submissions.gsql index fc727764..34fb17df 100644 --- a/connected_customer/customer_360/queries/Application_Submissions.gsql +++ b/connected_customer/customer_360/queries/Application_Submissions.gsql @@ -20,5 +20,3 @@ CREATE DISTRIBUTED QUERY Application_Submissions(/* Parameters here */) FOR GRAP PRINT finished_individuals; } - -UPDATE DESCRIPTION OF QUERY Application_Submissions "Returns all individuals who finished an application and submitted one" \ No newline at end of file diff --git a/connected_customer/customer_360/queries/Customers_No_Engagement.gsql b/connected_customer/customer_360/queries/Customers_No_Engagement.gsql index 0dff9398..1db28ee3 100644 --- a/connected_customer/customer_360/queries/Customers_No_Engagement.gsql +++ b/connected_customer/customer_360/queries/Customers_No_Engagement.gsql @@ -7,5 +7,3 @@ CREATE DISTRIBUTED QUERY Customers_No_Engagement(/* Parameters here */) FOR GRAP PRINT not_engaged; } - -UPDATE DESCRIPTION OF QUERY Customers_No_Engagement "Returns individuals who have never had any engagement" diff --git a/connected_customer/customer_360/queries/Customers_With_Product.gsql b/connected_customer/customer_360/queries/Customers_With_Product.gsql index 8714ea4b..3ad139bb 100644 --- a/connected_customer/customer_360/queries/Customers_With_Product.gsql +++ b/connected_customer/customer_360/queries/Customers_With_Product.gsql @@ -15,6 +15,3 @@ CREATE DISTRIBUTED QUERY Accounts_With_Product(STRING product_type) FOR GRAPH Cu PRINT @@accounts; } - -UPDATE DESCRIPTION OF QUERY Accounts_With_Product "Returns Accounts that hold the specified product type" -UPDATE DESCRIPTION OF QUERY_PARAM Accounts_With_Product.product_type "The type of product held by returned accounts" \ No newline at end of file diff --git a/connected_customer/customer_360/queries/Customers_With_Sessions.gsql b/connected_customer/customer_360/queries/Customers_With_Sessions.gsql index 99d71bfa..48673fa7 100644 --- a/connected_customer/customer_360/queries/Customers_With_Sessions.gsql +++ b/connected_customer/customer_360/queries/Customers_With_Sessions.gsql @@ -4,5 +4,3 @@ CREATE DISTRIBUTED QUERY Customers_With_Sessions(/* Parameters here */) FOR GRAP engaged = SELECT s FROM start:s - (created_session:e) - SessionID:t; PRINT engaged; } - -UPDATE DESCRIPTION OF QUERY Customers_With_Sessions "Returns all individuals who have created a session where they have engagement" \ No newline at end of file diff --git a/connected_customer/customer_360/queries/Email_Engagement_Accounts.gsql b/connected_customer/customer_360/queries/Email_Engagement_Accounts.gsql index ef675107..6cab9806 100644 --- a/connected_customer/customer_360/queries/Email_Engagement_Accounts.gsql +++ b/connected_customer/customer_360/queries/Email_Engagement_Accounts.gsql @@ -9,5 +9,3 @@ CREATE DISTRIBUTED QUERY Email_Engagement_Accounts(/* Parameters here */) FOR GR PRINT accounts; } - -UPDATE DESCRIPTION OF QUERY Email_Engagement_Accounts "Returns accounts that have had email engagement" \ No newline at end of file diff --git a/connected_customer/customer_360/queries/Email_Engagement_By_Product.gsql b/connected_customer/customer_360/queries/Email_Engagement_By_Product.gsql index 1fbcccf4..df2f821d 100644 --- a/connected_customer/customer_360/queries/Email_Engagement_By_Product.gsql +++ b/connected_customer/customer_360/queries/Email_Engagement_By_Product.gsql @@ -39,5 +39,3 @@ CREATE DISTRIBUTED QUERY Email_Engagement_By_Product(/* Parameters here */) FOR PRINT @@engagement_rate; } - -UPDATE DESCRIPTION OF QUERY Email_Engagement_By_Product "Returns email engagement percentage broken down by product type" \ No newline at end of file diff --git a/connected_customer/customer_360/queries/Individual_WebSearch.gsql b/connected_customer/customer_360/queries/Individual_WebSearch.gsql index 6c57c713..e3a73ef5 100644 --- a/connected_customer/customer_360/queries/Individual_WebSearch.gsql +++ b/connected_customer/customer_360/queries/Individual_WebSearch.gsql @@ -6,5 +6,3 @@ CREATE DISTRIBUTED QUERY Individual_WebSearch(/* Parameters here */) FOR GRAPH C PRINT individuals; } - -UPDATE DESCRIPTION OF QUERY Individual_WebSearch "Returns individuals who engaged in searching products on the web" diff --git a/connected_customer/customer_360/queries/Individuals_K_Most_Engagement.gsql b/connected_customer/customer_360/queries/Individuals_K_Most_Engagement.gsql index 94ccb4ec..5176d6fc 100644 --- a/connected_customer/customer_360/queries/Individuals_K_Most_Engagement.gsql +++ b/connected_customer/customer_360/queries/Individuals_K_Most_Engagement.gsql @@ -11,6 +11,3 @@ CREATE DISTRIBUTED QUERY Individuals_K_Most_Engagement(INT k = 10) FOR GRAPH Cus PRINT individuals; } - -UPDATE DESCRIPTION OF QUERY Individuals_K_Most_Engagement "Find the top k individuals based on total engagement" -UPDATE DESCRIPTION OF QUERY_PARAM Individuals_K_Most_Engagement.k "Top K individuals to return" diff --git a/connected_customer/customer_360/queries/Individuals_No_Application.gsql b/connected_customer/customer_360/queries/Individuals_No_Application.gsql index a2a302d7..797f5b25 100644 --- a/connected_customer/customer_360/queries/Individuals_No_Application.gsql +++ b/connected_customer/customer_360/queries/Individuals_No_Application.gsql @@ -11,5 +11,3 @@ CREATE DISTRIBUTED QUERY Individuals_No_Application(/* Parameters here */) FOR G PRINT individuals; } - -UPDATE DESCRIPTION OF QUERY Individuals_No_Application "Return individuals who have never started an application but have looked at products" diff --git a/connected_customer/customer_360/queries/Individuals_Product_Browse.gsql b/connected_customer/customer_360/queries/Individuals_Product_Browse.gsql index 75a6380e..8e5f0475 100644 --- a/connected_customer/customer_360/queries/Individuals_Product_Browse.gsql +++ b/connected_customer/customer_360/queries/Individuals_Product_Browse.gsql @@ -6,5 +6,3 @@ CREATE DISTRIBUTED QUERY Individuals_Product_Browse(/* Parameters here */) FOR G PRINT individuals; } - -UPDATE DESCRIPTION OF QUERY Individuals_Product_Browse "Return individuals who have looked at products" \ No newline at end of file diff --git a/connected_customer/entity_resolution/queries/delete_all_cc_connections.gsql b/connected_customer/entity_resolution/queries/delete_all_cc_connections.gsql index 948f7954..26170c4a 100644 --- a/connected_customer/entity_resolution/queries/delete_all_cc_connections.gsql +++ b/connected_customer/entity_resolution/queries/delete_all_cc_connections.gsql @@ -15,9 +15,3 @@ CREATE OR REPLACE DISTRIBUTED QUERY delete_all_cc_connections(INT num_of_batches PRINT result; } - -UPDATE DESCRIPTION OF QUERY delete_all_cc_connections "This query deletes all Entity_In_Ring edges associating Entity vertices to Connected_Component community vertices." - -UPDATE DESCRIPTION OF QUERY_PARAM delete_all_cc_connections.num_of_batches "Number of batches to partition the deletions for all Entity vertices in the graph. This query must be called the same number of times as batch_num with incrementing batch_id 0 through batch_num - 1 to process all Entity vertices in the graph. Defaults to 1." - -UPDATE DESCRIPTION OF QUERY_PARAM delete_all_cc_connections.batch_id "Current batch partition of Entity vertices to process and must be called with batch_id 0 through batch_num - 1. Defaults to 0." \ No newline at end of file diff --git a/connected_customer/entity_resolution/queries/delete_unused_cc_nodes.gsql b/connected_customer/entity_resolution/queries/delete_unused_cc_nodes.gsql index cc78f589..edbf8744 100644 --- a/connected_customer/entity_resolution/queries/delete_unused_cc_nodes.gsql +++ b/connected_customer/entity_resolution/queries/delete_unused_cc_nodes.gsql @@ -18,8 +18,3 @@ CREATE OR REPLACE DISTRIBUTED QUERY delete_unused_cc_nodes(INT num_of_batches = } -UPDATE DESCRIPTION OF QUERY delete_unused_cc_nodes "This query deletes all unused Connected_Component vertices in the graph." - -UPDATE DESCRIPTION OF QUERY_PARAM delete_unused_cc_nodes.num_of_batches "Number of batches to partition the deletions for all Connected_Component vertices in the graph. This query must be called the same number of times as batch_num with incrementing batch_id 0 through batch_num - 1 to process all Connected_Component vertices in the graph. Defaults to 1." - -UPDATE DESCRIPTION OF QUERY_PARAM delete_unused_cc_nodes.batch_id "Current batch partition of Connected_Component vertices to process and must be called with batch_id 0 through batch_num - 1. Defaults to 0." \ No newline at end of file diff --git a/connected_customer/entity_resolution/queries/find_shared_piis_of_two_entities.gsql b/connected_customer/entity_resolution/queries/find_shared_piis_of_two_entities.gsql index cc2eec3f..7f5cdcb9 100644 --- a/connected_customer/entity_resolution/queries/find_shared_piis_of_two_entities.gsql +++ b/connected_customer/entity_resolution/queries/find_shared_piis_of_two_entities.gsql @@ -26,9 +26,3 @@ CREATE OR REPLACE QUERY find_shared_piis_of_two_entities(VERTEX entity_1 PRINT @@degrees_of_shared_piis; } - -UPDATE DESCRIPTION OF QUERY find_shared_piis_of_two_entities "This query returns all shared PII vertex type, value, and degree between the two provided Entity vertex parameters." - -UPDATE DESCRIPTION OF QUERY_PARAM find_shared_piis_of_two_entities.entity_1 "First Entity vertex used to search for shared PII vertices." - -UPDATE DESCRIPTION OF QUERY_PARAM find_shared_piis_of_two_entities.entity_2 "Second Entity vertex used to search for shared PII vertices." \ No newline at end of file diff --git a/connected_customer/entity_resolution/queries/match_entities.gsql b/connected_customer/entity_resolution/queries/match_entities.gsql index c586ba36..c6362026 100644 --- a/connected_customer/entity_resolution/queries/match_entities.gsql +++ b/connected_customer/entity_resolution/queries/match_entities.gsql @@ -303,37 +303,3 @@ CREATE OR REPLACE DISTRIBUTED QUERY match_entities( PRINT execution_time AS execution_time_in_seconds; } - -UPDATE DESCRIPTION OF QUERY match_entities "This query finds matches and creates similarity edges between entities that match. It is used as the second step in the Entity Resolution process, right after running delete_all_cc_connections." - -UPDATE DESCRIPTION OF QUERY_PARAM match_entities.customer_has_birthdate_weight "Weight accumulation for matching customer_has_birthdate_weight PII vertices between Entoty vertices in the graph. Defaults to 0.5." - -UPDATE DESCRIPTION OF QUERY_PARAM match_entities.customer_has_email_address_weight "Weight accumulation for matching customer_has_email_address_weight PII vertices between Entoty vertices in the graph. Defaults to 0.5." - -UPDATE DESCRIPTION OF QUERY_PARAM match_entities.customer_has_name_weight "Weight accumulation for matching customer_has_name_weight PII vertices between Entoty vertices in the graph. Defaults to 0.2." - -UPDATE DESCRIPTION OF QUERY_PARAM match_entities.customer_has_phone_weight "Weight accumulation for matching customer_has_phone_weight PII vertices between Entoty vertices in the graph. Defaults to 0.5." - -UPDATE DESCRIPTION OF QUERY_PARAM match_entities.customer_has_std_city_weight "Weight accumulation for matching customer_has_std_city_weight PII vertices between Entoty vertices in the graph. Defaults to 0.2." - -UPDATE DESCRIPTION OF QUERY_PARAM match_entities.customer_has_std_postcode_weight "Weight accumulation for matching customer_has_std_postcode_weight PII vertices between Entoty vertices in the graph. Defaults to 0.2." - -UPDATE DESCRIPTION OF QUERY_PARAM match_entities.customer_has_std_state_weight "Weight accumulation for matching customer_has_std_state_weight PII vertices between Entoty vertices in the graph. Defaults to 0.2." - -UPDATE DESCRIPTION OF QUERY_PARAM match_entities.customer_has_std_street_address_weight "Weight accumulation for matching customer_has_std_street_address_weight PII vertices between Entoty vertices in the graph. Defaults to 0.2." - -UPDATE DESCRIPTION OF QUERY_PARAM match_entities.customer_has_tax_id_number_weight "Weight accumulation for matching customer_has_tax_id_number_weight PII vertices between Entoty vertices in the graph. Defaults to 0.5." - -UPDATE DESCRIPTION OF QUERY_PARAM match_entities.customer_has_source_customer_id_weight "Weight accumulation for matching customer_has_source_customer_id_weight PII vertices between Entoty vertices in the graph. Defaults to 0.5." - -UPDATE DESCRIPTION OF QUERY_PARAM match_entities.num_of_source_batches "Number of source batches to process. Defaults to 10." - -UPDATE DESCRIPTION OF QUERY_PARAM match_entities.num_of_target_batches "Number of target batches to process. Defaults to 1." - -UPDATE DESCRIPTION OF QUERY_PARAM match_entities.threshold "Accumulated weight threshold required for matching Entity vertices into a Connected_Component community using the provided weight for each PII vertex type. Defaults to 1.0." - -UPDATE DESCRIPTION OF QUERY_PARAM match_entities.pii_low_connections_limit "Maximum outdegree of PII vertex considered for 'low connection' matching. The case will be skipped if the same vertex is connected to too many entities. Defaults to 100." - -UPDATE DESCRIPTION OF QUERY_PARAM match_entities.pii_high_connections_limit "Maximum outdegree of PII vertex considered for 'high connection' matching. The case will be skipped if the same vertex is connected to too many entities. Defaults to 25000." - -UPDATE DESCRIPTION OF QUERY_PARAM match_entities.compute_entities_after_date "All Entity vertices with created_at after this date will be computed to find similarity edges related to these new entities. Defaults to 1970-01-01 00:00:00 (which is the earliest possible created_at and great for the initial running of this query)." \ No newline at end of file diff --git a/connected_customer/entity_resolution/queries/output_entity_cc_to_file.gsql b/connected_customer/entity_resolution/queries/output_entity_cc_to_file.gsql index 8971a9ff..22fff069 100644 --- a/connected_customer/entity_resolution/queries/output_entity_cc_to_file.gsql +++ b/connected_customer/entity_resolution/queries/output_entity_cc_to_file.gsql @@ -16,7 +16,3 @@ CREATE OR REPLACE QUERY output_entity_cc_to_file(STRING output_file_path = "/hom PRINT "Results file created!" AS result; } - -UPDATE DESCRIPTION OF QUERY output_entity_cc_to_file "This query outputs a file containing a mapping of all Entity vertices to their respective Connected_Component community vertex." - -UPDATE DESCRIPTION OF QUERY_PARAM output_entity_cc_to_file.output_file_path "File path location containing the output Entity vertex to Connected_Component vertex in CSV format. Defaults to /home/tigergraph/gsql_output/entity_cc_output.csv" \ No newline at end of file diff --git a/connected_customer/entity_resolution/queries/unify_entities.gsql b/connected_customer/entity_resolution/queries/unify_entities.gsql index 8c44ae54..d770df09 100644 --- a/connected_customer/entity_resolution/queries/unify_entities.gsql +++ b/connected_customer/entity_resolution/queries/unify_entities.gsql @@ -40,5 +40,3 @@ CREATE OR REPLACE DISTRIBUTED QUERY unify_entities() FOR GRAPH Entity_Resolution ; } - -UPDATE DESCRIPTION OF QUERY unify_entities "This query associates all Entity vertices in the graph to a Connected_Component vertex using Same_As edge previously inserted by match_entities query." \ No newline at end of file diff --git a/connected_customer/product_recommendations/queries/queries.gsql b/connected_customer/product_recommendations/queries/queries.gsql index 44fce1c4..e7fb967e 100644 --- a/connected_customer/product_recommendations/queries/queries.gsql +++ b/connected_customer/product_recommendations/queries/queries.gsql @@ -204,19 +204,6 @@ CREATE QUERY k_means( PRINT "Created Cluster Count:", rep_verts.size(); } -UPDATE DESCRIPTION OF QUERY k_means "This query clusters vertices according to some subset of their numerical attributes using the KMeans algorithm, which is a vector quantization clustering algorithm. The algorithm iteratively attempts to cluster the vertices using an incremental number of centroids and then selects the centroid assignment which results in the best sum of squared errors, such that the clusters are not under or overfit to the dataset. The algorithm inserts the centroids as Cluster vertices and links all vertices to their respective centroids via the In_Cluster edge type." -UPDATE DESCRIPTION OF QUERY_PARAM k_means.v_type "This is the target vertex type to cluster. It defaults to Customer." -UPDATE DESCRIPTION OF QUERY_PARAM k_means.attr_set "This is a JSON-formatted string representing a list of tuples containing an index and the key of a numerical value contained in the MAP attribute, 'attr_map', on the target vertex type. Example: '[0, \"Age\"]'" -UPDATE DESCRIPTION OF QUERY_PARAM k_means.min_cluster_count "This is the minimum number of centroids to try out when clustering." -UPDATE DESCRIPTION OF QUERY_PARAM k_means.max_cluster_count "This is the maximum number of centroids to try out when clustering. Must be greater than or equal to min_cluster_count." -UPDATE DESCRIPTION OF QUERY_PARAM k_means.cluster_inc "This is the value by which the centroid count is incremented when moving onto the next centroid count. Defaults to 1." -UPDATE DESCRIPTION OF QUERY_PARAM k_means.random_iter_count "The is the number of random initializations to attempt for a given centroid count. Defaults to 1." -UPDATE DESCRIPTION OF QUERY_PARAM k_means.conv_iter_limit "This is the maximum number of iterations to perform for a given centroid count. Defaults to 25." -UPDATE DESCRIPTION OF QUERY_PARAM k_means.conv_threshold "This is the minimum change in SSE during a clustering attempt before the algorithm considers a clustering to have converged. Defaults to 0.1" -UPDATE DESCRIPTION OF QUERY_PARAM k_means.random_seed "This is a seed to pseudorandomize the selection of centroids for a given centroid count." -UPDATE DESCRIPTION OF QUERY_PARAM k_means.use_custom_timestamp "This is the timestamp attributed to the Cluster vertices. If you wish to use a custom timestamp, set this to TRUE. Defaults to FALSE." -UPDATE DESCRIPTION OF QUERY_PARAM k_means.custom_timestamp "If 'use_custom_timestamp' is set to TRUE, then this value will be used as the timestamp for the Cluster vertices. Defaults to 'to_datetime(\"1970-01-01\")'" - CREATE QUERY combine_features( SET hub_v_type, @@ -279,15 +266,6 @@ CREATE QUERY combine_features( END; } -UPDATE DESCRIPTION OF QUERY combine_features "This query concatenates feature vertices with a degree above a certain threshold. Feature vertex types in this case refer to Cluster, Customer_Attribute, Product_Attribute, and Product_Variant. If two feature vertices are merged, a Combined_Feature vertex gets created and the constituent features get linked to the Combined_Feature via the Linked edge type." - -UPDATE DESCRIPTION OF QUERY_PARAM combine_features.hub_v_type "These are the feature vertex types which can be considered for the purposes of combination." -UPDATE DESCRIPTION OF QUERY_PARAM combine_features.e_type "These are the edge types which should be traversed in the query for the purposes of edge counting and combination." -UPDATE DESCRIPTION OF QUERY_PARAM combine_features.target_v_type "This is the vertex type which adjoins two different feature vertices for the purpose of combination. Defaults to 'Customer'." -UPDATE DESCRIPTION OF QUERY_PARAM combine_features.feature_v_type "These are the feature vertex types which the 'hub_v_types' should traverse to via the 'target_v_type' vertex type." -UPDATE DESCRIPTION OF QUERY_PARAM combine_features.hub_threshold "If a feature vertex has more than this number of edges, it is considered a hub vertex and is eligible to be combined into a Combined_Feature vertex." -UPDATE DESCRIPTION OF QUERY_PARAM combine_features.split_threshold "This number is the upper limit of neighbors that the source and target feature vertices may share in order to be combined into a Combined_Feature vertex." - CREATE DISTRIBUTED QUERY recommend_products( SET> src_customer_input, @@ -629,17 +607,3 @@ CREATE DISTRIBUTED QUERY recommend_products( PRINT src_customers[src_customers.@recommended_items]; } - -UPDATE DESCRIPTION OF QUERY recommend_products "This generates a list of recommendations, sorted by recommendation score, for each customer. Customers can either be provided a set of vertex ids, or a non-indexed batch of customers can be specified via the 'batch_index' and 'num_batches' parameters. Recommendations can be calculated via customer-customer similarity (and popularity of items among those similar customers) and/or co-purchasing frequency of the source customer's purchase history with other products." - -UPDATE DESCRIPTION OF QUERY_PARAM recommend_products.src_customer_input "This contains a set of customer ids for whom recommendations will be generated. If left empty, 'batch_index' and 'num_batches' will be used instead to produce the set of source customers." -UPDATE DESCRIPTION OF QUERY_PARAM recommend_products.batch_index "If used, this represents which indexed fraction of num_batches to use. e.g. if num_batches is 3, then 0 represents the first third of customers, 1, represents the second third, and 2 represents the final third of customers." -UPDATE DESCRIPTION OF QUERY_PARAM recommend_products.num_batches "If used, this represents the total number of batches into which the customer vertices which will split." -UPDATE DESCRIPTION OF QUERY_PARAM recommend_products.target_batch "This represents the number of batches into which target vertices will be split during the item-item collaborative filtering stage of the query. If the query is running out of memory when executing, consider increasing the value of this parameter. Defaults to 1." -UPDATE DESCRIPTION OF QUERY_PARAM recommend_products.ignore_threshold "This represents the minimum threshold which an item's popularity must exceed during the recommendation calculation in order to be included for further processing. Defaults to 0.001." -UPDATE DESCRIPTION OF QUERY_PARAM recommend_products.recommendation_count "This is the number of recommendations which are to be provided for the source customer(s). Defaults to 10." -UPDATE DESCRIPTION OF QUERY_PARAM recommend_products.data_types "This is JSON-formatted string containing the vertex and edge types to be included at each stage of the recommendation algorithm. The structure is an array of arrays, each of which contains the following elements in the following order: 1) A string value which is either 'Customer' or 'Item', which indicates whether the vertex/edge type will be used in the customer-customer similarity portion of the algorithm or the item-item co-purchasing portion of the algorithm. 2) A string value of either 'Feature' or 'Target', which specifies whether the vertex/edge type corresponds to the 'feature'/first traversal of the recommendation calculation or the 'target'/second traversal. Feature corresponds to calculating initial similarity, and target corresponds to traversing to target items to calculate popularity or recommendation score. 3) A string value of either 'Vertex' or Edge, which specifies whether the specified data type is a vertex or edge type. 4) An array of string names of the vertex or edge types matching this specification. The default value of this parameter includes Customers, Items, and all relevant edge types." -UPDATE DESCRIPTION OF QUERY_PARAM recommend_products.edge_importance_factors "This is a JSON-formatted string containing edge types mapped to a scalar multiplier on that edge type's importance to the recommendation calculation. The structure of the string is an outer array of arrays. The internal arrays contain 1) A string value which is either 'Customer' or 'Item', which indicates whether this scalar multiplier will be applied in the customer-customer similarity portion of the algorithm or the item-item co-purchasing portion of the algorithm. 2) A string value representing the edge type. 3) A floating-point value representing the importance multiplier. The default value includes all edge types which are relevant for the base version of the recommendation algorithm." -UPDATE DESCRIPTION OF QUERY_PARAM recommend_products.vertex_degree_scales "This is a set of strings containing the vertex types whose importance may be inversely scaled relative to their degree. e.g. If 'Item' is specified, then the importance of each item (in the context of the customer-customer similarity calculation) will be inversely dependent on how popular that item is. The intuition for this is that globally popular items may be less statistically significant than locally popular items for the purposes of recommendation. If left empty, all vertex types will be considered. If no vertex types should be considered, simply populate this parameter with a string that does not match any vertex type (such as 'null')." -UPDATE DESCRIPTION OF QUERY_PARAM recommend_products.customer_popularity_scale "This is a scalar multiplier which scales the recommendation scores generated via customer-customer similarity. Defaults to 1." -UPDATE DESCRIPTION OF QUERY_PARAM recommend_products.item_popularity_scale "This is a scalar multiplier which scales the recommendation scores generated via item-item co-purchasing frequency. Defaults to 1." diff --git a/financial_crime/application_fraud/queries/batch_application_cc_features.gsql b/financial_crime/application_fraud/queries/batch_application_cc_features.gsql index 3976d124..597b7e03 100644 --- a/financial_crime/application_fraud/queries/batch_application_cc_features.gsql +++ b/financial_crime/application_fraud/queries/batch_application_cc_features.gsql @@ -133,9 +133,3 @@ CREATE OR REPLACE DISTRIBUTED QUERY batch_application_cc_features(INT connection END; } - -UPDATE DESCRIPTION OF QUERY batch_application_cc_features "This query outputs a file containing the following graph features for each Application vertex in the graph: Application.id, Application.is_fraud, Application Connected_Component community, Application nodes in Connected_Component, Fraud Application Nodes in Connected_Component, distinct PII nodes in Connected_Component, number of Application nodes in Connected_Component connected by PII, number of Application nodes in Connected_Component only connected by PII. PII includes Name, DOB, Email, Phone, Address, IP, ID, Device, Party, Account, and Card vertices" - -UPDATE DESCRIPTION OF QUERY_PARAM batch_application_cc_features.connections "Maximum outdegree of each PII vertex allowed in search used to filter out hub nodes. Defaults to 25000." - -UPDATE DESCRIPTION OF QUERY_PARAM batch_application_cc_features.output_file_path "File path location to containing the output graph features in CSV format. Defaults to /home/tigergraph/gsql_output/batch_application_cc_features.csv" diff --git a/financial_crime/application_fraud/queries/batch_application_distance_and_path.gsql b/financial_crime/application_fraud/queries/batch_application_distance_and_path.gsql index 8c4aa344..b716ca75 100644 --- a/financial_crime/application_fraud/queries/batch_application_distance_and_path.gsql +++ b/financial_crime/application_fraud/queries/batch_application_distance_and_path.gsql @@ -68,9 +68,3 @@ CREATE DISTRIBUTED QUERY batch_application_distance_and_path(INT depth=5, STRING PRINT "Results file created!" AS result; } - -UPDATE DESCRIPTION OF QUERY batch_application_distance_and_path "This query outputs a file containing the following graph features for each Application vertex in the graph: Application.id, Application.is_fraud, Application Connected_Component community, Fraud Application.id, Fraud Application.id Connected_Component community, Degree of Connection from Application vertex to Fraud Application vertex, Path of Connection from Application vertex to Fraud Application vertex." - -UPDATE DESCRIPTION OF QUERY_PARAM batch_application_distance_and_path.depth "Maximum number of hops from source Application vertex to traverse in the graph. Defaults to 5." - -UPDATE DESCRIPTION OF QUERY_PARAM batch_application_distance_and_path.output_file_path "File path location to containing the output graph features in CSV format. Defaults to /home/tigergraph/gsql_output/batch_application_distance_and_path_features.csv" diff --git a/financial_crime/application_fraud/queries/delete_all_application_cc_connections.gsql b/financial_crime/application_fraud/queries/delete_all_application_cc_connections.gsql index 3ef93cd8..67d77385 100644 --- a/financial_crime/application_fraud/queries/delete_all_application_cc_connections.gsql +++ b/financial_crime/application_fraud/queries/delete_all_application_cc_connections.gsql @@ -13,9 +13,3 @@ CREATE OR REPLACE DISTRIBUTED QUERY delete_all_application_cc_connections(INT nu PRINT result; } - -UPDATE DESCRIPTION OF QUERY delete_all_application_cc_connections "This query deletes all Application_In_Ring edges associating Application vertices to Connected_Component community vertices." - -UPDATE DESCRIPTION OF QUERY_PARAM delete_all_application_cc_connections.num_of_batches "Number of batches to partition the deletions for all Application vertices in the graph. This query must be called the same number of times as batch_num with incrementing batch_id 0 through batch_num - 1 to process all Application vertices in the graph. Defaults to 1." - -UPDATE DESCRIPTION OF QUERY_PARAM delete_all_application_cc_connections.batch_id "Current batch partition of Application vertices to process and must be called with batch_id 0 through batch_num - 1. Defaults to 0." diff --git a/financial_crime/application_fraud/queries/delete_unused_cc_nodes.gsql b/financial_crime/application_fraud/queries/delete_unused_cc_nodes.gsql index 04be56a2..91f21784 100644 --- a/financial_crime/application_fraud/queries/delete_unused_cc_nodes.gsql +++ b/financial_crime/application_fraud/queries/delete_unused_cc_nodes.gsql @@ -15,9 +15,3 @@ CREATE OR REPLACE DISTRIBUTED QUERY delete_unused_cc_nodes(INT num_of_batches = PRINT result; } - -UPDATE DESCRIPTION OF QUERY delete_unused_cc_nodes "This query deletes all unused Connected_Component vertices in the graph." - -UPDATE DESCRIPTION OF QUERY_PARAM delete_unused_cc_nodes.num_of_batches "Number of batches to partition the deletions for all Connected_Component vertices in the graph. This query must be called the same number of times as batch_num with incrementing batch_id 0 through batch_num - 1 to process all Connected_Component vertices in the graph. Defaults to 1." - -UPDATE DESCRIPTION OF QUERY_PARAM delete_unused_cc_nodes.batch_id "Current batch partition of Connected_Component vertices to process and must be called with batch_id 0 through batch_num - 1. Defaults to 0." diff --git a/financial_crime/application_fraud/queries/distance_and_path_to_fraud_application.gsql b/financial_crime/application_fraud/queries/distance_and_path_to_fraud_application.gsql index 88956c59..0633a7a0 100644 --- a/financial_crime/application_fraud/queries/distance_and_path_to_fraud_application.gsql +++ b/financial_crime/application_fraud/queries/distance_and_path_to_fraud_application.gsql @@ -50,9 +50,3 @@ CREATE OR REPLACE QUERY distance_and_path_to_fraud_application(VERTEX PRINT @@degrees_of_shared_piis; } - -UPDATE DESCRIPTION OF QUERY find_shared_piis_of_two_applications "This query returns all shared PII vertex type, value, and degree between the two provided Application vertex parameters." - -UPDATE DESCRIPTION OF QUERY_PARAM find_shared_piis_of_two_applications.application_1 "First Application vertex used to search for shared PII vertices." - -UPDATE DESCRIPTION OF QUERY_PARAM find_shared_piis_of_two_applications.application_2 "Second Application vertex used to search for shared PII vertices." diff --git a/financial_crime/application_fraud/queries/get_application_cc_features.gsql b/financial_crime/application_fraud/queries/get_application_cc_features.gsql index 5eea27fb..331374fd 100644 --- a/financial_crime/application_fraud/queries/get_application_cc_features.gsql +++ b/financial_crime/application_fraud/queries/get_application_cc_features.gsql @@ -182,9 +182,3 @@ CREATE OR REPLACE QUERY get_application_cc_features(VERTEX applicat END; } - -UPDATE DESCRIPTION OF QUERY get_application_cc_features "This query returns the following graph features for the provided Application vertex parameter in near real time: Application Connected_Component community, Application nodes in Connected_Component, Fraud Application Nodes in Connected_Component, distinct PII nodes in Connected_Component, number of Application nodes in Connected_Component connected by PII, number of Application nodes in Connected_Component only connected by PII. PII includes Name, DOB, Email, Phone, Address, IP, ID, Device, Party, Account, and Card vertices" - -UPDATE DESCRIPTION OF QUERY_PARAM get_application_cc_features.application "Source Application vertex for generating graph features." - -UPDATE DESCRIPTION OF QUERY_PARAM get_application_cc_features.connections "Maximum outdegree of each PII vertex allowed in search used to filter out hub nodes. Defaults to 25000." diff --git a/financial_crime/application_fraud/queries/get_application_fraud_status.gsql b/financial_crime/application_fraud/queries/get_application_fraud_status.gsql index b8c6c6ad..c4ec79e9 100644 --- a/financial_crime/application_fraud/queries/get_application_fraud_status.gsql +++ b/financial_crime/application_fraud/queries/get_application_fraud_status.gsql @@ -9,7 +9,3 @@ CREATE OR REPLACE QUERY get_application_fraud_status(SET> ap print @@fraud_status as fraud_status; } - -UPDATE DESCRIPTION OF QUERY get_application_fraud_status "This query returns the boolean fraud status of each provided Application vertex." - -UPDATE DESCRIPTION OF QUERY_PARAM get_application_fraud_status.applications "Application vertices for obtaining fraud status as stored in is_fraud boolean attribute." diff --git a/financial_crime/application_fraud/queries/get_num_applications_by_app_status.gsql b/financial_crime/application_fraud/queries/get_num_applications_by_app_status.gsql index 6039ab0a..f3255596 100644 --- a/financial_crime/application_fraud/queries/get_num_applications_by_app_status.gsql +++ b/financial_crime/application_fraud/queries/get_num_applications_by_app_status.gsql @@ -18,5 +18,3 @@ CREATE OR REPLACE DISTRIBUTED QUERY get_num_applications_by_app_status () { print @@app_status_info_list as app_status_info_list; } - -UPDATE DESCRIPTION OF QUERY get_num_applications_by_app_status "This query returns the count of application status (e.g. PENDING, APPROVED) for all applications." diff --git a/financial_crime/application_fraud/queries/get_num_applications_by_fraud_status.gsql b/financial_crime/application_fraud/queries/get_num_applications_by_fraud_status.gsql index 4b0697c0..54d2e38a 100644 --- a/financial_crime/application_fraud/queries/get_num_applications_by_fraud_status.gsql +++ b/financial_crime/application_fraud/queries/get_num_applications_by_fraud_status.gsql @@ -21,5 +21,3 @@ CREATE OR REPLACE DISTRIBUTED QUERY get_num_applications_by_fraud_status () { PRINT @@fraud_status_info_list as fraud_status_info_list; } - -UPDATE DESCRIPTION OF QUERY get_num_applications_by_fraud_status "This query returns the count of fraudulent and legitimate applications for all applications." diff --git a/financial_crime/application_fraud/queries/get_top_k_connected_components_by_num_applications.gsql b/financial_crime/application_fraud/queries/get_top_k_connected_components_by_num_applications.gsql index 6eaabf47..62b84c85 100644 --- a/financial_crime/application_fraud/queries/get_top_k_connected_components_by_num_applications.gsql +++ b/financial_crime/application_fraud/queries/get_top_k_connected_components_by_num_applications.gsql @@ -37,7 +37,3 @@ CREATE OR REPLACE DISTRIBUTED QUERY get_top_k_connected_components_by_num_applic PRINT top_k_connected_components, connected_applications; PRINT @@edges_to_display AS edges_to_display; } - -UPDATE DESCRIPTION OF QUERY get_top_k_connected_components_by_num_applications "This query returns the top top_k connected components and their applications, ranked by the number of applications it connects to." - -UPDATE DESCRIPTION OF QUERY_PARAM get_top_k_connected_components_by_num_applications.top_k "The number of top connected components we want to return for this query." \ No newline at end of file diff --git a/financial_crime/application_fraud/queries/get_top_k_products_by_num_applications.gsql b/financial_crime/application_fraud/queries/get_top_k_products_by_num_applications.gsql index e2664adb..83a22a0f 100644 --- a/financial_crime/application_fraud/queries/get_top_k_products_by_num_applications.gsql +++ b/financial_crime/application_fraud/queries/get_top_k_products_by_num_applications.gsql @@ -81,9 +81,3 @@ CREATE OR REPLACE DISTRIBUTED QUERY get_top_k_products_by_num_applications ( top_k_products.description AS description ]; } - -UPDATE DESCRIPTION OF QUERY get_top_k_products_by_num_applications "This query returns the top top_k products, ranked by the number of applications each product connects to." - -UPDATE DESCRIPTION OF QUERY_PARAM get_top_k_products_by_num_applications.top_k "The number of top products we want to return for this query." -UPDATE DESCRIPTION OF QUERY_PARAM get_top_k_products_by_num_applications.input_application_fraud_status "(Optional) Filter for fraud status of an application. Defaults to '' (no filter)." -UPDATE DESCRIPTION OF QUERY_PARAM get_top_k_products_by_num_applications.input_application_status "(Optional) Filter for the status of an application (case-sensitive). Defaults to '' (no filter)." \ No newline at end of file diff --git a/financial_crime/application_fraud/queries/get_top_k_products_by_num_applications_with_other.gsql b/financial_crime/application_fraud/queries/get_top_k_products_by_num_applications_with_other.gsql index 96bc34c9..1e4e04aa 100644 --- a/financial_crime/application_fraud/queries/get_top_k_products_by_num_applications_with_other.gsql +++ b/financial_crime/application_fraud/queries/get_top_k_products_by_num_applications_with_other.gsql @@ -107,9 +107,3 @@ CREATE OR REPLACE DISTRIBUTED QUERY get_top_k_products_by_num_applications_with_ PRINT @@top_k_products_tuple_list AS top_k_product_list; } - -UPDATE DESCRIPTION OF QUERY get_top_k_products_by_num_applications_with_other "This query returns the top top_k products, ranked by the number of applications each product connects to. Also displays how many 'other' non top_k products as well - suitable for visualization." - -UPDATE DESCRIPTION OF QUERY_PARAM get_top_k_products_by_num_applications_with_other.top_k "The number of top products we want to return for this query." -UPDATE DESCRIPTION OF QUERY_PARAM get_top_k_products_by_num_applications_with_other.input_application_fraud_status "(Optional) Filter for fraud status of an application. Defaults to '' (no filter)." -UPDATE DESCRIPTION OF QUERY_PARAM get_top_k_products_by_num_applications_with_other.input_application_status "(Optional) Filter for the status of an application (case-sensitive). Defaults to '' (no filter)." \ No newline at end of file diff --git a/financial_crime/application_fraud/queries/incremental_application_match.gsql b/financial_crime/application_fraud/queries/incremental_application_match.gsql index c68829d4..3bbc42fa 100644 --- a/financial_crime/application_fraud/queries/incremental_application_match.gsql +++ b/financial_crime/application_fraud/queries/incremental_application_match.gsql @@ -441,31 +441,3 @@ CREATE OR REPLACE QUERY incremental_application_match( PRINT application AS Application, erv as entity_resolution; } - -UPDATE DESCRIPTION OF QUERY incremental_application_match "This query parses a JSON payload containing Application and associated PII vertex data, upserts the data into the graph, and returns whether the provided Application is able to be matched with any existing Applications using approximate weighted weakly connected components algorithm matching. The matching is controlled by the provided parameter weights for each PII attribute which must accumulate to the threshold to match." - -UPDATE DESCRIPTION OF QUERY_PARAM incremental_application_match.input "input is a JSON string containing a new or updated Application and associated PII vertex data with following structure {\"application\": \"string\", \"created_at\": \"string datetime\", \"status\": \"string\", \"line_of_credit\": \"string float\", \"annual_percentage_rate\": \"string float\", \"fraud\": \"boolean\", \"product\": \"string\", \"name\": \"string\", \"dob\": \"string datetime\", \"email\": \"string\", \"phone_numbers\": [{\"type\": \"string\", \"id\": \"string\"}], \"addresses\": [{\"type\": \"string\", \"line_1\": \"string\", \"line_2\": \"string\", \"city\": \"string\", \"state\": \"string\", \"zipcode\": \"string\", \"county\": \"string\", \"country\": \"string\"}], \"ip_address\": \"string\", \"ids\": [{\"type\": \"string\", \"id\": \"string\"}], \"device_id\": \"string\", \"party\": \"string\", \"accounts\": [{\"type\": \"string\", \"id\": \"string\"}], \"cards\": [{\"type\": \"string\", \"id\": \"int\"}]}" - -UPDATE DESCRIPTION OF QUERY_PARAM incremental_application_match.Application_Full_Name_weight "Weight accumulation for matching Full_Name PII vertices between provided input Application and every other Application in the graph. Defaults to 0.2." - -UPDATE DESCRIPTION OF QUERY_PARAM incremental_application_match.Application_DOB_weight "Weight accumulation for matching DOB PII vertices between provided input Application and every other Application in the graph. Defaults to 0.2." - -UPDATE DESCRIPTION OF QUERY_PARAM incremental_application_match.Application_Email_weight "Weight accumulation for matching Email PII vertices between provided input Application and every other Application in the graph. Defaults to 0.2." - -UPDATE DESCRIPTION OF QUERY_PARAM incremental_application_match.Application_Phone_weight "Weight accumulation for matching Phone PII vertices between provided input Application and every other Application in the graph. Defaults to 0.2." - -UPDATE DESCRIPTION OF QUERY_PARAM incremental_application_match.Application_Address_weight "Weight accumulation for matching Address PII vertices between provided input Application and every other Application in the graph. Defaults to 0.2." - -UPDATE DESCRIPTION OF QUERY_PARAM incremental_application_match.Application_IP_weight "Weight accumulation for matching IP PII vertices between provided input Application and every other Application in the graph. Defaults to 0.2." - -UPDATE DESCRIPTION OF QUERY_PARAM incremental_application_match.Application_ID_weight "Weight accumulation for matching ID PII vertices between provided input Application and every other Application in the graph. Defaults to 1.0." - -UPDATE DESCRIPTION OF QUERY_PARAM incremental_application_match.Application_Device_weight "Weight accumulation for matching Device PII vertices between provided input Application and every other Application in the graph. Defaults to 1.0." - -UPDATE DESCRIPTION OF QUERY_PARAM incremental_application_match.Application_Party_weight "Weight accumulation for matching Party vertices between provided input Application and every other Application in the graph. Defaults to 1.0." - -UPDATE DESCRIPTION OF QUERY_PARAM incremental_application_match.Application_Account_weight "Weight accumulation for matching Account PII vertices between provided input Application and every other Application in the graph. Defaults to 1.0." - -UPDATE DESCRIPTION OF QUERY_PARAM incremental_application_match.Application_Card_weight "Weight accumulation for matching Card PII vertices between provided input Application and every other Application in the graph. Defaults to 1.0." - -UPDATE DESCRIPTION OF QUERY_PARAM incremental_application_match.threshold "Accumulated weight threshold required for matching Application vertices into a Connected_Component community using the provided weight for each PII vertex type. Defaults to 1.0." diff --git a/financial_crime/application_fraud/queries/incremental_application_unify.gsql b/financial_crime/application_fraud/queries/incremental_application_unify.gsql index 51566034..9cdca8dc 100644 --- a/financial_crime/application_fraud/queries/incremental_application_unify.gsql +++ b/financial_crime/application_fraud/queries/incremental_application_unify.gsql @@ -38,7 +38,3 @@ CREATE OR REPLACE QUERY incremental_application_unify(SET> a ; } - -UPDATE DESCRIPTION OF QUERY incremental_application_unify "This query associates Application vertices unable to be matched to any existing Application using incremental_application_match into its own respective Connected_Component community vertex so it can be eligible for weighted weakly connected components matching in near real time." - -UPDATE DESCRIPTION OF QUERY_PARAM incremental_application_unify.applications "Application vertices to put into a Connected_Component community." diff --git a/financial_crime/application_fraud/queries/match_application_entities.gsql b/financial_crime/application_fraud/queries/match_application_entities.gsql index d13eb6eb..638af0ba 100644 --- a/financial_crime/application_fraud/queries/match_application_entities.gsql +++ b/financial_crime/application_fraud/queries/match_application_entities.gsql @@ -230,39 +230,3 @@ CREATE OR REPLACE DISTRIBUTED QUERY match_application_entities( PRINT execution_time AS execution_time_in_seconds; } - -UPDATE DESCRIPTION OF QUERY match_application_entities "This query finds matches and creates similarity edges between entities that match. It is used as the second step in the Entity Resolution process, right after running delete_all_application_cc_connections." - -UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.Application_Full_Name_weight "Weight accumulation for matching Full_Name PII vertices between each Application and every other Application in the graph. Defaults to 0.2." - -UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.Application_DOB_weight "Weight accumulation for matching DOB PII vertices between all Application vertices in the graph. Defaults to 0.2." - -UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.Application_Email_weight "Weight accumulation for matching Email PII vertices between all Application vertices in the graph. Defaults to 0.2." - -UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.Application_Phone_weight "Weight accumulation for matching Phone PII vertices between all Application vertices in the graph. Defaults to 0.2." - -UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.Application_Address_weight "Weight accumulation for matching Address PII vertices between all Application vertices in the graph. Defaults to 0.2." - -UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.Application_IP_weight "Weight accumulation for matching IP PII vertices between all Application vertices in the graph. Defaults to 0.2." - -UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.Application_ID_weight "Weight accumulation for matching ID PII vertices between all Application vertices in the graph. Defaults to 1.0." - -UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.Application_Device_weight "Weight accumulation for matching Device PII vertices between all Application vertices in the graph. Defaults to 1.0." - -UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.Application_Party_weight "Weight accumulation for matching Party vertices between all Application vertices in the graph. Defaults to 1.0." - -UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.Application_Account_weight "Weight accumulation for matching Account PII vertices between all Application vertices in the graph. Defaults to 1.0." - -UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.Application_Card_weight "Weight accumulation for matching Card PII vertices between all Application vertices in the graph. Defaults to 1.0." - -UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.num_of_source_batches "Number of source batches to process. Defaults to 10." - -UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.num_of_target_batches "Number of target batches to process. Defaults to 1." - -UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.threshold "Accumulated weight threshold required for matching Application vertices into a Connected_Component community using the provided weight for each PII vertex type. Defaults to 1.0." - -UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.pii_low_connections_limit "Maximum outdegree of PII vertex considered for 'low connection' matching. The case will be skipped if the same vertex is connected to too many entities. Defaults to 100." - -UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.pii_high_connections_limit "Maximum outdegree of PII vertex considered for 'high connection' matching. The case will be skipped if the same vertex is connected to too many entities. Defaults to 25000." - -UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.compute_entities_after_date "All Application vertices with created_at after this date will be computed to find similarity edges related to these new entities. Defaults to 1970-01-01 00:00:00 (which is the earliest possible created_at and great for the initial running of this query)." diff --git a/financial_crime/application_fraud/queries/output_application_cc_to_file.gsql b/financial_crime/application_fraud/queries/output_application_cc_to_file.gsql index 919a1f83..445125ef 100644 --- a/financial_crime/application_fraud/queries/output_application_cc_to_file.gsql +++ b/financial_crime/application_fraud/queries/output_application_cc_to_file.gsql @@ -14,8 +14,3 @@ CREATE OR REPLACE DISTRIBUTED QUERY output_application_cc_to_file(STRING output_ PRINT "Results file created!" AS result; } - -UPDATE DESCRIPTION OF QUERY output_application_cc_to_file "This query outputs a file containing a mapping of all Application vertices to their respective Connected_Component community vertex." - -UPDATE DESCRIPTION OF QUERY_PARAM output_application_cc_to_file.output_file_path "File path location containing the output Application vertex to Connected_Component vertex in CSV format. Defaults to /home/tigergraph/gsql_output/application_cc_output.csv" - diff --git a/financial_crime/application_fraud/queries/set_application_fraud_status.gsql b/financial_crime/application_fraud/queries/set_application_fraud_status.gsql index 21ad83eb..8d9d8c35 100644 --- a/financial_crime/application_fraud/queries/set_application_fraud_status.gsql +++ b/financial_crime/application_fraud/queries/set_application_fraud_status.gsql @@ -5,9 +5,3 @@ CREATE OR REPLACE QUERY set_application_fraud_status(VERTEX applica start = select s from start:s post-accum s.is_fraud = fraud_status; } - -UPDATE DESCRIPTION OF QUERY set_application_fraud_status "This query sets the boolean is_fraud attribute for the provided Application vertex to the provided fraud_status boolean value in near real time." - -UPDATE DESCRIPTION OF QUERY_PARAM set_application_fraud_status.application "Application vertex for which to set the respective is_fraud attribute." - -UPDATE DESCRIPTION OF QUERY_PARAM set_application_fraud_status.fraud_status "New Application.is_fraud value for the provided Application vertex." diff --git a/financial_crime/application_fraud/queries/unify_application_entities.gsql b/financial_crime/application_fraud/queries/unify_application_entities.gsql index 7be94454..f68b6666 100644 --- a/financial_crime/application_fraud/queries/unify_application_entities.gsql +++ b/financial_crime/application_fraud/queries/unify_application_entities.gsql @@ -38,5 +38,3 @@ CREATE OR REPLACE DISTRIBUTED QUERY unify_application_entities() { ; } - -UPDATE DESCRIPTION OF QUERY unify_application_entities "This query associates all Application vertices in the graph to a Connected_Component vertex using Same_Application edge previously inserted by match_application_entities query." diff --git a/financial_crime/entity_resolution_kyc/queries/batch_party_cc_features.gsql b/financial_crime/entity_resolution_kyc/queries/batch_party_cc_features.gsql index f4acba91..119f1246 100644 --- a/financial_crime/entity_resolution_kyc/queries/batch_party_cc_features.gsql +++ b/financial_crime/entity_resolution_kyc/queries/batch_party_cc_features.gsql @@ -121,9 +121,3 @@ CREATE OR REPLACE DISTRIBUTED QUERY batch_party_cc_features(INT connections=2500 END; } - -UPDATE DESCRIPTION OF QUERY batch_party_cc_features "This query outputs a file containing the following graph features for each Party vertex in the graph: Party.id, Party.is_fraud, Party Connected_Component community, Party nodes in Connected_Component, Fraud Party Nodes in Connected_Component, distinct PII nodes in Connected_Component, number of Party nodes in Connected_Component connected by PII, number of Party nodes in Connected_Component only connected by PII. PII includes Name, DOB, Email, Phone, Address, IP, ID, Device, Party, Account, and Card vertices" - -UPDATE DESCRIPTION OF QUERY_PARAM batch_party_cc_features.connections "Maximum outdegree of each PII vertex allowed in search used to filter out hub nodes. Defaults to 25000." - -UPDATE DESCRIPTION OF QUERY_PARAM batch_party_cc_features.output_file_path "File path location to containing the output graph features in CSV format. Defaults to /home/tigergraph/gsql_output/batch_party_cc_features.csv" \ No newline at end of file diff --git a/financial_crime/entity_resolution_kyc/queries/batch_party_distance_and_path.gsql b/financial_crime/entity_resolution_kyc/queries/batch_party_distance_and_path.gsql index 2c1ff471..2198c034 100644 --- a/financial_crime/entity_resolution_kyc/queries/batch_party_distance_and_path.gsql +++ b/financial_crime/entity_resolution_kyc/queries/batch_party_distance_and_path.gsql @@ -68,9 +68,3 @@ CREATE DISTRIBUTED QUERY batch_party_distance_and_path(INT depth=5, STRING outpu PRINT "Results file created!" AS result; } - -UPDATE DESCRIPTION OF QUERY batch_party_distance_and_path "This query outputs a file containing the following graph features for each Party vertex in the graph: Party.id, Party.is_fraud, Party Connected_Component community, Fraud Party.id, Fraud Party.id Connected_Component community, Degree of Connection from Party vertex to Fraud Party vertex, Path of Connection from Party vertex to Fraud Party vertex." - -UPDATE DESCRIPTION OF QUERY_PARAM batch_party_distance_and_path.depth "Maximum number of hops from source Party vertex to traverse in the graph. Defaults to 5." - -UPDATE DESCRIPTION OF QUERY_PARAM batch_party_distance_and_path.output_file_path "File path location to containing the output graph features in CSV format. Defaults to /home/tigergraph/gsql_output/batch_party_distance_and_path_features.csv" \ No newline at end of file diff --git a/financial_crime/entity_resolution_kyc/queries/delete_all_party_cc_connections.gsql b/financial_crime/entity_resolution_kyc/queries/delete_all_party_cc_connections.gsql index 8c9cb643..1dc3010e 100644 --- a/financial_crime/entity_resolution_kyc/queries/delete_all_party_cc_connections.gsql +++ b/financial_crime/entity_resolution_kyc/queries/delete_all_party_cc_connections.gsql @@ -13,9 +13,3 @@ CREATE OR REPLACE DISTRIBUTED QUERY delete_all_party_cc_connections(INT num_of_b PRINT result; } - -UPDATE DESCRIPTION OF QUERY delete_all_party_cc_connections "This query deletes all Entity_In_Ring edges associating Party vertices to Connected_Component community vertices." - -UPDATE DESCRIPTION OF QUERY_PARAM delete_all_party_cc_connections.num_of_batches "Number of batches to partition the deletions for all Party vertices in the graph. This query must be called the same number of times as batch_num with incrementing batch_id 0 through batch_num - 1 to process all Party vertices in the graph. Defaults to 1." - -UPDATE DESCRIPTION OF QUERY_PARAM delete_all_party_cc_connections.batch_id "Current batch partition of Party vertices to process and must be called with batch_id 0 through batch_num - 1. Defaults to 0." \ No newline at end of file diff --git a/financial_crime/entity_resolution_kyc/queries/delete_unused_cc_nodes.gsql b/financial_crime/entity_resolution_kyc/queries/delete_unused_cc_nodes.gsql index 98705745..91f21784 100644 --- a/financial_crime/entity_resolution_kyc/queries/delete_unused_cc_nodes.gsql +++ b/financial_crime/entity_resolution_kyc/queries/delete_unused_cc_nodes.gsql @@ -15,9 +15,3 @@ CREATE OR REPLACE DISTRIBUTED QUERY delete_unused_cc_nodes(INT num_of_batches = PRINT result; } - -UPDATE DESCRIPTION OF QUERY delete_unused_cc_nodes "This query deletes all unused Connected_Component vertices in the graph." - -UPDATE DESCRIPTION OF QUERY_PARAM delete_unused_cc_nodes.num_of_batches "Number of batches to partition the deletions for all Connected_Component vertices in the graph. This query must be called the same number of times as batch_num with incrementing batch_id 0 through batch_num - 1 to process all Connected_Component vertices in the graph. Defaults to 1." - -UPDATE DESCRIPTION OF QUERY_PARAM delete_unused_cc_nodes.batch_id "Current batch partition of Connected_Component vertices to process and must be called with batch_id 0 through batch_num - 1. Defaults to 0." \ No newline at end of file diff --git a/financial_crime/entity_resolution_kyc/queries/distance_and_path_to_fraud_party.gsql b/financial_crime/entity_resolution_kyc/queries/distance_and_path_to_fraud_party.gsql index 5dee6779..3b35f943 100644 --- a/financial_crime/entity_resolution_kyc/queries/distance_and_path_to_fraud_party.gsql +++ b/financial_crime/entity_resolution_kyc/queries/distance_and_path_to_fraud_party.gsql @@ -43,9 +43,3 @@ CREATE OR REPLACE QUERY distance_and_path_to_fraud_party(VERTEX input, IN //print L2[L2.party_id]; print result[result.@dis as degree_of_connection, result.is_fraud as is_fraud, result.id as party_id, result.@path_of_connection as path_of_connection]; } - -UPDATE DESCRIPTION OF QUERY distance_and_path_to_fraud_party "This query returns the distance and path of connection to any Fraud Party vertices up to depth hops deep from any given input Party in near real time." - -UPDATE DESCRIPTION OF QUERY_PARAM distance_and_path_to_fraud_party.input "Source Party vertex from which to begin traversal searching for closely connected Fraud Party vertices." - -UPDATE DESCRIPTION OF QUERY_PARAM distance_and_path_to_fraud_party.depth "Maximum number of hops from source Party vertex to traverse in the graph. Defaults to 5." \ No newline at end of file diff --git a/financial_crime/entity_resolution_kyc/queries/find_shared_piis_of_two_parties.gsql b/financial_crime/entity_resolution_kyc/queries/find_shared_piis_of_two_parties.gsql index 80b69d73..ed831473 100644 --- a/financial_crime/entity_resolution_kyc/queries/find_shared_piis_of_two_parties.gsql +++ b/financial_crime/entity_resolution_kyc/queries/find_shared_piis_of_two_parties.gsql @@ -24,9 +24,3 @@ CREATE OR REPLACE QUERY find_shared_piis_of_two_parties(VERTEX party_1, V PRINT @@degrees_of_shared_piis; } - -UPDATE DESCRIPTION OF QUERY find_shared_piis_of_two_parties "This query returns all shared PII vertex type, value, and degree between the two provided Party vertex parameters." - -UPDATE DESCRIPTION OF QUERY_PARAM find_shared_piis_of_two_parties.party_1 "First Party vertex used to search for shared PII vertices." - -UPDATE DESCRIPTION OF QUERY_PARAM find_shared_piis_of_two_parties.party_2 "Second Party vertex used to search for shared PII vertices." \ No newline at end of file diff --git a/financial_crime/entity_resolution_kyc/queries/get_party_cc_features.gsql b/financial_crime/entity_resolution_kyc/queries/get_party_cc_features.gsql index 8c99f0a7..a60afe67 100644 --- a/financial_crime/entity_resolution_kyc/queries/get_party_cc_features.gsql +++ b/financial_crime/entity_resolution_kyc/queries/get_party_cc_features.gsql @@ -161,9 +161,3 @@ CREATE OR REPLACE QUERY get_party_cc_features(VERTEX party, INT connectio } - -UPDATE DESCRIPTION OF QUERY get_party_cc_features "This query returns the following graph features for the provided Party vertex parameter in near real time: Party Connected_Component community, Party nodes in Connected_Component, Fraud Party Nodes in Connected_Component, distinct PII nodes in Connected_Component, number of Party nodes in Connected_Component connected by PII, number of Party nodes in Connected_Component only connected by PII. PII includes Name, DOB, Email, Phone, Address, IP, ID, Device, Party, Account, and Card vertices" - -UPDATE DESCRIPTION OF QUERY_PARAM get_party_cc_features.party "Source Party vertex for generating graph features." - -UPDATE DESCRIPTION OF QUERY_PARAM get_party_cc_features.connections "Maximum outdegree of each PII vertex allowed in search used to filter out hub nodes. Defaults to 25000." \ No newline at end of file diff --git a/financial_crime/entity_resolution_kyc/queries/get_party_fraud_status.gsql b/financial_crime/entity_resolution_kyc/queries/get_party_fraud_status.gsql index 5b75a5a0..bd58e393 100644 --- a/financial_crime/entity_resolution_kyc/queries/get_party_fraud_status.gsql +++ b/financial_crime/entity_resolution_kyc/queries/get_party_fraud_status.gsql @@ -10,7 +10,3 @@ CREATE DISTRIBUTED QUERY get_party_fraud_status(SET> parties) { } - -UPDATE DESCRIPTION OF QUERY get_party_fraud_status "This query returns the integer fraud status of each provided Party vertex." - -UPDATE DESCRIPTION OF QUERY_PARAM get_party_fraud_status.parties "Party vertices for obtaining fraud status as stored in is_fraud integer attribute." \ No newline at end of file diff --git a/financial_crime/entity_resolution_kyc/queries/incremental_party_match.gsql b/financial_crime/entity_resolution_kyc/queries/incremental_party_match.gsql index 67bfc9bc..41010106 100644 --- a/financial_crime/entity_resolution_kyc/queries/incremental_party_match.gsql +++ b/financial_crime/entity_resolution_kyc/queries/incremental_party_match.gsql @@ -432,31 +432,3 @@ CREATE OR REPLACE QUERY incremental_party_match( PRINT party AS Party, erv as entity_resolution; } - -UPDATE DESCRIPTION OF QUERY incremental_party_match "This query parses a JSON payload containing Party and associated PII vertex data, upserts the data into the graph, and returns whether the provided Party is able to be matched with any existing Parties using approximate weighted weakly connected components algorithm matching. The matching is controlled by the provided parameter weights for each PII attribute which must accumulate to the threshold to match." - -UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_match.input "input is a JSON string containing a new or updated Party and associated PII vertex data with following structure {\"party\": \"string\", \"created_at\": \"string datetime\", \"name\": \"string\", \"party_type\": \"string\", \"gender\": \"string\", \"dob\": \"string datetime\", \"fraud\": \"int\", \"email\": \"string\", \"phone_numbers\": [{\"type\": \"string\", \"id\": \"string\"}], \"addresses\": [{\"type\": \"string\", \"line_1\": \"string\", \"line_2\": \"string\", \"city\": \"string\", \"state\": \"string\", \"zipcode\": \"string\", \"county\": \"string\", \"country\": \"string\"}], \"ip_address\": \"string\", \"ids\": [{\"type\": \"string\", \"id\": \"string\"}], \"device_id\": \"string\", \"application\": \"string\", \"accounts\": [{\"type\": \"string\", \"id\": \"string\"}], \"cards\": [{\"type\": \"string\", \"id\": \"int\"}]}" - -UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_match.Customer_Full_Name_weight "Weight accumulation for matching Full_Name PII vertices between provided input Party and every other Party in the graph. Defaults to 0.2." - -UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_match.Customer_DOB_weight "Weight accumulation for matching DOB PII vertices between provided input Party and every other Party in the graph. Defaults to 0.2." - -UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_match.Customer_Email_weight "Weight accumulation for matching Email PII vertices between provided input Party and every other Party in the graph. Defaults to 0.2." - -UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_match.Customer_Phone_weight "Weight accumulation for matching Phone PII vertices between provided input Party and every other Party in the graph. Defaults to 0.2." - -UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_match.Customer_Address_weight "Weight accumulation for matching Address PII vertices between provided input Party and every other Party in the graph. Defaults to 0.2." - -UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_match.Customer_IP_weight "Weight accumulation for matching IP PII vertices between provided input Party and every other Party in the graph. Defaults to 0.2." - -UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_match.Customer_ID_weight "Weight accumulation for matching ID PII vertices between provided input Party and every other Party in the graph. Defaults to 1.0." - -UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_match.Customer_Device_weight "Weight accumulation for matching Device PII vertices between provided input Party and every other Party in the graph. Defaults to 1.0." - -UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_match.Customer_Application_weight "Weight accumulation for matching Application vertices between provided input Party and every other Party in the graph. Defaults to 1.0." - -UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_match.Customer_Account_weight "Weight accumulation for matching Account PII vertices between provided input Party and every other Party in the graph. Defaults to 1.0." - -UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_match.Customer_Card_weight "Weight accumulation for matching Card PII vertices between provided input Party and every other Party in the graph. Defaults to 1.0." - -UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_match.threshold "Accumulated weight threshold required for matching Party vertices into a Connected_Component community using the provided weight for each PII vertex type. Defaults to 1.0." \ No newline at end of file diff --git a/financial_crime/entity_resolution_kyc/queries/incremental_party_unify.gsql b/financial_crime/entity_resolution_kyc/queries/incremental_party_unify.gsql index ab029e0b..ca4bb247 100644 --- a/financial_crime/entity_resolution_kyc/queries/incremental_party_unify.gsql +++ b/financial_crime/entity_resolution_kyc/queries/incremental_party_unify.gsql @@ -38,7 +38,3 @@ CREATE OR REPLACE DISTRIBUTED QUERY incremental_party_unify(SET> p ; } - -UPDATE DESCRIPTION OF QUERY incremental_party_unify "This query associates Party vertices unable to be matched to any existing Party using incremental_party_match into its own respective Connected_Component community vertex so it can be eligible for weighted weakly connected components matching in near real time." - -UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_unify.parties "Party vertices to put into a Connected_Component community." \ No newline at end of file diff --git a/financial_crime/entity_resolution_kyc/queries/match_party_entities.gsql b/financial_crime/entity_resolution_kyc/queries/match_party_entities.gsql index 3f69c364..11bd134e 100644 --- a/financial_crime/entity_resolution_kyc/queries/match_party_entities.gsql +++ b/financial_crime/entity_resolution_kyc/queries/match_party_entities.gsql @@ -230,39 +230,3 @@ CREATE OR REPLACE DISTRIBUTED QUERY match_party_entities( PRINT execution_time AS execution_time_in_seconds; } - -UPDATE DESCRIPTION OF QUERY match_party_entities "This query parses a JSON payload containing Party and associated PII vertex data, upserts the data into the graph, and returns whether the provided Party is able to be matched with any existing Parties using approximate weighted weakly connected components algorithm matching. The matching is controlled by the provided parameter weights for each PII attribute which must accumulate to the threshold to match." - -UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.Customer_Full_Name_weight "Weight accumulation for matching Full_Name PII vertices between each Party and every other Party in the graph. Defaults to 0.2." - -UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.Customer_DOB_weight "Weight accumulation for matching DOB PII vertices between all Party vertices in the graph. Defaults to 0.2." - -UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.Customer_Email_weight "Weight accumulation for matching Email PII vertices between all Party vertices in the graph. Defaults to 0.2." - -UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.Customer_Phone_weight "Weight accumulation for matching Phone PII vertices between all Party vertices in the graph. Defaults to 0.2." - -UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.Customer_Address_weight "Weight accumulation for matching Address PII vertices between all Party vertices in the graph. Defaults to 0.2." - -UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.Customer_IP_weight "Weight accumulation for matching IP PII vertices between all Party vertices in the graph. Defaults to 0.2." - -UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.Customer_ID_weight "Weight accumulation for matching ID PII vertices between all Party vertices in the graph. Defaults to 1.0." - -UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.Customer_Device_weight "Weight accumulation for matching Device PII vertices between all Party vertices in the graph. Defaults to 1.0." - -UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.Customer_Application_weight "Weight accumulation for matching Application vertices between all Party vertices in the graph. Defaults to 1.0." - -UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.Customer_Account_weight "Weight accumulation for matching Account PII vertices between all Party vertices in the graph. Defaults to 1.0." - -UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.Customer_Card_weight "Weight accumulation for matching Card PII vertices between all Party vertices in the graph. Defaults to 1.0." - -UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.num_of_source_batches "Number of source batches to process. Defaults to 10." - -UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.num_of_target_batches "Number of target batches to process. Defaults to 1." - -UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.threshold "Accumulated weight threshold required for matching Party vertices into a Connected_Component community using the provided weight for each PII vertex type. Defaults to 1.0." - -UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.pii_low_connections_limit "Maximum outdegree of PII vertex considered for 'low connection' matching. The case will be skipped if the same vertex is connected to too many entities. Defaults to 100." - -UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.pii_high_connections_limit "Maximum outdegree of PII vertex considered for 'high connection' matching. The case will be skipped if the same vertex is connected to too many entities. Defaults to 25000." - -UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.compute_entities_after_date "All Party vertices with created_at after this date will be computed to find similarity edges related to these new entities. Defaults to 1970-01-01 00:00:00 (which is the earliest possible created_at and great for the initial running of this query)." \ No newline at end of file diff --git a/financial_crime/entity_resolution_kyc/queries/output_party_cc_to_file.gsql b/financial_crime/entity_resolution_kyc/queries/output_party_cc_to_file.gsql index 32543849..12ee1490 100644 --- a/financial_crime/entity_resolution_kyc/queries/output_party_cc_to_file.gsql +++ b/financial_crime/entity_resolution_kyc/queries/output_party_cc_to_file.gsql @@ -14,7 +14,3 @@ CREATE OR REPLACE QUERY output_party_cc_to_file(STRING output_file_path = "/home PRINT "Results file created!" AS result; } - -UPDATE DESCRIPTION OF QUERY output_party_cc_to_file "This query outputs a file containing a mapping of all Party vertices to their respective Connected_Component community vertex." - -UPDATE DESCRIPTION OF QUERY_PARAM output_party_cc_to_file.output_file_path "File path location containing the output Party vertex to Connected_Component vertex in CSV format. Defaults to /home/tigergraph/gsql_output/party_cc_output.csv" \ No newline at end of file diff --git a/financial_crime/entity_resolution_kyc/queries/set_party_fraud_status.gsql b/financial_crime/entity_resolution_kyc/queries/set_party_fraud_status.gsql index f0cff4f6..27e6bab0 100644 --- a/financial_crime/entity_resolution_kyc/queries/set_party_fraud_status.gsql +++ b/financial_crime/entity_resolution_kyc/queries/set_party_fraud_status.gsql @@ -5,9 +5,3 @@ CREATE DISTRIBUTED QUERY set_party_fraud_status(VERTEX party, INT fraud_s start = select s from start:s post-accum s.is_fraud = fraud_status; } - -UPDATE DESCRIPTION OF QUERY set_party_fraud_status "This query sets the integer is_fraud attribute for the provided Party vertex to the provided fraud_status integer value in near real time." - -UPDATE DESCRIPTION OF QUERY_PARAM set_party_fraud_status.party "Party vertex for which to set the respective is_fraud attribute." - -UPDATE DESCRIPTION OF QUERY_PARAM set_party_fraud_status.fraud_status "New Party.is_fraud value for the provided Party vertex." \ No newline at end of file diff --git a/financial_crime/entity_resolution_kyc/queries/unify_party_entities.gsql b/financial_crime/entity_resolution_kyc/queries/unify_party_entities.gsql index c469f5b9..235f0e77 100644 --- a/financial_crime/entity_resolution_kyc/queries/unify_party_entities.gsql +++ b/financial_crime/entity_resolution_kyc/queries/unify_party_entities.gsql @@ -38,5 +38,3 @@ CREATE OR REPLACE DISTRIBUTED QUERY unify_party_entities() { ; } - -UPDATE DESCRIPTION OF QUERY unify_party_entities "This query associates all Party vertices in the graph to a Connected_Component vertex using Same_As edge previously inserted by match_party_entities query." \ No newline at end of file diff --git a/financial_crime/mule_account_detection/queries/attributes_to_party_traversal.gsql b/financial_crime/mule_account_detection/queries/attributes_to_party_traversal.gsql index 8b645b09..707f9964 100644 --- a/financial_crime/mule_account_detection/queries/attributes_to_party_traversal.gsql +++ b/financial_crime/mule_account_detection/queries/attributes_to_party_traversal.gsql @@ -75,8 +75,3 @@ CREATE DISTRIBUTED QUERY attributes_to_party_traversal( } - -UPDATE DESCRIPTION OF QUERY attributes_to_party_traversal "Originating from a chosen attribute of party identification information, this query methodically searches the transaction fraud graph to retrieve all associated PII for the specified party. Its utility is paramount in fraud detection and prevention frameworks, enabling a comprehensive analysis of party identities." - -UPDATE DESCRIPTION OF QUERY_PARAM attributes_to_party_traversal.v_type "The vertex type of party's identification information Vertex type." -UPDATE DESCRIPTION OF QUERY_PARAM attributes_to_party_traversal.id "The id of the party's identification information Vertex." diff --git a/financial_crime/mule_account_detection/queries/party_full_address.gsql b/financial_crime/mule_account_detection/queries/party_full_address.gsql index 95a72a35..88701ca1 100644 --- a/financial_crime/mule_account_detection/queries/party_full_address.gsql +++ b/financial_crime/mule_account_detection/queries/party_full_address.gsql @@ -24,7 +24,3 @@ CREATE DISTRIBUTED QUERY party_full_address( } - -UPDATE DESCRIPTION OF QUERY party_full_address "This query retrieves the complete address for a party, encompassing the street address, city, state, and zipcode. It's essential for verifying and analyzing party location data, facilitating accurate and efficient address validation." - -UPDATE DESCRIPTION OF QUERY_PARAM party_full_address.p "The Party Vertex of interest." \ No newline at end of file diff --git a/financial_crime/mule_account_detection/queries/single_Party_PII.gsql b/financial_crime/mule_account_detection/queries/single_Party_PII.gsql index 66892fe5..9870abb8 100644 --- a/financial_crime/mule_account_detection/queries/single_Party_PII.gsql +++ b/financial_crime/mule_account_detection/queries/single_Party_PII.gsql @@ -61,7 +61,3 @@ CREATE DISTRIBUTED QUERY single_Party_PII( ]; } - -UPDATE DESCRIPTION OF QUERY single_Party_PII "This query retrieves party identification information, including details like full name, date of birth, email address, and other personal identifiers. It is used for verifying the identity of individuals associated with transactions, which is crucial for enhancing security measures and preventing identity theft or fraud within financial operations." - -UPDATE DESCRIPTION OF QUERY_PARAM single_Party_PII.ver "The single Party vertex of interest." diff --git a/financial_crime/transaction_fraud/queries/attributes_to_party_traversal.gsql b/financial_crime/transaction_fraud/queries/attributes_to_party_traversal.gsql index 42c4bf1f..5f140434 100644 --- a/financial_crime/transaction_fraud/queries/attributes_to_party_traversal.gsql +++ b/financial_crime/transaction_fraud/queries/attributes_to_party_traversal.gsql @@ -74,8 +74,3 @@ CREATE DISTRIBUTED QUERY attributes_to_party_traversal( } - -UPDATE DESCRIPTION OF QUERY attributes_to_party_traversal "Originating from a chosen attribute of party identification information, this query methodically searches the transaction fraud graph to retrieve all associated PII for the specified party. Its utility is paramount in fraud detection and prevention frameworks, enabling a comprehensive analysis of party identities." - -UPDATE DESCRIPTION OF QUERY_PARAM attributes_to_party_traversal.v_type "The vertex type of party's identification information Vertex type." -UPDATE DESCRIPTION OF QUERY_PARAM attributes_to_party_traversal.id "The id of the party's identification information Vertex." diff --git a/financial_crime/transaction_fraud/queries/card_has_frequent_transactions.gsql b/financial_crime/transaction_fraud/queries/card_has_frequent_transactions.gsql index 53272edf..d99739a7 100644 --- a/financial_crime/transaction_fraud/queries/card_has_frequent_transactions.gsql +++ b/financial_crime/transaction_fraud/queries/card_has_frequent_transactions.gsql @@ -28,9 +28,3 @@ CREATE DISTRIBUTED QUERY card_has_frequent_transactions( } - -UPDATE DESCRIPTION OF QUERY card_has_frequent_transactions "This query identifies and retrieves all card numbers that have conducted more than k transactions within a specified time period. It is used to detect potential fraud by pinpointing cards with unusually high transaction volumes, which may suggest stolen card details being exploited or other fraudulent behaviors." - -UPDATE DESCRIPTION OF QUERY_PARAM card_has_frequent_transactions.min_createTime "The earliest time to look back in history. Defaults to 2019-01-06" -UPDATE DESCRIPTION OF QUERY_PARAM card_has_frequent_transactions.max_createTime "The latest time to look back in history. Defaults to 2024-01-01" -UPDATE DESCRIPTION OF QUERY_PARAM card_has_frequent_transactions.freq "The threshold count or frequency value for transactions . Defaults to 3000" \ No newline at end of file diff --git a/financial_crime/transaction_fraud/queries/card_has_large_total_amount.gsql b/financial_crime/transaction_fraud/queries/card_has_large_total_amount.gsql index 5263381b..31e791a2 100644 --- a/financial_crime/transaction_fraud/queries/card_has_large_total_amount.gsql +++ b/financial_crime/transaction_fraud/queries/card_has_large_total_amount.gsql @@ -28,9 +28,3 @@ SetAccum @@edges; ACCUM @@edges +=e; PRINT @@edges; } - -UPDATE DESCRIPTION OF QUERY card_has_large_total_amount "This query identifies and retrieves all card numbers whose total transaction amount exceeds m dollars within a specified time period. It is used for identifying cards that may be involved in fraudulent activities by tracking the aggregate spending pattern, which, if unusually high, could indicate misuse or unauthorized transactions." - -UPDATE DESCRIPTION OF QUERY_PARAM card_has_large_total_amount.min_createTime "The earliest time to look back in history. Defaults to 2020-01-01" -UPDATE DESCRIPTION OF QUERY_PARAM card_has_large_total_amount.max_createTime "The latest time to look back in history. Defaults to 2024-01-01" -UPDATE DESCRIPTION OF QUERY_PARAM card_has_large_total_amount.m "The threshold amount for total transaction values. Defaults to 200000" \ No newline at end of file diff --git a/financial_crime/transaction_fraud/queries/card_transactions_stats.gsql b/financial_crime/transaction_fraud/queries/card_transactions_stats.gsql index 9e6b7e20..6b6b833a 100644 --- a/financial_crime/transaction_fraud/queries/card_transactions_stats.gsql +++ b/financial_crime/transaction_fraud/queries/card_transactions_stats.gsql @@ -30,6 +30,3 @@ CREATE DISTRIBUTED QUERY card_transactions_stats( } -UPDATE DESCRIPTION OF QUERY card_transactions_stats "This query is designed to yield comprehensive statistics on transactions made using a specific card, encapsulating transaction count, total amount, and the maximum, average, and minimum transaction amounts. It serves as a pivotal tool for monitoring card usage, identifying spending patterns, and enhancing fraud detection efforts by pinpointing irregularities in transaction activities." - -UPDATE DESCRIPTION OF QUERY_PARAM card_transactions_stats.v "The single Card vertex of interest." diff --git a/financial_crime/transaction_fraud/queries/card_with_single_large_transaction.gsql b/financial_crime/transaction_fraud/queries/card_with_single_large_transaction.gsql index fa1df95c..12abb57c 100644 --- a/financial_crime/transaction_fraud/queries/card_with_single_large_transaction.gsql +++ b/financial_crime/transaction_fraud/queries/card_with_single_large_transaction.gsql @@ -22,9 +22,3 @@ CREATE DISTRIBUTED QUERY card_with_single_large_transaction( } - -UPDATE DESCRIPTION OF QUERY card_with_single_large_transaction "This query identifies and retrieves all card numbers that have recorded a single transaction exceeding a specified amount m within a certain time frame. It is used for flagging potentially fraudulent activity by highlighting unusually large transactions that could indicate unauthorized use or testing of the card." - -UPDATE DESCRIPTION OF QUERY_PARAM card_with_single_large_transaction.min_createTime "The earliest time to look back in history. Defaults to 2020-01-01" -UPDATE DESCRIPTION OF QUERY_PARAM card_with_single_large_transaction.max_createTime "The latest time to look back in history. Defaults to 2024-01-01" -UPDATE DESCRIPTION OF QUERY_PARAM card_with_single_large_transaction.m "The threshold amount for transaction values. Defaults to 10000" \ No newline at end of file diff --git a/financial_crime/transaction_fraud/queries/merchant_category_transactions_stats.gsql b/financial_crime/transaction_fraud/queries/merchant_category_transactions_stats.gsql index 8e216d99..cb825951 100644 --- a/financial_crime/transaction_fraud/queries/merchant_category_transactions_stats.gsql +++ b/financial_crime/transaction_fraud/queries/merchant_category_transactions_stats.gsql @@ -30,6 +30,3 @@ CREATE DISTRIBUTED QUERY merchant_category_transactions_stats( } -UPDATE DESCRIPTION OF QUERY merchant_category_transactions_stats "This query furnishes detailed transaction statistics for a specific merchant category, covering aspects such as the count of transactions, total transaction amount, and the maximum, average, and minimum transaction amounts. It's crucial for analyzing market trends, assessing the financial health of merchant categories, and detecting anomalies that could indicate fraudulent activity or market shifts." - -UPDATE DESCRIPTION OF QUERY_PARAM merchant_category_transactions_stats.v "The single Merchant_Category vertex of interest." diff --git a/financial_crime/transaction_fraud/queries/merchant_has_frequent_transactions.gsql b/financial_crime/transaction_fraud/queries/merchant_has_frequent_transactions.gsql index daaa10aa..9323410c 100644 --- a/financial_crime/transaction_fraud/queries/merchant_has_frequent_transactions.gsql +++ b/financial_crime/transaction_fraud/queries/merchant_has_frequent_transactions.gsql @@ -28,9 +28,3 @@ CREATE DISTRIBUTED QUERY merchant_has_frequent_transactions( } - -UPDATE DESCRIPTION OF QUERY merchant_has_frequent_transactions "This query identifies and retrieves all merchants that have processed more than k transactions within a specified time period. It is used to detect potential fraudulent or unusual activity by identifying merchants with an exceptionally high volume of transactions, which may indicate a compromised merchant account or other forms of transactional fraud." - -UPDATE DESCRIPTION OF QUERY_PARAM merchant_has_frequent_transactions.min_createTime "The earliest time to look back in history. Defaults to 2020-01-01" -UPDATE DESCRIPTION OF QUERY_PARAM merchant_has_frequent_transactions.max_createTime "The latest time to look back in history. Defaults to 2024-01-01" -UPDATE DESCRIPTION OF QUERY_PARAM merchant_has_frequent_transactions.freq "The threshold amount for transaction values. Defaults to 3000" \ No newline at end of file diff --git a/financial_crime/transaction_fraud/queries/merchant_has_large_total_amount.gsql b/financial_crime/transaction_fraud/queries/merchant_has_large_total_amount.gsql index fe45c8ab..1c512abb 100644 --- a/financial_crime/transaction_fraud/queries/merchant_has_large_total_amount.gsql +++ b/financial_crime/transaction_fraud/queries/merchant_has_large_total_amount.gsql @@ -29,8 +29,3 @@ SetAccum @@edges; PRINT @@edges; } -UPDATE DESCRIPTION OF QUERY merchant_has_large_total_amount "This query identifies and retrieves all merchants whose total transaction amount exceeds m dollars within a specified time period. It is used for detecting potential fraudulent or unusual business activity by highlighting merchants with exceptionally high total sales, which could indicate fraudulent transactions or money laundering activities." - -UPDATE DESCRIPTION OF QUERY_PARAM merchant_has_large_total_amount.min_createTime "The earliest time to look back in history. Defaults to 2020-01-01" -UPDATE DESCRIPTION OF QUERY_PARAM merchant_has_large_total_amount.max_createTime "The latest time to look back in history. Defaults to 2024-01-01" -UPDATE DESCRIPTION OF QUERY_PARAM merchant_has_large_total_amount.m "The threshold amount for transaction values. Defaults to 200000" diff --git a/financial_crime/transaction_fraud/queries/merchant_transactions_stats.gsql b/financial_crime/transaction_fraud/queries/merchant_transactions_stats.gsql index 84045431..218030d0 100644 --- a/financial_crime/transaction_fraud/queries/merchant_transactions_stats.gsql +++ b/financial_crime/transaction_fraud/queries/merchant_transactions_stats.gsql @@ -31,6 +31,3 @@ CREATE DISTRIBUTED QUERY merchant_transactions_stats( } -UPDATE DESCRIPTION OF QUERY merchant_transactions_stats "This query offers crucial insights into a merchant's transactional behavior by providing detailed statistics, including the count of transactions, total transaction amount, and the maximum, average, and minimum transaction amounts. It is instrumental for financial analysis, aiding in the detection of anomalies, fraud prevention, and the assessment of merchant performance." - -UPDATE DESCRIPTION OF QUERY_PARAM merchant_transactions_stats.v "The single Merchant vertex of interest." diff --git a/financial_crime/transaction_fraud/queries/merchant_with_single_large_transaction.gsql b/financial_crime/transaction_fraud/queries/merchant_with_single_large_transaction.gsql index 3aceb249..0aebf053 100644 --- a/financial_crime/transaction_fraud/queries/merchant_with_single_large_transaction.gsql +++ b/financial_crime/transaction_fraud/queries/merchant_with_single_large_transaction.gsql @@ -22,9 +22,3 @@ CREATE DISTRIBUTED QUERY merchant_with_single_large_transaction( } - -UPDATE DESCRIPTION OF QUERY merchant_with_single_large_transaction "This query identifies and retrieves all merchants that have recorded a single transaction exceeding a specified amount m within a certain time frame. It is used to flag potential fraud or irregular activities by highlighting transactions that are unusually large for a given merchant, which could indicate suspicious or anomalous behavior." - -UPDATE DESCRIPTION OF QUERY_PARAM merchant_with_single_large_transaction.min_createTime "The earliest time to look back in history. Defaults to 2020-01-01" -UPDATE DESCRIPTION OF QUERY_PARAM merchant_with_single_large_transaction.max_createTime "The latest time to look back in history. Defaults to 2024-01-01" -UPDATE DESCRIPTION OF QUERY_PARAM merchant_with_single_large_transaction.m "The threshold amount for transaction values. Defaults to 10000" \ No newline at end of file diff --git a/financial_crime/transaction_fraud/queries/party_full_address.gsql b/financial_crime/transaction_fraud/queries/party_full_address.gsql index ce244674..3aea5e8e 100644 --- a/financial_crime/transaction_fraud/queries/party_full_address.gsql +++ b/financial_crime/transaction_fraud/queries/party_full_address.gsql @@ -24,6 +24,3 @@ CREATE QUERY party_full_address( } -UPDATE DESCRIPTION OF QUERY party_full_address "This query retrieves the complete address for a party, encompassing the street address, city, state, and zipcode. It's essential for verifying and analyzing party location data, facilitating accurate and efficient address validation." - -UPDATE DESCRIPTION OF QUERY_PARAM party_full_address.p "The Party Vertex of interest." diff --git a/financial_crime/transaction_fraud/queries/single_Party_PII.gsql b/financial_crime/transaction_fraud/queries/single_Party_PII.gsql index 2819f956..8bfc0d43 100644 --- a/financial_crime/transaction_fraud/queries/single_Party_PII.gsql +++ b/financial_crime/transaction_fraud/queries/single_Party_PII.gsql @@ -61,6 +61,3 @@ CREATE QUERY single_Party_PII( } -UPDATE DESCRIPTION OF QUERY single_Party_PII "This query retrieves party identification information, including details like full name, date of birth, email address, and other personal identifiers. It is used for verifying the identity of individuals associated with transactions, which is crucial for enhancing security measures and preventing identity theft or fraud within financial operations." - -UPDATE DESCRIPTION OF QUERY_PARAM single_Party_PII.ver "The single Party vertex of interest." diff --git a/financial_crime/transaction_fraud/queries/single_card_lookup.gsql b/financial_crime/transaction_fraud/queries/single_card_lookup.gsql index 379496ff..af02f51f 100644 --- a/financial_crime/transaction_fraud/queries/single_card_lookup.gsql +++ b/financial_crime/transaction_fraud/queries/single_card_lookup.gsql @@ -66,6 +66,3 @@ CREATE QUERY single_card_lookup( } -UPDATE DESCRIPTION OF QUERY single_card_lookup "This query retrieves the detailed attributes associated with a single Card vertex. It is used for a thorough examination of a specific card's details, aiding in fraud investigation and risk assessment by providing in-depth insight into the card's attributes and usage patterns." - -UPDATE DESCRIPTION OF QUERY_PARAM single_card_lookup.ver "The single Card vertex of interest." diff --git a/financial_crime/transaction_fraud/queries/single_merchant_lookup.gsql b/financial_crime/transaction_fraud/queries/single_merchant_lookup.gsql index 6d439aaa..09b7479b 100644 --- a/financial_crime/transaction_fraud/queries/single_merchant_lookup.gsql +++ b/financial_crime/transaction_fraud/queries/single_merchant_lookup.gsql @@ -31,6 +31,3 @@ CREATE QUERY single_merchant_lookup( } -UPDATE DESCRIPTION OF QUERY single_merchant_lookup "This query retrieves the detailed attributes associated with a single Merchant vertex. It is used to analyze the merchant's profile and transaction behaviors, which can help in assessing risk levels and identifying patterns indicative of fraudulent activities or compromised merchant accounts." - -UPDATE DESCRIPTION OF QUERY_PARAM single_merchant_lookup.ver "The single Merchant vertex of interest." diff --git a/financial_crime/transaction_fraud/queries/single_transaction_lookup.gsql b/financial_crime/transaction_fraud/queries/single_transaction_lookup.gsql index 8eee1290..08e35696 100644 --- a/financial_crime/transaction_fraud/queries/single_transaction_lookup.gsql +++ b/financial_crime/transaction_fraud/queries/single_transaction_lookup.gsql @@ -17,6 +17,4 @@ CREATE QUERY single_transaction_lookup( PRINT rlt; } -UPDATE DESCRIPTION OF QUERY single_transaction_lookup "This query retrieves the comprehensive attributes of a single Payment_Transaction vertex, including all features such as transaction amount, date, graph algoarithm features and aggregated features required for downstream machine learning models. It is used to gather detailed information on individual transactions, facilitating the development and refinement of ML models aimed at detecting fraudulent activity by providing a rich dataset for analysis and model training." -UPDATE DESCRIPTION OF QUERY_PARAM single_transaction_lookup.ver "The single Payment_Transaction vertex of interest." From 787485c63202591ae16d4c85c8a67c9e982d735d Mon Sep 17 00:00:00 2001 From: "lin.yu" Date: Thu, 19 Jun 2025 10:20:08 +0800 Subject: [PATCH 04/55] Revert "TCE-6719 remove update description block for all queries" This reverts commit 732dd57386cb4aef6af748789f878e6e482429c9. --- ...e_failure_impact_radius_visualization.gsql | 3 ++ ..._visualization_with_subgraph_topology.gsql | 4 +++ ...nstream_device_topology_visualization.gsql | 5 +++ .../explore_topology_from_all_router.gsql | 2 ++ ...xplore_topology_from_multiple_routers.gsql | 3 ++ .../explore_topology_from_one_router.gsql | 6 +++- ...nts_by_impacted_device_and_time_range.gsql | 7 ++++ ...d_events_by_time_range_and_event_type.gsql | 6 ++++ ...tial_incident_source_of_event_by_time.gsql | 6 ++++ ..._related_events_from_incident_by_time.gsql | 6 ++++ .../find_unsecured_servers_visualization.gsql | 4 +++ .../incident_impact_by_max_radius.gsql | 5 +++ .../top_k_devices_with_most_alerts.gsql | 4 +++ .../top_k_devices_with_most_incidents.gsql | 4 +++ .../Application_Engagement_Individuals.gsql | 2 ++ .../queries/Application_Starts.gsql | 2 ++ .../queries/Application_Submissions.gsql | 2 ++ .../queries/Customers_No_Engagement.gsql | 2 ++ .../queries/Customers_With_Product.gsql | 3 ++ .../queries/Customers_With_Sessions.gsql | 2 ++ .../queries/Email_Engagement_Accounts.gsql | 2 ++ .../queries/Email_Engagement_By_Product.gsql | 2 ++ .../queries/Individual_WebSearch.gsql | 2 ++ .../Individuals_K_Most_Engagement.gsql | 3 ++ .../queries/Individuals_No_Application.gsql | 2 ++ .../queries/Individuals_Product_Browse.gsql | 2 ++ .../queries/delete_all_cc_connections.gsql | 6 ++++ .../queries/delete_unused_cc_nodes.gsql | 5 +++ .../find_shared_piis_of_two_entities.gsql | 6 ++++ .../queries/match_entities.gsql | 34 ++++++++++++++++++ .../queries/output_entity_cc_to_file.gsql | 4 +++ .../queries/unify_entities.gsql | 2 ++ .../queries/queries.gsql | 36 +++++++++++++++++++ .../batch_application_cc_features.gsql | 6 ++++ .../batch_application_distance_and_path.gsql | 6 ++++ ...delete_all_application_cc_connections.gsql | 6 ++++ .../queries/delete_unused_cc_nodes.gsql | 6 ++++ ...istance_and_path_to_fraud_application.gsql | 6 ++++ ...nce_and_path_to_fraud_application_vis.gsql | 8 +++++ .../find_shared_piis_of_two_applications.gsql | 6 ++++ .../queries/get_application_cc_features.gsql | 6 ++++ .../queries/get_application_fraud_status.gsql | 4 +++ .../get_num_applications_by_app_status.gsql | 2 ++ .../get_num_applications_by_fraud_status.gsql | 2 ++ ...nected_components_by_num_applications.gsql | 4 +++ ...et_top_k_products_by_num_applications.gsql | 6 ++++ ...oducts_by_num_applications_with_other.gsql | 6 ++++ .../incremental_application_match.gsql | 28 +++++++++++++++ .../incremental_application_unify.gsql | 4 +++ .../queries/match_application_entities.gsql | 36 +++++++++++++++++++ .../output_application_cc_to_file.gsql | 5 +++ .../queries/set_application_fraud_status.gsql | 6 ++++ .../queries/unify_application_entities.gsql | 2 ++ .../queries/batch_party_cc_features.gsql | 6 ++++ .../batch_party_distance_and_path.gsql | 6 ++++ .../delete_all_party_cc_connections.gsql | 6 ++++ .../queries/delete_unused_cc_nodes.gsql | 6 ++++ .../distance_and_path_to_fraud_party.gsql | 6 ++++ .../find_shared_piis_of_two_parties.gsql | 6 ++++ .../queries/get_party_cc_features.gsql | 6 ++++ .../queries/get_party_fraud_status.gsql | 4 +++ .../queries/incremental_party_match.gsql | 28 +++++++++++++++ .../queries/incremental_party_unify.gsql | 4 +++ .../queries/match_party_entities.gsql | 36 +++++++++++++++++++ .../queries/output_party_cc_to_file.gsql | 4 +++ .../queries/set_party_fraud_status.gsql | 6 ++++ .../queries/unify_party_entities.gsql | 2 ++ .../attributes_to_party_traversal.gsql | 5 +++ .../queries/party_full_address.gsql | 4 +++ .../queries/single_Party_PII.gsql | 4 +++ .../attributes_to_party_traversal.gsql | 5 +++ .../card_has_frequent_transactions.gsql | 6 ++++ .../queries/card_has_large_total_amount.gsql | 6 ++++ .../queries/card_transactions_stats.gsql | 3 ++ .../card_with_single_large_transaction.gsql | 6 ++++ .../merchant_category_transactions_stats.gsql | 3 ++ .../merchant_has_frequent_transactions.gsql | 6 ++++ .../merchant_has_large_total_amount.gsql | 5 +++ .../queries/merchant_transactions_stats.gsql | 3 ++ ...erchant_with_single_large_transaction.gsql | 6 ++++ .../queries/party_full_address.gsql | 3 ++ .../queries/single_Party_PII.gsql | 3 ++ .../queries/single_card_lookup.gsql | 3 ++ .../queries/single_merchant_lookup.gsql | 3 ++ .../queries/single_transaction_lookup.gsql | 2 ++ 85 files changed, 540 insertions(+), 1 deletion(-) diff --git a/agile_operations/network_infrastructure/queries/device_failure_impact_radius_visualization.gsql b/agile_operations/network_infrastructure/queries/device_failure_impact_radius_visualization.gsql index 5370ed0b..c22df26b 100644 --- a/agile_operations/network_infrastructure/queries/device_failure_impact_radius_visualization.gsql +++ b/agile_operations/network_infrastructure/queries/device_failure_impact_radius_visualization.gsql @@ -48,3 +48,6 @@ CREATE OR REPLACE QUERY device_failure_impact_radius_visualization ( } +UPDATE DESCRIPTION OF QUERY device_failure_impact_radius_visualization "This query finds and visualizes the devices that will fail if the provided input device fails." + +UPDATE DESCRIPTION OF QUERY_PARAM device_failure_impact_radius_visualization.device "The input device (accepts devices of all types)." diff --git a/agile_operations/network_infrastructure/queries/device_failure_impact_radius_visualization_with_subgraph_topology.gsql b/agile_operations/network_infrastructure/queries/device_failure_impact_radius_visualization_with_subgraph_topology.gsql index 32e87c45..207200de 100644 --- a/agile_operations/network_infrastructure/queries/device_failure_impact_radius_visualization_with_subgraph_topology.gsql +++ b/agile_operations/network_infrastructure/queries/device_failure_impact_radius_visualization_with_subgraph_topology.gsql @@ -69,3 +69,7 @@ CREATE OR REPLACE QUERY device_failure_impact_radius_visualization_with_subgraph ; } + +UPDATE DESCRIPTION OF QUERY device_failure_impact_radius_visualization_with_subgraph_topology "This query finds and visualizes the devices that will fail if the provided input device fails along with the subgraph that the input device was in. Use only for visualization purposes." + +UPDATE DESCRIPTION OF QUERY_PARAM device_failure_impact_radius_visualization_with_subgraph_topology.device "The input device (accepts devices of all types)." \ No newline at end of file diff --git a/agile_operations/network_infrastructure/queries/downstream_device_topology_visualization.gsql b/agile_operations/network_infrastructure/queries/downstream_device_topology_visualization.gsql index 9d784bc1..d39e7c98 100644 --- a/agile_operations/network_infrastructure/queries/downstream_device_topology_visualization.gsql +++ b/agile_operations/network_infrastructure/queries/downstream_device_topology_visualization.gsql @@ -106,3 +106,8 @@ CREATE OR REPLACE QUERY downstream_device_topology_visualization ( } +UPDATE DESCRIPTION OF QUERY downstream_device_topology_visualization "This query visualizes the downstream device topology of a provided input device." + +UPDATE DESCRIPTION OF QUERY_PARAM downstream_device_topology_visualization.device "The input device (accepts devices of all types)." + +UPDATE DESCRIPTION OF QUERY_PARAM downstream_device_topology_visualization.k_hop_switch_limit "The amount of additional hops to branching Switch devices. It dtermines the maximum depth when iterating from one Switch to another. Defaults to 3." diff --git a/agile_operations/network_infrastructure/queries/explore_topology_from_all_router.gsql b/agile_operations/network_infrastructure/queries/explore_topology_from_all_router.gsql index e3922c7a..4f8862e5 100644 --- a/agile_operations/network_infrastructure/queries/explore_topology_from_all_router.gsql +++ b/agile_operations/network_infrastructure/queries/explore_topology_from_all_router.gsql @@ -12,3 +12,5 @@ CREATE OR REPLACE QUERY explore_topology_from_all_router () { PRINT all_devices_with_connections; PRINT @@edges_to_display AS edges_to_display; } + +UPDATE DESCRIPTION OF QUERY explore_topology_from_all_router "This query visualizes the network topology of all devices in the database. It shows the downstream connections from all routers." \ No newline at end of file diff --git a/agile_operations/network_infrastructure/queries/explore_topology_from_multiple_routers.gsql b/agile_operations/network_infrastructure/queries/explore_topology_from_multiple_routers.gsql index c8163b5b..2f7f345a 100644 --- a/agile_operations/network_infrastructure/queries/explore_topology_from_multiple_routers.gsql +++ b/agile_operations/network_infrastructure/queries/explore_topology_from_multiple_routers.gsql @@ -83,3 +83,6 @@ CREATE OR REPLACE QUERY explore_topology_from_multiple_routers (SET starter all_visited_servers ; PRINT @@edges_to_display AS edges_to_display; -} \ No newline at end of file +} + +UPDATE DESCRIPTION OF QUERY explore_topology_from_one_router "This query visualizes the network topology starting from the router 'starter_router'." + +UPDATE DESCRIPTION OF QUERY_PARAM explore_topology_from_one_router.starter_router "The input router device to explore the network topology." \ No newline at end of file diff --git a/agile_operations/network_infrastructure/queries/find_events_by_impacted_device_and_time_range.gsql b/agile_operations/network_infrastructure/queries/find_events_by_impacted_device_and_time_range.gsql index 3c4b28b3..924cfcf9 100644 --- a/agile_operations/network_infrastructure/queries/find_events_by_impacted_device_and_time_range.gsql +++ b/agile_operations/network_infrastructure/queries/find_events_by_impacted_device_and_time_range.gsql @@ -92,3 +92,10 @@ CREATE OR REPLACE QUERY find_events_by_impacted_device_and_time_range ( PRINT @@edges_to_display; } + +UPDATE DESCRIPTION OF QUERY find_events_by_impacted_device_and_time_range "This query finds the events based on an input device and time range. It also visualizes the events and optionally can visualize the event type." + +UPDATE DESCRIPTION OF QUERY_PARAM find_events_by_impacted_device_and_time_range.input_device "The input device (accepts devices of all types)." +UPDATE DESCRIPTION OF QUERY_PARAM find_events_by_impacted_device_and_time_range.start_time "The start time filter to search for events - min time an event can have." +UPDATE DESCRIPTION OF QUERY_PARAM find_events_by_impacted_device_and_time_range.end_time "The end time to search for events - max time an event can have." +UPDATE DESCRIPTION OF QUERY_PARAM find_events_by_impacted_device_and_time_range.show_event_types_vis "Whether or not to display vertices that relate to the type of the event. Defaults to False." diff --git a/agile_operations/network_infrastructure/queries/find_events_by_time_range_and_event_type.gsql b/agile_operations/network_infrastructure/queries/find_events_by_time_range_and_event_type.gsql index 831ee4c7..99d6ade8 100644 --- a/agile_operations/network_infrastructure/queries/find_events_by_time_range_and_event_type.gsql +++ b/agile_operations/network_infrastructure/queries/find_events_by_time_range_and_event_type.gsql @@ -111,3 +111,9 @@ CREATE OR REPLACE QUERY find_events_by_time_range_and_event_type ( selected_events_with_info.@event_impacted_device_list AS impacted_devices_list ]; } + +UPDATE DESCRIPTION OF QUERY find_events_by_time_range_and_event_type "This query finds the events based on a time range and optionally on event type." + +UPDATE DESCRIPTION OF QUERY_PARAM find_events_by_time_range_and_event_type.start_time "The start time filter to search for events - min time an event can have." +UPDATE DESCRIPTION OF QUERY_PARAM find_events_by_time_range_and_event_type.end_time "The end time to search for events - max time an event can have." +UPDATE DESCRIPTION OF QUERY_PARAM find_events_by_time_range_and_event_type.input_event_type_filter "The event type that can be used to search for events. Defaults to an empty string for no filter on event type." diff --git a/agile_operations/network_infrastructure/queries/find_potential_incident_source_of_event_by_time.gsql b/agile_operations/network_infrastructure/queries/find_potential_incident_source_of_event_by_time.gsql index ecc15180..c88da0ca 100644 --- a/agile_operations/network_infrastructure/queries/find_potential_incident_source_of_event_by_time.gsql +++ b/agile_operations/network_infrastructure/queries/find_potential_incident_source_of_event_by_time.gsql @@ -124,3 +124,9 @@ CREATE OR REPLACE QUERY find_potential_incident_source_of_event_by_time ( PRINT @@edges_to_display; } + +UPDATE DESCRIPTION OF QUERY find_potential_incident_source_of_event_by_time "This query finds the potential source or cause of an event based on the 'input_event' event that happened 'num_seconds_before_event_start' seconds before the event and at most 'max_radius' hops away from the connected device." + +UPDATE DESCRIPTION OF QUERY_PARAM find_potential_incident_source_of_event_by_time.input_event "The input events to find potential source incidents to" +UPDATE DESCRIPTION OF QUERY_PARAM find_potential_incident_source_of_event_by_time.max_radius "The maximum number of hops extending from the device(s) the input event has an impact on. Defaults to 3." +UPDATE DESCRIPTION OF QUERY_PARAM find_potential_incident_source_of_event_by_time.num_seconds_before_event_start "The number of seconds before an event starts to search for incidents. Defaults to 3600 seconds." \ No newline at end of file diff --git a/agile_operations/network_infrastructure/queries/find_potential_related_events_from_incident_by_time.gsql b/agile_operations/network_infrastructure/queries/find_potential_related_events_from_incident_by_time.gsql index 311d97b2..e27fbee7 100644 --- a/agile_operations/network_infrastructure/queries/find_potential_related_events_from_incident_by_time.gsql +++ b/agile_operations/network_infrastructure/queries/find_potential_related_events_from_incident_by_time.gsql @@ -130,3 +130,9 @@ CREATE OR REPLACE QUERY find_potential_related_events_from_incident_by_time ( ; PRINT @@edges_to_display; } + +UPDATE DESCRIPTION OF QUERY find_potential_related_events_from_incident_by_time "This query finds the events that can be potentially related to the incident within 'num_seconds_from_incident_start' seconds after the input incident. It goes over devices that is up to 'max_radius' hops away from devies the incident impacts." + +UPDATE DESCRIPTION OF QUERY_PARAM find_potential_related_events_from_incident_by_time.input_incident "The input incident to find related events to it." +UPDATE DESCRIPTION OF QUERY_PARAM find_potential_related_events_from_incident_by_time.max_radius "The maximum number of hops extending from the device(s) the input incident has an impact on. Defaults to 3." +UPDATE DESCRIPTION OF QUERY_PARAM find_potential_related_events_from_incident_by_time.num_seconds_from_incident_start "The number of seconds after an incident starts to search for related events. Defaults to 3600 seconds." diff --git a/agile_operations/network_infrastructure/queries/find_unsecured_servers_visualization.gsql b/agile_operations/network_infrastructure/queries/find_unsecured_servers_visualization.gsql index 9feff355..be31a400 100644 --- a/agile_operations/network_infrastructure/queries/find_unsecured_servers_visualization.gsql +++ b/agile_operations/network_infrastructure/queries/find_unsecured_servers_visualization.gsql @@ -69,3 +69,7 @@ CREATE OR REPLACE QUERY find_unsecured_servers_visualization (UINT k_hop_switch_ ; } + +UPDATE DESCRIPTION OF QUERY find_unsecured_servers_visualization "This query visualizes the unsecured paths from Router devices to Server devices that do not go through firewalls." + +UPDATE DESCRIPTION OF QUERY_PARAM find_unsecured_servers_visualization.k_hop_switch_limit "The amount of additional hops to branching Switch devices. It determines the maximum depth when iterating from one Switch to another. Defaults to 3." diff --git a/agile_operations/network_infrastructure/queries/incident_impact_by_max_radius.gsql b/agile_operations/network_infrastructure/queries/incident_impact_by_max_radius.gsql index 39c5f966..ad6e3a9d 100644 --- a/agile_operations/network_infrastructure/queries/incident_impact_by_max_radius.gsql +++ b/agile_operations/network_infrastructure/queries/incident_impact_by_max_radius.gsql @@ -57,3 +57,8 @@ CREATE OR REPLACE QUERY incident_impact_by_max_radius ( ; PRINT @@edges_to_display; } + +UPDATE DESCRIPTION OF QUERY incident_impact_by_max_radius "This query finds the devices that can be impacted by an incident within 'max_radius' hops away from the devices directly impacted by the event." + +UPDATE DESCRIPTION OF QUERY_PARAM incident_impact_by_max_radius.input_incident "The input incident to find devices that can be impacted by it." +UPDATE DESCRIPTION OF QUERY_PARAM incident_impact_by_max_radius.max_radius "The maximum number of hops extending from the device(s) the input incident has an impact on. Defaults to 4." \ No newline at end of file diff --git a/agile_operations/network_infrastructure/queries/top_k_devices_with_most_alerts.gsql b/agile_operations/network_infrastructure/queries/top_k_devices_with_most_alerts.gsql index de48d6b8..16b2a08f 100644 --- a/agile_operations/network_infrastructure/queries/top_k_devices_with_most_alerts.gsql +++ b/agile_operations/network_infrastructure/queries/top_k_devices_with_most_alerts.gsql @@ -22,3 +22,7 @@ CREATE OR REPLACE QUERY top_k_devices_with_most_alerts (INT k) { PRINT devices; } + +UPDATE DESCRIPTION OF QUERY top_k_devices_with_most_alerts "This query finds the top k devices with the most alerts in the system." + +UPDATE DESCRIPTION OF QUERY_PARAM top_k_devices_with_most_alerts.k "Determines the amount of top devices to find." diff --git a/agile_operations/network_infrastructure/queries/top_k_devices_with_most_incidents.gsql b/agile_operations/network_infrastructure/queries/top_k_devices_with_most_incidents.gsql index e0dd8fe4..80721d69 100644 --- a/agile_operations/network_infrastructure/queries/top_k_devices_with_most_incidents.gsql +++ b/agile_operations/network_infrastructure/queries/top_k_devices_with_most_incidents.gsql @@ -21,3 +21,7 @@ CREATE OR REPLACE QUERY top_k_devices_with_most_incidents (INT k) { PRINT devices; } + +UPDATE DESCRIPTION OF QUERY top_k_devices_with_most_incidents "This query finds the top k devices with the most incidents in the system." + +UPDATE DESCRIPTION OF QUERY_PARAM top_k_devices_with_most_incidents.k "Determines the amount of top devices to find." diff --git a/connected_customer/customer_360/queries/Application_Engagement_Individuals.gsql b/connected_customer/customer_360/queries/Application_Engagement_Individuals.gsql index 1fe386ad..69cc8338 100644 --- a/connected_customer/customer_360/queries/Application_Engagement_Individuals.gsql +++ b/connected_customer/customer_360/queries/Application_Engagement_Individuals.gsql @@ -6,3 +6,5 @@ CREATE DISTRIBUTED QUERY Application_Engagement_Individuals(/* Parameters here * PRINT individuals; } + +UPDATE DESCRIPTION OF QUERY Application_Engagement_Individuals "This query returns all individuals that have had engagement with a product application" diff --git a/connected_customer/customer_360/queries/Application_Starts.gsql b/connected_customer/customer_360/queries/Application_Starts.gsql index b60f7b71..9485e119 100644 --- a/connected_customer/customer_360/queries/Application_Starts.gsql +++ b/connected_customer/customer_360/queries/Application_Starts.gsql @@ -18,3 +18,5 @@ CREATE DISTRIBUTED QUERY Application_Starts(/* Parameters here */) FOR GRAPH Cus PRINT unfinished_individuals; } + +UPDATE DESCRIPTION OF QUERY Application_Starts "This query returns all individuals who started an application but did not finish the application" diff --git a/connected_customer/customer_360/queries/Application_Submissions.gsql b/connected_customer/customer_360/queries/Application_Submissions.gsql index 34fb17df..fc727764 100644 --- a/connected_customer/customer_360/queries/Application_Submissions.gsql +++ b/connected_customer/customer_360/queries/Application_Submissions.gsql @@ -20,3 +20,5 @@ CREATE DISTRIBUTED QUERY Application_Submissions(/* Parameters here */) FOR GRAP PRINT finished_individuals; } + +UPDATE DESCRIPTION OF QUERY Application_Submissions "Returns all individuals who finished an application and submitted one" \ No newline at end of file diff --git a/connected_customer/customer_360/queries/Customers_No_Engagement.gsql b/connected_customer/customer_360/queries/Customers_No_Engagement.gsql index 1db28ee3..0dff9398 100644 --- a/connected_customer/customer_360/queries/Customers_No_Engagement.gsql +++ b/connected_customer/customer_360/queries/Customers_No_Engagement.gsql @@ -7,3 +7,5 @@ CREATE DISTRIBUTED QUERY Customers_No_Engagement(/* Parameters here */) FOR GRAP PRINT not_engaged; } + +UPDATE DESCRIPTION OF QUERY Customers_No_Engagement "Returns individuals who have never had any engagement" diff --git a/connected_customer/customer_360/queries/Customers_With_Product.gsql b/connected_customer/customer_360/queries/Customers_With_Product.gsql index 3ad139bb..8714ea4b 100644 --- a/connected_customer/customer_360/queries/Customers_With_Product.gsql +++ b/connected_customer/customer_360/queries/Customers_With_Product.gsql @@ -15,3 +15,6 @@ CREATE DISTRIBUTED QUERY Accounts_With_Product(STRING product_type) FOR GRAPH Cu PRINT @@accounts; } + +UPDATE DESCRIPTION OF QUERY Accounts_With_Product "Returns Accounts that hold the specified product type" +UPDATE DESCRIPTION OF QUERY_PARAM Accounts_With_Product.product_type "The type of product held by returned accounts" \ No newline at end of file diff --git a/connected_customer/customer_360/queries/Customers_With_Sessions.gsql b/connected_customer/customer_360/queries/Customers_With_Sessions.gsql index 48673fa7..99d71bfa 100644 --- a/connected_customer/customer_360/queries/Customers_With_Sessions.gsql +++ b/connected_customer/customer_360/queries/Customers_With_Sessions.gsql @@ -4,3 +4,5 @@ CREATE DISTRIBUTED QUERY Customers_With_Sessions(/* Parameters here */) FOR GRAP engaged = SELECT s FROM start:s - (created_session:e) - SessionID:t; PRINT engaged; } + +UPDATE DESCRIPTION OF QUERY Customers_With_Sessions "Returns all individuals who have created a session where they have engagement" \ No newline at end of file diff --git a/connected_customer/customer_360/queries/Email_Engagement_Accounts.gsql b/connected_customer/customer_360/queries/Email_Engagement_Accounts.gsql index 6cab9806..ef675107 100644 --- a/connected_customer/customer_360/queries/Email_Engagement_Accounts.gsql +++ b/connected_customer/customer_360/queries/Email_Engagement_Accounts.gsql @@ -9,3 +9,5 @@ CREATE DISTRIBUTED QUERY Email_Engagement_Accounts(/* Parameters here */) FOR GR PRINT accounts; } + +UPDATE DESCRIPTION OF QUERY Email_Engagement_Accounts "Returns accounts that have had email engagement" \ No newline at end of file diff --git a/connected_customer/customer_360/queries/Email_Engagement_By_Product.gsql b/connected_customer/customer_360/queries/Email_Engagement_By_Product.gsql index df2f821d..1fbcccf4 100644 --- a/connected_customer/customer_360/queries/Email_Engagement_By_Product.gsql +++ b/connected_customer/customer_360/queries/Email_Engagement_By_Product.gsql @@ -39,3 +39,5 @@ CREATE DISTRIBUTED QUERY Email_Engagement_By_Product(/* Parameters here */) FOR PRINT @@engagement_rate; } + +UPDATE DESCRIPTION OF QUERY Email_Engagement_By_Product "Returns email engagement percentage broken down by product type" \ No newline at end of file diff --git a/connected_customer/customer_360/queries/Individual_WebSearch.gsql b/connected_customer/customer_360/queries/Individual_WebSearch.gsql index e3a73ef5..6c57c713 100644 --- a/connected_customer/customer_360/queries/Individual_WebSearch.gsql +++ b/connected_customer/customer_360/queries/Individual_WebSearch.gsql @@ -6,3 +6,5 @@ CREATE DISTRIBUTED QUERY Individual_WebSearch(/* Parameters here */) FOR GRAPH C PRINT individuals; } + +UPDATE DESCRIPTION OF QUERY Individual_WebSearch "Returns individuals who engaged in searching products on the web" diff --git a/connected_customer/customer_360/queries/Individuals_K_Most_Engagement.gsql b/connected_customer/customer_360/queries/Individuals_K_Most_Engagement.gsql index 5176d6fc..94ccb4ec 100644 --- a/connected_customer/customer_360/queries/Individuals_K_Most_Engagement.gsql +++ b/connected_customer/customer_360/queries/Individuals_K_Most_Engagement.gsql @@ -11,3 +11,6 @@ CREATE DISTRIBUTED QUERY Individuals_K_Most_Engagement(INT k = 10) FOR GRAPH Cus PRINT individuals; } + +UPDATE DESCRIPTION OF QUERY Individuals_K_Most_Engagement "Find the top k individuals based on total engagement" +UPDATE DESCRIPTION OF QUERY_PARAM Individuals_K_Most_Engagement.k "Top K individuals to return" diff --git a/connected_customer/customer_360/queries/Individuals_No_Application.gsql b/connected_customer/customer_360/queries/Individuals_No_Application.gsql index 797f5b25..a2a302d7 100644 --- a/connected_customer/customer_360/queries/Individuals_No_Application.gsql +++ b/connected_customer/customer_360/queries/Individuals_No_Application.gsql @@ -11,3 +11,5 @@ CREATE DISTRIBUTED QUERY Individuals_No_Application(/* Parameters here */) FOR G PRINT individuals; } + +UPDATE DESCRIPTION OF QUERY Individuals_No_Application "Return individuals who have never started an application but have looked at products" diff --git a/connected_customer/customer_360/queries/Individuals_Product_Browse.gsql b/connected_customer/customer_360/queries/Individuals_Product_Browse.gsql index 8e5f0475..75a6380e 100644 --- a/connected_customer/customer_360/queries/Individuals_Product_Browse.gsql +++ b/connected_customer/customer_360/queries/Individuals_Product_Browse.gsql @@ -6,3 +6,5 @@ CREATE DISTRIBUTED QUERY Individuals_Product_Browse(/* Parameters here */) FOR G PRINT individuals; } + +UPDATE DESCRIPTION OF QUERY Individuals_Product_Browse "Return individuals who have looked at products" \ No newline at end of file diff --git a/connected_customer/entity_resolution/queries/delete_all_cc_connections.gsql b/connected_customer/entity_resolution/queries/delete_all_cc_connections.gsql index 26170c4a..948f7954 100644 --- a/connected_customer/entity_resolution/queries/delete_all_cc_connections.gsql +++ b/connected_customer/entity_resolution/queries/delete_all_cc_connections.gsql @@ -15,3 +15,9 @@ CREATE OR REPLACE DISTRIBUTED QUERY delete_all_cc_connections(INT num_of_batches PRINT result; } + +UPDATE DESCRIPTION OF QUERY delete_all_cc_connections "This query deletes all Entity_In_Ring edges associating Entity vertices to Connected_Component community vertices." + +UPDATE DESCRIPTION OF QUERY_PARAM delete_all_cc_connections.num_of_batches "Number of batches to partition the deletions for all Entity vertices in the graph. This query must be called the same number of times as batch_num with incrementing batch_id 0 through batch_num - 1 to process all Entity vertices in the graph. Defaults to 1." + +UPDATE DESCRIPTION OF QUERY_PARAM delete_all_cc_connections.batch_id "Current batch partition of Entity vertices to process and must be called with batch_id 0 through batch_num - 1. Defaults to 0." \ No newline at end of file diff --git a/connected_customer/entity_resolution/queries/delete_unused_cc_nodes.gsql b/connected_customer/entity_resolution/queries/delete_unused_cc_nodes.gsql index edbf8744..cc78f589 100644 --- a/connected_customer/entity_resolution/queries/delete_unused_cc_nodes.gsql +++ b/connected_customer/entity_resolution/queries/delete_unused_cc_nodes.gsql @@ -18,3 +18,8 @@ CREATE OR REPLACE DISTRIBUTED QUERY delete_unused_cc_nodes(INT num_of_batches = } +UPDATE DESCRIPTION OF QUERY delete_unused_cc_nodes "This query deletes all unused Connected_Component vertices in the graph." + +UPDATE DESCRIPTION OF QUERY_PARAM delete_unused_cc_nodes.num_of_batches "Number of batches to partition the deletions for all Connected_Component vertices in the graph. This query must be called the same number of times as batch_num with incrementing batch_id 0 through batch_num - 1 to process all Connected_Component vertices in the graph. Defaults to 1." + +UPDATE DESCRIPTION OF QUERY_PARAM delete_unused_cc_nodes.batch_id "Current batch partition of Connected_Component vertices to process and must be called with batch_id 0 through batch_num - 1. Defaults to 0." \ No newline at end of file diff --git a/connected_customer/entity_resolution/queries/find_shared_piis_of_two_entities.gsql b/connected_customer/entity_resolution/queries/find_shared_piis_of_two_entities.gsql index 7f5cdcb9..cc2eec3f 100644 --- a/connected_customer/entity_resolution/queries/find_shared_piis_of_two_entities.gsql +++ b/connected_customer/entity_resolution/queries/find_shared_piis_of_two_entities.gsql @@ -26,3 +26,9 @@ CREATE OR REPLACE QUERY find_shared_piis_of_two_entities(VERTEX entity_1 PRINT @@degrees_of_shared_piis; } + +UPDATE DESCRIPTION OF QUERY find_shared_piis_of_two_entities "This query returns all shared PII vertex type, value, and degree between the two provided Entity vertex parameters." + +UPDATE DESCRIPTION OF QUERY_PARAM find_shared_piis_of_two_entities.entity_1 "First Entity vertex used to search for shared PII vertices." + +UPDATE DESCRIPTION OF QUERY_PARAM find_shared_piis_of_two_entities.entity_2 "Second Entity vertex used to search for shared PII vertices." \ No newline at end of file diff --git a/connected_customer/entity_resolution/queries/match_entities.gsql b/connected_customer/entity_resolution/queries/match_entities.gsql index c6362026..c586ba36 100644 --- a/connected_customer/entity_resolution/queries/match_entities.gsql +++ b/connected_customer/entity_resolution/queries/match_entities.gsql @@ -303,3 +303,37 @@ CREATE OR REPLACE DISTRIBUTED QUERY match_entities( PRINT execution_time AS execution_time_in_seconds; } + +UPDATE DESCRIPTION OF QUERY match_entities "This query finds matches and creates similarity edges between entities that match. It is used as the second step in the Entity Resolution process, right after running delete_all_cc_connections." + +UPDATE DESCRIPTION OF QUERY_PARAM match_entities.customer_has_birthdate_weight "Weight accumulation for matching customer_has_birthdate_weight PII vertices between Entoty vertices in the graph. Defaults to 0.5." + +UPDATE DESCRIPTION OF QUERY_PARAM match_entities.customer_has_email_address_weight "Weight accumulation for matching customer_has_email_address_weight PII vertices between Entoty vertices in the graph. Defaults to 0.5." + +UPDATE DESCRIPTION OF QUERY_PARAM match_entities.customer_has_name_weight "Weight accumulation for matching customer_has_name_weight PII vertices between Entoty vertices in the graph. Defaults to 0.2." + +UPDATE DESCRIPTION OF QUERY_PARAM match_entities.customer_has_phone_weight "Weight accumulation for matching customer_has_phone_weight PII vertices between Entoty vertices in the graph. Defaults to 0.5." + +UPDATE DESCRIPTION OF QUERY_PARAM match_entities.customer_has_std_city_weight "Weight accumulation for matching customer_has_std_city_weight PII vertices between Entoty vertices in the graph. Defaults to 0.2." + +UPDATE DESCRIPTION OF QUERY_PARAM match_entities.customer_has_std_postcode_weight "Weight accumulation for matching customer_has_std_postcode_weight PII vertices between Entoty vertices in the graph. Defaults to 0.2." + +UPDATE DESCRIPTION OF QUERY_PARAM match_entities.customer_has_std_state_weight "Weight accumulation for matching customer_has_std_state_weight PII vertices between Entoty vertices in the graph. Defaults to 0.2." + +UPDATE DESCRIPTION OF QUERY_PARAM match_entities.customer_has_std_street_address_weight "Weight accumulation for matching customer_has_std_street_address_weight PII vertices between Entoty vertices in the graph. Defaults to 0.2." + +UPDATE DESCRIPTION OF QUERY_PARAM match_entities.customer_has_tax_id_number_weight "Weight accumulation for matching customer_has_tax_id_number_weight PII vertices between Entoty vertices in the graph. Defaults to 0.5." + +UPDATE DESCRIPTION OF QUERY_PARAM match_entities.customer_has_source_customer_id_weight "Weight accumulation for matching customer_has_source_customer_id_weight PII vertices between Entoty vertices in the graph. Defaults to 0.5." + +UPDATE DESCRIPTION OF QUERY_PARAM match_entities.num_of_source_batches "Number of source batches to process. Defaults to 10." + +UPDATE DESCRIPTION OF QUERY_PARAM match_entities.num_of_target_batches "Number of target batches to process. Defaults to 1." + +UPDATE DESCRIPTION OF QUERY_PARAM match_entities.threshold "Accumulated weight threshold required for matching Entity vertices into a Connected_Component community using the provided weight for each PII vertex type. Defaults to 1.0." + +UPDATE DESCRIPTION OF QUERY_PARAM match_entities.pii_low_connections_limit "Maximum outdegree of PII vertex considered for 'low connection' matching. The case will be skipped if the same vertex is connected to too many entities. Defaults to 100." + +UPDATE DESCRIPTION OF QUERY_PARAM match_entities.pii_high_connections_limit "Maximum outdegree of PII vertex considered for 'high connection' matching. The case will be skipped if the same vertex is connected to too many entities. Defaults to 25000." + +UPDATE DESCRIPTION OF QUERY_PARAM match_entities.compute_entities_after_date "All Entity vertices with created_at after this date will be computed to find similarity edges related to these new entities. Defaults to 1970-01-01 00:00:00 (which is the earliest possible created_at and great for the initial running of this query)." \ No newline at end of file diff --git a/connected_customer/entity_resolution/queries/output_entity_cc_to_file.gsql b/connected_customer/entity_resolution/queries/output_entity_cc_to_file.gsql index 22fff069..8971a9ff 100644 --- a/connected_customer/entity_resolution/queries/output_entity_cc_to_file.gsql +++ b/connected_customer/entity_resolution/queries/output_entity_cc_to_file.gsql @@ -16,3 +16,7 @@ CREATE OR REPLACE QUERY output_entity_cc_to_file(STRING output_file_path = "/hom PRINT "Results file created!" AS result; } + +UPDATE DESCRIPTION OF QUERY output_entity_cc_to_file "This query outputs a file containing a mapping of all Entity vertices to their respective Connected_Component community vertex." + +UPDATE DESCRIPTION OF QUERY_PARAM output_entity_cc_to_file.output_file_path "File path location containing the output Entity vertex to Connected_Component vertex in CSV format. Defaults to /home/tigergraph/gsql_output/entity_cc_output.csv" \ No newline at end of file diff --git a/connected_customer/entity_resolution/queries/unify_entities.gsql b/connected_customer/entity_resolution/queries/unify_entities.gsql index d770df09..8c44ae54 100644 --- a/connected_customer/entity_resolution/queries/unify_entities.gsql +++ b/connected_customer/entity_resolution/queries/unify_entities.gsql @@ -40,3 +40,5 @@ CREATE OR REPLACE DISTRIBUTED QUERY unify_entities() FOR GRAPH Entity_Resolution ; } + +UPDATE DESCRIPTION OF QUERY unify_entities "This query associates all Entity vertices in the graph to a Connected_Component vertex using Same_As edge previously inserted by match_entities query." \ No newline at end of file diff --git a/connected_customer/product_recommendations/queries/queries.gsql b/connected_customer/product_recommendations/queries/queries.gsql index e7fb967e..44fce1c4 100644 --- a/connected_customer/product_recommendations/queries/queries.gsql +++ b/connected_customer/product_recommendations/queries/queries.gsql @@ -204,6 +204,19 @@ CREATE QUERY k_means( PRINT "Created Cluster Count:", rep_verts.size(); } +UPDATE DESCRIPTION OF QUERY k_means "This query clusters vertices according to some subset of their numerical attributes using the KMeans algorithm, which is a vector quantization clustering algorithm. The algorithm iteratively attempts to cluster the vertices using an incremental number of centroids and then selects the centroid assignment which results in the best sum of squared errors, such that the clusters are not under or overfit to the dataset. The algorithm inserts the centroids as Cluster vertices and links all vertices to their respective centroids via the In_Cluster edge type." +UPDATE DESCRIPTION OF QUERY_PARAM k_means.v_type "This is the target vertex type to cluster. It defaults to Customer." +UPDATE DESCRIPTION OF QUERY_PARAM k_means.attr_set "This is a JSON-formatted string representing a list of tuples containing an index and the key of a numerical value contained in the MAP attribute, 'attr_map', on the target vertex type. Example: '[0, \"Age\"]'" +UPDATE DESCRIPTION OF QUERY_PARAM k_means.min_cluster_count "This is the minimum number of centroids to try out when clustering." +UPDATE DESCRIPTION OF QUERY_PARAM k_means.max_cluster_count "This is the maximum number of centroids to try out when clustering. Must be greater than or equal to min_cluster_count." +UPDATE DESCRIPTION OF QUERY_PARAM k_means.cluster_inc "This is the value by which the centroid count is incremented when moving onto the next centroid count. Defaults to 1." +UPDATE DESCRIPTION OF QUERY_PARAM k_means.random_iter_count "The is the number of random initializations to attempt for a given centroid count. Defaults to 1." +UPDATE DESCRIPTION OF QUERY_PARAM k_means.conv_iter_limit "This is the maximum number of iterations to perform for a given centroid count. Defaults to 25." +UPDATE DESCRIPTION OF QUERY_PARAM k_means.conv_threshold "This is the minimum change in SSE during a clustering attempt before the algorithm considers a clustering to have converged. Defaults to 0.1" +UPDATE DESCRIPTION OF QUERY_PARAM k_means.random_seed "This is a seed to pseudorandomize the selection of centroids for a given centroid count." +UPDATE DESCRIPTION OF QUERY_PARAM k_means.use_custom_timestamp "This is the timestamp attributed to the Cluster vertices. If you wish to use a custom timestamp, set this to TRUE. Defaults to FALSE." +UPDATE DESCRIPTION OF QUERY_PARAM k_means.custom_timestamp "If 'use_custom_timestamp' is set to TRUE, then this value will be used as the timestamp for the Cluster vertices. Defaults to 'to_datetime(\"1970-01-01\")'" + CREATE QUERY combine_features( SET hub_v_type, @@ -266,6 +279,15 @@ CREATE QUERY combine_features( END; } +UPDATE DESCRIPTION OF QUERY combine_features "This query concatenates feature vertices with a degree above a certain threshold. Feature vertex types in this case refer to Cluster, Customer_Attribute, Product_Attribute, and Product_Variant. If two feature vertices are merged, a Combined_Feature vertex gets created and the constituent features get linked to the Combined_Feature via the Linked edge type." + +UPDATE DESCRIPTION OF QUERY_PARAM combine_features.hub_v_type "These are the feature vertex types which can be considered for the purposes of combination." +UPDATE DESCRIPTION OF QUERY_PARAM combine_features.e_type "These are the edge types which should be traversed in the query for the purposes of edge counting and combination." +UPDATE DESCRIPTION OF QUERY_PARAM combine_features.target_v_type "This is the vertex type which adjoins two different feature vertices for the purpose of combination. Defaults to 'Customer'." +UPDATE DESCRIPTION OF QUERY_PARAM combine_features.feature_v_type "These are the feature vertex types which the 'hub_v_types' should traverse to via the 'target_v_type' vertex type." +UPDATE DESCRIPTION OF QUERY_PARAM combine_features.hub_threshold "If a feature vertex has more than this number of edges, it is considered a hub vertex and is eligible to be combined into a Combined_Feature vertex." +UPDATE DESCRIPTION OF QUERY_PARAM combine_features.split_threshold "This number is the upper limit of neighbors that the source and target feature vertices may share in order to be combined into a Combined_Feature vertex." + CREATE DISTRIBUTED QUERY recommend_products( SET> src_customer_input, @@ -607,3 +629,17 @@ CREATE DISTRIBUTED QUERY recommend_products( PRINT src_customers[src_customers.@recommended_items]; } + +UPDATE DESCRIPTION OF QUERY recommend_products "This generates a list of recommendations, sorted by recommendation score, for each customer. Customers can either be provided a set of vertex ids, or a non-indexed batch of customers can be specified via the 'batch_index' and 'num_batches' parameters. Recommendations can be calculated via customer-customer similarity (and popularity of items among those similar customers) and/or co-purchasing frequency of the source customer's purchase history with other products." + +UPDATE DESCRIPTION OF QUERY_PARAM recommend_products.src_customer_input "This contains a set of customer ids for whom recommendations will be generated. If left empty, 'batch_index' and 'num_batches' will be used instead to produce the set of source customers." +UPDATE DESCRIPTION OF QUERY_PARAM recommend_products.batch_index "If used, this represents which indexed fraction of num_batches to use. e.g. if num_batches is 3, then 0 represents the first third of customers, 1, represents the second third, and 2 represents the final third of customers." +UPDATE DESCRIPTION OF QUERY_PARAM recommend_products.num_batches "If used, this represents the total number of batches into which the customer vertices which will split." +UPDATE DESCRIPTION OF QUERY_PARAM recommend_products.target_batch "This represents the number of batches into which target vertices will be split during the item-item collaborative filtering stage of the query. If the query is running out of memory when executing, consider increasing the value of this parameter. Defaults to 1." +UPDATE DESCRIPTION OF QUERY_PARAM recommend_products.ignore_threshold "This represents the minimum threshold which an item's popularity must exceed during the recommendation calculation in order to be included for further processing. Defaults to 0.001." +UPDATE DESCRIPTION OF QUERY_PARAM recommend_products.recommendation_count "This is the number of recommendations which are to be provided for the source customer(s). Defaults to 10." +UPDATE DESCRIPTION OF QUERY_PARAM recommend_products.data_types "This is JSON-formatted string containing the vertex and edge types to be included at each stage of the recommendation algorithm. The structure is an array of arrays, each of which contains the following elements in the following order: 1) A string value which is either 'Customer' or 'Item', which indicates whether the vertex/edge type will be used in the customer-customer similarity portion of the algorithm or the item-item co-purchasing portion of the algorithm. 2) A string value of either 'Feature' or 'Target', which specifies whether the vertex/edge type corresponds to the 'feature'/first traversal of the recommendation calculation or the 'target'/second traversal. Feature corresponds to calculating initial similarity, and target corresponds to traversing to target items to calculate popularity or recommendation score. 3) A string value of either 'Vertex' or Edge, which specifies whether the specified data type is a vertex or edge type. 4) An array of string names of the vertex or edge types matching this specification. The default value of this parameter includes Customers, Items, and all relevant edge types." +UPDATE DESCRIPTION OF QUERY_PARAM recommend_products.edge_importance_factors "This is a JSON-formatted string containing edge types mapped to a scalar multiplier on that edge type's importance to the recommendation calculation. The structure of the string is an outer array of arrays. The internal arrays contain 1) A string value which is either 'Customer' or 'Item', which indicates whether this scalar multiplier will be applied in the customer-customer similarity portion of the algorithm or the item-item co-purchasing portion of the algorithm. 2) A string value representing the edge type. 3) A floating-point value representing the importance multiplier. The default value includes all edge types which are relevant for the base version of the recommendation algorithm." +UPDATE DESCRIPTION OF QUERY_PARAM recommend_products.vertex_degree_scales "This is a set of strings containing the vertex types whose importance may be inversely scaled relative to their degree. e.g. If 'Item' is specified, then the importance of each item (in the context of the customer-customer similarity calculation) will be inversely dependent on how popular that item is. The intuition for this is that globally popular items may be less statistically significant than locally popular items for the purposes of recommendation. If left empty, all vertex types will be considered. If no vertex types should be considered, simply populate this parameter with a string that does not match any vertex type (such as 'null')." +UPDATE DESCRIPTION OF QUERY_PARAM recommend_products.customer_popularity_scale "This is a scalar multiplier which scales the recommendation scores generated via customer-customer similarity. Defaults to 1." +UPDATE DESCRIPTION OF QUERY_PARAM recommend_products.item_popularity_scale "This is a scalar multiplier which scales the recommendation scores generated via item-item co-purchasing frequency. Defaults to 1." diff --git a/financial_crime/application_fraud/queries/batch_application_cc_features.gsql b/financial_crime/application_fraud/queries/batch_application_cc_features.gsql index 597b7e03..3976d124 100644 --- a/financial_crime/application_fraud/queries/batch_application_cc_features.gsql +++ b/financial_crime/application_fraud/queries/batch_application_cc_features.gsql @@ -133,3 +133,9 @@ CREATE OR REPLACE DISTRIBUTED QUERY batch_application_cc_features(INT connection END; } + +UPDATE DESCRIPTION OF QUERY batch_application_cc_features "This query outputs a file containing the following graph features for each Application vertex in the graph: Application.id, Application.is_fraud, Application Connected_Component community, Application nodes in Connected_Component, Fraud Application Nodes in Connected_Component, distinct PII nodes in Connected_Component, number of Application nodes in Connected_Component connected by PII, number of Application nodes in Connected_Component only connected by PII. PII includes Name, DOB, Email, Phone, Address, IP, ID, Device, Party, Account, and Card vertices" + +UPDATE DESCRIPTION OF QUERY_PARAM batch_application_cc_features.connections "Maximum outdegree of each PII vertex allowed in search used to filter out hub nodes. Defaults to 25000." + +UPDATE DESCRIPTION OF QUERY_PARAM batch_application_cc_features.output_file_path "File path location to containing the output graph features in CSV format. Defaults to /home/tigergraph/gsql_output/batch_application_cc_features.csv" diff --git a/financial_crime/application_fraud/queries/batch_application_distance_and_path.gsql b/financial_crime/application_fraud/queries/batch_application_distance_and_path.gsql index b716ca75..8c4aa344 100644 --- a/financial_crime/application_fraud/queries/batch_application_distance_and_path.gsql +++ b/financial_crime/application_fraud/queries/batch_application_distance_and_path.gsql @@ -68,3 +68,9 @@ CREATE DISTRIBUTED QUERY batch_application_distance_and_path(INT depth=5, STRING PRINT "Results file created!" AS result; } + +UPDATE DESCRIPTION OF QUERY batch_application_distance_and_path "This query outputs a file containing the following graph features for each Application vertex in the graph: Application.id, Application.is_fraud, Application Connected_Component community, Fraud Application.id, Fraud Application.id Connected_Component community, Degree of Connection from Application vertex to Fraud Application vertex, Path of Connection from Application vertex to Fraud Application vertex." + +UPDATE DESCRIPTION OF QUERY_PARAM batch_application_distance_and_path.depth "Maximum number of hops from source Application vertex to traverse in the graph. Defaults to 5." + +UPDATE DESCRIPTION OF QUERY_PARAM batch_application_distance_and_path.output_file_path "File path location to containing the output graph features in CSV format. Defaults to /home/tigergraph/gsql_output/batch_application_distance_and_path_features.csv" diff --git a/financial_crime/application_fraud/queries/delete_all_application_cc_connections.gsql b/financial_crime/application_fraud/queries/delete_all_application_cc_connections.gsql index 67d77385..3ef93cd8 100644 --- a/financial_crime/application_fraud/queries/delete_all_application_cc_connections.gsql +++ b/financial_crime/application_fraud/queries/delete_all_application_cc_connections.gsql @@ -13,3 +13,9 @@ CREATE OR REPLACE DISTRIBUTED QUERY delete_all_application_cc_connections(INT nu PRINT result; } + +UPDATE DESCRIPTION OF QUERY delete_all_application_cc_connections "This query deletes all Application_In_Ring edges associating Application vertices to Connected_Component community vertices." + +UPDATE DESCRIPTION OF QUERY_PARAM delete_all_application_cc_connections.num_of_batches "Number of batches to partition the deletions for all Application vertices in the graph. This query must be called the same number of times as batch_num with incrementing batch_id 0 through batch_num - 1 to process all Application vertices in the graph. Defaults to 1." + +UPDATE DESCRIPTION OF QUERY_PARAM delete_all_application_cc_connections.batch_id "Current batch partition of Application vertices to process and must be called with batch_id 0 through batch_num - 1. Defaults to 0." diff --git a/financial_crime/application_fraud/queries/delete_unused_cc_nodes.gsql b/financial_crime/application_fraud/queries/delete_unused_cc_nodes.gsql index 91f21784..04be56a2 100644 --- a/financial_crime/application_fraud/queries/delete_unused_cc_nodes.gsql +++ b/financial_crime/application_fraud/queries/delete_unused_cc_nodes.gsql @@ -15,3 +15,9 @@ CREATE OR REPLACE DISTRIBUTED QUERY delete_unused_cc_nodes(INT num_of_batches = PRINT result; } + +UPDATE DESCRIPTION OF QUERY delete_unused_cc_nodes "This query deletes all unused Connected_Component vertices in the graph." + +UPDATE DESCRIPTION OF QUERY_PARAM delete_unused_cc_nodes.num_of_batches "Number of batches to partition the deletions for all Connected_Component vertices in the graph. This query must be called the same number of times as batch_num with incrementing batch_id 0 through batch_num - 1 to process all Connected_Component vertices in the graph. Defaults to 1." + +UPDATE DESCRIPTION OF QUERY_PARAM delete_unused_cc_nodes.batch_id "Current batch partition of Connected_Component vertices to process and must be called with batch_id 0 through batch_num - 1. Defaults to 0." diff --git a/financial_crime/application_fraud/queries/distance_and_path_to_fraud_application.gsql b/financial_crime/application_fraud/queries/distance_and_path_to_fraud_application.gsql index 0633a7a0..88956c59 100644 --- a/financial_crime/application_fraud/queries/distance_and_path_to_fraud_application.gsql +++ b/financial_crime/application_fraud/queries/distance_and_path_to_fraud_application.gsql @@ -50,3 +50,9 @@ CREATE OR REPLACE QUERY distance_and_path_to_fraud_application(VERTEX PRINT @@degrees_of_shared_piis; } + +UPDATE DESCRIPTION OF QUERY find_shared_piis_of_two_applications "This query returns all shared PII vertex type, value, and degree between the two provided Application vertex parameters." + +UPDATE DESCRIPTION OF QUERY_PARAM find_shared_piis_of_two_applications.application_1 "First Application vertex used to search for shared PII vertices." + +UPDATE DESCRIPTION OF QUERY_PARAM find_shared_piis_of_two_applications.application_2 "Second Application vertex used to search for shared PII vertices." diff --git a/financial_crime/application_fraud/queries/get_application_cc_features.gsql b/financial_crime/application_fraud/queries/get_application_cc_features.gsql index 331374fd..5eea27fb 100644 --- a/financial_crime/application_fraud/queries/get_application_cc_features.gsql +++ b/financial_crime/application_fraud/queries/get_application_cc_features.gsql @@ -182,3 +182,9 @@ CREATE OR REPLACE QUERY get_application_cc_features(VERTEX applicat END; } + +UPDATE DESCRIPTION OF QUERY get_application_cc_features "This query returns the following graph features for the provided Application vertex parameter in near real time: Application Connected_Component community, Application nodes in Connected_Component, Fraud Application Nodes in Connected_Component, distinct PII nodes in Connected_Component, number of Application nodes in Connected_Component connected by PII, number of Application nodes in Connected_Component only connected by PII. PII includes Name, DOB, Email, Phone, Address, IP, ID, Device, Party, Account, and Card vertices" + +UPDATE DESCRIPTION OF QUERY_PARAM get_application_cc_features.application "Source Application vertex for generating graph features." + +UPDATE DESCRIPTION OF QUERY_PARAM get_application_cc_features.connections "Maximum outdegree of each PII vertex allowed in search used to filter out hub nodes. Defaults to 25000." diff --git a/financial_crime/application_fraud/queries/get_application_fraud_status.gsql b/financial_crime/application_fraud/queries/get_application_fraud_status.gsql index c4ec79e9..b8c6c6ad 100644 --- a/financial_crime/application_fraud/queries/get_application_fraud_status.gsql +++ b/financial_crime/application_fraud/queries/get_application_fraud_status.gsql @@ -9,3 +9,7 @@ CREATE OR REPLACE QUERY get_application_fraud_status(SET> ap print @@fraud_status as fraud_status; } + +UPDATE DESCRIPTION OF QUERY get_application_fraud_status "This query returns the boolean fraud status of each provided Application vertex." + +UPDATE DESCRIPTION OF QUERY_PARAM get_application_fraud_status.applications "Application vertices for obtaining fraud status as stored in is_fraud boolean attribute." diff --git a/financial_crime/application_fraud/queries/get_num_applications_by_app_status.gsql b/financial_crime/application_fraud/queries/get_num_applications_by_app_status.gsql index f3255596..6039ab0a 100644 --- a/financial_crime/application_fraud/queries/get_num_applications_by_app_status.gsql +++ b/financial_crime/application_fraud/queries/get_num_applications_by_app_status.gsql @@ -18,3 +18,5 @@ CREATE OR REPLACE DISTRIBUTED QUERY get_num_applications_by_app_status () { print @@app_status_info_list as app_status_info_list; } + +UPDATE DESCRIPTION OF QUERY get_num_applications_by_app_status "This query returns the count of application status (e.g. PENDING, APPROVED) for all applications." diff --git a/financial_crime/application_fraud/queries/get_num_applications_by_fraud_status.gsql b/financial_crime/application_fraud/queries/get_num_applications_by_fraud_status.gsql index 54d2e38a..4b0697c0 100644 --- a/financial_crime/application_fraud/queries/get_num_applications_by_fraud_status.gsql +++ b/financial_crime/application_fraud/queries/get_num_applications_by_fraud_status.gsql @@ -21,3 +21,5 @@ CREATE OR REPLACE DISTRIBUTED QUERY get_num_applications_by_fraud_status () { PRINT @@fraud_status_info_list as fraud_status_info_list; } + +UPDATE DESCRIPTION OF QUERY get_num_applications_by_fraud_status "This query returns the count of fraudulent and legitimate applications for all applications." diff --git a/financial_crime/application_fraud/queries/get_top_k_connected_components_by_num_applications.gsql b/financial_crime/application_fraud/queries/get_top_k_connected_components_by_num_applications.gsql index 62b84c85..6eaabf47 100644 --- a/financial_crime/application_fraud/queries/get_top_k_connected_components_by_num_applications.gsql +++ b/financial_crime/application_fraud/queries/get_top_k_connected_components_by_num_applications.gsql @@ -37,3 +37,7 @@ CREATE OR REPLACE DISTRIBUTED QUERY get_top_k_connected_components_by_num_applic PRINT top_k_connected_components, connected_applications; PRINT @@edges_to_display AS edges_to_display; } + +UPDATE DESCRIPTION OF QUERY get_top_k_connected_components_by_num_applications "This query returns the top top_k connected components and their applications, ranked by the number of applications it connects to." + +UPDATE DESCRIPTION OF QUERY_PARAM get_top_k_connected_components_by_num_applications.top_k "The number of top connected components we want to return for this query." \ No newline at end of file diff --git a/financial_crime/application_fraud/queries/get_top_k_products_by_num_applications.gsql b/financial_crime/application_fraud/queries/get_top_k_products_by_num_applications.gsql index 83a22a0f..e2664adb 100644 --- a/financial_crime/application_fraud/queries/get_top_k_products_by_num_applications.gsql +++ b/financial_crime/application_fraud/queries/get_top_k_products_by_num_applications.gsql @@ -81,3 +81,9 @@ CREATE OR REPLACE DISTRIBUTED QUERY get_top_k_products_by_num_applications ( top_k_products.description AS description ]; } + +UPDATE DESCRIPTION OF QUERY get_top_k_products_by_num_applications "This query returns the top top_k products, ranked by the number of applications each product connects to." + +UPDATE DESCRIPTION OF QUERY_PARAM get_top_k_products_by_num_applications.top_k "The number of top products we want to return for this query." +UPDATE DESCRIPTION OF QUERY_PARAM get_top_k_products_by_num_applications.input_application_fraud_status "(Optional) Filter for fraud status of an application. Defaults to '' (no filter)." +UPDATE DESCRIPTION OF QUERY_PARAM get_top_k_products_by_num_applications.input_application_status "(Optional) Filter for the status of an application (case-sensitive). Defaults to '' (no filter)." \ No newline at end of file diff --git a/financial_crime/application_fraud/queries/get_top_k_products_by_num_applications_with_other.gsql b/financial_crime/application_fraud/queries/get_top_k_products_by_num_applications_with_other.gsql index 1e4e04aa..96bc34c9 100644 --- a/financial_crime/application_fraud/queries/get_top_k_products_by_num_applications_with_other.gsql +++ b/financial_crime/application_fraud/queries/get_top_k_products_by_num_applications_with_other.gsql @@ -107,3 +107,9 @@ CREATE OR REPLACE DISTRIBUTED QUERY get_top_k_products_by_num_applications_with_ PRINT @@top_k_products_tuple_list AS top_k_product_list; } + +UPDATE DESCRIPTION OF QUERY get_top_k_products_by_num_applications_with_other "This query returns the top top_k products, ranked by the number of applications each product connects to. Also displays how many 'other' non top_k products as well - suitable for visualization." + +UPDATE DESCRIPTION OF QUERY_PARAM get_top_k_products_by_num_applications_with_other.top_k "The number of top products we want to return for this query." +UPDATE DESCRIPTION OF QUERY_PARAM get_top_k_products_by_num_applications_with_other.input_application_fraud_status "(Optional) Filter for fraud status of an application. Defaults to '' (no filter)." +UPDATE DESCRIPTION OF QUERY_PARAM get_top_k_products_by_num_applications_with_other.input_application_status "(Optional) Filter for the status of an application (case-sensitive). Defaults to '' (no filter)." \ No newline at end of file diff --git a/financial_crime/application_fraud/queries/incremental_application_match.gsql b/financial_crime/application_fraud/queries/incremental_application_match.gsql index 3bbc42fa..c68829d4 100644 --- a/financial_crime/application_fraud/queries/incremental_application_match.gsql +++ b/financial_crime/application_fraud/queries/incremental_application_match.gsql @@ -441,3 +441,31 @@ CREATE OR REPLACE QUERY incremental_application_match( PRINT application AS Application, erv as entity_resolution; } + +UPDATE DESCRIPTION OF QUERY incremental_application_match "This query parses a JSON payload containing Application and associated PII vertex data, upserts the data into the graph, and returns whether the provided Application is able to be matched with any existing Applications using approximate weighted weakly connected components algorithm matching. The matching is controlled by the provided parameter weights for each PII attribute which must accumulate to the threshold to match." + +UPDATE DESCRIPTION OF QUERY_PARAM incremental_application_match.input "input is a JSON string containing a new or updated Application and associated PII vertex data with following structure {\"application\": \"string\", \"created_at\": \"string datetime\", \"status\": \"string\", \"line_of_credit\": \"string float\", \"annual_percentage_rate\": \"string float\", \"fraud\": \"boolean\", \"product\": \"string\", \"name\": \"string\", \"dob\": \"string datetime\", \"email\": \"string\", \"phone_numbers\": [{\"type\": \"string\", \"id\": \"string\"}], \"addresses\": [{\"type\": \"string\", \"line_1\": \"string\", \"line_2\": \"string\", \"city\": \"string\", \"state\": \"string\", \"zipcode\": \"string\", \"county\": \"string\", \"country\": \"string\"}], \"ip_address\": \"string\", \"ids\": [{\"type\": \"string\", \"id\": \"string\"}], \"device_id\": \"string\", \"party\": \"string\", \"accounts\": [{\"type\": \"string\", \"id\": \"string\"}], \"cards\": [{\"type\": \"string\", \"id\": \"int\"}]}" + +UPDATE DESCRIPTION OF QUERY_PARAM incremental_application_match.Application_Full_Name_weight "Weight accumulation for matching Full_Name PII vertices between provided input Application and every other Application in the graph. Defaults to 0.2." + +UPDATE DESCRIPTION OF QUERY_PARAM incremental_application_match.Application_DOB_weight "Weight accumulation for matching DOB PII vertices between provided input Application and every other Application in the graph. Defaults to 0.2." + +UPDATE DESCRIPTION OF QUERY_PARAM incremental_application_match.Application_Email_weight "Weight accumulation for matching Email PII vertices between provided input Application and every other Application in the graph. Defaults to 0.2." + +UPDATE DESCRIPTION OF QUERY_PARAM incremental_application_match.Application_Phone_weight "Weight accumulation for matching Phone PII vertices between provided input Application and every other Application in the graph. Defaults to 0.2." + +UPDATE DESCRIPTION OF QUERY_PARAM incremental_application_match.Application_Address_weight "Weight accumulation for matching Address PII vertices between provided input Application and every other Application in the graph. Defaults to 0.2." + +UPDATE DESCRIPTION OF QUERY_PARAM incremental_application_match.Application_IP_weight "Weight accumulation for matching IP PII vertices between provided input Application and every other Application in the graph. Defaults to 0.2." + +UPDATE DESCRIPTION OF QUERY_PARAM incremental_application_match.Application_ID_weight "Weight accumulation for matching ID PII vertices between provided input Application and every other Application in the graph. Defaults to 1.0." + +UPDATE DESCRIPTION OF QUERY_PARAM incremental_application_match.Application_Device_weight "Weight accumulation for matching Device PII vertices between provided input Application and every other Application in the graph. Defaults to 1.0." + +UPDATE DESCRIPTION OF QUERY_PARAM incremental_application_match.Application_Party_weight "Weight accumulation for matching Party vertices between provided input Application and every other Application in the graph. Defaults to 1.0." + +UPDATE DESCRIPTION OF QUERY_PARAM incremental_application_match.Application_Account_weight "Weight accumulation for matching Account PII vertices between provided input Application and every other Application in the graph. Defaults to 1.0." + +UPDATE DESCRIPTION OF QUERY_PARAM incremental_application_match.Application_Card_weight "Weight accumulation for matching Card PII vertices between provided input Application and every other Application in the graph. Defaults to 1.0." + +UPDATE DESCRIPTION OF QUERY_PARAM incremental_application_match.threshold "Accumulated weight threshold required for matching Application vertices into a Connected_Component community using the provided weight for each PII vertex type. Defaults to 1.0." diff --git a/financial_crime/application_fraud/queries/incremental_application_unify.gsql b/financial_crime/application_fraud/queries/incremental_application_unify.gsql index 9cdca8dc..51566034 100644 --- a/financial_crime/application_fraud/queries/incremental_application_unify.gsql +++ b/financial_crime/application_fraud/queries/incremental_application_unify.gsql @@ -38,3 +38,7 @@ CREATE OR REPLACE QUERY incremental_application_unify(SET> a ; } + +UPDATE DESCRIPTION OF QUERY incremental_application_unify "This query associates Application vertices unable to be matched to any existing Application using incremental_application_match into its own respective Connected_Component community vertex so it can be eligible for weighted weakly connected components matching in near real time." + +UPDATE DESCRIPTION OF QUERY_PARAM incremental_application_unify.applications "Application vertices to put into a Connected_Component community." diff --git a/financial_crime/application_fraud/queries/match_application_entities.gsql b/financial_crime/application_fraud/queries/match_application_entities.gsql index 638af0ba..d13eb6eb 100644 --- a/financial_crime/application_fraud/queries/match_application_entities.gsql +++ b/financial_crime/application_fraud/queries/match_application_entities.gsql @@ -230,3 +230,39 @@ CREATE OR REPLACE DISTRIBUTED QUERY match_application_entities( PRINT execution_time AS execution_time_in_seconds; } + +UPDATE DESCRIPTION OF QUERY match_application_entities "This query finds matches and creates similarity edges between entities that match. It is used as the second step in the Entity Resolution process, right after running delete_all_application_cc_connections." + +UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.Application_Full_Name_weight "Weight accumulation for matching Full_Name PII vertices between each Application and every other Application in the graph. Defaults to 0.2." + +UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.Application_DOB_weight "Weight accumulation for matching DOB PII vertices between all Application vertices in the graph. Defaults to 0.2." + +UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.Application_Email_weight "Weight accumulation for matching Email PII vertices between all Application vertices in the graph. Defaults to 0.2." + +UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.Application_Phone_weight "Weight accumulation for matching Phone PII vertices between all Application vertices in the graph. Defaults to 0.2." + +UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.Application_Address_weight "Weight accumulation for matching Address PII vertices between all Application vertices in the graph. Defaults to 0.2." + +UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.Application_IP_weight "Weight accumulation for matching IP PII vertices between all Application vertices in the graph. Defaults to 0.2." + +UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.Application_ID_weight "Weight accumulation for matching ID PII vertices between all Application vertices in the graph. Defaults to 1.0." + +UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.Application_Device_weight "Weight accumulation for matching Device PII vertices between all Application vertices in the graph. Defaults to 1.0." + +UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.Application_Party_weight "Weight accumulation for matching Party vertices between all Application vertices in the graph. Defaults to 1.0." + +UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.Application_Account_weight "Weight accumulation for matching Account PII vertices between all Application vertices in the graph. Defaults to 1.0." + +UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.Application_Card_weight "Weight accumulation for matching Card PII vertices between all Application vertices in the graph. Defaults to 1.0." + +UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.num_of_source_batches "Number of source batches to process. Defaults to 10." + +UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.num_of_target_batches "Number of target batches to process. Defaults to 1." + +UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.threshold "Accumulated weight threshold required for matching Application vertices into a Connected_Component community using the provided weight for each PII vertex type. Defaults to 1.0." + +UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.pii_low_connections_limit "Maximum outdegree of PII vertex considered for 'low connection' matching. The case will be skipped if the same vertex is connected to too many entities. Defaults to 100." + +UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.pii_high_connections_limit "Maximum outdegree of PII vertex considered for 'high connection' matching. The case will be skipped if the same vertex is connected to too many entities. Defaults to 25000." + +UPDATE DESCRIPTION OF QUERY_PARAM match_application_entities.compute_entities_after_date "All Application vertices with created_at after this date will be computed to find similarity edges related to these new entities. Defaults to 1970-01-01 00:00:00 (which is the earliest possible created_at and great for the initial running of this query)." diff --git a/financial_crime/application_fraud/queries/output_application_cc_to_file.gsql b/financial_crime/application_fraud/queries/output_application_cc_to_file.gsql index 445125ef..919a1f83 100644 --- a/financial_crime/application_fraud/queries/output_application_cc_to_file.gsql +++ b/financial_crime/application_fraud/queries/output_application_cc_to_file.gsql @@ -14,3 +14,8 @@ CREATE OR REPLACE DISTRIBUTED QUERY output_application_cc_to_file(STRING output_ PRINT "Results file created!" AS result; } + +UPDATE DESCRIPTION OF QUERY output_application_cc_to_file "This query outputs a file containing a mapping of all Application vertices to their respective Connected_Component community vertex." + +UPDATE DESCRIPTION OF QUERY_PARAM output_application_cc_to_file.output_file_path "File path location containing the output Application vertex to Connected_Component vertex in CSV format. Defaults to /home/tigergraph/gsql_output/application_cc_output.csv" + diff --git a/financial_crime/application_fraud/queries/set_application_fraud_status.gsql b/financial_crime/application_fraud/queries/set_application_fraud_status.gsql index 8d9d8c35..21ad83eb 100644 --- a/financial_crime/application_fraud/queries/set_application_fraud_status.gsql +++ b/financial_crime/application_fraud/queries/set_application_fraud_status.gsql @@ -5,3 +5,9 @@ CREATE OR REPLACE QUERY set_application_fraud_status(VERTEX applica start = select s from start:s post-accum s.is_fraud = fraud_status; } + +UPDATE DESCRIPTION OF QUERY set_application_fraud_status "This query sets the boolean is_fraud attribute for the provided Application vertex to the provided fraud_status boolean value in near real time." + +UPDATE DESCRIPTION OF QUERY_PARAM set_application_fraud_status.application "Application vertex for which to set the respective is_fraud attribute." + +UPDATE DESCRIPTION OF QUERY_PARAM set_application_fraud_status.fraud_status "New Application.is_fraud value for the provided Application vertex." diff --git a/financial_crime/application_fraud/queries/unify_application_entities.gsql b/financial_crime/application_fraud/queries/unify_application_entities.gsql index f68b6666..7be94454 100644 --- a/financial_crime/application_fraud/queries/unify_application_entities.gsql +++ b/financial_crime/application_fraud/queries/unify_application_entities.gsql @@ -38,3 +38,5 @@ CREATE OR REPLACE DISTRIBUTED QUERY unify_application_entities() { ; } + +UPDATE DESCRIPTION OF QUERY unify_application_entities "This query associates all Application vertices in the graph to a Connected_Component vertex using Same_Application edge previously inserted by match_application_entities query." diff --git a/financial_crime/entity_resolution_kyc/queries/batch_party_cc_features.gsql b/financial_crime/entity_resolution_kyc/queries/batch_party_cc_features.gsql index 119f1246..f4acba91 100644 --- a/financial_crime/entity_resolution_kyc/queries/batch_party_cc_features.gsql +++ b/financial_crime/entity_resolution_kyc/queries/batch_party_cc_features.gsql @@ -121,3 +121,9 @@ CREATE OR REPLACE DISTRIBUTED QUERY batch_party_cc_features(INT connections=2500 END; } + +UPDATE DESCRIPTION OF QUERY batch_party_cc_features "This query outputs a file containing the following graph features for each Party vertex in the graph: Party.id, Party.is_fraud, Party Connected_Component community, Party nodes in Connected_Component, Fraud Party Nodes in Connected_Component, distinct PII nodes in Connected_Component, number of Party nodes in Connected_Component connected by PII, number of Party nodes in Connected_Component only connected by PII. PII includes Name, DOB, Email, Phone, Address, IP, ID, Device, Party, Account, and Card vertices" + +UPDATE DESCRIPTION OF QUERY_PARAM batch_party_cc_features.connections "Maximum outdegree of each PII vertex allowed in search used to filter out hub nodes. Defaults to 25000." + +UPDATE DESCRIPTION OF QUERY_PARAM batch_party_cc_features.output_file_path "File path location to containing the output graph features in CSV format. Defaults to /home/tigergraph/gsql_output/batch_party_cc_features.csv" \ No newline at end of file diff --git a/financial_crime/entity_resolution_kyc/queries/batch_party_distance_and_path.gsql b/financial_crime/entity_resolution_kyc/queries/batch_party_distance_and_path.gsql index 2198c034..2c1ff471 100644 --- a/financial_crime/entity_resolution_kyc/queries/batch_party_distance_and_path.gsql +++ b/financial_crime/entity_resolution_kyc/queries/batch_party_distance_and_path.gsql @@ -68,3 +68,9 @@ CREATE DISTRIBUTED QUERY batch_party_distance_and_path(INT depth=5, STRING outpu PRINT "Results file created!" AS result; } + +UPDATE DESCRIPTION OF QUERY batch_party_distance_and_path "This query outputs a file containing the following graph features for each Party vertex in the graph: Party.id, Party.is_fraud, Party Connected_Component community, Fraud Party.id, Fraud Party.id Connected_Component community, Degree of Connection from Party vertex to Fraud Party vertex, Path of Connection from Party vertex to Fraud Party vertex." + +UPDATE DESCRIPTION OF QUERY_PARAM batch_party_distance_and_path.depth "Maximum number of hops from source Party vertex to traverse in the graph. Defaults to 5." + +UPDATE DESCRIPTION OF QUERY_PARAM batch_party_distance_and_path.output_file_path "File path location to containing the output graph features in CSV format. Defaults to /home/tigergraph/gsql_output/batch_party_distance_and_path_features.csv" \ No newline at end of file diff --git a/financial_crime/entity_resolution_kyc/queries/delete_all_party_cc_connections.gsql b/financial_crime/entity_resolution_kyc/queries/delete_all_party_cc_connections.gsql index 1dc3010e..8c9cb643 100644 --- a/financial_crime/entity_resolution_kyc/queries/delete_all_party_cc_connections.gsql +++ b/financial_crime/entity_resolution_kyc/queries/delete_all_party_cc_connections.gsql @@ -13,3 +13,9 @@ CREATE OR REPLACE DISTRIBUTED QUERY delete_all_party_cc_connections(INT num_of_b PRINT result; } + +UPDATE DESCRIPTION OF QUERY delete_all_party_cc_connections "This query deletes all Entity_In_Ring edges associating Party vertices to Connected_Component community vertices." + +UPDATE DESCRIPTION OF QUERY_PARAM delete_all_party_cc_connections.num_of_batches "Number of batches to partition the deletions for all Party vertices in the graph. This query must be called the same number of times as batch_num with incrementing batch_id 0 through batch_num - 1 to process all Party vertices in the graph. Defaults to 1." + +UPDATE DESCRIPTION OF QUERY_PARAM delete_all_party_cc_connections.batch_id "Current batch partition of Party vertices to process and must be called with batch_id 0 through batch_num - 1. Defaults to 0." \ No newline at end of file diff --git a/financial_crime/entity_resolution_kyc/queries/delete_unused_cc_nodes.gsql b/financial_crime/entity_resolution_kyc/queries/delete_unused_cc_nodes.gsql index 91f21784..98705745 100644 --- a/financial_crime/entity_resolution_kyc/queries/delete_unused_cc_nodes.gsql +++ b/financial_crime/entity_resolution_kyc/queries/delete_unused_cc_nodes.gsql @@ -15,3 +15,9 @@ CREATE OR REPLACE DISTRIBUTED QUERY delete_unused_cc_nodes(INT num_of_batches = PRINT result; } + +UPDATE DESCRIPTION OF QUERY delete_unused_cc_nodes "This query deletes all unused Connected_Component vertices in the graph." + +UPDATE DESCRIPTION OF QUERY_PARAM delete_unused_cc_nodes.num_of_batches "Number of batches to partition the deletions for all Connected_Component vertices in the graph. This query must be called the same number of times as batch_num with incrementing batch_id 0 through batch_num - 1 to process all Connected_Component vertices in the graph. Defaults to 1." + +UPDATE DESCRIPTION OF QUERY_PARAM delete_unused_cc_nodes.batch_id "Current batch partition of Connected_Component vertices to process and must be called with batch_id 0 through batch_num - 1. Defaults to 0." \ No newline at end of file diff --git a/financial_crime/entity_resolution_kyc/queries/distance_and_path_to_fraud_party.gsql b/financial_crime/entity_resolution_kyc/queries/distance_and_path_to_fraud_party.gsql index 3b35f943..5dee6779 100644 --- a/financial_crime/entity_resolution_kyc/queries/distance_and_path_to_fraud_party.gsql +++ b/financial_crime/entity_resolution_kyc/queries/distance_and_path_to_fraud_party.gsql @@ -43,3 +43,9 @@ CREATE OR REPLACE QUERY distance_and_path_to_fraud_party(VERTEX input, IN //print L2[L2.party_id]; print result[result.@dis as degree_of_connection, result.is_fraud as is_fraud, result.id as party_id, result.@path_of_connection as path_of_connection]; } + +UPDATE DESCRIPTION OF QUERY distance_and_path_to_fraud_party "This query returns the distance and path of connection to any Fraud Party vertices up to depth hops deep from any given input Party in near real time." + +UPDATE DESCRIPTION OF QUERY_PARAM distance_and_path_to_fraud_party.input "Source Party vertex from which to begin traversal searching for closely connected Fraud Party vertices." + +UPDATE DESCRIPTION OF QUERY_PARAM distance_and_path_to_fraud_party.depth "Maximum number of hops from source Party vertex to traverse in the graph. Defaults to 5." \ No newline at end of file diff --git a/financial_crime/entity_resolution_kyc/queries/find_shared_piis_of_two_parties.gsql b/financial_crime/entity_resolution_kyc/queries/find_shared_piis_of_two_parties.gsql index ed831473..80b69d73 100644 --- a/financial_crime/entity_resolution_kyc/queries/find_shared_piis_of_two_parties.gsql +++ b/financial_crime/entity_resolution_kyc/queries/find_shared_piis_of_two_parties.gsql @@ -24,3 +24,9 @@ CREATE OR REPLACE QUERY find_shared_piis_of_two_parties(VERTEX party_1, V PRINT @@degrees_of_shared_piis; } + +UPDATE DESCRIPTION OF QUERY find_shared_piis_of_two_parties "This query returns all shared PII vertex type, value, and degree between the two provided Party vertex parameters." + +UPDATE DESCRIPTION OF QUERY_PARAM find_shared_piis_of_two_parties.party_1 "First Party vertex used to search for shared PII vertices." + +UPDATE DESCRIPTION OF QUERY_PARAM find_shared_piis_of_two_parties.party_2 "Second Party vertex used to search for shared PII vertices." \ No newline at end of file diff --git a/financial_crime/entity_resolution_kyc/queries/get_party_cc_features.gsql b/financial_crime/entity_resolution_kyc/queries/get_party_cc_features.gsql index a60afe67..8c99f0a7 100644 --- a/financial_crime/entity_resolution_kyc/queries/get_party_cc_features.gsql +++ b/financial_crime/entity_resolution_kyc/queries/get_party_cc_features.gsql @@ -161,3 +161,9 @@ CREATE OR REPLACE QUERY get_party_cc_features(VERTEX party, INT connectio } + +UPDATE DESCRIPTION OF QUERY get_party_cc_features "This query returns the following graph features for the provided Party vertex parameter in near real time: Party Connected_Component community, Party nodes in Connected_Component, Fraud Party Nodes in Connected_Component, distinct PII nodes in Connected_Component, number of Party nodes in Connected_Component connected by PII, number of Party nodes in Connected_Component only connected by PII. PII includes Name, DOB, Email, Phone, Address, IP, ID, Device, Party, Account, and Card vertices" + +UPDATE DESCRIPTION OF QUERY_PARAM get_party_cc_features.party "Source Party vertex for generating graph features." + +UPDATE DESCRIPTION OF QUERY_PARAM get_party_cc_features.connections "Maximum outdegree of each PII vertex allowed in search used to filter out hub nodes. Defaults to 25000." \ No newline at end of file diff --git a/financial_crime/entity_resolution_kyc/queries/get_party_fraud_status.gsql b/financial_crime/entity_resolution_kyc/queries/get_party_fraud_status.gsql index bd58e393..5b75a5a0 100644 --- a/financial_crime/entity_resolution_kyc/queries/get_party_fraud_status.gsql +++ b/financial_crime/entity_resolution_kyc/queries/get_party_fraud_status.gsql @@ -10,3 +10,7 @@ CREATE DISTRIBUTED QUERY get_party_fraud_status(SET> parties) { } + +UPDATE DESCRIPTION OF QUERY get_party_fraud_status "This query returns the integer fraud status of each provided Party vertex." + +UPDATE DESCRIPTION OF QUERY_PARAM get_party_fraud_status.parties "Party vertices for obtaining fraud status as stored in is_fraud integer attribute." \ No newline at end of file diff --git a/financial_crime/entity_resolution_kyc/queries/incremental_party_match.gsql b/financial_crime/entity_resolution_kyc/queries/incremental_party_match.gsql index 41010106..67bfc9bc 100644 --- a/financial_crime/entity_resolution_kyc/queries/incremental_party_match.gsql +++ b/financial_crime/entity_resolution_kyc/queries/incremental_party_match.gsql @@ -432,3 +432,31 @@ CREATE OR REPLACE QUERY incremental_party_match( PRINT party AS Party, erv as entity_resolution; } + +UPDATE DESCRIPTION OF QUERY incremental_party_match "This query parses a JSON payload containing Party and associated PII vertex data, upserts the data into the graph, and returns whether the provided Party is able to be matched with any existing Parties using approximate weighted weakly connected components algorithm matching. The matching is controlled by the provided parameter weights for each PII attribute which must accumulate to the threshold to match." + +UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_match.input "input is a JSON string containing a new or updated Party and associated PII vertex data with following structure {\"party\": \"string\", \"created_at\": \"string datetime\", \"name\": \"string\", \"party_type\": \"string\", \"gender\": \"string\", \"dob\": \"string datetime\", \"fraud\": \"int\", \"email\": \"string\", \"phone_numbers\": [{\"type\": \"string\", \"id\": \"string\"}], \"addresses\": [{\"type\": \"string\", \"line_1\": \"string\", \"line_2\": \"string\", \"city\": \"string\", \"state\": \"string\", \"zipcode\": \"string\", \"county\": \"string\", \"country\": \"string\"}], \"ip_address\": \"string\", \"ids\": [{\"type\": \"string\", \"id\": \"string\"}], \"device_id\": \"string\", \"application\": \"string\", \"accounts\": [{\"type\": \"string\", \"id\": \"string\"}], \"cards\": [{\"type\": \"string\", \"id\": \"int\"}]}" + +UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_match.Customer_Full_Name_weight "Weight accumulation for matching Full_Name PII vertices between provided input Party and every other Party in the graph. Defaults to 0.2." + +UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_match.Customer_DOB_weight "Weight accumulation for matching DOB PII vertices between provided input Party and every other Party in the graph. Defaults to 0.2." + +UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_match.Customer_Email_weight "Weight accumulation for matching Email PII vertices between provided input Party and every other Party in the graph. Defaults to 0.2." + +UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_match.Customer_Phone_weight "Weight accumulation for matching Phone PII vertices between provided input Party and every other Party in the graph. Defaults to 0.2." + +UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_match.Customer_Address_weight "Weight accumulation for matching Address PII vertices between provided input Party and every other Party in the graph. Defaults to 0.2." + +UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_match.Customer_IP_weight "Weight accumulation for matching IP PII vertices between provided input Party and every other Party in the graph. Defaults to 0.2." + +UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_match.Customer_ID_weight "Weight accumulation for matching ID PII vertices between provided input Party and every other Party in the graph. Defaults to 1.0." + +UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_match.Customer_Device_weight "Weight accumulation for matching Device PII vertices between provided input Party and every other Party in the graph. Defaults to 1.0." + +UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_match.Customer_Application_weight "Weight accumulation for matching Application vertices between provided input Party and every other Party in the graph. Defaults to 1.0." + +UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_match.Customer_Account_weight "Weight accumulation for matching Account PII vertices between provided input Party and every other Party in the graph. Defaults to 1.0." + +UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_match.Customer_Card_weight "Weight accumulation for matching Card PII vertices between provided input Party and every other Party in the graph. Defaults to 1.0." + +UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_match.threshold "Accumulated weight threshold required for matching Party vertices into a Connected_Component community using the provided weight for each PII vertex type. Defaults to 1.0." \ No newline at end of file diff --git a/financial_crime/entity_resolution_kyc/queries/incremental_party_unify.gsql b/financial_crime/entity_resolution_kyc/queries/incremental_party_unify.gsql index ca4bb247..ab029e0b 100644 --- a/financial_crime/entity_resolution_kyc/queries/incremental_party_unify.gsql +++ b/financial_crime/entity_resolution_kyc/queries/incremental_party_unify.gsql @@ -38,3 +38,7 @@ CREATE OR REPLACE DISTRIBUTED QUERY incremental_party_unify(SET> p ; } + +UPDATE DESCRIPTION OF QUERY incremental_party_unify "This query associates Party vertices unable to be matched to any existing Party using incremental_party_match into its own respective Connected_Component community vertex so it can be eligible for weighted weakly connected components matching in near real time." + +UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_unify.parties "Party vertices to put into a Connected_Component community." \ No newline at end of file diff --git a/financial_crime/entity_resolution_kyc/queries/match_party_entities.gsql b/financial_crime/entity_resolution_kyc/queries/match_party_entities.gsql index 11bd134e..3f69c364 100644 --- a/financial_crime/entity_resolution_kyc/queries/match_party_entities.gsql +++ b/financial_crime/entity_resolution_kyc/queries/match_party_entities.gsql @@ -230,3 +230,39 @@ CREATE OR REPLACE DISTRIBUTED QUERY match_party_entities( PRINT execution_time AS execution_time_in_seconds; } + +UPDATE DESCRIPTION OF QUERY match_party_entities "This query parses a JSON payload containing Party and associated PII vertex data, upserts the data into the graph, and returns whether the provided Party is able to be matched with any existing Parties using approximate weighted weakly connected components algorithm matching. The matching is controlled by the provided parameter weights for each PII attribute which must accumulate to the threshold to match." + +UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.Customer_Full_Name_weight "Weight accumulation for matching Full_Name PII vertices between each Party and every other Party in the graph. Defaults to 0.2." + +UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.Customer_DOB_weight "Weight accumulation for matching DOB PII vertices between all Party vertices in the graph. Defaults to 0.2." + +UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.Customer_Email_weight "Weight accumulation for matching Email PII vertices between all Party vertices in the graph. Defaults to 0.2." + +UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.Customer_Phone_weight "Weight accumulation for matching Phone PII vertices between all Party vertices in the graph. Defaults to 0.2." + +UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.Customer_Address_weight "Weight accumulation for matching Address PII vertices between all Party vertices in the graph. Defaults to 0.2." + +UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.Customer_IP_weight "Weight accumulation for matching IP PII vertices between all Party vertices in the graph. Defaults to 0.2." + +UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.Customer_ID_weight "Weight accumulation for matching ID PII vertices between all Party vertices in the graph. Defaults to 1.0." + +UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.Customer_Device_weight "Weight accumulation for matching Device PII vertices between all Party vertices in the graph. Defaults to 1.0." + +UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.Customer_Application_weight "Weight accumulation for matching Application vertices between all Party vertices in the graph. Defaults to 1.0." + +UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.Customer_Account_weight "Weight accumulation for matching Account PII vertices between all Party vertices in the graph. Defaults to 1.0." + +UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.Customer_Card_weight "Weight accumulation for matching Card PII vertices between all Party vertices in the graph. Defaults to 1.0." + +UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.num_of_source_batches "Number of source batches to process. Defaults to 10." + +UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.num_of_target_batches "Number of target batches to process. Defaults to 1." + +UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.threshold "Accumulated weight threshold required for matching Party vertices into a Connected_Component community using the provided weight for each PII vertex type. Defaults to 1.0." + +UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.pii_low_connections_limit "Maximum outdegree of PII vertex considered for 'low connection' matching. The case will be skipped if the same vertex is connected to too many entities. Defaults to 100." + +UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.pii_high_connections_limit "Maximum outdegree of PII vertex considered for 'high connection' matching. The case will be skipped if the same vertex is connected to too many entities. Defaults to 25000." + +UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.compute_entities_after_date "All Party vertices with created_at after this date will be computed to find similarity edges related to these new entities. Defaults to 1970-01-01 00:00:00 (which is the earliest possible created_at and great for the initial running of this query)." \ No newline at end of file diff --git a/financial_crime/entity_resolution_kyc/queries/output_party_cc_to_file.gsql b/financial_crime/entity_resolution_kyc/queries/output_party_cc_to_file.gsql index 12ee1490..32543849 100644 --- a/financial_crime/entity_resolution_kyc/queries/output_party_cc_to_file.gsql +++ b/financial_crime/entity_resolution_kyc/queries/output_party_cc_to_file.gsql @@ -14,3 +14,7 @@ CREATE OR REPLACE QUERY output_party_cc_to_file(STRING output_file_path = "/home PRINT "Results file created!" AS result; } + +UPDATE DESCRIPTION OF QUERY output_party_cc_to_file "This query outputs a file containing a mapping of all Party vertices to their respective Connected_Component community vertex." + +UPDATE DESCRIPTION OF QUERY_PARAM output_party_cc_to_file.output_file_path "File path location containing the output Party vertex to Connected_Component vertex in CSV format. Defaults to /home/tigergraph/gsql_output/party_cc_output.csv" \ No newline at end of file diff --git a/financial_crime/entity_resolution_kyc/queries/set_party_fraud_status.gsql b/financial_crime/entity_resolution_kyc/queries/set_party_fraud_status.gsql index 27e6bab0..f0cff4f6 100644 --- a/financial_crime/entity_resolution_kyc/queries/set_party_fraud_status.gsql +++ b/financial_crime/entity_resolution_kyc/queries/set_party_fraud_status.gsql @@ -5,3 +5,9 @@ CREATE DISTRIBUTED QUERY set_party_fraud_status(VERTEX party, INT fraud_s start = select s from start:s post-accum s.is_fraud = fraud_status; } + +UPDATE DESCRIPTION OF QUERY set_party_fraud_status "This query sets the integer is_fraud attribute for the provided Party vertex to the provided fraud_status integer value in near real time." + +UPDATE DESCRIPTION OF QUERY_PARAM set_party_fraud_status.party "Party vertex for which to set the respective is_fraud attribute." + +UPDATE DESCRIPTION OF QUERY_PARAM set_party_fraud_status.fraud_status "New Party.is_fraud value for the provided Party vertex." \ No newline at end of file diff --git a/financial_crime/entity_resolution_kyc/queries/unify_party_entities.gsql b/financial_crime/entity_resolution_kyc/queries/unify_party_entities.gsql index 235f0e77..c469f5b9 100644 --- a/financial_crime/entity_resolution_kyc/queries/unify_party_entities.gsql +++ b/financial_crime/entity_resolution_kyc/queries/unify_party_entities.gsql @@ -38,3 +38,5 @@ CREATE OR REPLACE DISTRIBUTED QUERY unify_party_entities() { ; } + +UPDATE DESCRIPTION OF QUERY unify_party_entities "This query associates all Party vertices in the graph to a Connected_Component vertex using Same_As edge previously inserted by match_party_entities query." \ No newline at end of file diff --git a/financial_crime/mule_account_detection/queries/attributes_to_party_traversal.gsql b/financial_crime/mule_account_detection/queries/attributes_to_party_traversal.gsql index 707f9964..8b645b09 100644 --- a/financial_crime/mule_account_detection/queries/attributes_to_party_traversal.gsql +++ b/financial_crime/mule_account_detection/queries/attributes_to_party_traversal.gsql @@ -75,3 +75,8 @@ CREATE DISTRIBUTED QUERY attributes_to_party_traversal( } + +UPDATE DESCRIPTION OF QUERY attributes_to_party_traversal "Originating from a chosen attribute of party identification information, this query methodically searches the transaction fraud graph to retrieve all associated PII for the specified party. Its utility is paramount in fraud detection and prevention frameworks, enabling a comprehensive analysis of party identities." + +UPDATE DESCRIPTION OF QUERY_PARAM attributes_to_party_traversal.v_type "The vertex type of party's identification information Vertex type." +UPDATE DESCRIPTION OF QUERY_PARAM attributes_to_party_traversal.id "The id of the party's identification information Vertex." diff --git a/financial_crime/mule_account_detection/queries/party_full_address.gsql b/financial_crime/mule_account_detection/queries/party_full_address.gsql index 88701ca1..95a72a35 100644 --- a/financial_crime/mule_account_detection/queries/party_full_address.gsql +++ b/financial_crime/mule_account_detection/queries/party_full_address.gsql @@ -24,3 +24,7 @@ CREATE DISTRIBUTED QUERY party_full_address( } + +UPDATE DESCRIPTION OF QUERY party_full_address "This query retrieves the complete address for a party, encompassing the street address, city, state, and zipcode. It's essential for verifying and analyzing party location data, facilitating accurate and efficient address validation." + +UPDATE DESCRIPTION OF QUERY_PARAM party_full_address.p "The Party Vertex of interest." \ No newline at end of file diff --git a/financial_crime/mule_account_detection/queries/single_Party_PII.gsql b/financial_crime/mule_account_detection/queries/single_Party_PII.gsql index 9870abb8..66892fe5 100644 --- a/financial_crime/mule_account_detection/queries/single_Party_PII.gsql +++ b/financial_crime/mule_account_detection/queries/single_Party_PII.gsql @@ -61,3 +61,7 @@ CREATE DISTRIBUTED QUERY single_Party_PII( ]; } + +UPDATE DESCRIPTION OF QUERY single_Party_PII "This query retrieves party identification information, including details like full name, date of birth, email address, and other personal identifiers. It is used for verifying the identity of individuals associated with transactions, which is crucial for enhancing security measures and preventing identity theft or fraud within financial operations." + +UPDATE DESCRIPTION OF QUERY_PARAM single_Party_PII.ver "The single Party vertex of interest." diff --git a/financial_crime/transaction_fraud/queries/attributes_to_party_traversal.gsql b/financial_crime/transaction_fraud/queries/attributes_to_party_traversal.gsql index 5f140434..42c4bf1f 100644 --- a/financial_crime/transaction_fraud/queries/attributes_to_party_traversal.gsql +++ b/financial_crime/transaction_fraud/queries/attributes_to_party_traversal.gsql @@ -74,3 +74,8 @@ CREATE DISTRIBUTED QUERY attributes_to_party_traversal( } + +UPDATE DESCRIPTION OF QUERY attributes_to_party_traversal "Originating from a chosen attribute of party identification information, this query methodically searches the transaction fraud graph to retrieve all associated PII for the specified party. Its utility is paramount in fraud detection and prevention frameworks, enabling a comprehensive analysis of party identities." + +UPDATE DESCRIPTION OF QUERY_PARAM attributes_to_party_traversal.v_type "The vertex type of party's identification information Vertex type." +UPDATE DESCRIPTION OF QUERY_PARAM attributes_to_party_traversal.id "The id of the party's identification information Vertex." diff --git a/financial_crime/transaction_fraud/queries/card_has_frequent_transactions.gsql b/financial_crime/transaction_fraud/queries/card_has_frequent_transactions.gsql index d99739a7..53272edf 100644 --- a/financial_crime/transaction_fraud/queries/card_has_frequent_transactions.gsql +++ b/financial_crime/transaction_fraud/queries/card_has_frequent_transactions.gsql @@ -28,3 +28,9 @@ CREATE DISTRIBUTED QUERY card_has_frequent_transactions( } + +UPDATE DESCRIPTION OF QUERY card_has_frequent_transactions "This query identifies and retrieves all card numbers that have conducted more than k transactions within a specified time period. It is used to detect potential fraud by pinpointing cards with unusually high transaction volumes, which may suggest stolen card details being exploited or other fraudulent behaviors." + +UPDATE DESCRIPTION OF QUERY_PARAM card_has_frequent_transactions.min_createTime "The earliest time to look back in history. Defaults to 2019-01-06" +UPDATE DESCRIPTION OF QUERY_PARAM card_has_frequent_transactions.max_createTime "The latest time to look back in history. Defaults to 2024-01-01" +UPDATE DESCRIPTION OF QUERY_PARAM card_has_frequent_transactions.freq "The threshold count or frequency value for transactions . Defaults to 3000" \ No newline at end of file diff --git a/financial_crime/transaction_fraud/queries/card_has_large_total_amount.gsql b/financial_crime/transaction_fraud/queries/card_has_large_total_amount.gsql index 31e791a2..5263381b 100644 --- a/financial_crime/transaction_fraud/queries/card_has_large_total_amount.gsql +++ b/financial_crime/transaction_fraud/queries/card_has_large_total_amount.gsql @@ -28,3 +28,9 @@ SetAccum @@edges; ACCUM @@edges +=e; PRINT @@edges; } + +UPDATE DESCRIPTION OF QUERY card_has_large_total_amount "This query identifies and retrieves all card numbers whose total transaction amount exceeds m dollars within a specified time period. It is used for identifying cards that may be involved in fraudulent activities by tracking the aggregate spending pattern, which, if unusually high, could indicate misuse or unauthorized transactions." + +UPDATE DESCRIPTION OF QUERY_PARAM card_has_large_total_amount.min_createTime "The earliest time to look back in history. Defaults to 2020-01-01" +UPDATE DESCRIPTION OF QUERY_PARAM card_has_large_total_amount.max_createTime "The latest time to look back in history. Defaults to 2024-01-01" +UPDATE DESCRIPTION OF QUERY_PARAM card_has_large_total_amount.m "The threshold amount for total transaction values. Defaults to 200000" \ No newline at end of file diff --git a/financial_crime/transaction_fraud/queries/card_transactions_stats.gsql b/financial_crime/transaction_fraud/queries/card_transactions_stats.gsql index 6b6b833a..9e6b7e20 100644 --- a/financial_crime/transaction_fraud/queries/card_transactions_stats.gsql +++ b/financial_crime/transaction_fraud/queries/card_transactions_stats.gsql @@ -30,3 +30,6 @@ CREATE DISTRIBUTED QUERY card_transactions_stats( } +UPDATE DESCRIPTION OF QUERY card_transactions_stats "This query is designed to yield comprehensive statistics on transactions made using a specific card, encapsulating transaction count, total amount, and the maximum, average, and minimum transaction amounts. It serves as a pivotal tool for monitoring card usage, identifying spending patterns, and enhancing fraud detection efforts by pinpointing irregularities in transaction activities." + +UPDATE DESCRIPTION OF QUERY_PARAM card_transactions_stats.v "The single Card vertex of interest." diff --git a/financial_crime/transaction_fraud/queries/card_with_single_large_transaction.gsql b/financial_crime/transaction_fraud/queries/card_with_single_large_transaction.gsql index 12abb57c..fa1df95c 100644 --- a/financial_crime/transaction_fraud/queries/card_with_single_large_transaction.gsql +++ b/financial_crime/transaction_fraud/queries/card_with_single_large_transaction.gsql @@ -22,3 +22,9 @@ CREATE DISTRIBUTED QUERY card_with_single_large_transaction( } + +UPDATE DESCRIPTION OF QUERY card_with_single_large_transaction "This query identifies and retrieves all card numbers that have recorded a single transaction exceeding a specified amount m within a certain time frame. It is used for flagging potentially fraudulent activity by highlighting unusually large transactions that could indicate unauthorized use or testing of the card." + +UPDATE DESCRIPTION OF QUERY_PARAM card_with_single_large_transaction.min_createTime "The earliest time to look back in history. Defaults to 2020-01-01" +UPDATE DESCRIPTION OF QUERY_PARAM card_with_single_large_transaction.max_createTime "The latest time to look back in history. Defaults to 2024-01-01" +UPDATE DESCRIPTION OF QUERY_PARAM card_with_single_large_transaction.m "The threshold amount for transaction values. Defaults to 10000" \ No newline at end of file diff --git a/financial_crime/transaction_fraud/queries/merchant_category_transactions_stats.gsql b/financial_crime/transaction_fraud/queries/merchant_category_transactions_stats.gsql index cb825951..8e216d99 100644 --- a/financial_crime/transaction_fraud/queries/merchant_category_transactions_stats.gsql +++ b/financial_crime/transaction_fraud/queries/merchant_category_transactions_stats.gsql @@ -30,3 +30,6 @@ CREATE DISTRIBUTED QUERY merchant_category_transactions_stats( } +UPDATE DESCRIPTION OF QUERY merchant_category_transactions_stats "This query furnishes detailed transaction statistics for a specific merchant category, covering aspects such as the count of transactions, total transaction amount, and the maximum, average, and minimum transaction amounts. It's crucial for analyzing market trends, assessing the financial health of merchant categories, and detecting anomalies that could indicate fraudulent activity or market shifts." + +UPDATE DESCRIPTION OF QUERY_PARAM merchant_category_transactions_stats.v "The single Merchant_Category vertex of interest." diff --git a/financial_crime/transaction_fraud/queries/merchant_has_frequent_transactions.gsql b/financial_crime/transaction_fraud/queries/merchant_has_frequent_transactions.gsql index 9323410c..daaa10aa 100644 --- a/financial_crime/transaction_fraud/queries/merchant_has_frequent_transactions.gsql +++ b/financial_crime/transaction_fraud/queries/merchant_has_frequent_transactions.gsql @@ -28,3 +28,9 @@ CREATE DISTRIBUTED QUERY merchant_has_frequent_transactions( } + +UPDATE DESCRIPTION OF QUERY merchant_has_frequent_transactions "This query identifies and retrieves all merchants that have processed more than k transactions within a specified time period. It is used to detect potential fraudulent or unusual activity by identifying merchants with an exceptionally high volume of transactions, which may indicate a compromised merchant account or other forms of transactional fraud." + +UPDATE DESCRIPTION OF QUERY_PARAM merchant_has_frequent_transactions.min_createTime "The earliest time to look back in history. Defaults to 2020-01-01" +UPDATE DESCRIPTION OF QUERY_PARAM merchant_has_frequent_transactions.max_createTime "The latest time to look back in history. Defaults to 2024-01-01" +UPDATE DESCRIPTION OF QUERY_PARAM merchant_has_frequent_transactions.freq "The threshold amount for transaction values. Defaults to 3000" \ No newline at end of file diff --git a/financial_crime/transaction_fraud/queries/merchant_has_large_total_amount.gsql b/financial_crime/transaction_fraud/queries/merchant_has_large_total_amount.gsql index 1c512abb..fe45c8ab 100644 --- a/financial_crime/transaction_fraud/queries/merchant_has_large_total_amount.gsql +++ b/financial_crime/transaction_fraud/queries/merchant_has_large_total_amount.gsql @@ -29,3 +29,8 @@ SetAccum @@edges; PRINT @@edges; } +UPDATE DESCRIPTION OF QUERY merchant_has_large_total_amount "This query identifies and retrieves all merchants whose total transaction amount exceeds m dollars within a specified time period. It is used for detecting potential fraudulent or unusual business activity by highlighting merchants with exceptionally high total sales, which could indicate fraudulent transactions or money laundering activities." + +UPDATE DESCRIPTION OF QUERY_PARAM merchant_has_large_total_amount.min_createTime "The earliest time to look back in history. Defaults to 2020-01-01" +UPDATE DESCRIPTION OF QUERY_PARAM merchant_has_large_total_amount.max_createTime "The latest time to look back in history. Defaults to 2024-01-01" +UPDATE DESCRIPTION OF QUERY_PARAM merchant_has_large_total_amount.m "The threshold amount for transaction values. Defaults to 200000" diff --git a/financial_crime/transaction_fraud/queries/merchant_transactions_stats.gsql b/financial_crime/transaction_fraud/queries/merchant_transactions_stats.gsql index 218030d0..84045431 100644 --- a/financial_crime/transaction_fraud/queries/merchant_transactions_stats.gsql +++ b/financial_crime/transaction_fraud/queries/merchant_transactions_stats.gsql @@ -31,3 +31,6 @@ CREATE DISTRIBUTED QUERY merchant_transactions_stats( } +UPDATE DESCRIPTION OF QUERY merchant_transactions_stats "This query offers crucial insights into a merchant's transactional behavior by providing detailed statistics, including the count of transactions, total transaction amount, and the maximum, average, and minimum transaction amounts. It is instrumental for financial analysis, aiding in the detection of anomalies, fraud prevention, and the assessment of merchant performance." + +UPDATE DESCRIPTION OF QUERY_PARAM merchant_transactions_stats.v "The single Merchant vertex of interest." diff --git a/financial_crime/transaction_fraud/queries/merchant_with_single_large_transaction.gsql b/financial_crime/transaction_fraud/queries/merchant_with_single_large_transaction.gsql index 0aebf053..3aceb249 100644 --- a/financial_crime/transaction_fraud/queries/merchant_with_single_large_transaction.gsql +++ b/financial_crime/transaction_fraud/queries/merchant_with_single_large_transaction.gsql @@ -22,3 +22,9 @@ CREATE DISTRIBUTED QUERY merchant_with_single_large_transaction( } + +UPDATE DESCRIPTION OF QUERY merchant_with_single_large_transaction "This query identifies and retrieves all merchants that have recorded a single transaction exceeding a specified amount m within a certain time frame. It is used to flag potential fraud or irregular activities by highlighting transactions that are unusually large for a given merchant, which could indicate suspicious or anomalous behavior." + +UPDATE DESCRIPTION OF QUERY_PARAM merchant_with_single_large_transaction.min_createTime "The earliest time to look back in history. Defaults to 2020-01-01" +UPDATE DESCRIPTION OF QUERY_PARAM merchant_with_single_large_transaction.max_createTime "The latest time to look back in history. Defaults to 2024-01-01" +UPDATE DESCRIPTION OF QUERY_PARAM merchant_with_single_large_transaction.m "The threshold amount for transaction values. Defaults to 10000" \ No newline at end of file diff --git a/financial_crime/transaction_fraud/queries/party_full_address.gsql b/financial_crime/transaction_fraud/queries/party_full_address.gsql index 3aea5e8e..ce244674 100644 --- a/financial_crime/transaction_fraud/queries/party_full_address.gsql +++ b/financial_crime/transaction_fraud/queries/party_full_address.gsql @@ -24,3 +24,6 @@ CREATE QUERY party_full_address( } +UPDATE DESCRIPTION OF QUERY party_full_address "This query retrieves the complete address for a party, encompassing the street address, city, state, and zipcode. It's essential for verifying and analyzing party location data, facilitating accurate and efficient address validation." + +UPDATE DESCRIPTION OF QUERY_PARAM party_full_address.p "The Party Vertex of interest." diff --git a/financial_crime/transaction_fraud/queries/single_Party_PII.gsql b/financial_crime/transaction_fraud/queries/single_Party_PII.gsql index 8bfc0d43..2819f956 100644 --- a/financial_crime/transaction_fraud/queries/single_Party_PII.gsql +++ b/financial_crime/transaction_fraud/queries/single_Party_PII.gsql @@ -61,3 +61,6 @@ CREATE QUERY single_Party_PII( } +UPDATE DESCRIPTION OF QUERY single_Party_PII "This query retrieves party identification information, including details like full name, date of birth, email address, and other personal identifiers. It is used for verifying the identity of individuals associated with transactions, which is crucial for enhancing security measures and preventing identity theft or fraud within financial operations." + +UPDATE DESCRIPTION OF QUERY_PARAM single_Party_PII.ver "The single Party vertex of interest." diff --git a/financial_crime/transaction_fraud/queries/single_card_lookup.gsql b/financial_crime/transaction_fraud/queries/single_card_lookup.gsql index af02f51f..379496ff 100644 --- a/financial_crime/transaction_fraud/queries/single_card_lookup.gsql +++ b/financial_crime/transaction_fraud/queries/single_card_lookup.gsql @@ -66,3 +66,6 @@ CREATE QUERY single_card_lookup( } +UPDATE DESCRIPTION OF QUERY single_card_lookup "This query retrieves the detailed attributes associated with a single Card vertex. It is used for a thorough examination of a specific card's details, aiding in fraud investigation and risk assessment by providing in-depth insight into the card's attributes and usage patterns." + +UPDATE DESCRIPTION OF QUERY_PARAM single_card_lookup.ver "The single Card vertex of interest." diff --git a/financial_crime/transaction_fraud/queries/single_merchant_lookup.gsql b/financial_crime/transaction_fraud/queries/single_merchant_lookup.gsql index 09b7479b..6d439aaa 100644 --- a/financial_crime/transaction_fraud/queries/single_merchant_lookup.gsql +++ b/financial_crime/transaction_fraud/queries/single_merchant_lookup.gsql @@ -31,3 +31,6 @@ CREATE QUERY single_merchant_lookup( } +UPDATE DESCRIPTION OF QUERY single_merchant_lookup "This query retrieves the detailed attributes associated with a single Merchant vertex. It is used to analyze the merchant's profile and transaction behaviors, which can help in assessing risk levels and identifying patterns indicative of fraudulent activities or compromised merchant accounts." + +UPDATE DESCRIPTION OF QUERY_PARAM single_merchant_lookup.ver "The single Merchant vertex of interest." diff --git a/financial_crime/transaction_fraud/queries/single_transaction_lookup.gsql b/financial_crime/transaction_fraud/queries/single_transaction_lookup.gsql index 08e35696..8eee1290 100644 --- a/financial_crime/transaction_fraud/queries/single_transaction_lookup.gsql +++ b/financial_crime/transaction_fraud/queries/single_transaction_lookup.gsql @@ -17,4 +17,6 @@ CREATE QUERY single_transaction_lookup( PRINT rlt; } +UPDATE DESCRIPTION OF QUERY single_transaction_lookup "This query retrieves the comprehensive attributes of a single Payment_Transaction vertex, including all features such as transaction amount, date, graph algoarithm features and aggregated features required for downstream machine learning models. It is used to gather detailed information on individual transactions, facilitating the development and refinement of ML models aimed at detecting fraudulent activity by providing a rich dataset for analysis and model training." +UPDATE DESCRIPTION OF QUERY_PARAM single_transaction_lookup.ver "The single Payment_Transaction vertex of interest." From 20dae7c034e988e22b1f59b730ad85998f7860c5 Mon Sep 17 00:00:00 2001 From: Lin Yu Date: Tue, 9 Sep 2025 14:40:21 +0800 Subject: [PATCH 05/55] Update .gitignore, enhance markdown rendering, and add README handling in script.js --- .gitignore | 3 +- scripts/markdown.js | 59 +++++++++++++------ scripts/readme.md | 3 +- scripts/script.js | 137 +++++++++++++++++++++++++++++++++++++------- 4 files changed, 162 insertions(+), 40 deletions(-) diff --git a/.gitignore b/.gitignore index f6395895..29b142b0 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ node_modules/ **/model/*.csv **/model/*.json **/concepts/*.json -import_solution_metadata/ \ No newline at end of file +import_solution_metadata/ +solution_metadata/ \ No newline at end of file diff --git a/scripts/markdown.js b/scripts/markdown.js index fd6c24c5..a187f17b 100644 --- a/scripts/markdown.js +++ b/scripts/markdown.js @@ -1,32 +1,59 @@ -import fs from 'fs' -import { basename, extname } from 'path'; -import { marked } from 'marked'; +import fs from "fs"; +import { basename, extname } from "path"; +import { marked } from "marked"; -function capitalize(s) -{ - return s[0].toUpperCase() + s.slice(1); +function capitalize(s) { + return s[0].toUpperCase() + s.slice(1); } function normalizeName(name) { - return name.split('_').map(name => capitalize(name)).join(' '); + return name + .split("_") + .map((name) => capitalize(name)) + .join(" "); } export function renderMarkdown(solution, file) { - const content = fs.readFileSync(file, { encoding: 'utf8' }) + const content = fs.readFileSync(file, { encoding: "utf8" }); - // Convert Markdown to HTML - const htmlContent = marked(content); + // Convert Markdown to HTML + const htmlContent = marked(content); - // Wrap the HTML content with the necessary HTML and CSS - const htmlTemplate = ` + // Wrap the HTML content with the necessary HTML and CSS + const htmlTemplate = ` - TigerGraph - ${normalizeName(basename(solution, extname(solution)))} - ${normalizeName(basename(file, extname(file)))} - + TigerGraph - ${normalizeName( + basename(solution, extname(solution)) + )} - ${normalizeName(basename(file, extname(file)))} + From 40b7db60ed4ffe9afa7a2ea6ef685079b0cc9a92 Mon Sep 17 00:00:00 2001 From: Lin Yu Date: Tue, 9 Sep 2025 17:10:31 +0800 Subject: [PATCH 09/55] Add script to conditionally hide instructions section in markdown rendering --- scripts/markdown.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/scripts/markdown.js b/scripts/markdown.js index 7c43572d..dfc3dd13 100644 --- a/scripts/markdown.js +++ b/scripts/markdown.js @@ -67,6 +67,30 @@ export function renderMarkdown(solution, file) { } } +
From d8710095f9bdfeb68483502d941c1136bb6d2861 Mon Sep 17 00:00:00 2001 From: Lin Yu Date: Tue, 9 Sep 2025 17:13:56 +0800 Subject: [PATCH 10/55] Add background color to markdown rendering for improved visibility --- scripts/markdown.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/markdown.js b/scripts/markdown.js index dfc3dd13..efda9e76 100644 --- a/scripts/markdown.js +++ b/scripts/markdown.js @@ -60,6 +60,7 @@ export function renderMarkdown(solution, file) { min-width: 200px; max-width: 980px; margin: 0 auto; + background-color: transparent; } @media (max-width: 767px) { From 7ddd4c1789858fa1c177ad137046e28f64d09f67 Mon Sep 17 00:00:00 2001 From: Lin Yu Date: Wed, 10 Sep 2025 09:09:49 +0800 Subject: [PATCH 11/55] Enhance markdown rendering by adding color styling for article elements and conditionally hiding the "Installation Note for Queries" section based on solution ID. --- scripts/markdown.js | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/scripts/markdown.js b/scripts/markdown.js index efda9e76..5c106ad1 100644 --- a/scripts/markdown.js +++ b/scripts/markdown.js @@ -42,6 +42,8 @@ export function renderMarkdown(solution, file) { document.addEventListener('DOMContentLoaded', function () { document.body.style.backgroundColor = bg; document.body.style.color = fg; + var md = document.querySelector('article.markdown-body'); + if (md) { md.style.color = fg; } }); // Inject the correct theme stylesheet without flicker var link = document.createElement('link'); @@ -60,7 +62,7 @@ export function renderMarkdown(solution, file) { min-width: 200px; max-width: 980px; margin: 0 auto; - background-color: transparent; + background-color: transparent; } @media (max-width: 767px) { @@ -83,17 +85,36 @@ export function renderMarkdown(solution, file) { var txt = (h1s[i].textContent || h1s[i].innerText || '').trim().toLowerCase(); if (txt === 'instructions') { target = h1s[i]; break; } } - if (!target) return; - var el = target.nextElementSibling; - while (el && el.tagName !== 'H2') { - el.style.display = 'none'; - el = el.nextElementSibling; + if (target) { + var el = target.nextElementSibling; + while (el && el.tagName !== 'H2') { + el.style.display = 'none'; + el = el.nextElementSibling; + } + } + // Special handling for transaction_fraud: hide the entire "Installation Note for Queries" section + var solutionId = (document.body && document.body.getAttribute('data-solution-id')) || ''; + if (solutionId === 'transaction_fraud') { + var h2s = container.querySelectorAll('h2'); + for (var j = 0; j < h2s.length; j++) { + var h2txt = (h2s[j].textContent || h2s[j].innerText || '').trim().toLowerCase(); + if (h2txt === 'installation note for queries') { + var h2 = h2s[j]; + h2.style.display = 'none'; + var sib = h2.nextElementSibling; + while (sib && sib.tagName !== 'H2') { + sib.style.display = 'none'; + sib = sib.nextElementSibling; + } + break; + } + } } } catch (e) {} }); - +
${htmlContent}
From 8a7cbb1b108a1470bb967a4bd6b4e9533f244903 Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Fri, 21 Nov 2025 10:46:53 +0530 Subject: [PATCH 12/55] Enchanced Documentation for Customer_360 solution kit and started documentation on product recommendations --- connected_customer/customer_360/README.md | 379 +++++++++++++++- .../customer_360/meta/insights.json | 406 ++++++++++++++++++ .../product_recommendations/README.md | 251 +++++++++++ .../queries/combine_features.gsql | 75 ++++ .../queries/k_means.gsql | 239 +++++++++++ .../{queries.gsql => recommend_products.gsql} | 292 +------------ 6 files changed, 1328 insertions(+), 314 deletions(-) create mode 100644 connected_customer/customer_360/meta/insights.json create mode 100644 connected_customer/product_recommendations/README.md create mode 100644 connected_customer/product_recommendations/queries/combine_features.gsql create mode 100644 connected_customer/product_recommendations/queries/k_means.gsql rename connected_customer/product_recommendations/queries/{queries.gsql => recommend_products.gsql} (56%) diff --git a/connected_customer/customer_360/README.md b/connected_customer/customer_360/README.md index 748b2a4e..c82180be 100644 --- a/connected_customer/customer_360/README.md +++ b/connected_customer/customer_360/README.md @@ -1,39 +1,372 @@ -# Narratives +# Customer 360 – Financial (TigerGraph Solution Kit) -Customer 360 allows financial institutions to gain a comprehensive view of each customer's interactions, preferences, and needs across all touchpoints. Tigergraph can connect data silos across an institution such as banking transactions, online interactions, customer service inquiries, and social media engagements. +A TigerGraph solution kit that builds a Customer 360° view for financial institutions. -The Customer 360 platform provides valuable insights for targeted product offerings. Additionally, it fosters stronger customer relationships, improves retention rates, and ultimately drives profitability through increased customer satisfaction and loyalty. +This project provisions a complete graph environment — schema, sample data, +and GSQL queries — to analyze customer behavior and engagement across accounts, +products, and digital touchpoints. -# Components +With this kit you can: -This repository includes multiple components: +- Identify your most engaged customers. +- Find customers who browse products but never apply. +- Spot abandoned applications and the individuals behind them. +- Analyze engagement patterns across loans, credit cards, email, web, and apps. -- `data` - Sample data. -- `load_jobs` - Scripts for data loading tasks. -- `meta` - Solution Kit metadata. -- `queries` - Collection of GSQL queries. -- `schema` - Definition of database schema. -- `readme.md` - This usage guide. -- `setup.sh` - Automated setup script. +--- -# Instructions +## Contents -The `setup.sh` script is designed to streamline the initial setup process by sequentially executing the following steps: +- [Overview](#overview) +- [Features](#features) +- [Prerequisites](#prerequisites) +- [Quickstart](#quickstart) +- [Working-with-the-graph](#working-with-the-graph) + - [Query-Explanations](#query-explanations) + - [Running-example-queries](#running-example-queries) +- [Using-your-own-data](#using-your-own-data) +- [Resetting-the-environment](#resetting-the-environment) + +--- + +## Overview + +Customer 360° is about consolidating all customer interactions — accounts, +transactions, digital engagement, and product holdings — into a single, +connected view. Graph databases like TigerGraph are well-suited for this +because they can traverse deeply connected data (e.g., customers → sessions → +actions → products) in real time. + +This solution kit models a retail banking environment with: + +- **Individuals** and their **Accounts** +- **Products** (Loans and Credit Cards) held by those accounts +- **Sessions** and **Engagement events** (like web search, product browsing, email, + device, and application activity) +- **Contact information** (phone, email, address, and app contact points) + +You can use the included queries as building blocks for marketing, risk, +and analytics use cases, or extend the graph with your own entities and interactions. + +--- + +## Features + +- **Pre-built TigerGraph schema** + Vertices and edges for Individuals, Accounts, Contact Points, Sessions, + and Engagement events, defined in `schema/1_create_schema.gsql`. + +- **Sample Customer 360 dataset** + Mock CSV data for individuals, accounts, and sessions, stored in the + `data/` directory for a visualization and column mapping, but the actual data + is loaded from a public S3 bucket. + +- **End-to-end loading job** + A single loading job in `loading_job/load_data.gsql` that: + - Defines a data source pointing at S3. + - Loads vertices and edges from three CSV files: + - `Session.csv` + - `Account_Info.csv` + - `Individual_Info.csv` + +- **Curated query library** + Example GSQL queries in `queries/` that cover: + - Engagement analysis (top-k engaged individuals, never-engaged customers). + - Application funnel (started vs submitted vs never applied). + - Product interest and email engagement by product. + - Product holdings by account. + +- **Automated setup script** + `setup.sh` creates the graph, loads the data, and installs all queries in one go. + +--- + +## Prerequisites + +Before you run this solution kit, make sure you have: + +- **A running TigerGraph instance** + - TigerGraph installed and running, or use the prebuilt kit on TG cloud. + - You must have permission to create graphs and run GSQL commands. + +- **GSQL client access** + - The `gsql` command-line tool available on the same machine/container where you cloned this repo. + - Ability to connect to your TigerGraph service + +- **Network access for sample data** + - Outbound internet access from the TigerGraph machine to read the sample + CSV files from the public S3 bucket used in `loading_job/load_data.gsql`. + +- **Shell environment** + - A Unix-like shell (Linux, macOS, or WSL) to run `setup.sh` and `queries/install_queries.sh`. + - Executable permissions for the scripts: + ```bash + chmod +x setup.sh + chmod +x queries/install_queries.sh + ``` + +> **Graph name:** This kit creates and uses a graph named `Customer_360_Financial`. + +# Setup Instructions +The following instructions assume that you are running the following scripts +with `gsql` command installed. + +If you don't yet have the `gsql` command available, see the TigerGraph documentation: + +- **Local GSQL shell on the server** + [The GSQL Shell](https://docs.tigergraph.com/tigergraph-server/4.2/gsql-shell/) + (explains how to run `gsql` directly on a TigerGraph server) + +- **Remote GSQL client (from your laptop or another machine)** + [Using a Remote GSQL Client](https://docs.tigergraph.com/tigergraph-server/4.2/gsql-shell/using-a-remote-gsql-client) + (explains how to download the GSQL client JAR, configure SSL, and create a `gsql` alias) +Ensure that the script is executable with: +```bash + +chmod +x setup.sh + +``` +Then, run the automated script using: +```bash +./setup.sh +``` + +This script will: +* Create the Customer_360_Financial graph and schema by defining all the + vertices, edges and their attributes with the command: + gsql schema/1_create_schema.gsql + +* Loads all the data from TigerGraph's public s3 bucket using the command: + gsql loading_job/load_data.gsql + +* Installs all the queries using another shell script located in: + queries/install_queries.sh + This script runs through all the queries in the repository and installs them + one by one. + +* To check if all the queries have run successfully, please run the following + command: + ```bash + gsql -g Customer_360_Financial "SHOW QUERY *" + ``` + +You should see entries such as Individuals_K_Most_Engagement, Application_Starts, +Customers_No_Engagement, Email_Engagement_By_Product, and others. -1. **Schema Creation**: Initiates the schema creation process with the `schema/1_create_schema.gsql` script. -2. **Data Loading**: Load data into the schema by running the data loading jobs with the `loading_job/load_data.gsql` scripts. -3. **Query Installation**: Completes the setup by installing necessary queries through the `queries/install_queries.sh` script. ## Query Explanations -We have different queries to perform the following tasks: +This solution kit includes several pre-built queries. Below are the most useful +queries and what you can get from each of them, grouped by business +purpose. +### 1. Engagement Information + + +**Individuals_K_Most_Engagement(k)** +Returns the top *k* most engaged individuals based on all of their session +activities. Use this to quickly identify your most active customers for VIP +programs, targeted campaigns, or deeper segmentation, across every digital +touchpoint captured in the graph. + +--- + +**Customers_With_Sessions** +Returns all individuals who have created at least one session. This effectively +gives you your “digitally active” customer population and can be used to +segment customers into active versus inactive, or to measure adoption of your +digital channels over time. + +--- + +**Customers_No_Engagement** +Returns individuals who have never created a session and therefore have no +recorded digital engagement. This is a useful starting point for activation +campaigns, outreach, or branch-based follow-ups to encourage adoption of +online or mobile banking services. + +--- +### 2. Potential Sale Opportunities + +**Individuals_No_Application** +Returns individuals who have shown interest (for example, browsing products or +searching) but have never started an application. This is ideal for identifying +warm leads who may need a nudge, clearer information, or a simplified process +to move from research to application. + +--- + +**Application_Starts** +Returns individuals who have started but not submitted an application. This +represents abandoned applications and is useful for recovery campaigns, UX +diagnostics, and understanding friction points in your onboarding funnel across +products such as loans or credit cards. + +--- + +**Application_Submissions** +Returns individuals who have started and successfully submitted at least one +application. You can use this as a “converted” cohort, analyze their profiles +and behaviors, or compare them against abandoned-application segments to +understand which attributes or journeys correlate with successful completion. + +--- +### 3. Channel & Product Engagement + +**Individuals_Product_Browse** +Returns individuals who have browsed one or more products during their +sessions. This gives you a population of customers actively looking at your +offerings, which can be sliced further by product type or combined with +holding data to spot cross-sell and up-sell opportunities. + +--- + +**Individual_WebSearch** +Returns individuals who have engaged via web search around your products or +services. This can help you understand which customers are researching more +deeply, evaluate search-driven campaigns, or combine with application data to +see how search behavior correlates with eventual conversion. + +--- + +**Email_Engagement_Accounts** +Returns accounts associated with individuals who have engaged via email. This +lets you move beyond open/click metrics at the individual level and see which +accounts (often households or relationships) are actually interacting with your +email campaigns, supporting more accurate relationship-level marketing and +measurement. + +--- + +**Email_Engagement_By_Product** +Returns email engagement rates broken down by product type, such as loan types +or card categories. You can use this to compare how well email performs across +different products, identify under-engaged product lines, and prioritize where +to adjust messaging, targeting, or channels for better response. + +--- +This is a good starting point for product-level channel performance analysis. + +## Run Example Queries + +After the setup is complete and the data is loaded, you can run example +queries to validate that everything is working correctly. + +The graph name is `Customer_360_Financial` and that you are running them +from a shell where the `gsql` command is available. + +### Example: Top-K Most Engaged Individuals + +Find the top 10 individuals by total engagement (based on their session +actions): + +```bash +gsql -g Customer_360_Financial "RUN QUERY Individuals_K_Most_Engagement(10)" +``` + +## Using Your Own Data + +By default, this solution kit loads **mock data** from a TigerGraph-hosted +public S3 bucket, using the data source and loading job defined in +`loading_job/load_data.gsql`. The three CSV files it loads are: + +- `Session.csv` +- `Account_Info.csv` +- `Individual_Info.csv` + +If your data lives in cloud storage or another external system +(for example, Amazon S3, Google Cloud Storage, Azure Blob Storage, or a data warehouse), +you can keep the `CREATE DATA_SOURCE` pattern from the sample and adapt it to your environment. + +TigerGraph supports multiple loading options. For detailed, up-to-date examples, +please refer to the official documentation: + +- **Data loading overview** – supported sources and general workflow + https://docs.tigergraph.com/tigergraph-server/4.2/data-loading/data-loading-overview + +- **Data loading entry page** – index of all loading methods (local files, cloud storage, warehouses, Spark, etc.) + https://docs.tigergraph.com/tigergraph-server/4.2/data-loading/ + +- **Load data from cloud storage** – step-by-step guide for Amazon S3, Google Cloud Storage, and Azure Blob Storage + https://docs.tigergraph.com/tigergraph-server/4.2/data-loading/load-from-cloud + +At a high level, the steps are: + +1. Define a `DATA_SOURCE` object that matches your storage system and credentials +2. Update the `DEFINE FILENAME` lines in `load_all` so they point to your cloud URIs +3. Keep the `LOAD` blocks the same unless your column structure changes—if it does, +adjust the `VALUES(...)` mappings so each vertex/edge attribute receives the correct column. + +Once those changes are in place, you can reload your data with: + +```bash +gsql loading_job/load_data.gsql +``` + + +## Resetting the Environment + +Sometimes you’ll want to wipe the graph data and start fresh — for example, when +switching to a new dataset or after updating the schema and loading logic. + +This repository provides a utility query to delete all data, and a separate +reset script for more advanced workflows. + +> **Warning** +> All of the commands in this section permanently delete data. +> Use them only in development or when you are sure you want to clear the graph. + +### 1. Clear all data using `delete_all.gsql` + +The file `queries/delete_all.gsql` defines a GSQL query named `delete_all()` that +removes all vertices and edges from the `Customer_360_Financial` graph. + +Under the hood it uses the GSQL `DELETE` statement, which cascades deletions: +when a vertex is deleted, all incident edges are removed automatically. + +To use it: + +1. **Ensure the query is installed** + + Normally `queries/install_queries.sh` installs all queries, including + `delete_all`. If you need to (re)install just this one: + + ```bash + gsql -g Customer_360_Financial queries/delete_all.gsql + + +2. **Run the delete query** + +Once `delete_all()` is installed, run it to clear all vertices and edges: + +```bash +gsql -g Customer_360_Financial "RUN QUERY delete_all()" +``` + + +After this completes, the **schema and queries remain**, but **all data** has been +removed. + +--- + +### 2. Full reset using the `reset` script + +If you need a complete reset of the solution kit — not just data, but also the +loading job, data source, queries, and the graph definition — you can use the +`reset/reset.gsql` script. + +This script performs the following operations on `Customer_360_Financial`: -1. **Engagement Information**: Find accounts and customers that have the most engagement. There are multiple queries that allow the user to find accounts and individuals with certain engagement patterns. There are also queries to identify accounts with the most engagement and a breakdown of engagement based on the holding of loans and credit cards. +```gsql +USE GRAPH Customer_360_Financial -2. **Find Potential Sale Opportunities**: Find accounts and individuals who have been searching for products or browsing your product offering. Find individuals who have started an application but never finished it and what their account profile looks like. +-- data loading job & data source +DROP JOB load_all +DROP DATA_SOURCE s1 -3. **Find Customer Engagement Patterns**: Find accounts and individuals and identify their engagement patterns and customer's that have never engaged before. +-- clear all installed queries +DROP QUERY ALL -## Mock Data +-- clear schema and data +DROP GRAPH Customer_360_Financial +``` -The `data` folder is populated with sample data files. These files are crafted for testing and demonstration purposes. diff --git a/connected_customer/customer_360/meta/insights.json b/connected_customer/customer_360/meta/insights.json new file mode 100644 index 00000000..3a049b4f --- /dev/null +++ b/connected_customer/customer_360/meta/insights.json @@ -0,0 +1,406 @@ +{ + "defaultGraph": "Customer_360_Financial", + "iconURL": "/insights/static/media/circle-dollar-sign.8d7e49ebacc3cd7e7d503996e0fa61ed.svg", + "id": "3XoL8Yj78BwTk7C1KUSXKs", + "pageConfigSeparated": true, + "pages": [ + { + "chartMap": { + "4hyu4V6Vf7jMG9S7ANWLH3": { + "chartSettings": { + "markdown": "# Customer 360 – Financial Overview\n\nThis dashboard is built on the **Customer_360_Financial** graph from the TigerGraph solution kit. \nIt combines **Individuals, Accounts, Products, Sessions, and Engagement Events** into a single, connected view so you can analyze customer behaviour across digital touchpoints.\n\nUse this page as your **high-level summary** of customer activity and engagement. \nOther pages (Engagement, Funnel & Applications, Segments, Customer Detail) can be added later to drill into specific areas.\n\n---\n\n## What data is this using?\n\nThis application uses the schema and sample data provisioned by the solution kit:\n\n- **Individuals** and their **Accounts**\n- **Products** held by those accounts (Loans, Credit Cards)\n- **Sessions** and **Engagement events**:\n - Web search, product browsing, application-related events\n - Email interactions\n - App / device activity\n- **Contact information**, tied back to individuals and accounts\n\nAll widgets on this page read from the `Customer_360_Financial` graph. \nMost logic is implemented as **interpreted GSQL queries** in the dashboard (e.g. “engaged customers in date range”, “customers with no engagement”, “engaged vs not engaged split”).\n\n---\n\n## How to read this page\n\n### 1. Global Filters (top)\n\nUse the controls at the top of the page to change the scope of most metrics:\n\n- **Date Range** – `start_date` / `end_date` \n Used by the engagement KPIs, the engagement split chart, the trend widget, and the graph.\n\n- **Top-K Customers for Graph** – `top_k_customers` \n Controls how many high-engagement customers are included in the network graph.\n\n> Note: This kit ships with **sample data**, so trends and counts may look sparse. \n> In a real environment, the same queries will surface much richer patterns.\n\n---\n\n### 2. KPI Strip – “Who do we have, and who is active?”\n\nThe top row of **KPI tiles** answers the basic health questions:\n\n- **Total Customers** \n Total number of `Individual` vertices in the graph. \n This gives you the size of the customer base represented in the dataset.\n\n- **Engaged Customers (in range)** \n Customers who have at least one **Session** with at least one **action** between `start_date` and `end_date`. \n This is calculated from sessions whose `Created_On` falls within the selected window and have outgoing `\"action\"` edges.\n\n- **Customers with No Engagement** \n Individuals who exist in the graph but **have never had any session with actions**. \n This highlights dormant or unreachable customers who might need re-activation or data-quality review.\n\nUse these numbers together to understand:\n\n- How large the addressable base is.\n- What share of that base is actually active in the selected time window.\n- How much “hidden potential” sits in never-engaged customers.\n\n---\n\n### 3. Engagement Snapshot – “Who is active vs not?”\n\nThis section gives a quick **segmentation of the base**:\n\n- **Engaged vs Not Engaged (Pie Chart)** \n Splits customers into two segments for the selected date range:\n - **Engaged** – at least one session with actions in the window \n - **Not Engaged** – no engaged sessions in the window \n\nThis uses the same definition as the Engaged Customers KPI and is driven by an interpreted query that counts how many Individuals fall into each bucket.\n\nUse this to answer:\n\n- “What fraction of my base was active in this period?”\n- “If I ran a campaign, did the engaged slice grow?”\n\n---\n\n### 4. Activity Trend (Sample Data)\n\n> *Optional / demo:* Because the solution kit uses a small synthetic dataset, \n> activity over time can look sparse or spiky. In a real deployment, this chart becomes much more informative.\n\n- **Engaged Sessions Over Time (Line Chart)** \n Plots engaged sessions over time (e.g. per month) within the selected date range. \n Each point represents the number of sessions that:\n - Occurred between `start_date` and `end_date`, and \n - Have at least one `\"action\"` edge.\n\nUse this as a **sanity check** that queries and filters are wired correctly. \nIn production data, this widget will highlight peaks after campaigns, seasonal patterns, or drops in engagement.\n\n---\n\n### 5. Network Snapshot – “What does engagement look like as a graph?”\n\nThe bottom section shows a small **360° network slice** instead of just numbers:\n\n- **Top Engaged Customers Network (Graph Widget)** \n Shows the ego-network for the most engaged customers in the selected date range:\n - **Individuals**: the top-K customers ranked by engaged session count \n - **Sessions**: sessions (with actions) connected to those customers in the same window\n\nThe widget uses an interpreted query to:\n\n1. Find all engaged sessions within `start_date` / `end_date`. \n2. Aggregate session counts per `Individual`. \n3. Select the top-K customers (`top_k_customers`). \n4. Bring in their related sessions so the graph widget can visualize the connections.\n\nUse this graph to:\n\n- Visually inspect which customers are driving the most digital activity.\n- Explain the value of a connected Customer 360° view in demos.\n- Validate that the schema and engagement edges (`created_session`, `\"action\"`, etc.) are wired as expected.\n\n---\n\n## Where to go next\n\nThis Overview page is designed as a **starting point** for analysts:\n\n- Start here to understand **how many customers you have**, **how many are engaged**, and **how that engagement is distributed**.\n- If a number or slice looks unusual (e.g. very high never-engaged count, sudden drop in engaged customers), \n use dedicated pages (to be added) such as:\n - **Engagement** – channel usage, engagement by segment, session-level patterns \n - **Funnel & Applications** – application starts/submissions, drop-off by stage \n - **Segments** – product holdings, engagement tiers, region, or other attributes \n - **Customer Detail** – a focused 360° view for a single Individual\n\nAs you extend the app (e.g. add application KPIs or product splits), you can come back and expand this markdown to describe the new widgets. For now, it reflects the **current depth** of the dashboard you’ve actually built.\n" + }, + "graphName": "Customer_360_Financial", + "hideWidgetName": false, + "id": "4hyu4V6Vf7jMG9S7ANWLH3", + "patternLimit": 5, + "query": "", + "queryType": "pattern", + "refreshRate": 0, + "searchPattern": [], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "Overview Header & Help", + "type": "markdown", + "version": "1763474010331720264" + }, + "89P8HpShYntxmhLaJgi8sF": { + "chartSettings": {}, + "graphName": "Customer_360_Financial", + "hideWidgetName": false, + "id": "89P8HpShYntxmhLaJgi8sF", + "patternLimit": 5, + "query": "INTERPRET QUERY () FOR GRAPH Customer_360_Financial {\n\n // All individuals in the graph\n all_people = {Individual.*};\n\n // All sessions that have at least one action\n all_sessions = {SessionID.*};\n\n engaged_sessions = SELECT s\n FROM all_sessions:s\n WHERE s.outdegree(\"action\") > 0;\n\n // All individuals who *do* have at least one engaged session\n engaged_customers = SELECT i\n FROM all_people:i -(created_session>:cs)- engaged_sessions:s;\n\n // Now compute the set difference: all_people - engaged_customers\n never_engaged = all_people MINUS engaged_customers;\n\n // KPI: number of never-engaged customers\n PRINT never_engaged.size() AS customers_with_no_engagement;\n}\n", + "queryType": "interactive", + "refreshRate": 0, + "searchPattern": [], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "Customers With No Engagement", + "type": "value", + "version": "1763462170883246644" + }, + "8KvjFYQviXDrcQhcr7Y1kZ": { + "chartSettings": {}, + "graphName": "Customer_360_Financial", + "hideWidgetName": false, + "id": "8KvjFYQviXDrcQhcr7Y1kZ", + "patternLimit": 5, + "query": "INTERPRET QUERY (\n DATETIME start_date,\n DATETIME end_date,\n INT top_k\n)\nFOR GRAPH Customer_360_Financial SYNTAX V2 {\n\n // Count sessions per individual\n SumAccum @sess_ct;\n\n // Base sets\n all_people = {Individual.*};\n all_sessions = {SessionID.*};\n\n // 1) Sessions in the window with at least one action\n engaged_sessions = SELECT s\n FROM all_sessions:s\n WHERE s.Created_On >= start_date\n AND s.Created_On < end_date\n AND s.outdegree(\"action\") > 0;\n\n // 2) Individuals who own those engaged sessions, with session counts\n engaged_customers = SELECT i\n FROM all_people:i -(created_session>:cs)- engaged_sessions:s\n ACCUM i.@sess_ct += 1;\n\n // 3) Top-K engaged customers by session count\n top_customers = SELECT i\n FROM engaged_customers:i\n ORDER BY i.@sess_ct DESC\n LIMIT top_k;\n\n // 4) Sessions for those top customers in the same window\n top_sessions = SELECT s\n FROM top_customers:i -(created_session>:cs)- engaged_sessions:s;\n\n // 5) Output vertex sets for the graph widget\n PRINT top_customers;\n PRINT top_sessions;\n}\n", + "queryType": "interactive", + "refreshRate": 0, + "searchPattern": [], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "Top K Engaged Customers", + "type": "internal-graph", + "version": "1763473737976082290" + }, + "inoZB26qaA23sNaQdAHUHT": { + "chartSettings": { + "category": [ + { + "id": "segment", + "type": "string" + } + ], + "tableHeaders": [ + "segment", + "value" + ], + "tableIndex": 0, + "value": [ + { + "id": "value", + "type": "number" + } + ] + }, + "graphName": "Customer_360_Financial", + "hideWidgetName": false, + "id": "inoZB26qaA23sNaQdAHUHT", + "patternLimit": 5, + "query": "INTERPRET QUERY (\n DATETIME start_date,\n DATETIME end_date\n)\nFOR GRAPH Customer_360_Financial SYNTAX V2 {\n\n // Each record is: { segment: \"Engaged\"/\"Not Engaged\", value: count }\n TYPEDEF TUPLE SegmentRec;\n BagAccum @@segments;\n\n // Global counters\n SumAccum @@engaged_count;\n SumAccum @@not_engaged_count;\n\n // Base sets\n all_people = {Individual.*};\n all_sessions = {SessionID.*};\n\n // 1) Sessions in the window with at least one action\n engaged_sessions = SELECT s\n FROM all_sessions:s\n WHERE s.Created_On >= start_date\n AND s.Created_On < end_date\n AND s.outdegree(\"action\") > 0;\n\n // 2) Individuals who have at least one such session\n engaged_customers = SELECT i\n FROM all_people:i -(created_session>:cs)- engaged_sessions:s;\n\n // 3) Count engaged customers\n count_engaged = SELECT i\n FROM engaged_customers:i\n ACCUM @@engaged_count += 1;\n\n // 4) Individuals who are NOT engaged in this window\n not_engaged_customers = all_people MINUS engaged_customers;\n\n // 5) Count not-engaged customers\n count_not = SELECT i\n FROM not_engaged_customers:i\n ACCUM @@not_engaged_count += 1;\n\n // 6) Build the two segment records (no ACCUM, just body-level assignments)\n @@segments += SegmentRec(\"Engaged\", @@engaged_count);\n @@segments += SegmentRec(\"Not Engaged\", @@not_engaged_count);\n\n // 7) Output for the pie chart\n PRINT @@segments;\n}\n", + "queryType": "interactive", + "refreshRate": 0, + "searchPattern": [], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "New Widget", + "type": "pie", + "version": "1763462945105139904" + }, + "rnTgCXrPVVi7XiTUoWMtgu": { + "chartSettings": { + "category": [ + { + "id": "day", + "type": "string" + } + ], + "tableHeaders": [ + "day", + "sessions" + ], + "tableIndex": 0, + "value": [ + { + "id": "sessions", + "type": "number" + } + ], + "valueAggregation": [ + { + "id": "SUM" + } + ] + }, + "graphName": "Customer_360_Financial", + "hideWidgetName": false, + "id": "rnTgCXrPVVi7XiTUoWMtgu", + "patternLimit": 5, + "query": "INTERPRET QUERY (\n DATETIME start_date,\n DATETIME end_date\n)\nFOR GRAPH Customer_360_Financial SYNTAX V2 {\n\n // Each record: { day: \"YYYY-MM-DD\", sessions: 1 }\n TYPEDEF TUPLE DayRec;\n BagAccum @@rows;\n\n all_sessions = {SessionID.*};\n\n // Sessions in the window with at least one action\n engaged_sessions = SELECT s\n FROM all_sessions:s\n WHERE s.Created_On >= start_date\n AND s.Created_On < end_date\n AND s.outdegree(\"action\") > 0\n ACCUM\n // One row per engaged session, bucketed by day string\n @@rows += DayRec(SUBSTR(to_string(s.Created_On), 0, 10), 1);\n\n PRINT @@rows;\n}\n", + "queryType": "interactive", + "refreshRate": 0, + "searchPattern": [], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "Engaged Trend", + "type": "line", + "version": "1763472119725255424" + }, + "uwSbY4oNEapyvoWWxeRhPq": { + "chartSettings": {}, + "graphName": "Customer_360_Financial", + "hideWidgetName": false, + "id": "uwSbY4oNEapyvoWWxeRhPq", + "memoryLimit": 0, + "patternLimit": 5, + "query": "INTERPRET QUERY() FOR GRAPH Customer_360_Financial {\n \n // Get all customer vertices\n all_customers = {Individual.*};\n\n // Print the total number of customers\n PRINT all_customers.size();\n}", + "queryType": "interactive", + "refreshRate": 0, + "searchPattern": [], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "Total Customers", + "type": "value", + "version": "1763456053754105891" + }, + "v5PQtWshtbjLcMV6KkKsHP": { + "chartSettings": { + "inputStates": [ + { + "dataType": "datetime", + "id": "input_1pVKxX1oUujfsHEQgTF8vD", + "label": "Start Date", + "name": "start_date", + "placeholder": "YYYY-MM-DD", + "settings": {}, + "widgetType": "Input" + }, + { + "dataType": "datetime", + "id": "input_hVV5pmWdGHS6j8yxWDU9eB", + "label": "End Date", + "name": "end_date", + "placeholder": "YYYY-MM-DD", + "settings": {}, + "widgetType": "Input" + }, + { + "dataType": "string", + "id": "input_hoh3tu6PvjkG5oiBY84xyN", + "label": "Product", + "name": "product_filter", + "placeholder": "ALL / LOAN / CARD", + "settings": {}, + "widgetType": "Input" + }, + { + "dataType": "number", + "id": "input_rtqpRDDoTENRA4c45MzfEN", + "label": "Minimum Engagement", + "name": "min_engagement", + "placeholder": "e.g. 1", + "settings": {}, + "widgetType": "Input" + }, + { + "dataType": "number", + "id": "input_sqzUrnDcy26dhpx3CyijfS", + "label": "Top K Customers", + "name": "top_k", + "settings": {}, + "widgetType": "Input" + } + ] + }, + "graphName": "Customer_360_Financial", + "hideWidgetName": false, + "id": "v5PQtWshtbjLcMV6KkKsHP", + "patternLimit": 5, + "query": "", + "queryType": "pattern", + "refreshRate": 0, + "searchPattern": [], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "Filter", + "type": "Inputs", + "version": "1763473500047112960" + }, + "xv7HDXoJDMtoCc2s3g5B7S": { + "chartSettings": {}, + "graphName": "Customer_360_Financial", + "hideWidgetName": false, + "id": "xv7HDXoJDMtoCc2s3g5B7S", + "patternLimit": 5, + "query": "INTERPRET QUERY (\n DATETIME start_date,\n DATETIME end_date\n)\nFOR GRAPH Customer_360_Financial {\n\n SumAccum @@total_sessions;\n SumAccum @@total_customers;\n\n SumAccum @sess_ct;\n MaxAccum @last_seen;\n\n all_people = {Individual.*};\n all_sessions = {SessionID.*};\n\n engaged_sessions = SELECT s\n FROM all_sessions:s\n WHERE s.Created_On >= start_date\n AND s.Created_On < end_date\n AND s.outdegree(\"action\") > 0\n ACCUM @@total_sessions += 1;\n\n engaged_customers = SELECT i\n FROM all_people:i -(created_session>:cs)- engaged_sessions:s\n ACCUM i.@sess_ct += 1,\n i.@last_seen += s.Created_On;\n\n counted_customers = SELECT i\n FROM engaged_customers:i\n ACCUM @@total_customers += 1;\n\n FLOAT avg_sessions_per_customer = 0.0;\n IF @@total_customers != 0 THEN\n avg_sessions_per_customer =\n toFloat(@@total_sessions) / toFloat(@@total_customers);\n END;\n\n // Only print the KPI row for this widget\n PRINT @@total_customers AS total_engaged_customers;\n}\n", + "queryType": "interactive", + "refreshRate": 0, + "searchPattern": [], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "Engaged Customers", + "type": "value", + "version": "1763461647398691173" + } + }, + "globalParameters": { + "end_date": { + "id": "input_hVV5pmWdGHS6j8yxWDU9eB", + "name": "end_date", + "type": "DATETIME", + "value": "2025-11-01 00:00:00" + }, + "min_engagement": { + "id": "input_rtqpRDDoTENRA4c45MzfEN", + "name": "min_engagement", + "type": "NUMBER", + "value": 1 + }, + "product_filter": { + "id": "input_hoh3tu6PvjkG5oiBY84xyN", + "name": "product_filter", + "type": "STRING", + "value": "ALL" + }, + "start_date": { + "id": "input_1pVKxX1oUujfsHEQgTF8vD", + "name": "start_date", + "type": "DATETIME", + "value": "2020-08-01 00:00:00" + }, + "top_k": { + "id": "input_sqzUrnDcy26dhpx3CyijfS", + "name": "top_k", + "type": "NUMBER", + "value": 20 + } + }, + "iconURL": "/insights/static/media/library.d3f7f207c6bb1d7be8e64045a19991b2.svg", + "id": "ukVwHvhc7JkdnKrxEUuViw", + "isDetail": true, + "isNew": false, + "layouts": { + "md": [ + { + "h": 15, + "i": "4hyu4V6Vf7jMG9S7ANWLH3", + "moved": false, + "static": false, + "w": 2, + "x": 10, + "y": 0 + }, + { + "h": 5, + "i": "89P8HpShYntxmhLaJgi8sF", + "moved": false, + "static": false, + "w": 2, + "x": 4, + "y": 0 + }, + { + "h": 12, + "i": "8KvjFYQviXDrcQhcr7Y1kZ", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 15 + }, + { + "h": 10, + "i": "inoZB26qaA23sNaQdAHUHT", + "moved": false, + "static": false, + "w": 2, + "x": 8, + "y": 5 + }, + { + "h": 12, + "i": "rnTgCXrPVVi7XiTUoWMtgu", + "moved": false, + "static": false, + "w": 4, + "x": 4, + "y": 5 + }, + { + "h": 5, + "i": "uwSbY4oNEapyvoWWxeRhPq", + "moved": false, + "static": false, + "w": 2, + "x": 8, + "y": 0 + }, + { + "h": 15, + "i": "v5PQtWshtbjLcMV6KkKsHP", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 0 + }, + { + "h": 5, + "i": "xv7HDXoJDMtoCc2s3g5B7S", + "moved": false, + "static": false, + "w": 2, + "x": 6, + "y": 0 + } + ], + "xs": [ + { + "h": 15, + "i": "v5PQtWshtbjLcMV6KkKsHP", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 0 + }, + { + "h": 5, + "i": "89P8HpShYntxmhLaJgi8sF", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 15 + }, + { + "h": 5, + "i": "xv7HDXoJDMtoCc2s3g5B7S", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 20 + }, + { + "h": 5, + "i": "uwSbY4oNEapyvoWWxeRhPq", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 25 + }, + { + "h": 15, + "i": "4hyu4V6Vf7jMG9S7ANWLH3", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 30 + }, + { + "h": 12, + "i": "rnTgCXrPVVi7XiTUoWMtgu", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 45 + }, + { + "h": 10, + "i": "inoZB26qaA23sNaQdAHUHT", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 57 + } + ] + }, + "title": "Overview", + "version": "1763473738844877827", + "weight": 10 + } + ], + "title": "Customer_360_Financial", + "userRoleForApp": "owner", + "version": "1763451431356995546" +} \ No newline at end of file diff --git a/connected_customer/product_recommendations/README.md b/connected_customer/product_recommendations/README.md new file mode 100644 index 00000000..81fadfcf --- /dev/null +++ b/connected_customer/product_recommendations/README.md @@ -0,0 +1,251 @@ +# Product Recommendations – Retail (TigerGraph Solution Kit) + +A TigerGraph solution kit that builds a graph-powered product recommendation +engine for retail and e-commerce. + +This kit provides a complete graph environment — schema, sample data, +and GSQL queries — to understand customer behavior and generate personalized +product recommendations based on purchases, attributes, and shared affinities. + +With this kit you can: + +- Cluster customers by behavior and numeric attributes (e.g., age). +- Discover products that are frequently co-purchased or share similar audiences. +- Build and explore combined “feature” nodes (clusters, styles, attributes). +- Generate ranked product recommendations for one or many customers. + +--- + +## Contents + +- [Overview](#overview) +- [Features](#features) +- [Prerequisites](#prerequisites) +- [Setup Instructions](#setup-instructions) +- [Working-with-the-graph](#working-with-the-graph) + - [Query-Explanations](#query-explanations) + - [Running-example-queries](#running-example-queries) +- [Using-your-own-data](#using-your-own-data) +- [Resetting-the-environment](#resetting-the-environment) + +--- + +## Overview + +Product recommendations are about connecting who your customers are with +what they buy and why they choose certain products — and then turning +those insights into relevant suggestions in real time. Graph databases like +TigerGraph are well-suited for this because they can traverse deeply connected +data (e.g., customers → purchases → products → attributes → clusters) efficiently. + +This solution kit models a retail / e-commerce environment with: + +- Customers and their attributes (like age). +- Products and Product Variants organized under a category hierarchy. +- Attributes for both customers and products (e.g., style and color). +- Clusters and Combined_Feature nodes that represent higher-level + behavioral or attribute groupings. +- Interactions capturing purchase events between customers and product variants. + +You can use the included queries as building blocks for recommendation, marketing, +and analytics use cases, or extend the graph with your own entities, attributes, +and interaction types. + +--- + +## Features + +- **Pre-built TigerGraph schema** + Vertices and edges for Customers, Products, Product Variants, Product Categories, + Attributes, Clusters, Combined_Features, and Interactions, all defined in + `schema/schema.gsql`. + +- **Sample product recommendation dataset** + Mock CSV data for customers, products, categories, styles, and purchases is + provided in the `data/` directory for inspection and column mapping, while the + actual data used by the loading job is pulled from a public S3 bucket. + +- **End-to-end loading job** + A single loading job in `loading_job/load_jobs.gsql` that: + - Defines an S3 data source for the solution-kit dataset. + - Loads vertices and edges from: + - `product_categories.csv` + - `products.csv` + - `customers.csv` + - `customer_styles.csv` + - `customer_purchases.csv` + +- **Curated query library** + GSQL queries in `queries/queries.gsql` that cover: + - `k_means`: clustering vertices using numeric attributes. + - `combine_features`: merging high-degree feature nodes into Combined_Feature vertices. + - `recommend_products`: generating ranked product recommendations per customer. + +- **Reset script** + `reset/reset.gsql` cleans up the environment by dropping the loading job, + data source, queries, and the `Product_Recommendation` graph itself. + + +## Prerequisites + +Before you run this solution kit, make sure you have: + +- **A running TigerGraph instance** + - TigerGraph installed and running (TigerGraph Server or TigerGraph Cloud). + - Permissions to create/drop graphs and run GSQL commands. + +- **GSQL client access** + - The `gsql` command-line tool available on the same machine/container where you cloned this repo. + - Ability to connect to your TigerGraph service + +- **Network access for sample data** + - Outbound internet access from the TigerGraph machine to read the sample + CSV files from the public S3 bucket used in `loading_job/load_data.gsql`. + +- **Shell environment** + - A Unix-like shell (Linux, macOS, or WSL) to run `gsql` commands and any helper scripts you may add. + +- **(Optional) Python environment for local mock data** + - Python 3.x if you want to run `data/data_generator.py` to regenerate local mock CSVs. + - Python packages: + - `numpy` + - `scipy` + +> **Graph name:** This kit creates and uses a graph named `Product_Recommendation`. + +--- + +## Setup Instructions + +The instructions below assume that: + +- You have cloned or copied this repository onto the TigerGraph server. +- The `gsql` command is available in your shell. + +If you don't yet have the `gsql` command available, see the TigerGraph documentation: + +- **Local GSQL shell on the server** + [The GSQL Shell](https://docs.tigergraph.com/tigergraph-server/4.2/gsql-shell/) + (explains how to run `gsql` directly on a TigerGraph server) + +- **Remote GSQL client (from your laptop or another machine)** + [Using a Remote GSQL Client](https://docs.tigergraph.com/tigergraph-server/4.2/gsql-shell/using-a-remote-gsql-client) + (explains how to download the GSQL client JAR, configure SSL, and create a `gsql` alias) + +### 1. Create the graph and schema + +From the root of the repository, run: + +```bash +gsql schema/schema.gsql +``` + +This command will create the Product_Recommendation graph, define all +vertices, edges and all their attributes, and add these to the graph. +You can vertify this with the confirmation messages that you receive. + +### 2. Load the data from the cloud + +Running the following command will load all the necessary data from the +cloud: + +```bash +gsql schema/schema.gsql +``` +The mock data in the data/ folder is provided so that you can understand the +columns in the actual data being loaded, since they have the same structure. +These columns are directly mapped onto the vertices and edges of the graph. + + +### 3. Install the queries + +Once all the data has been loaded, install all the recommended querries using: + +```bash +gsql queries/queries.gsql +``` + +Verify that all the queries have been installed with the following command: +```bash +gsql -g Product_Recommendation "SHOW QUERY *" +``` + +## Query Explanations + +This solution kit includes several pre-built queries. Below are the most useful +queries and what you can get from each of them, grouped by business purpose. + +--- + +### 1. Customer Segmentation & Profiling + +**k_means(v_type, attr_set, min_cluster_count, max_cluster_count, …)** +This uses the K-Means algorithm to turn random data into groups of k clusters. +Clusters customers (or any vertex type) are grouped into behaviorally similar +groups based on numerical attributes. The query normalizes the selected +attributes, tries different numbers of clusters, and uses the elbow method to +pick a good number of clusters. The output is a set of Cluster vertices and In_Cluster edges +linking every input vertex to its assigned cluster. + +Use this to build customer segments for look-alike modeling, targeted campaigns, +A/B tests, or to enrich downstream analytics with stable cluster labels. + +--- + +### 2. Feature Engineering & Graph Compression + +**combine_features(hub_v_type, e_type, target_v_type, feature_v_type, hub_threshold, split_threshold)** +This builds combined feature concepts from frequently co-occurring attributes. +The query first finds “hub” feature vertices whose degree is above hub_threshold. +Hub vertices are those that have a very high degree - above our threshold. +It then looks for other feature vertices that share a moderate number of common +target vertices with each hub vertex, where the shared neighbor count is between 2 and +split_threshold. For each such pair of features, the query: + 1. Makes a Combined_Feature representing the pattern + 2. Links these Combined_Features to the original via Linked edges + 3. Links the target vertex that qualitied via a Has_Attribute edge +We flag `e.ignore_edge = TRUE` on the Combined_Feature edges for future queries +to avoid them and reduce noise. + +Use this to turn very popular but generic features into more informative +interaction features (e.g. “Nike × RunningShoes” instead of just “Nike”), +to enrich recommendation or similarity queries with more meaningful combinations +of customer and product attributes. + +--- + +### 3. Product Recommendations & Personalization + +**recommend_products(src_customer_input, batch_index, num_batches, target_batch, +ignore_threshold, recommendation_count, data_types, edge_importance_factors, vertex_degree_scales, customer_popularity_scale, item_popularity_scale)** +Generates a ranked list of product recommendations (at the `Product_Variant` +level) for each source customer. It combines: + +- **Customer–customer similarity** based on shared interactions and features. +- **Item–item co-purchasing patterns** (which products are often bought together). +- Optional **category importance** and **degree-based scaling** to downweight + overly popular items and emphasize more informative signals. + +Use this when you want to: + +- Produce personalized product suggestions for an individual customer or a batch + of customers. +- Blend “people like you also bought…” (customer-based) with + “also frequently purchased with this item…” (item-based) logic. +- Experiment with different weightings for edges and categories via + `edge_importance_factors`, `vertex_degree_scales`, `customer_popularity_scale`, + and `item_popularity_scale`. + +The query can operate in two modes: + +- **Direct**: You pass specific customers in `src_customer_input`. +- **Batch**: You leave `src_customer_input` empty and use `batch_index` and + `num_batches` to process the full customer base in chunks. + +The output is printed as: + +- Each source customer plus a list of recommended `Product_Variant` vertices with + their normalized recommendation scores, ready to be exported, inspected in + GraphStudio, or integrated into an application layer. + +--- diff --git a/connected_customer/product_recommendations/queries/combine_features.gsql b/connected_customer/product_recommendations/queries/combine_features.gsql new file mode 100644 index 00000000..43842034 --- /dev/null +++ b/connected_customer/product_recommendations/queries/combine_features.gsql @@ -0,0 +1,75 @@ +CREATE QUERY combine_features( + SET hub_v_type, + SET e_type, + STRING target_v_type="Customer", + SET feature_v_type, + INT hub_threshold, + INT split_threshold) SYNTAX V2 +{ + TYPEDEF TUPLE Vertex_Tuple; + MapAccum> @intersection_size_map; + OrAccum @hub; + DATETIME timestamp = now(); + SetAccum @combined_feature_ids; + SetAccum @combined_verts; + + // pick hub vertices + hub_verts = {hub_v_type}; + hub_verts = + SELECT s FROM hub_verts:s + WHERE s.outdegree(e_type) > hub_threshold + POST-ACCUM s.@hub += TRUE; + + // count how many customers each pair of features shares + other_verts = + SELECT t FROM hub_verts:s -(e_type)- target_v_type -(e_type)- feature_v_type:t + WHERE t.@hub == FALSE OR getvid(s) > getvid(t) + ACCUM + s.@intersection_size_map += (t -> 1); + + // mark (hub, feature) pairs with moderate overlap on each target vertex + // This section of the code cannot be made schema-free + hub_verts = + SELECT s FROM hub_verts:s -(e_type:e1)- target_v_type:v -(e_type:e2)- feature_v_type:t + WHERE (t.@hub == FALSE OR getvid(s) > getvid(t)) AND s.@intersection_size_map.get(t) BETWEEN 2 AND split_threshold + ACCUM + v.@combined_feature_ids += to_string(getvid(s)) + "_" + to_string(getvid(t)), + v.@combined_verts += s, + v.@combined_verts += t; + + all_verts = hub_verts UNION other_verts; + + // assign combined_feature_ids to each feature in pair (s, t) + hub_verts = + SELECT s FROM hub_verts:s -(e_type)- target_v_type -(e_type)- feature_v_type:t + WHERE (t.@hub == FALSE OR getvid(s) > getvid(t)) AND s.@intersection_size_map.get(t) BETWEEN 2 AND split_threshold + PER (s, t) + ACCUM + s.@combined_feature_ids += to_string(getvid(s)) + "_" + to_string(getvid(t)), + t.@combined_feature_ids += to_string(getvid(s)) + "_" + to_string(getvid(t)); + + // connect to combined features + all_verts = + SELECT s FROM all_verts:s -(e_type:e)- target_v_type:t + WHERE t.@combined_verts.contains(s) + ACCUM + e.ignore_edge = TRUE + POST-ACCUM (t) + FOREACH combined_feature_id IN t.@combined_verts DO + INSERT INTO Has_Attribute VALUES (t, combined_feature_id Combined_Feature, timestamp, 1, _) + END + POST-ACCUM (s) + FOREACH combined_feature_id IN s.@combined_feature_ids DO + INSERT INTO Linked VALUES (s, combined_feature_id Combined_Feature, timestamp) + END; +} + +UPDATE DESCRIPTION OF QUERY combine_features "This query concatenates feature vertices with a degree above a certain threshold. Feature vertex types in this case refer to Cluster, Customer_Attribute, Product_Attribute, and Product_Variant. If two feature vertices are merged, a Combined_Feature vertex gets created and the constituent features get linked to the Combined_Feature via the Linked edge type." + +UPDATE DESCRIPTION OF QUERY_PARAM combine_features.hub_v_type "These are the feature vertex types which can be considered for the purposes of combination." +UPDATE DESCRIPTION OF QUERY_PARAM combine_features.e_type "These are the edge types which should be traversed in the query for the purposes of edge counting and combination." +UPDATE DESCRIPTION OF QUERY_PARAM combine_features.target_v_type "This is the vertex type which adjoins two different feature vertices for the purpose of combination. Defaults to 'Customer'." +UPDATE DESCRIPTION OF QUERY_PARAM combine_features.feature_v_type "These are the feature vertex types which the 'hub_v_types' should traverse to via the 'target_v_type' vertex type." +UPDATE DESCRIPTION OF QUERY_PARAM combine_features.hub_threshold "If a feature vertex has more than this number of edges, it is considered a hub vertex and is eligible to be combined into a Combined_Feature vertex." +UPDATE DESCRIPTION OF QUERY_PARAM combine_features.split_threshold "This number is the upper limit of neighbors that the source and target feature vertices may share in order to be combined into a Combined_Feature vertex." + diff --git a/connected_customer/product_recommendations/queries/k_means.gsql b/connected_customer/product_recommendations/queries/k_means.gsql new file mode 100644 index 00000000..7e394bf8 --- /dev/null +++ b/connected_customer/product_recommendations/queries/k_means.gsql @@ -0,0 +1,239 @@ +CREATE QUERY k_means( + STRING v_type="Customer", + SET attr_set, + INT min_cluster_count, + INT max_cluster_count, + INT cluster_inc=1, + INT random_iter_count=1, + INT conv_iter_limit=25, + FLOAT conv_threshold=0.1, + INT random_seed=42, + BOOL use_custom_timestamp=FALSE, + DATETIME custom_timestamp=to_datetime("1970-01-01")) SYNTAX V1 +{ + TYPEDEF TUPLE Centroid_Distance; + TYPEDEF TUPLE Cluster_Count_Tuple; + // accum to store the smallest distance + HeapAccum (1, distance ASC) @centroid_distance_heap; + MaxAccum @@max_sse_delta; + MapAccum> @cluster_assignment; + MapAccum> @@centroid_write_values; + MapAccum> @@centroid_read_values; + MapAccum> @@cluster_rep_verts; + + GroupByAccum centroid_values> @@final_centroid_values; + + MapAccum> @@cluster_count_sse_map; + + SumAccum @random_init, @@sse; + AvgAccum @avg_value; + SumAccum @random_base; + MaxAccum @cluster_id; + + DATETIME cluster_timestamp = now(); + IF use_custom_timestamp == TRUE THEN + cluster_timestamp = custom_timestamp; + END; + + INT _mod, _mult, _inc; + _mod = pow(2, 31)-1; + _mult = 1664525; + _inc = 1013904223; + + verts = {v_type}; + INT feature_count = attr_set.size(); + + // build feature vectors + MapAccum> @@attr_map; + ListAccum @attr_values; + MaxAccum @@max_attr_value; + MinAccum @@min_attr_value; + + DATETIME timestamp = now(); + + JSONARRAY arr; + // parse attr set features and store in arr + FOREACH attr IN attr_set DO + arr = parse_json_array(attr); + @@attr_map += (arr.getInt(0) -> arr.getString(1)); + END; + + STRING attr_name; + // compute normalized features for each vertex + FOREACH attr_idx IN RANGE[0, attr_set.size()-1] DO + attr_name = @@attr_map.get(attr_idx); + PRINT attr_name; + verts = + SELECT s FROM verts:s + ACCUM + FLOAT value = 0, + IF s.attr_map.containsKey(attr_name) THEN + value = s.attr_map.get(attr_name) + END, + @@max_attr_value += s.attr_map.get(attr_name), + @@min_attr_value += s.attr_map.get(attr_name) + POST-ACCUM + FLOAT denominator = @@max_attr_value - @@min_attr_value, + IF denominator <= 0 THEN + denominator = 1 + END, + s.@attr_values += (s.attr_map.get(attr_name)-@@min_attr_value) / denominator, + s.@random_base = (((getvid(s)+_inc)+_mult*random_seed) % _mod), + s.@random_init = s.@random_base / (_mod * 1.0); + @@min_attr_value = GSQL_INT_MAX; + @@max_attr_value = GSQL_INT_MIN; + END; + + // if two clusters converge, merge them and just stop there. + FLOAT merge_threshold=0.01; + GroupByAccum merge_centroid_id> @@merge_map; + SumAccum @@terminate_count; + INT terminate_threshold = 5; + // Centroid Initialization + FOREACH cluster_count IN RANGE[min_cluster_count, max_cluster_count].STEP(cluster_inc) DO + IF @@terminate_count >= terminate_threshold THEN + BREAK; + END; + FOREACH iter IN RANGE[0, random_iter_count-1] DO + IF @@terminate_count >= terminate_threshold THEN + BREAK; + END; + @@terminate_count = 0; + // CENTROID INITIALIZATION + centroids = + SELECT s FROM verts:s + // refresh the random number for each vertex + POST-ACCUM + s.@random_base = (s.@random_base * _mult + (getvid(s)+_inc)) % _mod, + s.@random_init = s.@random_base / (_mod * 1.0) + ORDER BY s.@random_init + LIMIT cluster_count; + // Choose k vertices uniformly at random to be initial centroids + centroids = + SELECT s FROM centroids:s + POST-ACCUM + FOREACH i IN RANGE[0, feature_count-1] DO + @@centroid_read_values += (s -> s.@attr_values.get(i) + s.@random_init) + END; + + // PRINT cluster_count, iter, centroids, @@centroid_read_values; + @@sse = 0; + FLOAT last_sse = 1; + BOOL first_iter = TRUE; + // KMEANS ITERATION UNTIL COVNVERGENCE + WHILE (abs(@@sse - last_sse) > conv_threshold OR first_iter == TRUE) AND last_sse > @@sse LIMIT conv_iter_limit DO + first_iter = FALSE; + last_sse = @@sse; + @@sse = 0; + verts = + SELECT s FROM verts:s + ACCUM + FOREACH (centroid, centroid_values) IN @@centroid_read_values DO + // compute distance + s.@centroid_distance_heap += Centroid_Distance(tg_similarity_accum(s.@attr_values, centroid_values, "EUCLIDEAN"), centroid) + END + POST-ACCUM + FOREACH i IN RANGE[0, feature_count-1] DO + @@centroid_write_values += (s.@centroid_distance_heap.top().centroid -> (i -> s.@attr_values.get(i))) + END, + // compute sum squared errors + @@sse += pow(s.@centroid_distance_heap.top().distance, 2); + // move the newly computed centroids back to the read values for the next iteration + @@centroid_read_values.clear(); + centroids = + SELECT s FROM centroids:s + POST-ACCUM + FOREACH i IN RANGE[0, feature_count-1] DO + @@centroid_read_values += (s -> @@centroid_write_values.get(s).get(i)) + END; + @@centroid_write_values.clear(); + END; + // CLEAN UP + centroids = + SELECT s FROM centroids:s + POST-ACCUM + // compare against other centroids + // if too close, and self less than other cluster id, add to merge map + // for now, even a single overlap results in termination + // in the future, we might want to add a threshold for X overlaps + BOOL terminate = FALSE, + FOREACH (centroid, centroid_values) IN @@centroid_read_values DO + IF + centroid != s AND + getvid(s) < getvid(centroid) AND + tg_similarity_accum(@@centroid_read_values.get(s), centroid_values, "EUCLIDEAN") < merge_threshold + THEN + @@merge_map += (cluster_count, iter, getvid(s) -> getvid(centroid)), + @@terminate_count += 1, + terminate = TRUE, + BREAK + END + END, + IF terminate == FALSE THEN + @@final_centroid_values += (cluster_count, iter, getvid(s) -> @@centroid_read_values.get(s)) + END; + @@centroid_read_values.clear(); + + verts = + SELECT s FROM verts:s + POST-ACCUM + // remember this vertex's assigned centroid and the corresponding SSE + s.@cluster_assignment += (cluster_count -> Centroid_Distance(@@sse, s.@centroid_distance_heap.top().centroid)), + s.@centroid_distance_heap.clear(); + // store the sse for each vertex to compare accross k values + @@cluster_count_sse_map += (cluster_count -> @@sse); + // approximate the elbow with the second difference + IF (cluster_count - min_cluster_count) > 2*cluster_inc THEN + FLOAT prev_sse_delta = @@cluster_count_sse_map.get(cluster_count-(2*cluster_inc)) - @@cluster_count_sse_map.get(cluster_count-(1*cluster_inc)); + FLOAT next_sse_delta = @@cluster_count_sse_map.get(cluster_count-(1*cluster_inc)) - @@cluster_count_sse_map.get(cluster_count); + @@max_sse_delta += Cluster_Count_Tuple(next_sse_delta - prev_sse_delta, cluster_count-(1*cluster_inc), iter); + END; + END; + END; + + rep_verts = + SELECT s FROM verts:s + ACCUM + INT cluster_id = getvid(s.@cluster_assignment.get(@@max_sse_delta.cluster_count).centroid), + IF @@merge_map.containsKey( + @@max_sse_delta.cluster_count, + @@max_sse_delta.iter, + cluster_id) THEN + cluster_id = @@merge_map.get(@@max_sse_delta.cluster_count, @@max_sse_delta.iter,cluster_id).merge_centroid_id + END, + s.@cluster_id = cluster_id, + @@cluster_rep_verts += (cluster_id -> s) + POST-ACCUM + INSERT INTO In_Cluster VALUES (s, s.@cluster_id, cluster_timestamp, _) + HAVING + @@cluster_rep_verts.get(s.@cluster_id) == s; + + rep_verts = + SELECT s FROM rep_verts:s + POST-ACCUM + // for each cluster_id create a cluster vertex using the stored centroid values + INSERT INTO Cluster VALUES ( + s.@cluster_id, + @@final_centroid_values.get( + @@max_sse_delta.cluster_count, + @@max_sse_delta.iter, + s.@cluster_id + ).centroid_values + ); + + PRINT "Created Cluster Count:", rep_verts.size(); +} + +UPDATE DESCRIPTION OF QUERY k_means "This query clusters vertices according to some subset of their numerical attributes using the KMeans algorithm, which is a vector quantization clustering algorithm. The algorithm iteratively attempts to cluster the vertices using an incremental number of centroids and then selects the centroid assignment which results in the best sum of squared errors, such that the clusters are not under or overfit to the dataset. The algorithm inserts the centroids as Cluster vertices and links all vertices to their respective centroids via the In_Cluster edge type." +UPDATE DESCRIPTION OF QUERY_PARAM k_means.v_type "This is the target vertex type to cluster. It defaults to Customer." +UPDATE DESCRIPTION OF QUERY_PARAM k_means.attr_set "This is a JSON-formatted string representing a list of tuples containing an index and the key of a numerical value contained in the MAP attribute, 'attr_map', on the target vertex type. Example: '[0, \"Age\"]'" +UPDATE DESCRIPTION OF QUERY_PARAM k_means.min_cluster_count "This is the minimum number of centroids to try out when clustering." +UPDATE DESCRIPTION OF QUERY_PARAM k_means.max_cluster_count "This is the maximum number of centroids to try out when clustering. Must be greater than or equal to min_cluster_count." +UPDATE DESCRIPTION OF QUERY_PARAM k_means.cluster_inc "This is the value by which the centroid count is incremented when moving onto the next centroid count. Defaults to 1." +UPDATE DESCRIPTION OF QUERY_PARAM k_means.random_iter_count "The is the number of random initializations to attempt for a given centroid count. Defaults to 1." +UPDATE DESCRIPTION OF QUERY_PARAM k_means.conv_iter_limit "This is the maximum number of iterations to perform for a given centroid count. Defaults to 25." +UPDATE DESCRIPTION OF QUERY_PARAM k_means.conv_threshold "This is the minimum change in SSE during a clustering attempt before the algorithm considers a clustering to have converged. Defaults to 0.1" +UPDATE DESCRIPTION OF QUERY_PARAM k_means.random_seed "This is a seed to pseudorandomize the selection of centroids for a given centroid count." +UPDATE DESCRIPTION OF QUERY_PARAM k_means.use_custom_timestamp "This is the timestamp attributed to the Cluster vertices. If you wish to use a custom timestamp, set this to TRUE. Defaults to FALSE." +UPDATE DESCRIPTION OF QUERY_PARAM k_means.custom_timestamp "If 'use_custom_timestamp' is set to TRUE, then this value will be used as the timestamp for the Cluster vertices. Defaults to 'to_datetime(\"1970-01-01\")'" + diff --git a/connected_customer/product_recommendations/queries/queries.gsql b/connected_customer/product_recommendations/queries/recommend_products.gsql similarity index 56% rename from connected_customer/product_recommendations/queries/queries.gsql rename to connected_customer/product_recommendations/queries/recommend_products.gsql index 44fce1c4..15fdd262 100644 --- a/connected_customer/product_recommendations/queries/queries.gsql +++ b/connected_customer/product_recommendations/queries/recommend_products.gsql @@ -1,294 +1,3 @@ -CREATE QUERY k_means( - STRING v_type="Customer", - SET attr_set, - INT min_cluster_count, - INT max_cluster_count, - INT cluster_inc=1, - INT random_iter_count=1, - INT conv_iter_limit=25, - FLOAT conv_threshold=0.1, - INT random_seed=42, - BOOL use_custom_timestamp=FALSE, - DATETIME custom_timestamp=to_datetime("1970-01-01")) SYNTAX V1 -{ - TYPEDEF TUPLE Centroid_Distance; - TYPEDEF TUPLE Cluster_Count_Tuple; - HeapAccum (1, distance ASC) @centroid_distance_heap; - MaxAccum @@max_sse_delta; - MapAccum> @cluster_assignment; - MapAccum> @@centroid_write_values; - MapAccum> @@centroid_read_values; - MapAccum> @@cluster_rep_verts; - - GroupByAccum centroid_values> @@final_centroid_values; - - MapAccum> @@cluster_count_sse_map; - - ListAccum @attr_values; - SumAccum @random_init, @@sse; - AvgAccum @avg_value; - SumAccum @random_base; - MaxAccum @cluster_id; - MaxAccum @@max_attr_value; - MinAccum @@min_attr_value; - - DATETIME cluster_timestamp = now(); - IF use_custom_timestamp == TRUE THEN - cluster_timestamp = custom_timestamp; - END; - - INT _mod, _mult, _inc; - _mod = pow(2, 31)-1; - _mult = 1664525; - _inc = 1013904223; - - verts = {v_type}; - INT feature_count = attr_set.size(); - - MapAccum> @@attr_map; - - DATETIME timestamp = now(); - - JSONARRAY arr; - FOREACH attr IN attr_set DO - arr = parse_json_array(attr); - @@attr_map += (arr.getInt(0) -> arr.getString(1)); - END; - - STRING attr_name; - FOREACH attr_idx IN RANGE[0, attr_set.size()-1] DO - attr_name = @@attr_map.get(attr_idx); - PRINT attr_name; - verts = - SELECT s FROM verts:s - ACCUM - FLOAT value = 0, - IF s.attr_map.containsKey(attr_name) THEN - value = s.attr_map.get(attr_name) - END, - @@max_attr_value += s.attr_map.get(attr_name), - @@min_attr_value += s.attr_map.get(attr_name) - POST-ACCUM - FLOAT denominator = @@max_attr_value - @@min_attr_value, - IF denominator <= 0 THEN - denominator = 1 - END, - s.@attr_values += (s.attr_map.get(attr_name)-@@min_attr_value) / denominator, - s.@random_base = (((getvid(s)+_inc)+_mult*random_seed) % _mod), - s.@random_init = s.@random_base / (_mod * 1.0); - @@min_attr_value = GSQL_INT_MAX; - @@max_attr_value = GSQL_INT_MIN; - END; - - // if two clusters converge, merge them and just stop there. - FLOAT merge_threshold=0.01; - GroupByAccum merge_centroid_id> @@merge_map; - SumAccum @@terminate_count; - INT terminate_threshold = 5; - - FOREACH cluster_count IN RANGE[min_cluster_count, max_cluster_count].STEP(cluster_inc) DO - IF @@terminate_count >= terminate_threshold THEN - BREAK; - END; - FOREACH iter IN RANGE[0, random_iter_count-1] DO - IF @@terminate_count >= terminate_threshold THEN - BREAK; - END; - @@terminate_count = 0; - // CENTROID INITIALIZATION - centroids = - SELECT s FROM verts:s - POST-ACCUM - s.@random_base = (s.@random_base * _mult + (getvid(s)+_inc)) % _mod, - s.@random_init = s.@random_base / (_mod * 1.0) - ORDER BY s.@random_init - LIMIT cluster_count; - centroids = - SELECT s FROM centroids:s - POST-ACCUM - FOREACH i IN RANGE[0, feature_count-1] DO - @@centroid_read_values += (s -> s.@attr_values.get(i) + s.@random_init) - END; - - // PRINT cluster_count, iter, centroids, @@centroid_read_values; - @@sse = 0; - FLOAT last_sse = 1; - BOOL first_iter = TRUE; - // KMEANS ITERATION UNTIL COVNVERGENCE - WHILE (abs(@@sse - last_sse) > conv_threshold OR first_iter == TRUE) AND last_sse > @@sse LIMIT conv_iter_limit DO - first_iter = FALSE; - last_sse = @@sse; - @@sse = 0; - verts = - SELECT s FROM verts:s - ACCUM - FOREACH (centroid, centroid_values) IN @@centroid_read_values DO - // compute distance - s.@centroid_distance_heap += Centroid_Distance(tg_similarity_accum(s.@attr_values, centroid_values, "EUCLIDEAN"), centroid) - END - POST-ACCUM - FOREACH i IN RANGE[0, feature_count-1] DO - @@centroid_write_values += (s.@centroid_distance_heap.top().centroid -> (i -> s.@attr_values.get(i))) - END, - @@sse += pow(s.@centroid_distance_heap.top().distance, 2); - @@centroid_read_values.clear(); - centroids = - SELECT s FROM centroids:s - POST-ACCUM - FOREACH i IN RANGE[0, feature_count-1] DO - @@centroid_read_values += (s -> @@centroid_write_values.get(s).get(i)) - END; - @@centroid_write_values.clear(); - END; - // CLEAN UP - centroids = - SELECT s FROM centroids:s - POST-ACCUM - // compare against other centroids - // if too close, and self less than other cluster id, add to merge map - // for now, even a single overlap results in termination, in the future, we might want to add a threshold for X overlaps - BOOL terminate = FALSE, - FOREACH (centroid, centroid_values) IN @@centroid_read_values DO - IF - centroid != s AND - getvid(s) < getvid(centroid) AND - tg_similarity_accum(@@centroid_read_values.get(s), centroid_values, "EUCLIDEAN") < merge_threshold - THEN - @@merge_map += (cluster_count, iter, getvid(s) -> getvid(centroid)), - @@terminate_count += 1, - terminate = TRUE, - BREAK - END - END, - IF terminate == FALSE THEN - @@final_centroid_values += (cluster_count, iter, getvid(s) -> @@centroid_read_values.get(s)) - END; - @@centroid_read_values.clear(); - verts = - SELECT s FROM verts:s - POST-ACCUM - s.@cluster_assignment += (cluster_count -> Centroid_Distance(@@sse, s.@centroid_distance_heap.top().centroid)), - s.@centroid_distance_heap.clear(); - @@cluster_count_sse_map += (cluster_count -> @@sse); - - IF (cluster_count - min_cluster_count) > 2*cluster_inc THEN - FLOAT prev_sse_delta = @@cluster_count_sse_map.get(cluster_count-(2*cluster_inc)) - @@cluster_count_sse_map.get(cluster_count-(1*cluster_inc)); - FLOAT next_sse_delta = @@cluster_count_sse_map.get(cluster_count-(1*cluster_inc)) - @@cluster_count_sse_map.get(cluster_count); - @@max_sse_delta += Cluster_Count_Tuple(next_sse_delta - prev_sse_delta, cluster_count-(1*cluster_inc), iter); - END; - END; - END; - - rep_verts = - SELECT s FROM verts:s - ACCUM - INT cluster_id = getvid(s.@cluster_assignment.get(@@max_sse_delta.cluster_count).centroid), - IF @@merge_map.containsKey( - @@max_sse_delta.cluster_count, - @@max_sse_delta.iter, - cluster_id) THEN - cluster_id = @@merge_map.get(@@max_sse_delta.cluster_count, @@max_sse_delta.iter,cluster_id).merge_centroid_id - END, - s.@cluster_id = cluster_id, - @@cluster_rep_verts += (cluster_id -> s) - POST-ACCUM - INSERT INTO In_Cluster VALUES (s, s.@cluster_id, cluster_timestamp, _) - HAVING - @@cluster_rep_verts.get(s.@cluster_id) == s; - - rep_verts = - SELECT s FROM rep_verts:s - POST-ACCUM - INSERT INTO Cluster VALUES (s.@cluster_id, @@final_centroid_values.get(@@max_sse_delta.cluster_count, @@max_sse_delta.iter, s.@cluster_id).centroid_values); - - PRINT "Created Cluster Count:", rep_verts.size(); -} - -UPDATE DESCRIPTION OF QUERY k_means "This query clusters vertices according to some subset of their numerical attributes using the KMeans algorithm, which is a vector quantization clustering algorithm. The algorithm iteratively attempts to cluster the vertices using an incremental number of centroids and then selects the centroid assignment which results in the best sum of squared errors, such that the clusters are not under or overfit to the dataset. The algorithm inserts the centroids as Cluster vertices and links all vertices to their respective centroids via the In_Cluster edge type." -UPDATE DESCRIPTION OF QUERY_PARAM k_means.v_type "This is the target vertex type to cluster. It defaults to Customer." -UPDATE DESCRIPTION OF QUERY_PARAM k_means.attr_set "This is a JSON-formatted string representing a list of tuples containing an index and the key of a numerical value contained in the MAP attribute, 'attr_map', on the target vertex type. Example: '[0, \"Age\"]'" -UPDATE DESCRIPTION OF QUERY_PARAM k_means.min_cluster_count "This is the minimum number of centroids to try out when clustering." -UPDATE DESCRIPTION OF QUERY_PARAM k_means.max_cluster_count "This is the maximum number of centroids to try out when clustering. Must be greater than or equal to min_cluster_count." -UPDATE DESCRIPTION OF QUERY_PARAM k_means.cluster_inc "This is the value by which the centroid count is incremented when moving onto the next centroid count. Defaults to 1." -UPDATE DESCRIPTION OF QUERY_PARAM k_means.random_iter_count "The is the number of random initializations to attempt for a given centroid count. Defaults to 1." -UPDATE DESCRIPTION OF QUERY_PARAM k_means.conv_iter_limit "This is the maximum number of iterations to perform for a given centroid count. Defaults to 25." -UPDATE DESCRIPTION OF QUERY_PARAM k_means.conv_threshold "This is the minimum change in SSE during a clustering attempt before the algorithm considers a clustering to have converged. Defaults to 0.1" -UPDATE DESCRIPTION OF QUERY_PARAM k_means.random_seed "This is a seed to pseudorandomize the selection of centroids for a given centroid count." -UPDATE DESCRIPTION OF QUERY_PARAM k_means.use_custom_timestamp "This is the timestamp attributed to the Cluster vertices. If you wish to use a custom timestamp, set this to TRUE. Defaults to FALSE." -UPDATE DESCRIPTION OF QUERY_PARAM k_means.custom_timestamp "If 'use_custom_timestamp' is set to TRUE, then this value will be used as the timestamp for the Cluster vertices. Defaults to 'to_datetime(\"1970-01-01\")'" - - -CREATE QUERY combine_features( - SET hub_v_type, - SET e_type, - STRING target_v_type="Customer", - SET feature_v_type, - INT hub_threshold, - INT split_threshold) SYNTAX V2 -{ - TYPEDEF TUPLE Vertex_Tuple; - MapAccum> @intersection_size_map; - OrAccum @hub; - DATETIME timestamp = now(); - SetAccum @combined_feature_ids; - SetAccum @combined_verts; - - hub_verts = {hub_v_type}; - hub_verts = - SELECT s FROM hub_verts:s - WHERE s.outdegree(e_type) > hub_threshold - POST-ACCUM s.@hub += TRUE; - - other_verts = - SELECT t FROM hub_verts:s -(e_type)- target_v_type -(e_type)- feature_v_type:t - WHERE t.@hub == FALSE OR getvid(s) > getvid(t) - ACCUM - s.@intersection_size_map += (t -> 1); - - // This section of the code cannot be made schema-free - hub_verts = - SELECT s FROM hub_verts:s -(e_type:e1)- target_v_type:v -(e_type:e2)- feature_v_type:t - WHERE (t.@hub == FALSE OR getvid(s) > getvid(t)) AND s.@intersection_size_map.get(t) BETWEEN 2 AND split_threshold - ACCUM - v.@combined_feature_ids += to_string(getvid(s)) + "_" + to_string(getvid(t)), - v.@combined_verts += s, - v.@combined_verts += t; - - all_verts = hub_verts UNION other_verts; - - hub_verts = - SELECT s FROM hub_verts:s -(e_type)- target_v_type -(e_type)- feature_v_type:t - WHERE (t.@hub == FALSE OR getvid(s) > getvid(t)) AND s.@intersection_size_map.get(t) BETWEEN 2 AND split_threshold - PER (s, t) - ACCUM - s.@combined_feature_ids += to_string(getvid(s)) + "_" + to_string(getvid(t)), - t.@combined_feature_ids += to_string(getvid(s)) + "_" + to_string(getvid(t)); - - all_verts = - SELECT s FROM all_verts:s -(e_type:e)- target_v_type:t - WHERE t.@combined_verts.contains(s) - ACCUM - e.ignore_edge = TRUE - POST-ACCUM (t) - FOREACH combined_feature_id IN t.@combined_verts DO - INSERT INTO Has_Attribute VALUES (t, combined_feature_id Combined_Feature, timestamp, 1, _) - END - POST-ACCUM (s) - FOREACH combined_feature_id IN s.@combined_feature_ids DO - INSERT INTO Linked VALUES (s, combined_feature_id Combined_Feature, timestamp) - END; -} - -UPDATE DESCRIPTION OF QUERY combine_features "This query concatenates feature vertices with a degree above a certain threshold. Feature vertex types in this case refer to Cluster, Customer_Attribute, Product_Attribute, and Product_Variant. If two feature vertices are merged, a Combined_Feature vertex gets created and the constituent features get linked to the Combined_Feature via the Linked edge type." - -UPDATE DESCRIPTION OF QUERY_PARAM combine_features.hub_v_type "These are the feature vertex types which can be considered for the purposes of combination." -UPDATE DESCRIPTION OF QUERY_PARAM combine_features.e_type "These are the edge types which should be traversed in the query for the purposes of edge counting and combination." -UPDATE DESCRIPTION OF QUERY_PARAM combine_features.target_v_type "This is the vertex type which adjoins two different feature vertices for the purpose of combination. Defaults to 'Customer'." -UPDATE DESCRIPTION OF QUERY_PARAM combine_features.feature_v_type "These are the feature vertex types which the 'hub_v_types' should traverse to via the 'target_v_type' vertex type." -UPDATE DESCRIPTION OF QUERY_PARAM combine_features.hub_threshold "If a feature vertex has more than this number of edges, it is considered a hub vertex and is eligible to be combined into a Combined_Feature vertex." -UPDATE DESCRIPTION OF QUERY_PARAM combine_features.split_threshold "This number is the upper limit of neighbors that the source and target feature vertices may share in order to be combined into a Combined_Feature vertex." - - CREATE DISTRIBUTED QUERY recommend_products( SET> src_customer_input, INT batch_index, @@ -302,6 +11,7 @@ CREATE DISTRIBUTED QUERY recommend_products( FLOAT customer_popularity_scale = 1, FLOAT item_popularity_scale = 1) SYNTAX V1 { + TYPEDEF TUPLE Item_Tuple; HeapAccum (recommendation_count, score DESC, item DESC) @recommended_items; MapAccum> @sum_intersection_size, @@sum_set_size; From 071db2ae93057efeb810a380c3ff835519627c71 Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Fri, 21 Nov 2025 18:00:34 +0530 Subject: [PATCH 13/55] Enhanced documentation for Product Recommendation readme --- .../product_recommendations/README.md | 162 ++++++++++++------ .../queries/recommend_products.gsql | 11 +- .../product_recommendations/setup.sh | 28 +++ 3 files changed, 143 insertions(+), 58 deletions(-) create mode 100644 connected_customer/product_recommendations/setup.sh diff --git a/connected_customer/product_recommendations/README.md b/connected_customer/product_recommendations/README.md index 81fadfcf..72823e7c 100644 --- a/connected_customer/product_recommendations/README.md +++ b/connected_customer/product_recommendations/README.md @@ -115,6 +115,7 @@ Before you run this solution kit, make sure you have: --- + ## Setup Instructions The instructions below assume that: @@ -132,42 +133,13 @@ If you don't yet have the `gsql` command available, see the TigerGraph documenta [Using a Remote GSQL Client](https://docs.tigergraph.com/tigergraph-server/4.2/gsql-shell/using-a-remote-gsql-client) (explains how to download the GSQL client JAR, configure SSL, and create a `gsql` alias) -### 1. Create the graph and schema - -From the root of the repository, run: - -```bash -gsql schema/schema.gsql -``` - -This command will create the Product_Recommendation graph, define all -vertices, edges and all their attributes, and add these to the graph. -You can vertify this with the confirmation messages that you receive. +### Run the setup script (recommended) -### 2. Load the data from the cloud - -Running the following command will load all the necessary data from the -cloud: +From the root of the repository, make the setup script executable (once), then run it: ```bash -gsql schema/schema.gsql -``` -The mock data in the data/ folder is provided so that you can understand the -columns in the actual data being loaded, since they have the same structure. -These columns are directly mapped onto the vertices and edges of the graph. - - -### 3. Install the queries - -Once all the data has been loaded, install all the recommended querries using: - -```bash -gsql queries/queries.gsql -``` - -Verify that all the queries have been installed with the following command: -```bash -gsql -g Product_Recommendation "SHOW QUERY *" +chmod +x setup.sh +./setup.sh ``` ## Query Explanations @@ -217,35 +189,111 @@ of customer and product attributes. ### 3. Product Recommendations & Personalization **recommend_products(src_customer_input, batch_index, num_batches, target_batch, -ignore_threshold, recommendation_count, data_types, edge_importance_factors, vertex_degree_scales, customer_popularity_scale, item_popularity_scale)** -Generates a ranked list of product recommendations (at the `Product_Variant` -level) for each source customer. It combines: +ignore_threshold, recommendation_count, data_types, edge_importance_factors, +vertex_degree_scales, customer_popularity_scale, item_popularity_scale)** -- **Customer–customer similarity** based on shared interactions and features. -- **Item–item co-purchasing patterns** (which products are often bought together). -- Optional **category importance** and **degree-based scaling** to downweight - overly popular items and emphasize more informative signals. +This query generates personalized product recommendations for each customer by +combining customer–customer similarity and item–item co-purchasing patterns +over the graph. -Use this when you want to: +It outputs a ranked list of Product_Variant vertices for each source +customer, with scores that reflect both what similar customers like and which +products are frequently bought together. -- Produce personalized product suggestions for an individual customer or a batch - of customers. -- Blend “people like you also bought…” (customer-based) with - “also frequently purchased with this item…” (item-based) logic. -- Experiment with different weightings for edges and categories via - `edge_importance_factors`, `vertex_degree_scales`, `customer_popularity_scale`, - and `item_popularity_scale`. +Use this to power e-commerce product recommendations, cross-sell/upsell offers, +and personalized merchandising experiences directly on top of your TigerGraph data. -The query can operate in two modes: +--- -- **Direct**: You pass specific customers in `src_customer_input`. -- **Batch**: You leave `src_customer_input` empty and use `batch_index` and - `num_batches` to process the full customer base in chunks. +## Run Example Queries -The output is printed as: +After the setup is complete and the data is loaded, you can run an example query +to validate that everything is working correctly. -- Each source customer plus a list of recommended `Product_Variant` vertices with - their normalized recommendation scores, ready to be exported, inspected in - GraphStudio, or integrated into an application layer. +On TigerGraph Cloud (or locally), open the **GSQL Console** (the interactive `gsql` +shell) for your `Product_Recommendation` graph, then run: ---- +```gsql +USE GRAPH Product_Recommendation +RUN QUERY recommend_products({}, 0, 1) +``` + +The first parameter (`{}`) leaves `src_customer_input` empty, so the query falls +back to **batch mode** (controlled by `batch_index` and `num_batches`). + +`batch_index = 0` and `num_batches = 1` mean “run for all customers in a single batch,” +returning up to the default `recommendation_count` recommendations per customer. + +## Using Your Own Data + +By default, this solution kit loads **sample product recommendation data** from a +TigerGraph-hosted public S3 bucket, using the data source and loading job defined in +`loading_job/load_data.gsql`. The five CSV files it loads are: + +- `product_categories.csv` +- `products.csv` +- `customers.csv` +- `customer_styles.csv` +- `customer_purchases.csv` + +TigerGraph supports multiple loading options. For detailed, up-to-date examples, +please refer to the official documentation: + +- **Data loading overview** – supported sources and general workflow + https://docs.tigergraph.com/tigergraph-server/4.2/data-loading/data-loading-overview + +- **Data loading entry page** – index of all loading methods (local files, cloud storage, warehouses, Spark, etc.) + https://docs.tigergraph.com/tigergraph-server/4.2/data-loading/ + +- **Load data from cloud storage** – step-by-step guide for Amazon S3, Google Cloud Storage, and Azure Blob Storage + https://docs.tigergraph.com/tigergraph-server/4.2/data-loading/load-from-cloud + +At a high level, the steps to use your own data are: + +1. Define a `DATA_SOURCE` object that matches your storage system and credentials. +2. Update the `DEFINE FILENAME` lines in `load_all` so they point to your own + URIs (for example, your customer, product, and transaction files). +3. Keep the `LOAD` blocks the same unless your column structure changes—if it + does, adjust the `VALUES(...)` mappings so each vertex/edge attribute + (for example `Customer.id`, `Product_Variant.id`, `Interacted.timestamp`, + `Has_Attribute.score`) receives the correct input column. + +Once those changes are in place, you can reload your data with: + +```bash +gsql loading_job/load_data.gsql +``` + +## Resetting the Environment + +Sometimes you’ll want to wipe the graph environment and start fresh — for example, +when switching to a new dataset or after changing the schema, loading logic, or +recommendation queries. + +This repository provides a reset script for this purpose. + +> **Warning** +> All of the commands in this section permanently delete data (and, for a full reset, +> the graph and its queries). Use them only in development or when you are sure +> you want to clear the environment. + +### Full reset using the `reset` script + +If you need a complete reset of the Product Recommendation solution kit — not just +data, but also the loading job, data source, queries, and the graph definition — +you can use the `reset/reset.gsql` script. + +This script performs the following operations on the `Product_Recommendation` graph: + +```gsql +USE GRAPH Product_Recommendation + +-- data loading job & data source +DROP JOB load_all +DROP DATA_SOURCE s1 + +-- clear all installed queries (e.g., recommend_products, k_means, combine_features) +DROP QUERY ALL + +-- clear schema and data +DROP GRAPH Product_Recommendation diff --git a/connected_customer/product_recommendations/queries/recommend_products.gsql b/connected_customer/product_recommendations/queries/recommend_products.gsql index 15fdd262..213c3ebe 100644 --- a/connected_customer/product_recommendations/queries/recommend_products.gsql +++ b/connected_customer/product_recommendations/queries/recommend_products.gsql @@ -32,6 +32,7 @@ CREATE DISTRIBUTED QUERY recommend_products( JSONARRAY arr = parse_json_array(data_types); JSONARRAY inner_arr; JSONARRAY entry; + // parse types out of json array FOREACH i IN RANGE[0, arr.size()-1] DO entry = arr.getJSONArray(i); // Customer/Item, Feature/Target, Edge/Vertex -> Type @@ -52,7 +53,7 @@ CREATE DISTRIBUTED QUERY recommend_products( END; src_customers (ANY) = {src_customer_input}; - + // select customers if no input was provided IF src_customers.size() == 0 THEN all_customers (ANY) = {Customer.*}; @@ -82,6 +83,7 @@ CREATE DISTRIBUTED QUERY recommend_products( previous_items = SELECT t FROM previous_items:s -(reverse_Belongs_To)- Product_Variant:t ACCUM + // avoid recommendations to the customers since they already bought the variants t.@ignore += s.@temp_set POST-ACCUM s.@temp_set.clear(); @@ -109,12 +111,14 @@ CREATE DISTRIBUTED QUERY recommend_products( SELECT s FROM included_customers:s -(@@e_types:e)- @@v_types:t WHERE @@vertex_degree_scales.contains(t.type) ACCUM + // measure feature popularity @@max_value += (t.type -> t.outdegree(@@data_types.get("Customer", "Target", "Edge").types)), @@min_value += (t.type -> t.outdegree(@@data_types.get("Customer", "Target", "Edge").types)); @@e_types = @@data_types.get("Customer", "Feature", "Edge").types; @@v_types = @@data_types.get("Customer", "Feature", "Vertex").types; features = + // calculate per customer feature weights SELECT t FROM included_customers:s -(@@e_types:e)- @@v_types:t ACCUM FLOAT sim_score = 1, @@ -135,17 +139,20 @@ CREATE DISTRIBUTED QUERY recommend_products( @@e_types = @@data_types.get("Customer", "Target", "Edge").types; @@v_types = @@data_types.get("Customer", "Target", "Vertex").types; + // convert feature overlaps into customer-customer similarity other_customers = SELECT t FROM features:s -(@@e_types:e)- @@v_types:t ACCUM t.@sum_intersection_size += s.@sum_similarity POST-ACCUM (t) + //weighted Jaccard similarity between customers FOREACH (tgt, score) IN t.@sum_intersection_size DO FLOAT div = (@@sum_set_size.get(t) + @@sum_set_size.get(tgt) - score), IF div <= 0 THEN div = 1.0 END, + //Jaccard similarity stored here t.@sum_similarity += (tgt -> score*1.0/div) END, t.@sum_intersection_size.clear() @@ -209,11 +216,13 @@ CREATE DISTRIBUTED QUERY recommend_products( @@e_types = @@data_types.get("Item", "Feature", "Edge").types; @@v_types = @@data_types.get("Item", "Feature", "Vertex").types; + // find customers connected to seed items customers (ANY) = SELECT t FROM interacted_items:s -(@@e_types)- @@v_types:t; @@e_types = @@data_types.get("Item", "Target", "Edge").types; @@v_types = @@data_types.get("Item", "Target", "Vertex").types; + // from these customers find other items other_items = SELECT t FROM customers:s -(@@e_types)- @@v_types:t; diff --git a/connected_customer/product_recommendations/setup.sh b/connected_customer/product_recommendations/setup.sh new file mode 100644 index 00000000..ec1f4bca --- /dev/null +++ b/connected_customer/product_recommendations/setup.sh @@ -0,0 +1,28 @@ +#!/bin/bash +set -e + +# Graph name for this solution kit +GRAPH_NAME="Product_Recommendation" + +echo "=== Creating schema for $GRAPH_NAME ===" +gsql schema/1_create_schema.gsql + +echo "=== Loading sample data into $GRAPH_NAME ===" +gsql loading_job/load_data.gsql + +echo "=== Installing queries for $GRAPH_NAME ===" +QUERY_DIR="./queries" + +# Run each .gsql query file against the graph +for file in "$QUERY_DIR"/*.gsql; do + echo "Running $file..." + gsql -g "$GRAPH_NAME" "$file" +done + +echo "Installing all queries on $GRAPH_NAME..." +gsql --graph "$GRAPH_NAME" INSTALL QUERY ALL + +echo "=== Setup complete for $GRAPH_NAME ===" +echo "You can now open the GSQL console and run, for example:" +echo " USE GRAPH Product_Recommendation" +echo " RUN QUERY recommend_products({}, 0, 1)" From b1c7c754bb7706866b16c8c37cbbe46279fb1752 Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Mon, 24 Nov 2025 22:03:56 +0530 Subject: [PATCH 14/55] added setup.sh and insights.json to product recommendations --- .../meta/insights.json | 288 ++++++++++++++++++ .../product_recommendations/setup.sh | 4 +- 2 files changed, 290 insertions(+), 2 deletions(-) create mode 100644 connected_customer/product_recommendations/meta/insights.json diff --git a/connected_customer/product_recommendations/meta/insights.json b/connected_customer/product_recommendations/meta/insights.json new file mode 100644 index 00000000..5de9f36a --- /dev/null +++ b/connected_customer/product_recommendations/meta/insights.json @@ -0,0 +1,288 @@ +{ + "defaultGraph": "Product_Recommendation", + "iconURL": "/insights/static/media/atom.14f5dd297b1a450cae3413a44f69a75b.svg", + "id": "t7fsPyC7qKW5V5Rngu9JnB", + "pageConfigSeparated": true, + "pages": [ + { + "chartMap": { + "9ArCu34uUJA22RCnncEKUk": { + "chartSettings": {}, + "graphName": "Product_Recommendation", + "hideWidgetName": false, + "id": "9ArCu34uUJA22RCnncEKUk", + "patternLimit": 5, + "query": "INTERPRET QUERY get_customer_neighborhood()\nFOR GRAPH Product_Recommendation\nSYNTAX V1 {\n\n // Pick a small sample of customers (to keep the graph readable)\n customers =\n SELECT c\n FROM Customer:c\n LIMIT 3;\n\n // Customer --(Interacted)-- Product_Variant (purchases)\n variants =\n SELECT pv\n FROM customers:c -(Interacted:e)- Product_Variant:pv\n WHERE e.interaction_type == \"purchase\";\n\n // Product_Variant --(Belongs_To)-> Product\n prods =\n SELECT p\n FROM variants:pv -(Belongs_To:e)-> Product:p;\n\n // Product --(Belongs_To)-> Product_Category\n cats =\n SELECT cat\n FROM prods:p -(Belongs_To:e)-> Product_Category:cat;\n\n // Customers' style preferences: Customer --(Has_Attribute)-- Customer_Attribute\n cust_attrs =\n SELECT ca\n FROM customers:c -(Has_Attribute:e)- Customer_Attribute:ca;\n\n // Product attributes: Product_Variant --(Has_Attribute)-- Product_Attribute\n prod_attrs =\n SELECT pa\n FROM variants:pv -(Has_Attribute:e)- Product_Attribute:pa;\n\n // Output all vertex sets so Insights can draw them\n PRINT customers;\n // PRINT cust_attrs;\n}\n", + "queryType": "interactive", + "refreshRate": 0, + "searchPattern": [], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "Customer Neighborhood", + "type": "internal-graph", + "version": "1764000506666763971" + } + }, + "globalParameters": {}, + "iconURL": "/insights/static/media/globe-lock.ce36fa55b625acabad473652568e1813.svg", + "id": "hxb4w9c9zu3Lyzmgkhptsm", + "isDetail": true, + "isNew": false, + "layouts": { + "md": [ + { + "h": 32, + "i": "9ArCu34uUJA22RCnncEKUk", + "moved": false, + "static": false, + "w": 8, + "x": 0, + "y": 0 + } + ], + "xs": [ + { + "h": 12, + "i": "9ArCu34uUJA22RCnncEKUk", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 0 + } + ] + }, + "title": "Customer Explorer", + "version": "1763990634442345384", + "weight": 20 + }, + { + "chartMap": { + "2FYeuKde8FNqTMhxYgtkA6": { + "chartSettings": { + "category": [ + { + "id": "v_id", + "type": "string" + } + ], + "tableHeaders": [ + "v_id", + "v_type", + "@purchase_count", + "id" + ], + "tableIndex": 0, + "value": [ + { + "id": "@purchase_count", + "type": "number" + } + ] + }, + "graphName": "Product_Recommendation", + "hideWidgetName": false, + "id": "2FYeuKde8FNqTMhxYgtkA6", + "patternLimit": 5, + "query": "INTERPRET QUERY get_purchases_by_category()\nFOR GRAPH Product_Recommendation\nSYNTAX V1 {\n\n // We’ll reuse this vertex-attached accumulator on multiple vertex types.\n // It will hold:\n // - on Product_Variant: number of purchases for that variant\n // - on Product: total purchases for that product\n // - on Product_Category:total purchases for that category\n SumAccum @purchase_count;\n\n // Start from all customers\n start = {Customer.*};\n\n // Customer --(Interacted)--> Product_Variant\n // Count how many purchase edges hit each variant.\n variants =\n SELECT pv\n FROM start:s -(Interacted:e)- Product_Variant:pv\n WHERE e.interaction_type == \"purchase\"\n ACCUM pv.@purchase_count += 1;\n\n // Product_Variant --(Belongs_To)-> Product\n // Roll up purchase counts from variants to products.\n prods =\n SELECT p\n FROM variants:pv -(Belongs_To:e)-> Product:p\n ACCUM p.@purchase_count += pv.@purchase_count;\n\n // Product --(Belongs_To)-> Product_Category\n // Roll up purchase counts from products to categories.\n categories =\n SELECT cat\n FROM prods:p -(Belongs_To:e)-> Product_Category:cat\n ACCUM cat.@purchase_count += p.@purchase_count;\n\n // Print categories\n PRINT categories;\n}\n", + "queryType": "interactive", + "refreshRate": 0, + "searchPattern": [], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "Purchases By Product Category", + "type": "pie", + "version": "1763984583051629662" + }, + "82qGfNTjt6wBAxcx6qnhmp": { + "chartSettings": { + "category": [ + { + "id": "v_id", + "type": "string" + } + ], + "tableHeaders": [ + "v_id", + "v_type", + "@purchase_count", + "id", + "in_stock", + "price", + "timestamp" + ], + "tableIndex": 0, + "value": [ + { + "id": "@purchase_count", + "type": "number" + } + ] + }, + "graphName": "Product_Recommendation", + "hideWidgetName": false, + "id": "82qGfNTjt6wBAxcx6qnhmp", + "patternLimit": 5, + "query": "INTERPRET QUERY get_top_products_by_purchases()\nFOR GRAPH Product_Recommendation\nSYNTAX V1 {\n\n // Each Product_Variant will store how many times it was purchased.\n SumAccum @purchase_count;\n\n // Start from all customers\n start = {Customer.*};\n\n // Customer --(Interacted)--> Product_Variant\n // Count purchases per variant, then sort & limit in the SELECT.\n variants =\n SELECT pv\n FROM start:s -(Interacted:e)- Product_Variant:pv\n WHERE e.interaction_type == \"purchase\"\n ACCUM pv.@purchase_count += 1\n ORDER BY pv.@purchase_count DESC\n LIMIT 10; \n\n // Print the vertex set for Insights\n PRINT variants;\n}\n", + "queryType": "interactive", + "refreshRate": 0, + "searchPattern": [], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "Top Products By Purchases", + "type": "bar", + "version": "1763985739818153518" + }, + "aGXWqVkdrcr1fJD3Nr2PZQ": { + "chartSettings": {}, + "graphName": "Product_Recommendation", + "hideWidgetName": false, + "id": "aGXWqVkdrcr1fJD3Nr2PZQ", + "patternLimit": 5, + "query": "INTERPRET QUERY get_total_purchase_events() FOR GRAPH Product_Recommendation {\n\n SumAccum @@total_purchases;\n\n start = {Customer.*};\n\n start =\n SELECT s FROM start:s -(Interacted:e)- Product_Variant:t\n WHERE e.interaction_type == \"purchase\"\n ACCUM\n @@total_purchases += 1;\n\n PRINT @@total_purchases AS total_purchase_events;\n}\n", + "queryType": "interactive", + "refreshRate": 0, + "searchPattern": [], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "Total Purchases", + "type": "value", + "version": "1763983800972271547" + }, + "qiJvz1R3jtwheHY7n248fh": { + "chartSettings": {}, + "graphName": "Product_Recommendation", + "hideWidgetName": false, + "id": "qiJvz1R3jtwheHY7n248fh", + "patternLimit": 5, + "query": "INTERPRET QUERY get_total_customers() FOR GRAPH Product_Recommendation {\n start = {Customer.*};\n PRINT start.size();\n}\n", + "queryType": "interactive", + "refreshRate": 0, + "searchPattern": [], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "Total Customers", + "type": "value", + "version": "1763983589358074731" + }, + "rtLCUDSijWCnyJ4VZKigVE": { + "chartSettings": {}, + "graphName": "Product_Recommendation", + "hideWidgetName": false, + "id": "rtLCUDSijWCnyJ4VZKigVE", + "patternLimit": 5, + "query": "INTERPRET QUERY get_total_products() FOR GRAPH Product_Recommendation {\n start = {Product.*};\n PRINT start.size();\n}", + "queryType": "interactive", + "refreshRate": 0, + "searchPattern": [], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "Total Products", + "type": "value", + "version": "1763983674813496143" + } + }, + "globalParameters": {}, + "iconURL": "/insights/static/media/badge-info.d87dd45b19b490018f24dc6e82113abc.svg", + "id": "oPpn6XgAzT2Barjjzit6U5", + "isDetail": true, + "isNew": false, + "layouts": { + "md": [ + { + "h": 21, + "i": "2FYeuKde8FNqTMhxYgtkA6", + "moved": false, + "static": false, + "w": 3, + "x": 0, + "y": 9 + }, + { + "h": 36, + "i": "82qGfNTjt6wBAxcx6qnhmp", + "moved": false, + "static": false, + "w": 7, + "x": 3, + "y": 9 + }, + { + "h": 9, + "i": "aGXWqVkdrcr1fJD3Nr2PZQ", + "moved": false, + "static": false, + "w": 2, + "x": 4, + "y": 0 + }, + { + "h": 9, + "i": "qiJvz1R3jtwheHY7n248fh", + "moved": false, + "static": false, + "w": 2, + "x": 0, + "y": 0 + }, + { + "h": 9, + "i": "rtLCUDSijWCnyJ4VZKigVE", + "moved": false, + "static": false, + "w": 2, + "x": 2, + "y": 0 + } + ], + "xs": [ + { + "h": 9, + "i": "qiJvz1R3jtwheHY7n248fh", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 0 + }, + { + "h": 9, + "i": "rtLCUDSijWCnyJ4VZKigVE", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 9 + }, + { + "h": 9, + "i": "aGXWqVkdrcr1fJD3Nr2PZQ", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 18 + }, + { + "h": 21, + "i": "2FYeuKde8FNqTMhxYgtkA6", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 27 + }, + { + "h": 36, + "i": "82qGfNTjt6wBAxcx6qnhmp", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 48 + } + ] + }, + "title": "Overview", + "version": "1763994516788879761", + "weight": 10 + } + ], + "title": "Retail Insights", + "userRoleForApp": "owner", + "version": "1763982709365410423" +} \ No newline at end of file diff --git a/connected_customer/product_recommendations/setup.sh b/connected_customer/product_recommendations/setup.sh index ec1f4bca..8688b726 100644 --- a/connected_customer/product_recommendations/setup.sh +++ b/connected_customer/product_recommendations/setup.sh @@ -5,10 +5,10 @@ set -e GRAPH_NAME="Product_Recommendation" echo "=== Creating schema for $GRAPH_NAME ===" -gsql schema/1_create_schema.gsql +gsql schema/schema.gsql echo "=== Loading sample data into $GRAPH_NAME ===" -gsql loading_job/load_data.gsql +gsql loading_job/load_jobs.gsql echo "=== Installing queries for $GRAPH_NAME ===" QUERY_DIR="./queries" From fbd11c1a10a8bb7669150d52a0d22bf2f469c934 Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Wed, 26 Nov 2025 13:32:21 +0530 Subject: [PATCH 15/55] Added schema to Application Fraud and enhanced documentation --- financial_crime/application_fraud/README.md | 488 ++++++++++++++++-- .../schema/create_schema.gsql | 171 +++++- 2 files changed, 605 insertions(+), 54 deletions(-) diff --git a/financial_crime/application_fraud/README.md b/financial_crime/application_fraud/README.md index 09baf7f9..b73627f8 100644 --- a/financial_crime/application_fraud/README.md +++ b/financial_crime/application_fraud/README.md @@ -1,54 +1,192 @@ -# Narratives +# Application Fraud (TigerGraph Solution Kit) -Application fraud detection identifies and prevents unauthorized or deceptive financial applications in real-time. It analyzes shared PII attributes between Applications to detect anomalies and suspicious activity indicative of fraud. +A TigerGraph solution kit for detecting **application fraud** and uncovering +fraud rings using shared Personally Identifiable Information (PII) and **entity resolution**. -TigerGraph models complex relationships and patterns among entities such as shared Name, DOB, Email, Phone, Address, IP, ID, Device, Party, Account, and Card. This enables detection of fraudulent networks and patterns that may be difficult to uncover using traditional relational databases. +This project provisions a complete graph environment — schema, sample data, +and GSQL queries — to analyze how applications are connected via devices, IPs, +addresses, and other identity signals. -Using graph algorithms enable organizations to detect and respond to fraudulent transactions quickly and efficiently, ultimately reducing financial losses and protecting consumers from fraudulent activity. +With this kit you can: -# Components +- Link applications into fraud rings based on shared PII. +- Judge a new application's proximity to known fraud. +- Quantify the impact of graph features with uplift metrics and other insights +- Generate rich graph features for downstream fraud ML models. -This repository includes multiple components: +--- -- `data` - Sample data. -- `load_jobs` - Scripts for data loading tasks. -- `meta` - Solution Kit metadata. -- `queries` - Collection of GSQL queries. -- `schema` - Definition of database schema. -- `readme.md` - This usage guide. -- `setup.sh` - Automated setup script. +## Contents -# Instructions +- [Overview](#overview) +- [Features](#features) +- [Prerequisites](#prerequisites) +- [Setup Instructions](#setupInstructions) +- [Installation Notes](#InstallationNotes) +- [Working-with-the-graph](#working-with-the-graph) + - [Query-Explanations](#query-explanations) + - [Running-example-queries](#running-example-queries) +- [Using-your-own-data](#using-your-own-data) +- [Resetting-the-environment](#resetting-the-environment) +- [ML-model-and-insights-application](#ml-model-and-insights-application) -The `setup.sh` script is designed to streamline the initial setup process by sequentially executing the following steps: +--- -1. **Schema Creation**: Initiates the schema creation process using the `schema/create_schema.gsql` script. This schema is a subset of the Super Schema in financial_crime/library. -2. **Data Loading**: Load data into the schema by running the data loading job with the `loading_job/load_data.gsql` script. -3. **Query Installation**: Completes the setup by installing necessary queries through the `queries/install_queries.sh` script. +## Overview -## Installation Note for Queries +Application fraud detection focuses on spotting suspicious credit applications +before they are approved, when: -When installing the queries there are **queries** that require special attention: +- Multiple applications share the same PII. +- Applications are part of a larger, tightly connected ring. +- New applications appear “close” in the graph to known fraud. -- `match_application_entities` and `incremental_match_application_entities`. +Relational databases struggle to track these many-to-many, multi-hop +relationships at scale. TigerGraph’s native graph engine is well suited +because it can: + +**Fast traversals.** Move from an application through its PII and back to other +applications in just a few hops. + +**Community detection.** Group related applications into communities that +represent fraud rings or clusters of highly related applications. + +**Real-time signals.** Compute graph-based fraud features on demand for +scoring new applications as they arrive. + +In this solution kit, we model a fraud detection environment with: + +1. Application vertices +2. PII vertices such as name, DOB, email, phone, address, IP, ID, device, etc. +3. Location vertices +4. Connected_Component vertices: representing communities +5. Product vertices linked to applications. + +You can use the included queries as building blocks for fraud analytics, +operational alerting, and model feature generation. + +> **Graph name:** This kit creates and uses a graph named `Application_Fraud`. + +--- + +## Features + +- **Application Fraud Graph Environment** + +This kit includes a ready-to-use `Application_Fraud` graph schema, sample data, +and a loading job that pulls this data from a public S3 bucket into TigerGraph. +The loaded data is identical to the data found in the `data/` folder of the +kit. + +- **Entity resolution pipeline** + +Use queries to determine if different records with possibly different and +overlapping details actually refer to the same real-world entity -> the same +person, account or application. + +We do this by creating a pipeline that computes similarity between +applications based on shared PII, groups related applications and then +supports incremental matching of new applications in real time. + +- **Graph feature and proximity queries** -These queries have weights for each PII attribute and threshold parameters used by the weighted WCC matching process. +Queries that generate: + - Component-level features (ring size, number of fraud apps, distinct PII). + - Distances and paths from a given application to known fraud. + - CSV feature files for ML training. -- FLOAT Application_Full_Name_weight = 0.2 -- FLOAT Application_DOB_weight = 0.2 -- FLOAT Application_Email_weight = 0.2 -- FLOAT Application_Phone_weight = 0.2 -- FLOAT Application_Address_weight = 0.2 -- FLOAT Application_IP_weight = 0.2 -- FLOAT Application_ID_weight = 1.0 -- FLOAT Application_Device_weight = 1.0 -- FLOAT Application_Party_weight = 1.0 -- FLOAT Application_Account_weight = 1.0 -- FLOAT Application_Card_weight = 1.0 -- FLOAT threshold = 1.0 +- **Insights analysis** + +Queries that take confusion-matrix counts from your ML model and calculate: + - Precision, recall, specificity, and NPV. + - Detected and missed fraud amounts. + - Net financial gain and relative performance uplift. + +- **Automated setup script** + `setup.sh` creates the graph, loads the data, and installs all queries in one go. + +--- +## Prerequisites + +Before you run this solution kit, make sure you have: + +- **A running TigerGraph instance** + - TigerGraph installed and running, or use the prebuilt kit on TG cloud. + - You must have permission to create graphs and run GSQL commands. + +- **GSQL client access** + - The `gsql` command-line tool available on the same machine/container where you cloned this repo. + - Ability to connect to your TigerGraph service + +- **Network access for sample data** + - Outbound internet access from the TigerGraph machine to read the sample + CSV files from the public S3 bucket used in `loading_job/load_data.gsql`. + +- **Shell environment** + - A Unix-like shell (Linux, macOS, or WSL) to run `setup.sh` and `queries/install_queries.sh`. + - Executable permissions for the scripts: + ```bash + chmod +x setup.sh + chmod +x queries/install_queries.sh + ``` + +> **Graph name:** This kit creates and uses a graph named `Customer_360_Financial`. + +# Setup Instructions +The following instructions assume that you are running the following scripts +with `gsql` command installed. + +If you don't yet have the `gsql` command available, see the TigerGraph documentation: + +- **Local GSQL shell on the server** + [The GSQL Shell](https://docs.tigergraph.com/tigergraph-server/4.2/gsql-shell/) + (explains how to run `gsql` directly on a TigerGraph server) + +- **Remote GSQL client (from your laptop or another machine)** + [Using a Remote GSQL Client](https://docs.tigergraph.com/tigergraph-server/4.2/gsql-shell/using-a-remote-gsql-client) + (explains how to download the GSQL client JAR, configure SSL, and create a `gsql` alias) +Ensure that the script is executable with: +```bash + +chmod +x setup.sh + +``` +Then, run the automated script using: +```bash +./setup.sh +``` +This script with create and run the schema, load all the mock data into graph +and install all available queries. -When an Application matches another Application through shared PII attribute(s) the respective weight(s) will accumulate and any accumulated weights equal or over threshold is considered a match. For example if device id has a weight of 1.0 and the threshold is 1.0 then any single matching device id between any Applications will cause those respective Applications to be put into the same Connected_Component community. If device id has a weight of 0.5 and threshold is 1.0 then it would require at least two shared device ids between any Applications to match etc. Default weights and thresholds are assigned to these queries but should be adapted if there are different business rules for matching required to satisfy. +To check if all the queries have run successfully, please run the following + command: + ```bash + gsql -g Customer_360_Financial "SHOW QUERY *" + ``` + +## Installation Note for Queries + +When installing the queries there are **queries** that require special attention: + +- `match_application_entities` and `incremental_match_application_entities`. + +These queries have weights for each PII attribute and threshold parameters used by the weighted WCC matching process. + + - FLOAT Application_Full_Name_weight = 0.2 + - FLOAT Application_DOB_weight = 0.2 + - FLOAT Application_Email_weight = 0.2 + - FLOAT Application_Phone_weight = 0.2 + - FLOAT Application_Address_weight = 0.2 + - FLOAT Application_IP_weight = 0.2 + - FLOAT Application_ID_weight = 1.0 + - FLOAT Application_Device_weight = 1.0 + - FLOAT Application_Party_weight = 1.0 + - FLOAT Application_Account_weight = 1.0 + - FLOAT Application_Card_weight = 1.0 + - FLOAT threshold = 1.0 + +When an Application matches another Application through shared PII attribute(s) the respective weight(s) will accumulate and any accumulated weights equal or over threshold is considered a match. For example if device id has a weight of 1.0 and the threshold is 1.0 then any single matching device id between any Applications will cause those respective Applications to be put into the same Connected_Component community. If device id has a weight of 0.5 and threshold is 1.0 then it would require at least two shared device ids between any Applications to match etc. Default weights and thresholds are assigned to these queries but should be adapted if there are different business rules for matching required to satisfy. ## Query Execution Order and Explanations ### Step 1: Insert Edges between Matching Applications @@ -67,7 +205,7 @@ The feature engineering queries generate feature values to test and train the do `batch_application_cc_features` -Application, Application.is_fraud, Application Connected_Component, Application nodes in Connected_Component, Fraud Application Nodes in Connected_Component, distinct PII nodes in Connected_Component, number of Application nodes in Connected_Component connected by PII, number of Application nodes in Connected_Component only connected by PII. PII includes Name, DOB, Email, Phone, Address, IP, ID, Device, Party, Account, and Card vertices. +Application, Application.is_fraud, Application Connected_Component, Application nodes in Connected_Component, Fraud Application Nodes in Connected_Component, distinct PII nodes in Connected_Component, number of Application nodes in Connected_Component connected by PII, number of Application nodes in Connected_Component only connected by PII. PII includes Name, DOB, Email, Phone, Address, IP, ID, Device, Party, Account, and Card vertices. `batch_application_distance_and_path` @@ -75,42 +213,286 @@ Application, Application.is_fraud, Application Connected_Component, Fraud Applic ### Step 4: Add New Applications and Detect Fraud in Real-Time -As new Applications get submitted these can be added to the graph and associated to a Connected_Component community in near real time using `incremental_application_match` query accepting a JSON payload containing all Application and PII data including Name, DOB, Email, Phone, Address, IP, ID, Device, Party, Account, and Card vertices. It's typical to have consistent weight and threshold settings between this and the former `match_application_entities` query for consistent matching behavior. +As new Applications get submitted these can be added to the graph and associated to a Connected_Component community in near real time using `incremental_application_match` query accepting a JSON payload containing all Application and PII data including Name, DOB, Email, Phone, Address, IP, ID, Device, Party, Account, and Card vertices. It's typical to have consistent weight and threshold settings between this and the former `match_application_entities` query for consistent matching behavior. Raw Request Example -``` http://18.217.164.69:14240/restpp/query/Application_Fraud/incremental_application_match input={"application": "6f9e1ca5-e60c-48fd-a1c1-03d13348f888", "created_at": "2024-04-17 11:15:26.076265", "status": "740e989a-3614-427b-b3b0-21fc96ce2fc4", "line_of_credit": "9884.82", "annual_percentage_rate": "36.06", "fraud": true, "product": "414882cd-3fcb-42ef-8ee6-0423ce526124", "name": "3a58b7ce-33cb-46c2-ab65-527e1334927b", "dob": "a1d4472b-0f3c-4465-82a0-e2f889138989", "email": "18baaf68-12de-4291-8fae-8df7f2bf6026", "phone_numbers": [{"type": "mobile", "id": "5ed411af-e38c-4e97-b657-25017695148a"}, {"type": "landline", "id": "60669163-67e7-4e0e-a46b-2c25bdd0ee6b"}], "addresses": [{"type": "mailing", "line_1": "61d2bd45-57f1-4b3c-98a9-265cbcefdead", "line_2": "ed7020ae-35ef-4aae-a13d-3513222fcc50", "city": "fd1e10fb-91e4-4ecf-9ec1-afb2280dfbb5", "state": "694d083b-7006-4561-a2c1-913d80831ba7", "zipcode": "5b8eafe4-01c0-4b1e-8024-595ac6969a7c", "county": "fb4b12a7-bc0e-47cc-852f-ff7da7e61d66", "country": "US"}, {"type": "physical", "line_1": "8f1a04ed-ef4a-4324-8321-be3fe85f17da", "line_2": "e9a58e29-68cd-4e35-a43b-f28914c56e5a", "city": "13cf8f10-814b-4db6-930d-c0a162ef24f1", "state": "d8d7e179-5bb7-4cde-8fb5-97f0e9e55bde", "zipcode": "ff1456dd-b2f8-4c55-9dcc-e2b13b42d05b", "county": "6ba0eec9-4e72-45f1-91aa-1fd246d7400c", "country": "US"}], "ip_address": "83f7dc2f-928c-48a7-b157-0c50572071aa", "ids": [{"type": "Passport", "id": "36f26ab2-35ba-4361-b719-964ae23283b4"}, {"type": "Driver's License Number", "id": "8cefe5da-9f83-4013-a1e8-2e59108726c7"}], "device_id": "7a1f0167-1004-4d6c-8411-e38e414a6129", "party": "00cb01dc-e3e3-492d-b1fe-7c225ba3dde1", "accounts": [{"type": "savings", "id": "fb08e35b-b428-4840-8800-c0f841e7650e"}, {"type": "checking", "id": "3f41189c-b55b-4ec5-b073-182f9f87ceee"}], "cards": [{"type": "credit_card", "id": 241556}, {"type": "debit_card", "id": 278916}]} -``` Response -``` {'Application': '6f9e1ca5-e60c-48fd-a1c1-03d13348f888', 'entity_resolution': True} -``` -If the entity_resolution response is True the incoming Application was successfully able to be matched with the historical Application dataset using the matching logic. The `distance_and_path_to_fraud_account` and `get_application_cc_features` queries can be called to retrieve the graph features to augment the downstream ML decision model in near real time. +If the entity_resolution response is True the incoming Application was successfully able to be matched with the historical Application dataset using the matching logic. The `distance_and_path_to_fraud_account` and `get_application_cc_features` queries can be called to retrieve the graph features to augment the downstream ML decision model in near real time. -``` -http://18.217.164.69:14240/restpp/query/Application_Fraud/distance_and_path_to_fraud_application + http://18.217.164.69:14240/restpp/query/Application_Fraud/distance_and_path_to_fraud_application -{"version":{"edition":"enterprise","api":"v2","schema":59},"error":false,"message":"","results":[{"result":[{"v_id":"31aeadde-d12a-486d-b2b4-a217daa9404d","v_type":"Application","attributes":{"input_application_id":"fa881c0a-adff-42d5-adf5-19de0cbe3547","input_application_id_cc":"675282944","degree_of_connection":2,"is_fraud":true,"application_id":"31aeadde-d12a-486d-b2b4-a217daa9404d","application_id_cc":"675282944","path_of_connection":["Device"]}}]}]} -``` + {"version":{"edition":"enterprise","api":"v2","schema":59},"error":false,"message":"","results":[{"result":[{"v_id":"31aeadde-d12a-486d-b2b4-a217daa9404d","v_type":"Application","attributes":{"input_application_id":"fa881c0a-adff-42d5-adf5-19de0cbe3547","input_application_id_cc":"675282944","degree_of_connection":2,"is_fraud":true,"application_id":"31aeadde-d12a-486d-b2b4-a217daa9404d","application_id_cc":"675282944","path_of_connection":["Device"]}}]}]} -``` -http://18.217.164.69:14240/restpp/query/Application_Fraud/get_application_cc_features + http://18.217.164.69:14240/restpp/query/Application_Fraud/get_application_cc_features -{"version":{"edition":"enterprise","api":"v2","schema":59},"error":false,"message":"","results":[{"result":[{"v_id":"31aeadde-d12a-486d-b2b4-a217daa9404d","v_type":"Application","attributes":{"connectedComponentID":643825664,"total_cc_nodes":2,"fraud_applications_in_cc":2,"name_distinct_in_cc":2,"name_number_nodes":0,"name_number_nodes_only_connected":0,"dob_distinct_in_cc":2,"dob_number_nodes":0,"dob_number_nodes_only_connected":0,"email_distinct_in_cc":2,"email_number_nodes":0,"email_number_nodes_only_connected":0,"phone_distinct_in_cc":4,"phone_number_nodes":0,"phone_number_nodes_only_connected":0,"address_distinct_in_cc":4,"address_number_nodes":0,"address_number_nodes_only_connected":0,"ip_distinct_in_cc":2,"ip_number_nodes":0,"ip_number_nodes_only_connected":0,"id_distinct_in_cc":4,"id_number_nodes":0,"id_number_nodes_only_connected":0,"device_distinct_in_cc":1,"device_number_nodes":0,"device_number_nodes_only_connected":0,"party_distinct_in_cc":0,"party_number_nodes":0,"party_number_nodes_only_connected":0,"account_distinct_in_cc":4,"account_number_nodes":0,"account_number_nodes_only_connected":0,"card_distinct_in_cc":4,"card_number_nodes":0,"card_number_nodes_only_connected":0}},{"v_id":"fa881c0a-adff-42d5-adf5-19de0cbe3547","v_type":"Application","attributes":{"connectedComponentID":643825664,"total_cc_nodes":2,"fraud_applications_in_cc":2,"name_distinct_in_cc":2,"name_number_nodes":0,"name_number_nodes_only_connected":0,"dob_distinct_in_cc":2,"dob_number_nodes":0,"dob_number_nodes_only_connected":0,"email_distinct_in_cc":2,"email_number_nodes":0,"email_number_nodes_only_connected":0,"phone_distinct_in_cc":4,"phone_number_nodes":0,"phone_number_nodes_only_connected":0,"address_distinct_in_cc":4,"address_number_nodes":0,"address_number_nodes_only_connected":0,"ip_distinct_in_cc":2,"ip_number_nodes":0,"ip_number_nodes_only_connected":0,"id_distinct_in_cc":4,"id_number_nodes":0,"id_number_nodes_only_connected":0,"device_distinct_in_cc":1,"device_number_nodes":0,"device_number_nodes_only_connected":0,"party_distinct_in_cc":0,"party_number_nodes":0,"party_number_nodes_only_connected":0,"account_distinct_in_cc":4,"account_number_nodes":0,"account_number_nodes_only_connected":0,"card_distinct_in_cc":4,"card_number_nodes":0,"card_number_nodes_only_connected":0}}]}]} -``` + {"version":{"edition":"enterprise","api":"v2","schema":59},"error":false,"message":"","results":[{"result":[{"v_id":"31aeadde-d12a-486d-b2b4-a217daa9404d","v_type":"Application","attributes":{"connectedComponentID":643825664,"total_cc_nodes":2,"fraud_applications_in_cc":2,"name_distinct_in_cc":2,"name_number_nodes":0,"name_number_nodes_only_connected":0,"dob_distinct_in_cc":2,"dob_number_nodes":0,"dob_number_nodes_only_connected":0,"email_distinct_in_cc":2,"email_number_nodes":0,"email_number_nodes_only_connected":0,"phone_distinct_in_cc":4,"phone_number_nodes":0,"phone_number_nodes_only_connected":0,"address_distinct_in_cc":4,"address_number_nodes":0,"address_number_nodes_only_connected":0,"ip_distinct_in_cc":2,"ip_number_nodes":0,"ip_number_nodes_only_connected":0,"id_distinct_in_cc":4,"id_number_nodes":0,"id_number_nodes_only_connected":0,"device_distinct_in_cc":1,"device_number_nodes":0,"device_number_nodes_only_connected":0,"party_distinct_in_cc":0,"party_number_nodes":0,"party_number_nodes_only_connected":0,"account_distinct_in_cc":4,"account_number_nodes":0,"account_number_nodes_only_connected":0,"card_distinct_in_cc":4,"card_number_nodes":0,"card_number_nodes_only_connected":0}},{"v_id":"fa881c0a-adff-42d5-adf5-19de0cbe3547","v_type":"Application","attributes":{"connectedComponentID":643825664,"total_cc_nodes":2,"fraud_applications_in_cc":2,"name_distinct_in_cc":2,"name_number_nodes":0,"name_number_nodes_only_connected":0,"dob_distinct_in_cc":2,"dob_number_nodes":0,"dob_number_nodes_only_connected":0,"email_distinct_in_cc":2,"email_number_nodes":0,"email_number_nodes_only_connected":0,"phone_distinct_in_cc":4,"phone_number_nodes":0,"phone_number_nodes_only_connected":0,"address_distinct_in_cc":4,"address_number_nodes":0,"address_number_nodes_only_connected":0,"ip_distinct_in_cc":2,"ip_number_nodes":0,"ip_number_nodes_only_connected":0,"id_distinct_in_cc":4,"id_number_nodes":0,"id_number_nodes_only_connected":0,"device_distinct_in_cc":1,"device_number_nodes":0,"device_number_nodes_only_connected":0,"party_distinct_in_cc":0,"party_number_nodes":0,"party_number_nodes_only_connected":0,"account_distinct_in_cc":4,"account_number_nodes":0,"account_number_nodes_only_connected":0,"card_distinct_in_cc":4,"card_number_nodes":0,"card_number_nodes_only_connected":0}}]}]} If entity_resolution is False the incoming Application was not able to be matched with the historical Application dataset and the same respective Application should be sent to `incremental_unify_entity` query so it's eligible for matching against future Applications in near real time. -## Mock Data -The `data` folder is populated with sample data files. These files are crafted for testing and demonstration purposes. + Here is a write-up of +some of the queries along with their uses: + +--- + +### 1. Entity Resolution & Fraud Ring Construction + +**match_application_entities(...)** +Creates a `Same_Application` similarity edge if the computed score of +application similarity exceeds a given threshold by comparing applications +using weighted matches on shared PII. + +--- + +**unify_application_entities()** +Scans the applications that are linked by `Same_Application` similarity edges, +and groups them into a shared `Connected_Component` vertex, and connects each +member via `Application_In_Ring`. Pairwise similarity links are turned into +"fraud ring" clusters. + +--- + +**incremental_application_match(input JSON, weights..., threshold)** +Scores a *single* new or updated application in near real time. It parses a +JSON payload containing application attributes + PII, upserts it into the graph, and +compares it against existing applications to see if it is part of the +`Connected_Component` or an existing fraud ring. + +--- + +**incremental_application_unify(SET\)** +Given a set of Application vertices, this query walks their existing +Same_Application (if any) links to determine which applications belong together, +ensures there is a Connected_Component vertex for each group, and creates the +corresponding Application_In_Ring edges. + +--- + +**delete_all_application_cc_connections(num_of_batches, batch_id)** +Deletes all `Application_In_Ring` edges in batches. Use this before re-running +a full entity-resolution cycle so that you can rebuild communities from +scratch without dropping the whole graph. + +--- + +**delete_unused_cc_nodes(num_of_batches, batch_id)** +Cleans up `Connected_Component` vertices that no longer have any edges. +This keeps the graph lean and avoids clutter from obsolete components. + +--- + +**output_application_cc_to_file(output_file_path)** +Exports a simple mapping of `Application` → `Connected_Component` into a CSV +file. This is useful for joining graph-based communities back into downstream +systems such as your warehouse, feature store, or BI dashboards. + +--- + +### 2. Graph Features for Machine Learning + +**batch_application_cc_features(connections, output_file_path)** +Generates a CSV of connected-component features for every `Application`. +For each application’s community, it counts distinct PII nodes and how many +applications are connected via each PII type. Use this to build offline +training datasets capturing the structure and density of each fraud ring. + +--- + +**batch_application_distance_and_path(depth, output_file_path)** +For every `Application`, finds the closest fraud application reachable via +shared PII within a maximum hop distance, and writes out features such as: +degree of connection (number of hops), the specific fraud counterpart, and a +path description. These features can further be used for ML training. + +--- + +**get_application_cc_features(application, connections)** +Computes graph features for the connected component of a single Application +Returns a structured result you can use directly at scoring time, with +`connections` acting as a cap to filter out very high-degree PII hubs. + +--- + +**get_application_fraud_status(SET\)** +Given a set of application vertices, returns the `is_fraud` boolean label for +each one. + +--- + +**set_application_fraud_status(application, fraud_status)** +Updates the `is_fraud` attribute for a specific `Application`. Use this to push +investigation outcomes or model decisions back into the graph so that future +graph features and proximity queries are based on the latest fraud labels. + +--- + +### 3. Fraud Proximity & Case Investigation + +**distance_and_path_to_fraud_application(application, depth)** +Starting from a single `Application`, this query traverses through shared PII +to find any reachable fraud applications within a given hop limit. It returns +the distance in hops, the matching fraud applications, their communities, and +the path of connection. + +--- + +**distance_and_path_to_fraud_application_vis(application, min_depth, max_depth)** +Finds fraud applications reachable from the input Application between min_depth +and max_depth hops, then collects the vertices and edges on those paths. +Returns a subgraph that can be rendered in TigerGraph visualization. + +--- + +**find_shared_piis_of_two_applications(...)** +Highlights the PII elements shared between two selected applications, +such as common device, IP, phone, or address. + +--- + +**get_top_k_connected_components_by_num_applications(top_k)** +Returns the `Connected_Component` communities with the most applications attached, +along with their member applications. Use this to find your largest rings. + +--- + +### 4. Portfolio & Product Insights + +**get_num_applications_by_app_status()** +Counts applications by their status (e.g., PENDING, APPROVED, DECLINED). +View your onboarding pipeline and how many applications are sitting in each decision bucket. + +--- + +**get_num_applications_by_fraud_status()** +Returns the total number of fraudulent versus legitimate applications. + +--- + +**get_top_k_products_by_num_applications(top_k, input_application_fraud_status, input_application_status)** +Returns the top products ranked by number of connected applications, +optionally filtered by application fraud status and/or application status. + +--- + +**get_top_k_products_by_num_applications_with_other(top_k, input_application_fraud_status, input_application_status)** +Provides a “top-k + Other” breakdown: the top products by number of applications +plus an aggregated “Other Products” bucket. + +--- + +### 5. Business Impact & Uplift Analytics + +These queries work together to quantify how much value you get from using graph +features (TigerGraph) versus your baseline ML model or rules. They take +confusion-matrix counts *with* and *without* graph features and turn them +into uplift and dollar amounts. + +--- + +**insights_get_application_count_by_binary_classification(...)** +Takes confusion-matrix counts for two model runs—typically a baseline model +and a model augmented with graph features—and returns a side-by-side table +plus totals. + +--- + +**insights_get_binary_classification_ratios(...)** +Computes standard model performance metrics (precision, recall, specificity, +negative predictive value) for both scenarios and expresses them as percentages. + +--- + +**insights_get_detected_fraud_amount(no_tg_true_positive, tg_true_positive, avg_loss_per_app_fraud)** +Given an average monetary loss per fraudulent application, this query converts +true positives into “detected fraud dollars” with and without graph features. + +--- + +**insights_get_missed_fraud_amount(no_tg_false_negative, tg_false_negative, avg_loss_per_app_fraud)** +Calculates how much fraud slips through undetected in each scenario. This +highlights the reduction in missed fraud when graph features improve recall. + +--- + +**insights_get_net_gain_numbers_and_percentages(...)** +Computes the *counts* and *percentages* of: additional fraud blocked, net +reduction in missed fraud, and net reduction in false positives. + +--- + +### 6. Metric Utility Functions + +**utils_get_precision(true_positive, false_positive)** +**utils_get_recall(true_positive, false_negative)** +**utils_get_specificity(true_negative, false_positive)** +**utils_get_negative_predictive_value(true_negative, false_negative)** + +--- + + +## Run an Example Query + +Once the schema and data are loaded, you can inspect graph-based features for a single application using `get_application_cc_features`. + +1. Pick an `Application` ID from `data/application_fraud.csv` (for example `APP_10001`). +2. From the GSQL shell, run: + +```bash +gsql 'USE GRAPH Application_Fraud RUN QUERY get_application_cc_features("7da7048b-16f3-46f0-bdf4-a1ddd5c8005a", 25000)' +``` + + +This returns a JSON-style record with the application’s connected-component +ID, fraud count in its ring, and PII-based graph feature that you can feed +directly into an ML model or rules engine. + +## Using Your Own Data + +By default, this solution kit loads **mock application and product data** from a +TigerGraph-hosted public S3 bucket, using the loading job defined in +`loading_job/load_data.gsql`. The two CSV files it loads are: + +- `application_fraud.csv` +- `products.csv` + +TigerGraph supports multiple loading options. For detailed, up-to-date examples, +please refer to the official documentation: + +- **Data loading overview** – supported sources and general workflow + https://docs.tigergraph.com/tigergraph-server/4.2/data-loading/data-loading-overview + +- **Data loading entry page** – index of all loading methods (local files, cloud storage, warehouses, Spark, etc.) + https://docs.tigergraph.com/tigergraph-server/4.2/data-loading/ + +- **Load data from cloud storage** – step-by-step guide for Amazon S3, Google Cloud Storage, and Azure Blob Storage + https://docs.tigergraph.com/tigergraph-server/4.2/data-loading/load-from-cloud + + + +## Resetting the Environment + +Sometimes you’ll want to wipe the graph data and start fresh — for example, when +switching to a new dataset or after updating the schema and loading logic. + +This repository provides a utility query to delete all data, and a separate +reset script for more advanced workflows. + +To run it: + +```bash +gsql reset/reset.gsql +``` ## ML Model and Insights Application +You can find the instructions for training the ML model and its performance metrics +within the documentation located in the model folder. The Insights Applications +are available as JSON files in the meta folder. -You can find the instructions for training the ML model and its performance metrics within the documentation located in the model folder. The Insights Applications are available as JSON files in the meta folder. diff --git a/financial_crime/application_fraud/schema/create_schema.gsql b/financial_crime/application_fraud/schema/create_schema.gsql index 6df55ace..11cb39c7 100644 --- a/financial_crime/application_fraud/schema/create_schema.gsql +++ b/financial_crime/application_fraud/schema/create_schema.gsql @@ -1,2 +1,171 @@ -CREATE GRAPH Application_Fraud(Phone, Email, Address, IP, Device, City, Country, State, Full_Name, Zipcode, County, ID, Party, Connected_Component, DOB, Application, Product, Account, Card, Has_Address, Has_ID, Has_IP, Has_Device, Has_Phone, Has_Email, Located_In, Assigned_To_County, Located_In_State, Located_In_Country, Same_As, Entity_In_Ring, Has_DOB, Has_Full_Name, Application_Has_Full_Name, Application_Has_Phone, Application_Has_Email, Application_Has_DOB, Application_Has_ID, Application_Has_Device, Application_Has_IP, Same_Application, Application_Has_Address, Application_In_Ring, Application_Has_Product, Application_Has_Account, Application_Has_Party, Assigned_To, Application_Has_Card) +USE GLOBAL + +// (PII) +CREATE VERTEX Phone ( + PRIMARY_ID phone_number STRING +) WITH primary_id_as_attribute="true" + +CREATE VERTEX Device ( + PRIMARY_ID id STRING, + is_blocked BOOL +) WITH primary_id_as_attribute="true" + +CREATE VERTEX Full_Name ( + PRIMARY_ID name STRING +) WITH primary_id_as_attribute="true" + +CREATE VERTEX Card ( + PRIMARY_ID card_number INT, + is_fraud INT, + pagerank FLOAT, + c_id INT, + c_size INT, + occupation STRING +) WITH primary_id_as_attribute="true" + +CREATE VERTEX DOB ( + PRIMARY_ID dob STRING +) WITH primary_id_as_attribute="true" + +// Products (cards, loans, etc.) +CREATE VERTEX Product ( + PRIMARY_ID id STRING, + name STRING, + description STRING, + type_of STRING +) WITH primary_id_as_attribute="true" + +// Connected component / fraud ring ID +CREATE VERTEX Connected_Component ( + PRIMARY_ID id INT +) WITH primary_id_as_attribute="true" + +// Account entity +CREATE VERTEX Account ( + PRIMARY_ID id STRING, + create_Time DATETIME, + is_fraud INT, + account_type STRING, + account_level STRING, + com_size INT, + pagerank FLOAT, + shortest_path_length INT, + ip_collision INT, + fraud_ip INT, + device_collision INT, + fraud_device INT, + trans_in_mule_ratio FLOAT, + trans_out_mule_ratio FLOAT, + mule_cnt INT, + com_id INT, +) WITH primary_id_as_attribute="true" + +CREATE VERTEX State ( + PRIMARY_ID id STRING +) WITH primary_id_as_attribute="true" + +CREATE VERTEX IP ( + PRIMARY_ID id STRING, + is_blocked BOOL +) WITH primary_id_as_attribute="true" + +CREATE VERTEX Address ( + PRIMARY_ID address STRING +) WITH primary_id_as_attribute="true" + +CREATE VERTEX Email ( + PRIMARY_ID email STRING +) WITH primary_id_as_attribute="true" + +CREATE VERTEX Country ( + PRIMARY_ID country STRING +) WITH primary_id_as_attribute="true" + +CREATE VERTEX City ( + PRIMARY_ID id STRING, + city STRING, + population INT +) WITH primary_id_as_attribute="true" + +CREATE VERTEX County ( + PRIMARY_ID id STRING +) WITH primary_id_as_attribute="true" + +CREATE VERTEX Zipcode ( + PRIMARY_ID id STRING +) WITH primary_id_as_attribute="true" + +// Party (person / organization) +CREATE VERTEX Party ( + PRIMARY_ID id STRING, + is_fraud INT, + gender STRING, + dob DATETIME, + party_type STRING, + name STRING, + created_at DATETIME +) WITH primary_id_as_attribute="true" + +CREATE VERTEX ID ( + PRIMARY_ID id STRING, + id_type STRING +) WITH primary_id_as_attribute="true" + +// Credit application +CREATE VERTEX Application ( + PRIMARY_ID id STRING, + created_at DATETIME, + status STRING, + line_of_credit FLOAT, + annual_percentage_rate FLOAT, + is_fraud BOOL +) WITH primary_id_as_attribute="true" + + +CREATE UNDIRECTED EDGE Has_Address (FROM Party, TO Address) +CREATE UNDIRECTED EDGE Has_ID (FROM Party, TO ID) +CREATE UNDIRECTED EDGE Has_IP (FROM Party, TO IP) +CREATE UNDIRECTED EDGE Has_Device (FROM Party, TO Device) +CREATE UNDIRECTED EDGE Has_Phone (FROM Party, TO Phone) +CREATE UNDIRECTED EDGE Has_Email (FROM Party, TO Email) +CREATE UNDIRECTED EDGE Has_DOB (FROM Party, TO DOB) +CREATE UNDIRECTED EDGE Has_Full_Name (FROM Party, TO Full_Name) + +CREATE UNDIRECTED EDGE Application_Has_Full_Name (FROM Application, TO Full_Name) +CREATE UNDIRECTED EDGE Application_Has_Phone (FROM Application, TO Phone) +CREATE UNDIRECTED EDGE Application_Has_Email (FROM Application, TO Email) +CREATE UNDIRECTED EDGE Application_Has_DOB (FROM Application, TO DOB) +CREATE UNDIRECTED EDGE Application_Has_ID (FROM Application, TO ID) +CREATE UNDIRECTED EDGE Application_Has_Device (FROM Application, TO Device) +CREATE UNDIRECTED EDGE Application_Has_IP (FROM Application, TO IP) +CREATE UNDIRECTED EDGE Application_Has_Address (FROM Application, TO Address) +CREATE UNDIRECTED EDGE Application_Has_Product (FROM Application, TO Product) +CREATE UNDIRECTED EDGE Application_Has_Account (FROM Application, TO Account) +CREATE UNDIRECTED EDGE Application_Has_Party (FROM Party, TO Application) +CREATE UNDIRECTED EDGE Application_Has_Card (FROM Application, TO Card) + + +CREATE UNDIRECTED EDGE Assigned_To (FROM Address|Zipcode, TO Zipcode|City) +CREATE UNDIRECTED EDGE Assigned_To_County (FROM Zipcode, TO County) +CREATE UNDIRECTED EDGE Located_In (FROM Address|City, TO City|State) +CREATE UNDIRECTED EDGE Located_In_State (FROM County, TO State) +CREATE UNDIRECTED EDGE Located_In_Country (FROM State, TO Country) + + +CREATE UNDIRECTED EDGE Application_In_Ring (FROM Application, TO Connected_Component) +CREATE UNDIRECTED EDGE Entity_In_Ring (FROM Connected_Component, TO Party) +CREATE UNDIRECTED EDGE Same_As (FROM Party, TO Party, score DOUBLE) +CREATE UNDIRECTED EDGE Same_Application (FROM Application, TO Application, score DOUBLE) + + +CREATE GRAPH Application_Fraud(Phone, Email, Address, IP, Device, City, +Country,State, Full_Name, Zipcode, County, ID, Party, Connected_Component, DOB, +Application, Product, Account, Card, Has_Address, Has_ID, Has_IP, Has_Device, +Has_Phone, Has_Email, Located_In, Assigned_To_County, Located_In_State, +Located_In_Country, Same_As, Entity_In_Ring, Has_DOB, Has_Full_Name, +Application_Has_Full_Name, Application_Has_Phone, Application_Has_Email, +Application_Has_DOB, Application_Has_ID, Application_Has_Device, +Application_Has_IP, Same_Application, Application_Has_Address, +Application_In_Ring, Application_Has_Product, Application_Has_Account, +Application_Has_Party, Assigned_To, Application_Has_Card) set exit_on_error = "false" From c388d86e4fc642e9aae9cd7b779a40352a69e25c Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Thu, 27 Nov 2025 10:19:57 +0530 Subject: [PATCH 16/55] fixed a comment in unify_application_entities file --- .../queries/unify_application_entities.gsql | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/financial_crime/application_fraud/queries/unify_application_entities.gsql b/financial_crime/application_fraud/queries/unify_application_entities.gsql index 7be94454..735dc484 100644 --- a/financial_crime/application_fraud/queries/unify_application_entities.gsql +++ b/financial_crime/application_fraud/queries/unify_application_entities.gsql @@ -7,18 +7,18 @@ CREATE OR REPLACE DISTRIBUTED QUERY unify_application_entities() { start = {Application.*}; - # Initialize: Label each vertex with its own internal ID + // Initialize: Label each vertex with its own internal ID S = SELECT x FROM start:x POST-ACCUM x.@cc_id = getvid(x) ; - # Propagate smaller internal IDs until no more ID changes can be done + // Propagate smaller internal IDs until no more ID changes can be done WHILE (S.size()>0) DO S = SELECT t FROM S:s -(Same_Application:e)- :t ACCUM t.@cc_id += s.@cc_id // If s has smaller id than t, copy the id to t - HAVING t.@cc_id != t.@cc_id'; + HAVING t.@cc_id != t.@cc_id; END; result = SELECT t From d549a96ea7914e73781d679b50c69c00cfdffdc9 Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Mon, 1 Dec 2025 11:25:46 +0530 Subject: [PATCH 17/55] added insights application to entity resolution connected customer kit --- .../entity_resolution/meta/insights.json | 332 ++++++++++++++++++ financial_crime/application_fraud/README.md | 4 +- 2 files changed, 333 insertions(+), 3 deletions(-) create mode 100644 connected_customer/entity_resolution/meta/insights.json diff --git a/connected_customer/entity_resolution/meta/insights.json b/connected_customer/entity_resolution/meta/insights.json new file mode 100644 index 00000000..4ecaec57 --- /dev/null +++ b/connected_customer/entity_resolution/meta/insights.json @@ -0,0 +1,332 @@ +{ + "defaultGraph": "Entity_Resolution", + "iconURL": "/insights/static/media/airplay.245a1409e0f415cae4a5c62546967039.svg", + "id": "tPswFHGrTWpL5A64mjdhtV", + "pageConfigSeparated": true, + "pages": [ + { + "chartMap": { + "1sDFWDdKeFuPcv1HJSyFLt": { + "chartSettings": { + "inputStates": [ + { + "dataType": "number", + "id": "input_rW8fa6DcKRSe18pcNWvenV", + "label": "Top N Clusters", + "name": "topN", + "placeholder": "Top N", + "settings": { + "max": "10", + "min": "1", + "step": "1" + }, + "widgetType": "Input" + } + ] + }, + "graphName": "Entity_Resolution", + "hideWidgetName": false, + "id": "1sDFWDdKeFuPcv1HJSyFLt", + "patternLimit": 5, + "query": "", + "queryType": "pattern", + "refreshRate": 0, + "searchPattern": [], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "TopN", + "type": "Inputs", + "version": "1764560443931821966" + }, + "8L4MKHtNiBav9ufE6V7U7Z": { + "chartSettings": {}, + "graphName": "Entity_Resolution", + "hideWidgetName": false, + "id": "8L4MKHtNiBav9ufE6V7U7Z", + "patternLimit": 5, + "query": "INTERPRET QUERY() FOR GRAPH Entity_Resolution {\n\n SumAccum @@entity_cnt;\n SumAccum @@cc_cnt;\n\n // Count all Entity vertices\n _entities =\n SELECT s\n FROM Entity:s\n ACCUM @@entity_cnt += 1;\n\n // Count all Connected_Component vertices\n _components =\n SELECT c\n FROM Connected_Component:c\n ACCUM @@cc_cnt += 1;\n\n // Compute duplicates resolved = entities - components\n PRINT (@@entity_cnt - @@cc_cnt) AS duplicates_resolved;\n}\n", + "queryType": "interactive", + "refreshRate": 0, + "searchPattern": [], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "Entities Resolved", + "type": "value", + "version": "1764562711877287648" + }, + "9PZnMP9LiZrpDkRR3P7Gh4": { + "chartSettings": { + "category": [ + { + "id": "bucket", + "type": "string" + } + ], + "showColumns": [ + { + "isChecked": true, + "name": "bucket" + }, + { + "isChecked": true, + "name": "freq" + } + ], + "tableColumns": [ + { + "id": "Matched pattern", + "isChecked": "true", + "label": "" + }, + { + "children": [], + "id": "Vertices", + "isChecked": "true", + "isExpanded": true, + "label": "Vertices" + }, + { + "children": [], + "id": "Edges", + "isChecked": "true", + "isExpanded": false, + "label": "Edges" + } + ], + "tableHeaders": [ + "bucket", + "freq" + ], + "tableIndex": 0, + "value": [ + { + "id": "freq", + "type": "number" + } + ] + }, + "graphName": "Entity_Resolution", + "hideWidgetName": false, + "id": "9PZnMP9LiZrpDkRR3P7Gh4", + "patternLimit": 5, + "query": "INTERPRET QUERY() FOR GRAPH Entity_Resolution {\n\n // Per-component size\n SumAccum @size;\n\n // Global bucket counters\n SumAccum @@size1;\n SumAccum @@size2;\n SumAccum @@size3_5;\n SumAccum @@size6_10;\n SumAccum @@size11_20;\n SumAccum @@size21p;\n\n // Compute size for each Connected_Component\n Seed = { Connected_Component.* };\n\n comps =\n SELECT c\n FROM Seed:c -(Entity_In_Ring:e)- Entity:ent\n ACCUM c.@size += 1;\n\n // Bucket by size\n agg =\n SELECT c\n FROM comps:c\n POST-ACCUM\n CASE\n WHEN c.@size == 1 THEN\n @@size1 += 1\n WHEN c.@size == 2 THEN\n @@size2 += 1\n WHEN c.@size >= 3 AND c.@size <= 5 THEN\n @@size3_5 += 1\n WHEN c.@size >= 6 AND c.@size <= 10 THEN\n @@size6_10 += 1\n WHEN c.@size >= 11 AND c.@size <= 20 THEN\n @@size11_20 += 1\n ELSE\n @@size21p += 1\n END;\n\n // Instead of one row with 6 columns,\n // print 6 rows with 2 columns: (bucket, freq)\n PRINT \"Size = 1\" AS bucket, @@size1 AS freq;\n PRINT \"Size = 2\" AS bucket, @@size2 AS freq;\n PRINT \"Size 3–5\" AS bucket, @@size3_5 AS freq;\n PRINT \"Size 6–10\" AS bucket, @@size6_10 AS freq;\n PRINT \"Size 11–20\" AS bucket, @@size11_20 AS freq;\n PRINT \"Size 21+\" AS bucket, @@size21p AS freq;\n}\n", + "queryType": "interactive", + "refreshRate": 0, + "searchPattern": [], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "Cluster Size Distribution", + "type": "table", + "version": "1764566105487612322" + }, + "rFHwVwsv15H4BKUi13uU5F": { + "chartSettings": { + "markdown": "## Entity Resolution Run Guide\n\nIf the charts and tables on this page look **empty** or show **0 Connected Components**, it means the Entity Resolution process has not been run yet on this graph.\n\nFollow this order when running ER on the **Entity_Resolution** graph:\n\n---\n\n### Load / refresh the base data (one-time or as needed)\n\nThese steps are usually done once when the solution kit is installed, or whenever you refresh the dataset:\n\n1. **Load entities and PII**\n\n - `RUN LOADING JOB load_data_to_er_graph`\n\n2. **Load MinHash hashes for fuzzy matching (email, name, phone)**\n\n - `RUN LOADING JOB load_hash_to_er_graph`\n\nIf the **Entity** count is greater than 0, your base data is loaded.\n\n---\n\n### Run the Entity Resolution Pipeline\n\nFor a fresh run (no Connected_Component data yet), you typically do:\n\n**Match entities (create Same_As edges)** \n Uses MinHash + Jaro–Winkler + PII weights to score similarity. \n\n ```gsql\n RUN QUERY match_entities();\n```\n\n**Unify entities into Connected Components**\n\nAssigns every `Entity` to a `Connected_Component` community based on `Same_As` edges.\n\n```gsql\nRUN QUERY unify_entities();\n```\nThis should fill up the Connected_Component vertices.\n" + }, + "graphName": "Entity_Resolution", + "hideWidgetName": false, + "id": "rFHwVwsv15H4BKUi13uU5F", + "patternLimit": 5, + "query": "", + "queryType": "pattern", + "refreshRate": 0, + "searchPattern": [], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "StartUp Notes", + "type": "markdown", + "version": "1764561962840319249" + }, + "sXc7DZ3wwqkqei21z36X2z": { + "chartSettings": {}, + "graphName": "Entity_Resolution", + "hideWidgetName": false, + "id": "sXc7DZ3wwqkqei21z36X2z", + "patternLimit": 5, + "query": "INTERPRET QUERY(INT topN) FOR GRAPH Entity_Resolution {\n\n // Count how many Entity vertices belong to each Connected_Component\n SumAccum @size;\n\n Seed = { Connected_Component.* };\n\n Result =\n SELECT c\n FROM Seed:c -(Entity_In_Ring:e)- Entity:ent\n ACCUM c.@size += 1\n ORDER BY c.@size DESC\n LIMIT topN;\n\n PRINT Result;\n}\n", + "queryType": "interactive", + "refreshRate": 0, + "searchPattern": [], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "Top N Clusters", + "type": "table", + "version": "1764323817053623261" + }, + "whCPA74bT5KKghF4aUYAxX": { + "chartSettings": { + "showColumns": [ + { + "isChecked": true, + "name": "avg_score" + } + ], + "tableColumns": [ + { + "id": "Matched pattern", + "isChecked": "true", + "label": "" + }, + { + "children": [], + "id": "Vertices", + "isChecked": "true", + "isExpanded": true, + "label": "Vertices" + }, + { + "children": [], + "id": "Edges", + "isChecked": "true", + "isExpanded": false, + "label": "Edges" + } + ], + "tableHeaders": [ + "avg_score" + ] + }, + "graphName": "Entity_Resolution", + "hideWidgetName": false, + "id": "whCPA74bT5KKghF4aUYAxX", + "patternLimit": 5, + "query": "INTERPRET QUERY() FOR GRAPH Entity_Resolution {\n\n // 1) Compute size (number of Entities) for each Connected_Component\n SumAccum @size;\n SetAccum @@same_as_edges; // collect Same_As edges explicitly\n\n comps =\n SELECT c\n FROM Connected_Component:c -(Entity_In_Ring:er)- Entity:ent\n ACCUM c.@size += 1;\n\n // 2) Choose one non-singleton component (size > 1), e.g. the largest\n cc_to_show =\n SELECT c\n FROM comps:c\n WHERE c.@size > 1\n ORDER BY c.@size DESC\n LIMIT 1;\n\n // 3) Get all Entities in that component\n ents =\n SELECT ent\n FROM cc_to_show:c -(Entity_In_Ring:er)- Entity:ent;\n\n // 4) Collect Same_As edges between those Entities\n result_ents =\n SELECT s\n FROM ents:s -(Same_As:e)- ents:t\n ACCUM\n @@same_as_edges += e;\n\n // 5) Print both vertices and edges so the Graph widget can render them\n PRINT result_ents;\n PRINT @@same_as_edges;\n}\n", + "queryType": "interactive", + "refreshRate": 0, + "searchPattern": [], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "Resolved Entity Graph", + "type": "internal-graph", + "version": "1764567808410839821" + } + }, + "globalParameters": { + "topN": { + "id": "input_rW8fa6DcKRSe18pcNWvenV", + "name": "topN", + "type": "NUMBER", + "value": 4 + } + }, + "iconURL": "/insights/static/media/brain-circuit.29a9b2394d2ecb53df1b9e1c861720be.svg", + "id": "6xa7y24BQaKkAS2ddKMEH6", + "isDetail": true, + "isNew": false, + "layouts": { + "md": [ + { + "h": 12, + "i": "1sDFWDdKeFuPcv1HJSyFLt", + "moved": false, + "static": false, + "w": 2, + "x": 4, + "y": 0 + }, + { + "h": 12, + "i": "8L4MKHtNiBav9ufE6V7U7Z", + "moved": false, + "static": false, + "w": 2, + "x": 10, + "y": 0 + }, + { + "h": 15, + "i": "9PZnMP9LiZrpDkRR3P7Gh4", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 12 + }, + { + "h": 12, + "i": "rFHwVwsv15H4BKUi13uU5F", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 0 + }, + { + "h": 12, + "i": "sXc7DZ3wwqkqei21z36X2z", + "moved": false, + "static": false, + "w": 4, + "x": 6, + "y": 0 + }, + { + "h": 15, + "i": "whCPA74bT5KKghF4aUYAxX", + "moved": false, + "static": false, + "w": 5, + "x": 4, + "y": 12 + } + ], + "xs": [ + { + "h": 12, + "i": "rFHwVwsv15H4BKUi13uU5F", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 0 + }, + { + "h": 12, + "i": "1sDFWDdKeFuPcv1HJSyFLt", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 12 + }, + { + "h": 12, + "i": "sXc7DZ3wwqkqei21z36X2z", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 24 + }, + { + "h": 12, + "i": "8L4MKHtNiBav9ufE6V7U7Z", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 36 + }, + { + "h": 15, + "i": "9PZnMP9LiZrpDkRR3P7Gh4", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 48 + }, + { + "h": 12, + "i": "whCPA74bT5KKghF4aUYAxX", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 63 + } + ] + }, + "title": "Overview", + "version": "1764567463509216731", + "weight": 10 + } + ], + "title": "New Application", + "userRoleForApp": "owner", + "version": "1764322614974303069" +} \ No newline at end of file diff --git a/financial_crime/application_fraud/README.md b/financial_crime/application_fraud/README.md index b73627f8..ed83909b 100644 --- a/financial_crime/application_fraud/README.md +++ b/financial_crime/application_fraud/README.md @@ -492,7 +492,5 @@ gsql reset/reset.gsql ``` ## ML Model and Insights Application -You can find the instructions for training the ML model and its performance metrics -within the documentation located in the model folder. The Insights Applications -are available as JSON files in the meta folder. + The Insights Applications are available as JSON files in the meta folder. From 9ecac00d7af85646879decc6d5b41e0d2c85ff64 Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Wed, 3 Dec 2025 19:37:12 +0530 Subject: [PATCH 18/55] readme update for customer_360, prod_rec and entity_res --- connected_customer/customer_360/README.md | 18 ++- .../entity_resolution/README.md | 142 +++++++++++++----- .../product_recommendations/README.md | 9 +- 3 files changed, 121 insertions(+), 48 deletions(-) diff --git a/connected_customer/customer_360/README.md b/connected_customer/customer_360/README.md index c82180be..29513135 100644 --- a/connected_customer/customer_360/README.md +++ b/connected_customer/customer_360/README.md @@ -57,9 +57,9 @@ and analytics use cases, or extend the graph with your own entities and interact and Engagement events, defined in `schema/1_create_schema.gsql`. - **Sample Customer 360 dataset** - Mock CSV data for individuals, accounts, and sessions, stored in the - `data/` directory for a visualization and column mapping, but the actual data - is loaded from a public S3 bucket. + Mock CSV data for individuals, accounts, and sessions is stored in the data/ + directory for reference and visualization. At runtime, the application + actually loads this data from a public S3 bucket. - **End-to-end loading job** A single loading job in `loading_job/load_data.gsql` that: @@ -277,21 +277,25 @@ If your data lives in cloud storage or another external system (for example, Amazon S3, Google Cloud Storage, Azure Blob Storage, or a data warehouse), you can keep the `CREATE DATA_SOURCE` pattern from the sample and adapt it to your environment. +If your data is stored on the TigerGraph server’s local filesystem instead, you can +skip `CREATE DATA_SOURCE` entirely and point `DEFINE FILENAME` directly to your local paths. + TigerGraph supports multiple loading options. For detailed, up-to-date examples, please refer to the official documentation: - **Data loading overview** – supported sources and general workflow - https://docs.tigergraph.com/tigergraph-server/4.2/data-loading/data-loading-overview + https://docs.tigergraph.com/tigergraph-server/current/data-loading/data-loading-overview - **Data loading entry page** – index of all loading methods (local files, cloud storage, warehouses, Spark, etc.) - https://docs.tigergraph.com/tigergraph-server/4.2/data-loading/ + https://docs.tigergraph.com/tigergraph-server/current/data-loading/ - **Load data from cloud storage** – step-by-step guide for Amazon S3, Google Cloud Storage, and Azure Blob Storage - https://docs.tigergraph.com/tigergraph-server/4.2/data-loading/load-from-cloud + https://docs.tigergraph.com/tigergraph-server/current/data-loading/load-from-cloud At a high level, the steps are: -1. Define a `DATA_SOURCE` object that matches your storage system and credentials +1. For cloud or external systems, define a `DATA_SOURCE` object that matches your storage system and credentials. + For local files on the TigerGraph server, you can omit `DATA_SOURCE`. 2. Update the `DEFINE FILENAME` lines in `load_all` so they point to your cloud URIs 3. Keep the `LOAD` blocks the same unless your column structure changes—if it does, adjust the `VALUES(...)` mappings so each vertex/edge attribute receives the correct column. diff --git a/connected_customer/entity_resolution/README.md b/connected_customer/entity_resolution/README.md index 96d0cd79..7d7f826c 100644 --- a/connected_customer/entity_resolution/README.md +++ b/connected_customer/entity_resolution/README.md @@ -1,8 +1,16 @@ # Narratives -Entity Resolution is the cornerstone of our approach to addressing the challenges posed by the vast array of data in today's interconnected digital landscape. Specifically tailored to the Connected Customer use-case, our Entity Resolution solution excels at accurately identifying and linking entities across disparate datasets, focusing on Personally Identifiable Information (PII) such as email addresses, phone numbers, and physical addresses. - -By seamlessly integrating PII from various sources, businesses can create a unified view of each customer. This comprehensive understanding empowers organizations to personalize marketing campaigns, tailor product recommendations, and optimize customer service interactions, fostering stronger customer relationships and driving business success. +Entity Resolution is the cornerstone of our approach to addressing the +challenges posed by the vast array of data in today's interconnected digital +landscape. Specifically tailored to the Connected Customer use-case, our +Entity Resolution solution excels at accurately identifying and linking +entities across disparate datasets, focusing on Personally Identifiable +Information (PII) such as email addresses, phone numbers, and physical +addresses. By seamlessly integrating PII from various sources, businesses can +create a unified view of each customer. This comprehensive understanding +empowers organizations to personalize marketing campaigns, tailor product +recommendations, and optimize customer service interactions, fostering stronger +customer relationships and driving business success. # Components @@ -39,21 +47,30 @@ The default batching parameters allows the user to execute this query in one bat By setting the params differently the user can execute this query in multiple batches: -To execute this process in 50 hard batches, we need to run this query 50 times while incremeting the `batch_id` value each time. -In that case, we'll set the value of `num_of_batches` to `50` each call, but the value of `batch_id` will initially be `0` and we'll increment it each run until `49`. -Instead of running this query 50 times manually from the UI, we can run this query 50 times as a REST endpoint from Linux shell: +To execute this process in 50 hard batches, we need to run this query 50 times +while incremeting the `batch_id` value each time. +In that case, we'll set the value of `num_of_batches` to `50` each call, but +the value of `batch_id` will initially be `0` and we'll increment it each run +until `49`. +Instead of running this query 50 times manually from the UI, we can run this +query 50 times as a REST endpoint from Linux shell: `for i in {0..49};do curl -X GET "http://:9000/query/ER_2023/delete_all_cc_connections?num_of_batches=50&batch_id=${i}";done;` -**Note**: By calling this query multiple times and deleting data in hard batches it allows us to delete data in parallel, and make the deltion process run smoother and substantially faster on high scale. +**Note**: By calling this query multiple times and deleting data in hard +batches it allows us to delete data in parallel, and make the deltion process +run smoother and substantially faster on high scale. ### Step 2: Insert Edges between Matching Entities -After loading historical Entity and PII data and configuring wcc weights for matching, run `match_entities` to perform matching on the entire graph. +After loading historical Entity and PII data and configuring wcc weights for +matching, run `match_entities` to perform matching on the entire graph. -The `match_entities` query creates `Same_As` edges (similarity edges) between Entity vertices that match. +The `match_entities` query creates `Same_As` edges (similarity edges) between +Entity vertices that match. -This query has weights for each PII attribute and threshold parameters used by the weighted WCC matching process: +This query has weights for each PII attribute and threshold parameters used by +the weighted WCC matching process: - FLOAT customer_has_birthdate_weight = 0.5 - FLOAT customer_has_email_address_weight = 0.5 @@ -67,76 +84,123 @@ This query has weights for each PII attribute and threshold parameters used by t - FLOAT customer_has_source_customer_id_weight = 0.5 - FLOAT threshold = 1.0 -When an Entity matches another Entity through shared PII attribute(s) the respective weight(s) will accumulate and any accumulated weights equal or over threshold is considered a match. For example if a phone number has a weight of 1.0 and the threshold is 1.0 then any single matching phone between any Entities will cause those respective Entities to be put into the same Connected_Component community. If phone number has a weight of 0.5 and threshold is 1.0 then it would require at least two shared phones between any Entities to match etc. Default weights and thresholds are assigned to these queries but should be adapted if there are different business rules for matching required to satisfy. +When an Entity matches another Entity through shared PII attribute(s) the +respective weight(s) will accumulate and any accumulated weights equal or over +threshold is considered a match. For example if a phone number has a weight of +1.0 and the threshold is 1.0 then any single matching phone between any Entities +will cause those respective Entities to be put into the same Connected_Component +community. If phone number has a weight of 0.5 and threshold is 1.0 then it +would require at least two shared phones between any Entities to match etc. +Default weights and thresholds are assigned to these queries but should be +adapted if there are different business rules for matching required to satisfy. This query also has batching parameters: - INT num_of_source_batches = 10 - INT num_of_target_batches = 1 -Setting `num_of_source_batches` to a **higher number** can potentially help **reduce memory consumption** at runtime, but the query will take **longer to execute**. -Setting `num_of_target_batches` has the same pros/cons as `num_of_source_batches`, while both pros and cons are to a **greater extent**. +Setting `num_of_source_batches` to a **higher number** can potentially help +**reduce memory consumption** at runtime, but the query will take **longer to execute**. +Setting `num_of_target_batches` has the same pros/cons as `num_of_source_batches`, +while both pros and cons are to a **greater extent**. This query also has degree limitation parameters: - INT pii_low_connections_limit = 100 - INT pii_high_connections_limit = 25000 -Setting `pii_low_connections_limit` to a **higher number** will allow us to **detect more matches**, but **the risk is an exponential increase in memory consumption and computation time**. -Setting `pii_high_connections_limit` to a **higher number** will allow us to **detect more matches** while there is **no memory consumption penalty**, but the query will take **longer to execute**. - -The logic behind these parameters determines if a pair of entities will be evaluated for a potential match or not, depending on the connectivity of their shared PIIs. If the query finds at least one low-connectivity PII connecting a pair of entities (while the connectivity limit is determined by `pii_low_connections_limit`), the query will then also include all other PIIs shared between the two entities in the decison-making process (as long as these other PIIs' degree does not exeed the value set by `pii_high_connections_limit`). - -**Note**: Fine-tuning of the parameters in this query allows the process to identify matches more accuratly while also taking computation time and memory efficiency into account. +Setting `pii_low_connections_limit` to a **higher number** will allow us to +**detect more matches**, but **the risk is an exponential increase in memory +consumption and computation time**. +Setting `pii_high_connections_limit` to a **higher number** will allow us to +**detect more matches** while there is **no memory consumption penalty**, but +the query will take **longer to execute**. + +The logic behind these parameters determines if a pair of entities will be evaluated +for a potential match or not, depending on the connectivity of their shared +PIIs. If the query finds at least one low-connectivity PII connecting a pair of +entities (while the connectivity limit is determined by `pii_low_connections_limit`), +the query will then also include all other PIIs shared between the two entities +in the decison-making process (as long as these other PIIs' degree does not +exeed the value set by `pii_high_connections_limit`). + +**Note**: Fine-tuning of the parameters in this query allows the process to +identify matches more accuratly while also taking computation time and memory +efficiency into account. This query also has a timestamp parameter: - DATETIME compute_entities_after_date = to_datetime("1970-01-01 00:00:00") -The `compute_entities_after_date` defaults to the minimum DATETIME value, which will make this query execute on all the entities in the graph. -If this parameter is set to another timestamp, then this query will calculate similarities only for the newer entities inserted to the system after this time (it will compare these entities to eachother and also compare them to the older entities, but it won't compare older entities to one another). -This parameter's value could be set to the timestamp we got from the `unify_entities` query after we executed Entity Resolution in the previous time. +The `compute_entities_after_date` defaults to the minimum DATETIME value, +which will make this query execute on all the entities in the graph. +If this parameter is set to another timestamp, then this query will calculate +similarities only for the newer entities inserted to the system after this time +(it will compare these entities to eachother and also compare them to the older +entities, but it won't compare older entities to one another). +This parameter's value could be set to the timestamp we got from the +`unify_entities` query after we executed Entity Resolution in the previous time. ### Step 3: Form Communities -Run the `unify_entities` query to associate all matched Entities from the previous step into a Connected_Component community. +Run the `unify_entities` query to associate all matched Entities from the +previous step into a Connected_Component community. Subsequent queries can be run following the completion of the aforementioned two. -This query's output includes a timestamp we can pass into the `match_entities` query as the `compute_entities_after_date` parameter the next time we execute the Entity Resolution process. +This query's output includes a timestamp we can pass into the `match_entities` +query as the `compute_entities_after_date` parameter the next time we execute +the Entity Resolution process. ### Step 4 (Optional): Clear Out Remaining Disconnected Nodes of Connected Components -**Note**: This step **can be skipped** if running the Entity Resolution process for **the first time** on a graph. +**Note**: This step **can be skipped** if running the Entity Resolution +process for **the first time** on a graph. -Run the `delete_unused_cc_nodes` query to delete the unused `Connected_Component` vertices that are not connected to anything as a result of re-running the Entity Resolution process. +Run the `delete_unused_cc_nodes` query to delete the unused `Connected_Component` +vertices that are not connected to anything as a result of re-running the Entity +Resolution process. The default batching parameters allows the user to execute this query in one batch: - INT num_of_batches = 1 - INT batch_id = 0 -By setting the params differently the user can execute this query in multiple batches: +By setting the params differently the user can execute this query in multiple +batches: -To execute this process in 50 hard batches, we need to run this query 50 times while incremeting the `batch_id` value each time. -In that case, we'll set the value of `num_of_batches` to `50` each call, but the value of `batch_id` will initially be `0` and we'll increment it each run until `49`. -Instead of running this query 50 times manually from the UI, we can run this query 50 times as a REST endpoint from Linux shell: +To execute this process in 50 hard batches, we need to run this query 50 times +while incremeting the `batch_id` value each time. +In that case, we'll set the value of `num_of_batches` to `50` each call, +but the value of `batch_id` will initially be `0` and we'll increment it each run +until `49`. +Instead of running this query 50 times manually from the UI, we can run this +query 50 times as a REST endpoint from Linux shell: `for i in {0..49};do curl -X GET "http://:9000/query/ER_2023/delete_unused_cc_nodes?num_of_batches=50&batch_id=${i}";done;` -**Note**: By calling this query multiple times and deleting data in hard batches it allows us to delete data in parallel, and make the deltion process run smoother and substantially faster on high scale. +**Note**: By calling this query multiple times and deleting data in hard batches +it allows us to delete data in parallel, and make the deltion process run +smoother and substantially faster on high scale. ### Additional Queries -- `results_file`: Takes a file path as a parameters. This query prints out the Entity Resolution results to a CSV file. -- `find_shared_piis_of_two_entities`: Takes two Entity IDs as parameters. This query returns the attribute type, value, and degree for each PII that is shared between the provided entities. + - `results_file`: Takes a file path as a parameters. This query prints out + the Entity Resolution results to a CSV file. + - `find_shared_piis_of_two_entities`: Takes two Entity IDs as parameters. + This query returns the attribute type, value, and degree for each PII that + is shared between the provided entities. ## Scalability -The main heavy query in the Entity Resolution process is `match_entities`, as it performs the entire matching computation. -The `unify_entities` query is light, and the other deletion queries can be run in hard batches. +The main heavy query in the Entity Resolution process is `match_entities`, as +it performs the entire matching computation. +The `unify_entities` query is light, and the other deletion queries can be run +in hard batches. -If the user wishes to execute the Entity Resolution process on high scale and it is expected to find an inourmous amount of pairs of matching entities, it might be wise to consider saving the edges on disk instead of RAM. +If the user wishes to execute the Entity Resolution process on high scale and +it is expected to find an inourmous amount of pairs of matching entities, it +might be wise to consider saving the edges on disk instead of RAM. The steps to configure the system to save all edges on disk instead of Ram are as follows: @@ -145,7 +209,11 @@ The steps to configure the system to save all edges on disk instead of Ram are a - Run the command: `gadmin config apply -y` - Restart cluster: `gadmin restart -y` -**Note**: Additioanl scalability-related information is noted in the query details above. +**Note**: Additional scalability-related information is noted in the query details above. + +Please reference the following link for more information on MinHash: + +[MinHash Based Fuzzy Match on Graph](https://www.tigergraph.com/blog/minhash-based-fuzzy-match-on-graph/) ## Mock Data diff --git a/connected_customer/product_recommendations/README.md b/connected_customer/product_recommendations/README.md index 72823e7c..94e36244 100644 --- a/connected_customer/product_recommendations/README.md +++ b/connected_customer/product_recommendations/README.md @@ -240,17 +240,18 @@ TigerGraph supports multiple loading options. For detailed, up-to-date examples, please refer to the official documentation: - **Data loading overview** – supported sources and general workflow - https://docs.tigergraph.com/tigergraph-server/4.2/data-loading/data-loading-overview + https://docs.tigergraph.com/tigergraph-server/current/data-loading/data-loading-overview - **Data loading entry page** – index of all loading methods (local files, cloud storage, warehouses, Spark, etc.) - https://docs.tigergraph.com/tigergraph-server/4.2/data-loading/ + https://docs.tigergraph.com/tigergraph-server/current/data-loading/ - **Load data from cloud storage** – step-by-step guide for Amazon S3, Google Cloud Storage, and Azure Blob Storage - https://docs.tigergraph.com/tigergraph-server/4.2/data-loading/load-from-cloud + https://docs.tigergraph.com/tigergraph-server/current/data-loading/load-from-cloud At a high level, the steps to use your own data are: -1. Define a `DATA_SOURCE` object that matches your storage system and credentials. +1. For cloud or external systems, define a `DATA_SOURCE` object that matches your storage system and credentials. + For local files on the TigerGraph server, you can omit `DATA_SOURCE`. 2. Update the `DEFINE FILENAME` lines in `load_all` so they point to your own URIs (for example, your customer, product, and transaction files). 3. Keep the `LOAD` blocks the same unless your column structure changes—if it From 263fadd6aa42db60ffa109a49bdfd9b06c3b6935 Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Thu, 4 Dec 2025 13:05:26 +0530 Subject: [PATCH 19/55] standardized agile_operations kit documentation --- .../network_infrastructure/README.md | 137 +- ...e_failure_impact_radius_visualization.gsql | 20 + ..._visualization_with_subgraph_topology.gsql | 21 +- ...nstream_device_topology_visualization.gsql | 23 + .../explore_topology_from_all_router.gsql | 16 +- ...xplore_topology_from_multiple_routers.gsql | 25 +- .../explore_topology_from_one_router.gsql | 26 +- ...nts_by_impacted_device_and_time_range.gsql | 29 + ...d_events_by_time_range_and_event_type.gsql | 24 + ...tial_incident_source_of_event_by_time.gsql | 27 +- ..._related_events_from_incident_by_time.gsql | 29 + .../find_unsecured_servers_visualization.gsql | 25 +- .../incident_impact_by_max_radius.gsql | 27 +- .../top_k_devices_with_most_alerts.gsql | 19 + .../top_k_devices_with_most_incidents.gsql | 19 + .../supply_chain_management/README.md | 192 ++- .../meta/Supply_Chain_Insights.json | 1262 +++++++++++++++++ .../queries/add_purchase_to_inventory.gsql | 62 + .../queries/check_shipment_capacity.gsql | 45 + .../datewise_product_availability.gsql | 65 + .../queries/explore_bom.gsql | 48 + .../queries/explore_bom_line.gsql | 67 + .../queries/get_biggest_customers.gsql | 52 + .../queries/get_biggest_suppliers.gsql | 51 + .../queries/plant_failure_impact_nodes.gsql | 132 ++ .../queries/product_quantity_sales_order.gsql | 57 + .../queries/queries.gsql | 568 -------- .../queries/top_k_product_sales_order.gsql | 32 + .../top_k_products_purchase_order.gsql | 32 + .../queries/trace_bom_line_nations.gsql | 86 ++ .../queries/unfulfilled_orders.gsql | 0 connected_customer/customer_360/README.md | 4 +- .../Application_Engagement_Individuals.gsql | 26 + .../queries/Application_Starts.gsql | 39 + .../queries/Application_Submissions.gsql | 18 +- 35 files changed, 2696 insertions(+), 609 deletions(-) create mode 100644 agile_operations/supply_chain_management/meta/Supply_Chain_Insights.json create mode 100644 agile_operations/supply_chain_management/queries/add_purchase_to_inventory.gsql create mode 100644 agile_operations/supply_chain_management/queries/check_shipment_capacity.gsql create mode 100644 agile_operations/supply_chain_management/queries/datewise_product_availability.gsql create mode 100644 agile_operations/supply_chain_management/queries/explore_bom.gsql create mode 100644 agile_operations/supply_chain_management/queries/explore_bom_line.gsql create mode 100644 agile_operations/supply_chain_management/queries/get_biggest_customers.gsql create mode 100644 agile_operations/supply_chain_management/queries/get_biggest_suppliers.gsql create mode 100644 agile_operations/supply_chain_management/queries/plant_failure_impact_nodes.gsql create mode 100644 agile_operations/supply_chain_management/queries/product_quantity_sales_order.gsql delete mode 100644 agile_operations/supply_chain_management/queries/queries.gsql create mode 100644 agile_operations/supply_chain_management/queries/top_k_product_sales_order.gsql create mode 100644 agile_operations/supply_chain_management/queries/top_k_products_purchase_order.gsql create mode 100644 agile_operations/supply_chain_management/queries/trace_bom_line_nations.gsql create mode 100644 agile_operations/supply_chain_management/queries/unfulfilled_orders.gsql diff --git a/agile_operations/network_infrastructure/README.md b/agile_operations/network_infrastructure/README.md index 4caab760..961166e9 100644 --- a/agile_operations/network_infrastructure/README.md +++ b/agile_operations/network_infrastructure/README.md @@ -1,22 +1,143 @@ -# Narratives - -Cybersecurity is a crucial aspect of big organizations. Enterprises have their own data centers and their own network infrastructure involving a lot of devices. Cyberattacks and other incidents can lead to issues such as data breach, corrupted files, and loss of data, resulting in billions of dollars lost each year. One aspect that can help with detecting those attacks and other incidents is to gain a better understanding of the network infrastructure of your organization. - -TigerGraph allows you to connect data from different sources and load data in scale of terabytes into TigerGraph. With visualizations in TigerGraph, users can gain a better visibility of the platform by seeing different components and the topology of their Network Infrastructure. Different graph algorithms can be run at scale and allows for discovery of related incidents and events based on the device topology close to real-time. - +# Network Infrastructure – Cybersecurity (TigerGraph Solution Kit) + +Cybersecurity is a crucial aspect of large organizations. Enterprises operate +their own data centers and network infrastructure involving many different +devices. Cyberattacks and other incidents can lead to issues such as data +breaches, corrupted files, and loss of data — resulting in billions of +dollars lost each year. One key capability for detecting and responding to +these threats is a deep understanding of your organization’s network +infrastructure and how devices are connected. + +TigerGraph allows you to connect data from multiple sources and load data +at terabyte scale. With TigerGraph visualizations, users can see the +topology of their network infrastructure, understand how components relate, +and run graph algorithms at scale to discover related incidents and events +based on device topology in near real time. + +This solution kit provisions a complete graph environment — schema, sample +data, and GSQL queries — to help you: + +- Visualize router–firewall–switch–server topologies. +- Identify single points of failure and the blast radius of device failures. +- Trace incidents and alerts through network paths and time. +- Find unsecured routes and devices with frequent alerts and incidents. + +--- + +## Contents + +- [Overview](#overview) +- [Features](#features) +- [Repository layout](#repository-layout) +- [Prerequisites](#prerequisites) +- [Quickstart](#quickstart) +- [Working with the graph](#working-with-the-graph) + - [Query explanations](#query-explanations) + - [Insights application](#insights-application) + +--- +## Overview + +Modern enterprises operate complex on-premise and hybrid data centers with +routers, firewalls, switches, and servers forming intricate topologies. +Cyber attacks, misconfigurations, and hardware failures can cascade across +these networks, causing outages, data loss, and service disruptions. + +Graph databases like TigerGraph are well-suited for this type of analysis: +they can represent topology, events, alerts, and incidents as a connected +model and traverse it in real time. This solution kit combines: + +- **Network devices** + A generic `Device` vertex plus specialized device types: + `Router`, `Firewall`, `Switch`, and `Server`. + +- **Events, alerts, and incidents** + `Event`, `Alert`, and `Incident` vertices with associated classification + vertices (`Event_Type`, `Alert_Type`, `Incident_Type`) to capture what + happened and how it was categorized. + +- **Time hierarchy** + A time dimension for temporal analysis and visualization: + `Time_Year`, `Time_Date_Month`, `Time_Date`, `Time_Date_Hour`, + `Time_Date_Minute`. + +- **Topology and connectivity** + `Connect_To` edges between devices and impact/causal relationships such as: + `Impacts`, `Linked_With_Alert`, `Linked_With_Incident`, `From_Device`, + `To_Device`, plus relationships along the time hierarchy + (`Has_Minute`, `Has_Hour`, `Has_Date`, `Has_Month`, `Has_Year`). + +You can use the included queries as building blocks for operations, +security analysis, and incident investigation — or extend the graph with +your own device types, log sources, or analytics. + +> **Graph name:** This kit creates and uses a graph named `Network_Infrastructure`. + +--- # Components This repository includes multiple components: - `data` - Sample data. - `load_jobs` - Scripts for data loading tasks. -- `meta` - Solution Kit metadata. +- `meta` - Solution Kit metadata - includes a TG Insights application. - `queries` - Collection of GSQL queries. - `schema` - Definition of database schema. - `readme.md` - This usage guide. - `setup.sh` - Automated setup script. -# Instructions + +## Prerequisites + +Before you run this solution kit, make sure you have: + +- **A running TigerGraph instance** + - TigerGraph installed and running, or use the prebuilt kit on TG cloud. + - You must have permission to create graphs and run GSQL commands. + +- **GSQL client access** + - The `gsql` command-line tool available on the same machine/container where you cloned this repo. + - Ability to connect to your TigerGraph service + +- **Network access for sample data** + - Outbound internet access from the TigerGraph machine to read the sample + CSV files from the public S3 bucket used in `loading_job/load_data.gsql`. + +- **Shell environment** + - A Unix-like shell (Linux, macOS, or WSL) to run `setup.sh` and `queries/install_queries.sh`. + - Executable permissions for the scripts: + ```bash + chmod +x setup.sh + chmod +x queries/install_queries.sh + ``` + +> **Graph name:** This kit creates and uses a graph named +> `Network_Infrastructure`. + +# Setup Instructions +The following instructions assume that you are running the following scripts +with `gsql` command installed. + +If you don't yet have the `gsql` command available, see the TigerGraph documentation: + +- **Local GSQL shell on the server** + [The GSQL Shell](https://docs.tigergraph.com/tigergraph-server/current/gsql-shell/) + (explains how to run `gsql` directly on a TigerGraph server) + +- **Remote GSQL client (from your laptop or another machine)** + [Using a Remote GSQL Client](https://docs.tigergraph.com/tigergraph-server/current/gsql-shell/using-a-remote-gsql-client) + (explains how to download the GSQL client JAR, configure SSL, and create a `gsql` alias) +Ensure that the script is executable with: +```bash + +chmod +x setup.sh + +``` +Then, run the automated script using: +```bash +./setup.sh +``` + The `setup.sh` script is designed to streamline the initial setup process by sequentially executing the following steps: diff --git a/agile_operations/network_infrastructure/queries/device_failure_impact_radius_visualization.gsql b/agile_operations/network_infrastructure/queries/device_failure_impact_radius_visualization.gsql index c22df26b..ef65600c 100644 --- a/agile_operations/network_infrastructure/queries/device_failure_impact_radius_visualization.gsql +++ b/agile_operations/network_infrastructure/queries/device_failure_impact_radius_visualization.gsql @@ -1,6 +1,26 @@ CREATE OR REPLACE QUERY device_failure_impact_radius_visualization ( VERTEX device ) { + /* + Query Name: + device_failure_impact_radius_visualization + + Purpose: + 1. Find devices that do NOT have an alternative communication path. + 2. Determine devices that will be impacted if the input device fails. + 3. Display impacted devices and their interconnections. + + Concept: + - If a device has an alternative path (redundant connectivity), it won't fail. + - Devices without alternative paths are marked as 'impacted'. + + Inputs: + - device: The device whose failure we are analyzing. + + Outputs: + - impacted_devices: Devices that will fail if the input device fails. + - @@edges_to_display: Edges between impacted devices for visual impact analysis. + */ SetAccum @@edges_to_display; OrAccum @has_alternative_path; diff --git a/agile_operations/network_infrastructure/queries/device_failure_impact_radius_visualization_with_subgraph_topology.gsql b/agile_operations/network_infrastructure/queries/device_failure_impact_radius_visualization_with_subgraph_topology.gsql index 207200de..d77b56fa 100644 --- a/agile_operations/network_infrastructure/queries/device_failure_impact_radius_visualization_with_subgraph_topology.gsql +++ b/agile_operations/network_infrastructure/queries/device_failure_impact_radius_visualization_with_subgraph_topology.gsql @@ -1,6 +1,25 @@ CREATE OR REPLACE QUERY device_failure_impact_radius_visualization_with_subgraph_topology ( VERTEX device ) { + /* + Query Name: + device_failure_impact_radius_visualization_with_subgraph_topology + + Purpose: + 1. Build the subgraph that contains the input device (based on connectivity). + 2. Explore and detect devices that have alternative network paths (redundancy). + 3. Identify devices that will be impacted (fail) if the given device fails. + 4. Collect and return visualization data: subgraph topology and failure impact edges. + + Input: + - device: The starting device whose failure impact we want to analyze. + + Output: + - all_vertices_in_subgraph: Devices in the connectivity region of the input device. + - @@edges_to_display_in_subgraph: Edges representing overall subgraph structure. + - impacted_devices: Devices that do not have an alternative path (will fail). + - @@edges_to_display: Edges among impacted devices (failure impact radius). + */ SetAccum @@edges_to_display; SetAccum @@edges_to_display_in_subgraph; OrAccum @has_alternative_path; @@ -72,4 +91,4 @@ CREATE OR REPLACE QUERY device_failure_impact_radius_visualization_with_subgraph UPDATE DESCRIPTION OF QUERY device_failure_impact_radius_visualization_with_subgraph_topology "This query finds and visualizes the devices that will fail if the provided input device fails along with the subgraph that the input device was in. Use only for visualization purposes." -UPDATE DESCRIPTION OF QUERY_PARAM device_failure_impact_radius_visualization_with_subgraph_topology.device "The input device (accepts devices of all types)." \ No newline at end of file +UPDATE DESCRIPTION OF QUERY_PARAM device_failure_impact_radius_visualization_with_subgraph_topology.device "The input device (accepts devices of all types)." diff --git a/agile_operations/network_infrastructure/queries/downstream_device_topology_visualization.gsql b/agile_operations/network_infrastructure/queries/downstream_device_topology_visualization.gsql index d39e7c98..2d52a4f0 100644 --- a/agile_operations/network_infrastructure/queries/downstream_device_topology_visualization.gsql +++ b/agile_operations/network_infrastructure/queries/downstream_device_topology_visualization.gsql @@ -2,6 +2,29 @@ CREATE OR REPLACE QUERY downstream_device_topology_visualization ( VERTEX device, UINT k_hop_switch_limit = 3 ) { + /* + Query Name: + downstream_device_topology_visualization + + Purpose: + Visualize the downstream topology from a given device, following the device hierarchy: + Router → Firewall → Switch → Server + Also explores multiple downstream Switch layers (k-hop depth traversal). + + Key Features: + ✔ Identifies all downstream devices classified by device type. + ✔ Follows device hierarchy dynamically based on input device type. + ✔ Limits multi-hop Switch traversal using k_hop_switch_limit. + ✔ Returns devices and connecting edges for visualization. + + Inputs: + - device: Starting device. + - k_hop_switch_limit: Maximum depth for switch-to-switch iterations (default = 3). + + Outputs: + - impacted_devices: All discovered downstream devices. + - @@edges_to_display: Edges to visualize the downstream network path. +*/ SetAccum @@impacted_devices; SetAccum @@edges_to_display; diff --git a/agile_operations/network_infrastructure/queries/explore_topology_from_all_router.gsql b/agile_operations/network_infrastructure/queries/explore_topology_from_all_router.gsql index 4f8862e5..fa22ad03 100644 --- a/agile_operations/network_infrastructure/queries/explore_topology_from_all_router.gsql +++ b/agile_operations/network_infrastructure/queries/explore_topology_from_all_router.gsql @@ -1,4 +1,18 @@ CREATE OR REPLACE QUERY explore_topology_from_all_router () { + /* + Query Name: + explore_topology_from_all_router + + Purpose: + Visualize the entire network topology by: + - Retrieving all devices in the graph + - Exploring all connected edges using Connect_To + - Displaying both devices and their connections + + Outputs: + - all_devices_with_connections: List of devices connected via Connect_To edges + - @@edges_to_display: All edges among connected devices for visualization +*/ SetAccum @@edges_to_display; all_devices = {Device.*}; @@ -13,4 +27,4 @@ CREATE OR REPLACE QUERY explore_topology_from_all_router () { PRINT @@edges_to_display AS edges_to_display; } -UPDATE DESCRIPTION OF QUERY explore_topology_from_all_router "This query visualizes the network topology of all devices in the database. It shows the downstream connections from all routers." \ No newline at end of file +UPDATE DESCRIPTION OF QUERY explore_topology_from_all_router "This query visualizes the network topology of all devices in the database. It shows the downstream connections from all routers." diff --git a/agile_operations/network_infrastructure/queries/explore_topology_from_multiple_routers.gsql b/agile_operations/network_infrastructure/queries/explore_topology_from_multiple_routers.gsql index 2f7f345a..acf6cafc 100644 --- a/agile_operations/network_infrastructure/queries/explore_topology_from_multiple_routers.gsql +++ b/agile_operations/network_infrastructure/queries/explore_topology_from_multiple_routers.gsql @@ -1,4 +1,27 @@ CREATE OR REPLACE QUERY explore_topology_from_multiple_routers (SET> starter_router_set) { + /* + Query Name: + explore_topology_from_multiple_routers + + Purpose: + Visualizes the downstream network topology starting from one or more routers. + Traverses devices in this structured order: + Router → Firewall → Switch → Server + + Key Features: + ✔ Uses BFS to exhaustively discover downstream switches. + + Inputs: + starter_router_set — Set of router vertices to start the topology exploration. + If empty, all routers in the graph are selected automatically. + + Outputs: + - all_visited_router_devices — Routers connected to input routers via Device_Has_Type + - all_visited_firewalls — First downstream stage (Firewalls) + - all_visited_switches — All connected Switches (multi-hop via BFS) + - all_visited_servers — Downstream Servers + - @@edges_to_display — All edges to display full topology +*/ OrAccum @visited; SetAccum @@edges_to_display; @@ -85,4 +108,4 @@ CREATE OR REPLACE QUERY explore_topology_from_multiple_routers (SET starter_router) { + /* + Query Name: + explore_topology_from_one_router + + Purpose: + Visualizes the downstream network topology starting from a single router. + Traverses devices in this structured order: + Router → Firewall → Switch → Server + + Key Features: + ✔ Discovers all downstream Firewalls and Switches (including bypass paths) + ✔ Uses BFS to find all connected Switches (multi-hop exploration) + ✔ Captures all edges forming the full topology view + + Input: + starter_router — A single Router vertex that acts as the exploration starting point. + + Outputs: + - all_visited_router_devices — Devices directly connected to starter router via Device_Has_Type + - all_visited_firewalls — Firewalls downstream of the router + - all_visited_switches — All discovered Switches (including BFS expansion) + - all_visited_servers — Servers connected downstream of switches + - @@edges_to_display — Complete collection of edges forming the explored topology +*/ OrAccum @visited; SetAccum @@edges_to_display; @@ -80,4 +104,4 @@ CREATE OR REPLACE QUERY explore_topology_from_one_router (VERTEX starter UPDATE DESCRIPTION OF QUERY explore_topology_from_one_router "This query visualizes the network topology starting from the router 'starter_router'." -UPDATE DESCRIPTION OF QUERY_PARAM explore_topology_from_one_router.starter_router "The input router device to explore the network topology." \ No newline at end of file +UPDATE DESCRIPTION OF QUERY_PARAM explore_topology_from_one_router.starter_router "The input router device to explore the network topology." diff --git a/agile_operations/network_infrastructure/queries/find_events_by_impacted_device_and_time_range.gsql b/agile_operations/network_infrastructure/queries/find_events_by_impacted_device_and_time_range.gsql index 924cfcf9..9945fbd3 100644 --- a/agile_operations/network_infrastructure/queries/find_events_by_impacted_device_and_time_range.gsql +++ b/agile_operations/network_infrastructure/queries/find_events_by_impacted_device_and_time_range.gsql @@ -4,6 +4,35 @@ CREATE OR REPLACE QUERY find_events_by_impacted_device_and_time_range ( DATETIME end_time, BOOL show_event_types_vis = FALSE ) { + /* + Query Name: + find_events_by_impacted_device_and_time_range + + Purpose: + Finds and visualizes events linked to a specific device within a given time range. + Also traces time hierarchy (Minute → Hour → Date → Month → Year) for chronological visualization. + Optionally includes detailed event type information (Alert, Incident, and their classifications). + + Key Features: + ✔ Filters events by impacted device and time window. + ✔ Visualizes full time-based hierarchy for each event. + ✔ Option to include related event type, alert, and incident classifications. + ✔ Collects all traversal edges for easy visualization. + + Inputs: + input_device — The impacted device to search events for. + start_time — Minimum timestamp of events to include. + end_time — Maximum timestamp of events to include. + show_event_types_vis — (Optional) If TRUE, includes event/alert/incident types. + + Outputs: + - linked_events_within_time — Events impacting the input device within the time range. + - linked_time_date_minute / hour / date / month / year — Chronologically related time vertices. + - linked_event_types — Event classification (if enabled). + - linked_alerts_within_time, linked_incidents_within_time — Associated alerts and incidents. + - linked_alert_types, linked_incident_types — Alert/Incident categories. + - @@edges_to_display — All edges used to visualize event and time relationships. +*/ SetAccum @@edges_to_display; input_device_set = {input_device}; diff --git a/agile_operations/network_infrastructure/queries/find_events_by_time_range_and_event_type.gsql b/agile_operations/network_infrastructure/queries/find_events_by_time_range_and_event_type.gsql index 99d6ade8..5be011a7 100644 --- a/agile_operations/network_infrastructure/queries/find_events_by_time_range_and_event_type.gsql +++ b/agile_operations/network_infrastructure/queries/find_events_by_time_range_and_event_type.gsql @@ -3,6 +3,30 @@ CREATE OR REPLACE QUERY find_events_by_time_range_and_event_type ( DATETIME end_time, STRING input_event_type_filter = "" ) { + /* + Query Name: + find_events_by_time_range_and_event_type + + Purpose: + Retrieves events occurring within a specific time range and optionally filters by event type. + Additionally, collects related metadata such as impacted devices, alert types, + and incident types for comprehensive event analysis. + + Inputs: + start_time — Minimum datetime filter for event retrieval. + end_time — Maximum datetime filter for event retrieval. + input_event_type_filter — (Optional) Filters by event type. If empty, all types are included. + + Outputs: + - selected_events_with_info: + • event_id — Event identifier + • event_time — Timestamp of the event + • event_type — Type classification (Security, System, Network, etc.) + • event_alert_type — Enriched alert type data (if any) + • event_incident_type — Enriched incident type data (if any) + • impacted_devices_list — Devices affected by this event + +*/ MaxAccum @event_type; MaxAccum @incident_type; MaxAccum @alert_type; diff --git a/agile_operations/network_infrastructure/queries/find_potential_incident_source_of_event_by_time.gsql b/agile_operations/network_infrastructure/queries/find_potential_incident_source_of_event_by_time.gsql index c88da0ca..94730f7c 100644 --- a/agile_operations/network_infrastructure/queries/find_potential_incident_source_of_event_by_time.gsql +++ b/agile_operations/network_infrastructure/queries/find_potential_incident_source_of_event_by_time.gsql @@ -3,6 +3,31 @@ CREATE OR REPLACE QUERY find_potential_incident_source_of_event_by_time ( INT max_radius = 3, INT num_seconds_before_event_start = 3600 ) { + /* + Query Name: + find_potential_incident_source_of_event_by_time + + Purpose: + Identifies potential root-cause incidents for a given input event by: + • Retrieving impacted devices of the event + • Exploring connected devices within a specified hop radius + • Scanning for past incident events within a defined timeframe + • Linking discovered incidents with their incident types + + Inputs: + input_event — The event vertex to investigate potential source incidents for + max_radius — Max number of hops allowed for device connectivity exploration (default: 3) + num_seconds_before_event_start — Time window (in seconds) before input_event.timestamp to search for related incidents (default: 3600) + + Outputs: + • input_event_set — Original event input + • related_devices_within_radius — Connected devices marked by event radius + • incident_events_from_related_devices — Incident events found in time range + • incidents_from_related_devices — Linked incident entities + • incident_types_of_related_devices — Final categorized incident types + • @@edges_to_display — All traversal edges for visualization/UI mapping + +*/ SetAccum @@related_devices_set; SetAccum @@edges_to_display; MinAccum @@end_time_accum; @@ -129,4 +154,4 @@ UPDATE DESCRIPTION OF QUERY find_potential_incident_source_of_event_by_time "Thi UPDATE DESCRIPTION OF QUERY_PARAM find_potential_incident_source_of_event_by_time.input_event "The input events to find potential source incidents to" UPDATE DESCRIPTION OF QUERY_PARAM find_potential_incident_source_of_event_by_time.max_radius "The maximum number of hops extending from the device(s) the input event has an impact on. Defaults to 3." -UPDATE DESCRIPTION OF QUERY_PARAM find_potential_incident_source_of_event_by_time.num_seconds_before_event_start "The number of seconds before an event starts to search for incidents. Defaults to 3600 seconds." \ No newline at end of file +UPDATE DESCRIPTION OF QUERY_PARAM find_potential_incident_source_of_event_by_time.num_seconds_before_event_start "The number of seconds before an event starts to search for incidents. Defaults to 3600 seconds." diff --git a/agile_operations/network_infrastructure/queries/find_potential_related_events_from_incident_by_time.gsql b/agile_operations/network_infrastructure/queries/find_potential_related_events_from_incident_by_time.gsql index e27fbee7..c69c369d 100644 --- a/agile_operations/network_infrastructure/queries/find_potential_related_events_from_incident_by_time.gsql +++ b/agile_operations/network_infrastructure/queries/find_potential_related_events_from_incident_by_time.gsql @@ -3,6 +3,35 @@ CREATE OR REPLACE QUERY find_potential_related_events_from_incident_by_time ( INT max_radius = 3, INT num_seconds_from_incident_start = 3600 ) { + /* + Query Name: + find_potential_related_events_from_incident_by_time + + Purpose: + Identifies events that could be potentially related to a given input incident. + It does so by: + • Discovering all devices impacted by the incident + • Expanding outward through connected devices within max_radius hops + • Searching for events (Alerts, Incidents) that occurred within a time window + starting from the incident occurrence time + • Classifying and linking detected related alerts and incidents with types + + Inputs: + input_incident — Starting Incident vertex for event correlation analysis + max_radius — Max number of hops to discover connected impacted devices (default: 3) + num_seconds_from_incident_start — Time window (in seconds) after incident start to look for related events (default: 3600) + + Outputs: + • input_incident_set — Original input incident + • linked_event — Event directly linked to input incident + • impacted_devices_within_radius — All devices reached via radius traversal + • alerts_from_impacted_devices — Related Alerts discovered in time range + • incidents_from_impacted_devices — Related Incidents discovered + • alert_types_of_impacted_devices — Enriched alert category details + • incident_types_of_impacted_devices — Enriched incident category details + • @@edges_to_display — All collected edges for UI / Graph visualization + +*/ SetAccum @@impacted_devices_set; SetAccum @@edges_to_display; MinAccum @@start_time_accum; diff --git a/agile_operations/network_infrastructure/queries/find_unsecured_servers_visualization.gsql b/agile_operations/network_infrastructure/queries/find_unsecured_servers_visualization.gsql index be31a400..926d0dad 100644 --- a/agile_operations/network_infrastructure/queries/find_unsecured_servers_visualization.gsql +++ b/agile_operations/network_infrastructure/queries/find_unsecured_servers_visualization.gsql @@ -1,4 +1,27 @@ -CREATE OR REPLACE QUERY find_unsecured_servers_visualization (UINT k_hop_switch_limit = 3) { +CREATE OR REPLACE QUERY find_unsecured_servers_visualization (UINT k_hop_switch_limit = 3) { + /* + Query: find_unsecured_servers_visualization + + Purpose: + Visualizes unsecured network paths from Routers to Servers through Switches. + It identifies Servers that are reachable via Switches without passing through security devices (like Firewalls). + + What It Does: + • Finds all Routers, Switches, and Servers. + • Traverses paths from Routers → Switches → Servers using Connect_To edges. + • Expands through Switch-to-Switch connections up to 'k_hop_switch_limit' hops. + • Collects all involved vertices and edges for visualization. + + Key Outputs: + - routers_to_display → Starting routers + - switches_to_display → Switches on the unsecured path + - servers_to_display → Potentially unsecured servers + - edges_to_display → All traversal edges for graph visualization + + Parameter: + k_hop_switch_limit → Maximum number of Switch-to-Switch traversal hops (default: 3) + +*/ SetAccum @@routers_to_display; SetAccum @@switches_to_display; diff --git a/agile_operations/network_infrastructure/queries/incident_impact_by_max_radius.gsql b/agile_operations/network_infrastructure/queries/incident_impact_by_max_radius.gsql index ad6e3a9d..ff781930 100644 --- a/agile_operations/network_infrastructure/queries/incident_impact_by_max_radius.gsql +++ b/agile_operations/network_infrastructure/queries/incident_impact_by_max_radius.gsql @@ -2,6 +2,31 @@ CREATE OR REPLACE QUERY incident_impact_by_max_radius ( VERTEX input_incident, INT max_radius = 4 ) { + /* + Query: incident_impact_by_max_radius + + Purpose: + Determines how far an incident can propagate through the network. + It finds all devices that may be impacted by a given incident, + up to a specified hop limit (max_radius). + + What It Does: + 1. Gets the Event linked to the input Incident. + 2. Finds devices directly impacted by the incident (radius 0). + 3. Iteratively explores additional devices connected via Connect_To edges, + marking each with its hop distance (incident_radius). + 4. Collects all impacted devices and edges for visualization. + + Key Outputs: + - input_incident_set → The provided incident + - linked_event → Event associated with the incident + - impacted_devices_within_radius → All potentially impacted devices + - @@edges_to_display → Edges used during traversal + + Parameters: + max_radius → Max number of hops to explore propagation (default: 4) + +*/ SetAccum @@impacted_devices_set; SetAccum @@edges_to_display; OrAccum @visited; @@ -61,4 +86,4 @@ CREATE OR REPLACE QUERY incident_impact_by_max_radius ( UPDATE DESCRIPTION OF QUERY incident_impact_by_max_radius "This query finds the devices that can be impacted by an incident within 'max_radius' hops away from the devices directly impacted by the event." UPDATE DESCRIPTION OF QUERY_PARAM incident_impact_by_max_radius.input_incident "The input incident to find devices that can be impacted by it." -UPDATE DESCRIPTION OF QUERY_PARAM incident_impact_by_max_radius.max_radius "The maximum number of hops extending from the device(s) the input incident has an impact on. Defaults to 4." \ No newline at end of file +UPDATE DESCRIPTION OF QUERY_PARAM incident_impact_by_max_radius.max_radius "The maximum number of hops extending from the device(s) the input incident has an impact on. Defaults to 4." diff --git a/agile_operations/network_infrastructure/queries/top_k_devices_with_most_alerts.gsql b/agile_operations/network_infrastructure/queries/top_k_devices_with_most_alerts.gsql index 16b2a08f..c8794393 100644 --- a/agile_operations/network_infrastructure/queries/top_k_devices_with_most_alerts.gsql +++ b/agile_operations/network_infrastructure/queries/top_k_devices_with_most_alerts.gsql @@ -1,4 +1,23 @@ CREATE OR REPLACE QUERY top_k_devices_with_most_alerts (INT k) { + /* + Query: top_k_devices_with_most_alerts + + Purpose: + Identifies the devices that are associated with the highest number of alerts. + Useful for prioritizing monitoring, troubleshooting, and risk assessment. + + What It Does: + 1. Counts how many alerts are linked to each Event. + 2. Aggregates alert counts from Events to connected Devices. + 3. Sorts devices by total alert frequency in descending order. + 4. Returns the top K most alert-prone devices. + + Key Outputs: + - devices → Top K devices with highest aggregated alert_count + + Parameter: + k → Number of devices to return (top K) +*/ SumAccum @alert_count; diff --git a/agile_operations/network_infrastructure/queries/top_k_devices_with_most_incidents.gsql b/agile_operations/network_infrastructure/queries/top_k_devices_with_most_incidents.gsql index 80721d69..44104d40 100644 --- a/agile_operations/network_infrastructure/queries/top_k_devices_with_most_incidents.gsql +++ b/agile_operations/network_infrastructure/queries/top_k_devices_with_most_incidents.gsql @@ -1,4 +1,23 @@ CREATE OR REPLACE QUERY top_k_devices_with_most_incidents (INT k) { + /* + Query: top_k_devices_with_most_incidents + + Purpose: + Identifies devices that are associated with the highest number of incidents. + Helps in recognizing critical devices prone to failures, security breaches, or outages. + + What It Does: + 1. Counts the number of incidents linked to each Event. + 2. Propagates incident counts from Events to connected Devices. + 3. Sorts devices by total incident frequency in descending order. + 4. Returns the top K devices with the most incidents. + + Key Output: + - devices → List of top K devices ranked by incident_count + + Parameter: + k → Number of top devices to return.. +*/ SumAccum @incident_count; diff --git a/agile_operations/supply_chain_management/README.md b/agile_operations/supply_chain_management/README.md index b0bd1ba5..41b7527e 100644 --- a/agile_operations/supply_chain_management/README.md +++ b/agile_operations/supply_chain_management/README.md @@ -1,38 +1,184 @@ -# Narratives +# Supply Chain Management (TigerGraph Solution Kit) -Inventory Management is the process of ordering, storing, and using a company's inventory. In retail, manufacturing, and other inventory-intensive sectors, a company's raw materials and finished goods are the core of its business. A shortage of inventory when and where needed can be detrimental and at the same time a large inventory is at risk of spoilage, theft, damage, or shifts in demand. To manage inventory and transport logistics, it is important to understand the quantity of materials or products available in inventory. +The Supply Chain Management solution for TigerGraph delivers a complete, +enterprise-ready graph that models an end-to-end manufacturing and distribution +supply chain. It is built for manufacturers and distributors and mirrors +real-world ERP structures such as **Material Masters**, multi-level **Bills of +Materials (BOMs)**, **Purchase Orders**, **Sales Orders**, **Inventory**, +**Production Batches (SFC)**, **Plants**, **Shipments**, and full batch +genealogy. -Products in inventory management have intricate relationships with various components of the supply chain also having a network of dependencies for each calculation. TigerGraph's ability to model and navigate complex relationships makes it the ideal choice. Querying is also more intuitive and faster with TigerGraph leading to more efficient querying. The large volumes of products, their inventories, along with the other components in a supply chain require a scalable database platform which has the ability to maintain the large data like TigerGraph. +Unlike traditional relational warehouses that struggle with deep, recursive +questions, this graph enables real-time, highly connected analytics, including: -# Components +- Impact analysis of **plant** or **supplier** failures. +- Full **forward/backward traceability** of production batches. +- Real-time **Available-to-Promise (ATP)**, with optional inventory freshness rules. +- **Country-of-origin** and domestic vs. international sourcing analysis. +- **Demand–supply matching** and identification of bottlenecks or constraints. + +By leveraging TigerGraph’s native parallel engine and bi-directional traversals, +analysts, planners, sourcing managers, and risk teams can get instant answers to +complex multi-hop questions, improving agility, resilience, and decision quality +across the supply chain. + +--- + +## Contents + +- [Overview](#overview) +- [Features](#features) +- [Repository layout](#repository-layout) +- [Prerequisites](#prerequisites) +- [Quickstart](#quickstart) +- [Working with the graph](#working-with-the-graph) + - [Query explanations](#query-explanations) + - [Scalability](#Scalability) + +--- + +## Overview + +This solution kit models the **end-to-end flow** of materials and orders across +a manufacturing supply chain: + +- **Master data** + - `Material` and `BOM` for finished goods, subassemblies, and components. + - `Supplier`, `Customer`, `Plant`, `Nation`, `Region`, `Feature`. +- **Production & genealogy** + - `SFC_Material` and `SFC_Assembly` represent actual production batches and + assembly operations. + - Graph edges capture “produced at”, “used for”, and BOM relationships. +- **Orders & pricing** + - `Purchase_Order`, `Line_Number` for inbound procurement. + - `Sales_Order`, `Sales_Order_Item` for outbound customer demand. +- **Inventory & movements** + - `Inventory_Held` for stock levels by location. + - `Material_Document_Item` for stock movements and their impact. +- **Shipments & logistics** + - `Shipment_Doc`, `Shipment_Item`, `Shipping_Doc`, `Delivery_Doc` for + transportation and fulfillment. +- **Geography & ownership** + - `Belongs_To`, `is_located_in` edges connect plants, suppliers, and + customers to `Nation` and `Region`, enabling regional risk and origin + analysis. +> **Graph name:** This kit creates and uses a graph named `Supply_Chain_Management`. + +You can use the included queries for operations, planning, risk, and analytics +— or extend the graph with your own entities, KPIs, and business rules. + +--- +# Components This repository includes multiple components: -- `data` - Sample data. -- `load_jobs` - Scripts for data loading tasks. -- `meta` - Solution Kit metadata. -- `queries` - Collection of GSQL queries. -- `schema` - Definition of database schema. -- `readme.md` - This usage guide. -- `setup.sh` - Automated setup script. +* `data` - Realistic sample dataset (CSV files) with master and transactional data. +* `load_jobs` - Data loading jobs and scripts (supports offline batch and optional AWS S3 loading). +* `meta` - Solution Kit metadata includes the Insights application. +* `queries` - Complete set of 13 production-ready GSQL analytical queries. +* `schema` - Full graph schema definition (vertices, edges, indexes). +* `readme.md` - This usage guide. +* `setup.sh` - One-click automated installation script. + + +## Prerequisites + +Before you run this solution kit, make sure you have: + +- **A running TigerGraph instance** + - TigerGraph installed and running, or use the prebuilt kit on TG cloud. + - You must have permission to create graphs and run GSQL commands. + +- **GSQL client access** + - The `gsql` command-line tool available on the same machine/container where you cloned this repo. + - Ability to connect to your TigerGraph service + +- **Network access for sample data** + - Outbound internet access from the TigerGraph machine to read the sample + CSV files from the public S3 bucket used in `loading_job/load_data.gsql`. -# Instructions +- **Shell environment** + - A Unix-like shell (Linux, macOS, or WSL) to run `setup.sh` and `queries/install_queries.sh`. + - Executable permissions for the scripts: + ```bash + chmod +x setup.sh + chmod +x queries/install_queries.sh + ``` + +> **Graph name:** This kit creates and uses a graph named +> `Supply_Chain_Management`. + +# Setup Instructions +The following instructions assume that you are running the following scripts +with `gsql` command installed. + +If you don't yet have the `gsql` command available, see the TigerGraph documentation: + +- **Local GSQL shell on the server** + [The GSQL Shell](https://docs.tigergraph.com/tigergraph-server/current/gsql-shell/) + (explains how to run `gsql` directly on a TigerGraph server) + +- **Remote GSQL client (from your laptop or another machine)** + [Using a Remote GSQL Client](https://docs.tigergraph.com/tigergraph-server/current/gsql-shell/using-a-remote-gsql-client) + (explains how to download the GSQL client JAR, configure SSL, and create a `gsql` alias) +Ensure that the script is executable with: +```bash + +chmod +x setup.sh + +``` +Then, run the automated script using: +```bash +./setup.sh +``` The `setup.sh` script is designed to streamline the initial setup process by sequentially executing the following steps: -1. **Schema Creation**: Initiates the schema creation process with the `schema/create_inventory_management_graph.gsql` script. -2. **Data Loading**: Load data into the schema by running the data loading jobs with the scripts in the `loading_job` folder. -3. **Query Installation**: Completes the setup by installing necessary queries through the `queries/install_queries.sh` script. +1. **Schema Creation**: Creates the global graph `Supply_Chain_Management` and runs all schema definition files located in the `schema/` folder. -## Query Explanations +2. **Data Loading**: Executes all loading jobs defined in `load_jobs/` to populate master data (Materials, Plants, BOMs) and transactional data (Purchase Orders, Sales Orders, Inventory, Shipments, SFC batches, etc.). -We have different queries to perform the following tasks: +3. **Query Installation**: Automatically installs the full library of 13 analytical queries from the `queries/` folder. -1. **Available Inventory Measure:** This query shows the available inventory for every product. It also tells us how many inventories a particular product is stored at and the time at which the inventory stock value was last updated. With this we can get a general idea of inventory and make decisions regarding re-stocking the inventory or putting a halt on the manufacturing of products. -2. **Determine Fulfillment of Sales Orders :** This query determines the stock of product required in each sales order placed by a customer. It then determines the quantity of available stock of the product in the inventory which is used to realise fulfillment of sales orders. If a sales order can be fulfilled, it tells us the result stock in the inventory after fulfillment of sales order. -3. **Impact of Plant Failure on the Components :** This query gives us the impact that the failure of a plant has. With this query we can keep a track of affected products and inventories and make analysis based on this information. -4. **Top Products in a Sales Order/Purchase Order :** To maintain inventory levels, it is necessary to know the product that is most required and its required quantity. Among all the sales orders, certain products can most popular and important which should also require maintainance of a certain quantity of these products. +After the script finishes (typically within minutes), the graph is fully loaded and ready for immediate use in GraphStudio or via REST API. -## Mock Data -The `data` folder is populated with sample data files. These files are crafted for testing and demonstration purposes. +## Query Execution Order and Explanations +The queries are independent and can be executed in any order for day-to-day analysis. There is no mandatory sequential workflow except when performing inventory simulation or batch allocation. + +### Core Operational Queries (run on-demand) +- `product_quantity_sales_order` – Standard Available-to-Promise (ATP): checks if sufficient quantity exists for a Sales Order. +- `datewise_product_availability` – Smart ATP with freshness: only considers inventory updated within the last X months. +- `unfulfilled_orders` – Batch allocation engine: processes a set of Sales Orders and virtually reserves inventory. +- `check_shipment_capacity` – Compares ordered vs shipped quantities to find partially fulfilled items. +- `add_purchase_to_inventory` – Simulates the effect of receiving a Purchase Order on current stock levels. + +### Traceability & Risk Queries (recursive – run anytime) +- `explore_BOM` – Explodes a Bill of Materials downstream or traces where-used upstream to any depth. +- `explore_BOM_line` – Traces the actual physical genealogy of a specific batch (SFC) across production and consumption. +- `plant_failure_impact_nodes` – BFS impact analysis: returns all customers, orders, and finished goods affected if a Plant fails. +- `trace_BOM_line_nations` – Calculates the exact percentage of domestic vs international raw materials in a finished good. + +### Analytics & Reporting Queries (aggregation – run on-demand) +- `get_biggest_customers` – Top-K customers by ordered quantity (optional date range and nation filter). +- `get_biggest_suppliers` – Top-K suppliers by supplied quantity (optional date range and nation filter). +- `top_k_products_sales_order` – Most demanded finished goods across all Sales Orders. +- `top_k_products_purchase_order` – Most purchased raw/semi-finished materials. + +All queries are distributed, highly parameterized (depth, date ranges, top-K, nation filters, etc.), and complete in seconds even on datasets with millions of orders and multi-level BOMs. + +## Scalability +The heaviest queries (`explore_BOM`, `explore_BOM_line`, `plant_failure_impact_nodes`, `unfulfilled_orders`) use TigerGraph’s native parallel traversal and recursion engines and scale linearly across the cluster. + +For extremely large supply chains (hundreds of millions of SFC instances or deep BOMs): +- Depth/hop limits are built into queries to guarantee predictable runtime. +- When an enormous number of temporary edges is expected (e.g., massive impact analysis), configure the engine to spill edges to disk instead of RAM: + +```bash +gadmin config set GPE.EdgeDataMemoryLimit 0 +gadmin config apply -y +gadmin restart -y +``` + +## Mock Data +The data folder is populated with sample data files. These files are crafted for testing and demonstration purposes. diff --git a/agile_operations/supply_chain_management/meta/Supply_Chain_Insights.json b/agile_operations/supply_chain_management/meta/Supply_Chain_Insights.json new file mode 100644 index 00000000..955d55df --- /dev/null +++ b/agile_operations/supply_chain_management/meta/Supply_Chain_Insights.json @@ -0,0 +1,1262 @@ +{ + "defaultGraph": "Supply_Chain_Management", + "globalVariables": [], + "iconURL": "/insights/static/media/factory.4354dc741f4585127af6b70c9821a211.svg", + "id": "3fjAZmRYTeABxn4ncYY7M3", + "owner": "tigergraph", + "pageConfigSeparated": true, + "pages": [ + { + "chartMap": { + "6wa4sFXLXHJmWQzGJeDojT": { + "chartSettings": { + "category": [ + { + "id": "v_id", + "type": "string" + } + ], + "showColumns": [ + { + "isChecked": true, + "name": "v_id" + }, + { + "isChecked": false, + "name": "v_type" + }, + { + "isChecked": true, + "name": "customers.@total_quantity" + } + ], + "sortedColumns": [ + "v_id", + "customers.@total_quantity" + ], + "tableColumns": [ + { + "id": "Matched pattern", + "isChecked": "true", + "label": "" + }, + { + "children": [], + "id": "Vertices", + "isChecked": "true", + "isExpanded": true, + "label": "Vertices" + }, + { + "children": [], + "id": "Edges", + "isChecked": "true", + "isExpanded": false, + "label": "Edges" + } + ], + "tableHeaders": [ + "v_id", + "v_type", + "customers.@total_quantity" + ], + "value": [ + { + "id": "customers.@total_quantity", + "type": "number" + } + ] + }, + "graphName": "Supply_Chain_Management", + "hideWidgetName": false, + "id": "6wa4sFXLXHJmWQzGJeDojT", + "patternLimit": 5, + "query": "", + "queryType": "pattern", + "refreshRate": 0, + "searchPattern": [ + { + "data": "get_biggest_customers", + "id": "get_biggest_customers", + "type": "QUERY" + }, + { + "data": "Top_K", + "id": "d01266d0-d7b3-451d-bb0b-71969d3dac88", + "paramGlobalInput": "Top_K", + "paramName": "top_k", + "paramType": "INT", + "paramTypeReadonly": true, + "type": "PARAM" + }, + { + "data": "", + "id": "e644912d-4d39-4544-8b97-afa36fbfcbdc", + "paramGlobalInput": "", + "paramName": "start_date", + "paramType": "DATETIME", + "paramTypeReadonly": true, + "type": "PARAM" + }, + { + "data": "", + "id": "ad9bd726-e56d-4edf-b21e-7c1ef2bb66aa", + "paramGlobalInput": "", + "paramName": "end_date", + "paramType": "DATETIME", + "paramTypeReadonly": true, + "type": "PARAM" + }, + { + "data": "", + "elementType": "VERTEX", + "id": "b91d25e5-d27a-43d5-9b2b-0e2ef77bc969", + "paramGlobalInput": "", + "paramName": "input_nations", + "paramType": "SET", + "paramTypeReadonly": true, + "type": "PARAM" + } + ], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "Customers", + "type": "bar", + "version": "1763931181376086659" + }, + "aepZYhHtdrKMZscobrGixq": { + "chartSettings": { + "showColumns": [ + { + "isChecked": true, + "name": "p" + }, + { + "isChecked": true, + "name": "quantity" + } + ], + "sortedColumns": [ + "p", + "quantity" + ], + "tableColumns": [ + { + "id": "Matched pattern", + "isChecked": "true", + "label": "" + }, + { + "children": [], + "id": "Vertices", + "isChecked": "true", + "isExpanded": true, + "label": "Vertices" + }, + { + "children": [], + "id": "Edges", + "isChecked": "true", + "isExpanded": false, + "label": "Edges" + } + ], + "tableHeaders": [ + "p", + "quantity" + ] + }, + "graphName": "Supply_Chain_Management", + "hideWidgetName": false, + "id": "aepZYhHtdrKMZscobrGixq", + "patternLimit": 5, + "query": "", + "queryType": "pattern", + "refreshRate": 0, + "searchPattern": [ + { + "data": "top_k_products_sales_order", + "id": "top_k_products_sales_order", + "type": "QUERY" + }, + { + "data": "Top_K", + "id": "f0c011d4-d5d2-49fa-ba8c-3dd430d405af", + "paramGlobalInput": "Top_K", + "paramName": "k", + "paramType": "INT", + "paramTypeReadonly": true, + "type": "PARAM" + } + ], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "Top Products Sales Order", + "type": "table", + "version": "1763931489536489074" + }, + "d1F1DV6GoqU8eBNjYT53b6": { + "chartSettings": { + "borderRadius": 26, + "borderWidth": 16, + "category": [ + { + "id": "v_id", + "type": "string" + } + ], + "radius": [ + 0, + 74 + ], + "roseType": true, + "showColumns": [ + { + "isChecked": true, + "name": "v_id" + }, + { + "isChecked": false, + "name": "v_type" + }, + { + "isChecked": true, + "name": "suppliers.@total_quantity" + } + ], + "showLegend": false, + "sortedColumns": [ + "v_id", + "suppliers.@total_quantity" + ], + "tableColumns": [ + { + "id": "Matched pattern", + "isChecked": "true", + "label": "" + }, + { + "children": [], + "id": "Vertices", + "isChecked": "true", + "isExpanded": true, + "label": "Vertices" + }, + { + "children": [], + "id": "Edges", + "isChecked": "true", + "isExpanded": false, + "label": "Edges" + } + ], + "tableHeaders": [ + "v_id", + "v_type", + "suppliers.@total_quantity" + ], + "value": [ + { + "id": "suppliers.@total_quantity", + "type": "number" + } + ] + }, + "graphName": "Supply_Chain_Management", + "hideWidgetName": false, + "id": "d1F1DV6GoqU8eBNjYT53b6", + "patternLimit": 5, + "query": "", + "queryType": "pattern", + "refreshRate": 0, + "searchPattern": [ + { + "data": "get_biggest_suppliers", + "id": "get_biggest_suppliers", + "type": "QUERY" + }, + { + "data": "Top_K", + "id": "c04ee73c-eedf-46a5-bd4a-ff8135c33bde", + "paramGlobalInput": "Top_K", + "paramName": "top_k", + "paramType": "INT", + "paramTypeReadonly": true, + "type": "PARAM" + }, + { + "data": "", + "id": "b4447a62-707f-4e06-839f-c7f95a624fc9", + "paramGlobalInput": "", + "paramName": "start_date", + "paramType": "DATETIME", + "paramTypeReadonly": true, + "type": "PARAM" + }, + { + "data": "", + "id": "6646046a-e5fb-4700-9e15-0c04ec289e36", + "paramGlobalInput": "", + "paramName": "end_date", + "paramType": "DATETIME", + "paramTypeReadonly": true, + "type": "PARAM" + }, + { + "data": "", + "elementType": "VERTEX", + "id": "0ed291e9-16c4-427f-983b-1ab45829fe70", + "paramGlobalInput": "", + "paramName": "input_nations", + "paramType": "SET", + "paramTypeReadonly": true, + "type": "PARAM" + } + ], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "Suppliers", + "type": "pie", + "version": "1763931246459106033" + }, + "h1DS1HwMbhESTmLuSNseGF": { + "chartSettings": { + "borderRadius": 32, + "borderWidth": 21, + "category": [ + { + "id": "IsProduct", + "type": "boolean" + } + ], + "radius": [ + 0, + 81 + ], + "roseType": false, + "tableHeaders": [ + "IsProduct", + "cnt" + ], + "tableIndex": 0, + "value": [ + { + "id": "cnt", + "type": "number" + } + ] + }, + "graphName": "Supply_Chain_Management", + "hideWidgetName": false, + "id": "h1DS1HwMbhESTmLuSNseGF", + "patternLimit": 5, + "query": "INTERPRET QUERY() FOR GRAPH Supply_Chain_Management {\n GroupByAccum cnt> @@grp;\n res = \n SELECT s from Material:s \n ACCUM CASE when s.is_product then @@grp += (\"Product\"->1) else @@grp += (\"Material\"->1) end\n ;\n PRINT @@grp;\n}", + "queryType": "interactive", + "refreshRate": 0, + "searchPattern": [], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "Material Product", + "type": "pie", + "version": "1764011824285635441" + }, + "iM7aR5tBHUSDNPuLkqCoeK": { + "chartSettings": { + "showColumns": [ + { + "isChecked": true, + "name": "p" + }, + { + "isChecked": true, + "name": "quantity" + } + ], + "sortedColumns": [ + "p", + "quantity" + ], + "tableColumns": [ + { + "id": "Matched pattern", + "isChecked": "true", + "label": "" + }, + { + "children": [], + "id": "Vertices", + "isChecked": "true", + "isExpanded": true, + "label": "Vertices" + }, + { + "children": [], + "id": "Edges", + "isChecked": "true", + "isExpanded": false, + "label": "Edges" + } + ], + "tableHeaders": [ + "p", + "quantity" + ] + }, + "graphName": "Supply_Chain_Management", + "hideWidgetName": false, + "id": "iM7aR5tBHUSDNPuLkqCoeK", + "patternLimit": 5, + "query": "", + "queryType": "pattern", + "refreshRate": 0, + "searchPattern": [ + { + "data": "top_k_products_purchase_order", + "id": "top_k_products_purchase_order", + "type": "QUERY" + }, + { + "data": "Top_K", + "id": "27f34beb-d818-44e9-b997-9538f52f37ac", + "paramGlobalInput": "Top_K", + "paramName": "k", + "paramType": "INT", + "paramTypeReadonly": true, + "type": "PARAM" + } + ], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "Top Products Purchase Order", + "type": "table", + "version": "1763931519243173565" + }, + "jiFArchKghkbBhGC4d548q": { + "chartSettings": { + "inputStates": [ + { + "dataType": "number", + "id": "input_jVfdmk2x1Ajk4kjWZAzFmK", + "name": "Top_K", + "settings": { + "max": "50", + "min": "1" + }, + "widgetType": "Input" + } + ] + }, + "graphName": "AntiFraud", + "hideWidgetName": false, + "id": "jiFArchKghkbBhGC4d548q", + "patternLimit": 5, + "query": "", + "queryType": "pattern", + "refreshRate": 0, + "searchPattern": [], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "", + "type": "Inputs", + "version": "1763933359536882568" + }, + "vYoJnjMMFWLQWr6r1hThzL": { + "chartSettings": { + "values": [ + { + "fontColor": "#000000", + "fontSize": 36, + "iconColor": "#000000", + "iconPostion": "", + "iconSize": 24, + "iconURL": "/insights/static/media/layout-template.4aadad8f62567d7e8735dd875cf9d307.svg", + "id": "7pTWXkngZm4jQ68oDa8i8d", + "key": "Plants", + "label": "Plant", + "labelColor": "#000000", + "labelPostion": "", + "labelSize": 24, + "styleRule": [] + }, + { + "fontColor": "#000000", + "fontSize": 36, + "iconColor": "#000000", + "iconPostion": "", + "iconSize": 24, + "iconURL": "/insights/static/media/user-round.a4c78e138aaee380f0d4d18c1e5de9a2.svg", + "id": "jNjXkiSwzVtWQGPHSkVYyA", + "key": "Customers", + "label": "Customer", + "labelColor": "#000000", + "labelPostion": "", + "labelSize": 24, + "styleRule": [] + }, + { + "fontColor": "#000000", + "fontSize": 36, + "iconColor": "#000000", + "iconPostion": "", + "iconSize": 24, + "iconURL": "/insights/static/media/user-pen.e57c9feb5a07ce1f9b9c05d8ee2d0786.svg", + "id": "7a15tQkEd4KzMo8kJXWn9e", + "key": "Suppliers", + "label": "Supplier", + "labelColor": "#000000", + "labelPostion": "", + "labelSize": 24, + "styleRule": [] + }, + { + "fontColor": "#000000", + "fontSize": 36, + "iconColor": "#000000", + "iconPostion": "", + "iconSize": 24, + "iconURL": "/insights/static/media/flag.ce538663ace2f1f8f1ef1eb29d46f4bf.svg", + "id": "5Nwa7ruXMyTRNz7J226TDh", + "key": "Nations", + "label": "Nation", + "labelColor": "#000000", + "labelPostion": "", + "labelSize": 24, + "styleRule": [] + } + ] + }, + "graphName": "Supply_Chain_Management", + "hideWidgetName": false, + "id": "vYoJnjMMFWLQWr6r1hThzL", + "patternLimit": 5, + "query": "INTERPRET QUERY() FOR GRAPH Supply_Chain_Management {\n res = SELECT s from Plant:s \n ;\n PRINT res.size() as Plants;\n res = SELECT s FROM Customer:s; \n PRINT res.size() as Customers;\n res = SELECT s from Nation:s;\n PRINT res.size() as Nations;\n res = SELECT s FROM Supplier:s;\n PRINT res.size() as Suppliers;\n}", + "queryType": "interactive", + "refreshRate": 0, + "searchPattern": [], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "", + "type": "value", + "version": "1763933189824585386" + } + }, + "globalParameters": { + "Top_K": { + "id": "input_jVfdmk2x1Ajk4kjWZAzFmK", + "name": "Top_K", + "type": "NUMBER", + "value": 10 + } + }, + "iconURL": "/insights/static/media/chart-scatter.c83829978fdeba96c343f7cc07ee2bf9.svg", + "id": "mfgR3jZc9v78jGjp3Vn8jf", + "isDetail": true, + "isNew": false, + "layouts": { + "md": [ + { + "h": 20, + "i": "6wa4sFXLXHJmWQzGJeDojT", + "moved": false, + "static": false, + "w": 8, + "x": 4, + "y": 32 + }, + { + "h": 23, + "i": "aepZYhHtdrKMZscobrGixq", + "moved": false, + "static": false, + "w": 4, + "x": 4, + "y": 9 + }, + { + "h": 24, + "i": "d1F1DV6GoqU8eBNjYT53b6", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 28 + }, + { + "h": 19, + "i": "h1DS1HwMbhESTmLuSNseGF", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 9 + }, + { + "h": 23, + "i": "iM7aR5tBHUSDNPuLkqCoeK", + "moved": false, + "static": false, + "w": 4, + "x": 8, + "y": 9 + }, + { + "h": 9, + "i": "jiFArchKghkbBhGC4d548q", + "moved": false, + "static": false, + "w": 2, + "x": 0, + "y": 0 + }, + { + "h": 9, + "i": "vYoJnjMMFWLQWr6r1hThzL", + "moved": false, + "static": false, + "w": 10, + "x": 2, + "y": 0 + } + ], + "xs": [ + { + "h": 9, + "i": "jiFArchKghkbBhGC4d548q", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 0 + }, + { + "h": 9, + "i": "vYoJnjMMFWLQWr6r1hThzL", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 9 + }, + { + "h": 19, + "i": "h1DS1HwMbhESTmLuSNseGF", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 18 + }, + { + "h": 23, + "i": "aepZYhHtdrKMZscobrGixq", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 37 + }, + { + "h": 23, + "i": "iM7aR5tBHUSDNPuLkqCoeK", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 60 + }, + { + "h": 24, + "i": "d1F1DV6GoqU8eBNjYT53b6", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 83 + }, + { + "h": 20, + "i": "6wa4sFXLXHJmWQzGJeDojT", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 107 + } + ] + }, + "title": "Analytics", + "version": "1764009629639742855", + "weight": -10 + }, + { + "chartMap": { + "5ZQJVEuiq5cAxVxZWVqoLS": { + "chartSettings": { + "edgeLength": 175, + "rulesByType": { + "Line_Number": [ + { + "condition": "=", + "conditionEndValue": 0, + "conditionStartValue": 0, + "conditionValue": true, + "fieldName": "@isSrc", + "fieldType": "boolean", + "palateName": "", + "styleEndLabel": "", + "styleEndValue": "", + "styleKey": "background-color", + "styleLabel": "Vertex color", + "styleStartLabel": "", + "styleStartValue": "", + "styleType": "color", + "styleValue": "#1a7c0e" + } + ] + } + }, + "graphName": "Supply_Chain_Management", + "hideWidgetName": false, + "id": "5ZQJVEuiq5cAxVxZWVqoLS", + "patternLimit": 5, + "query": "", + "queryType": "pattern", + "refreshRate": 0, + "searchPattern": [ + { + "data": "explore_BOM_line_Insights", + "id": "explore_BOM_line_Insights", + "type": "QUERY" + }, + { + "data": "Line_Number", + "id": "9eed304b-a520-48ce-8da6-40203562c238", + "paramGlobalInput": "", + "paramName": "vertType", + "paramType": "STRING", + "paramTypeReadonly": true, + "type": "PARAM" + }, + { + "data": "LineVertex", + "id": "ec0f667f-e6d9-42e9-9d6b-98ad1be0bb33", + "paramGlobalInput": "LineVertex", + "paramName": "id", + "paramType": "STRING", + "paramTypeReadonly": true, + "type": "PARAM" + }, + { + "data": "LineDepth", + "id": "cb2c8a11-87d4-410f-980d-bd08fa44aa1e", + "paramGlobalInput": "LineDepth", + "paramName": "depth", + "paramType": "INT", + "paramTypeReadonly": true, + "type": "PARAM" + }, + { + "data": "LineUpstream", + "id": "02e56b6f-bcf7-40ab-9c0f-2508fc886654", + "paramGlobalInput": "LineUpstream", + "paramName": "upstream", + "paramType": "BOOL", + "paramTypeReadonly": true, + "type": "PARAM" + }, + { + "data": "", + "id": "2ea2db6f-3568-467b-a6bf-fcb78f19b1a5", + "paramGlobalInput": "", + "paramName": "use_date_range", + "paramType": "BOOL", + "paramTypeReadonly": true, + "type": "PARAM" + }, + { + "data": "", + "id": "323ec303-484d-4ed0-a21e-74dc8de82cbe", + "paramGlobalInput": "", + "paramName": "start_date", + "paramType": "DATETIME", + "paramTypeReadonly": true, + "type": "PARAM" + }, + { + "data": "", + "id": "b5fea6eb-6b3c-47df-bddd-e1aadbc6ee9e", + "paramGlobalInput": "", + "paramName": "end_date", + "paramType": "DATETIME", + "paramTypeReadonly": true, + "type": "PARAM" + } + ], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "", + "type": "internal-graph", + "version": "1764021018679482546" + }, + "7GwzFLnQ5dDDVtUtVx7mPv": { + "chartSettings": { + "inputStates": [ + { + "dataType": "string", + "id": "input_rAjS1bcFu9NBPSX8wdUK4U", + "name": "LineVertex", + "settings": { + "graphName": "Supply_Chain_Management", + "labelKey": "Line_Number.id", + "open": false, + "options": [], + "patternLimit": 300, + "query": "", + "searchPattern": [ + { + "alias": "Line_Number", + "data": "Line_Number", + "id": "93d5c9b3-f987-4cfa-9d4d-369211e92af2", + "orderBy": [ + { + "asc": true, + "expression": { + "type": "AttrVariable", + "value": "alias_schema_Line_Number_0.id" + }, + "label": "Line_Number.id" + } + ], + "type": "vertex" + } + ], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "tableHeaders": [ + "Matched pattern", + "Line_Number" + ], + "useQuery": true, + "valueKey": "Line_Number.id" + }, + "widgetType": "Dropdown" + }, + { + "dataType": "number", + "id": "input_1cRTwt6EKyjHGDuhpK7XUT", + "name": "LineDepth", + "settings": { + "max": "20", + "min": "2" + }, + "widgetType": "Input" + }, + { + "dataType": "bool", + "id": "input_p7HJXg2VhZhgeF1pK8hv4S", + "name": "LineUpstream", + "settings": { + "options": [] + }, + "widgetType": "Dropdown" + } + ] + }, + "graphName": "AntiFraud", + "hideWidgetName": false, + "id": "7GwzFLnQ5dDDVtUtVx7mPv", + "patternLimit": 5, + "query": "", + "queryType": "pattern", + "refreshRate": 0, + "searchPattern": [], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "BOM Line", + "type": "Inputs", + "version": "1763928107928372418" + }, + "hx3rVgRRGfLEwkeabZwKKi": { + "chartSettings": { + "edgeLength": 285, + "rulesByType": { + "BOM": [ + { + "condition": "always", + "conditionEndValue": 0, + "conditionStartValue": 0, + "conditionValue": "", + "fieldName": "id", + "fieldType": "string", + "palateName": "", + "styleEndLabel": "", + "styleEndValue": "", + "styleKey": "node-radius", + "styleLabel": "0.25x", + "styleStartLabel": "", + "styleStartValue": "", + "styleType": "numeric", + "styleValue": 0.25 + }, + { + "condition": "=", + "conditionEndValue": 0, + "conditionStartValue": 0, + "conditionValue": true, + "fieldName": "@isSrc", + "fieldType": "boolean", + "palateName": "", + "styleEndLabel": "", + "styleEndValue": "", + "styleKey": "background-color", + "styleLabel": "Vertex color", + "styleStartLabel": "", + "styleStartValue": "", + "styleType": "color", + "styleValue": "#0571f0" + }, + { + "condition": "=", + "conditionEndValue": 0, + "conditionStartValue": 0, + "conditionValue": true, + "fieldName": "@isSrc", + "fieldType": "boolean", + "palateName": "", + "styleEndLabel": "", + "styleEndValue": "", + "styleKey": "node-radius", + "styleLabel": "1x", + "styleStartLabel": "", + "styleStartValue": "", + "styleType": "numeric", + "styleValue": 1 + }, + { + "condition": "=", + "conditionEndValue": 20, + "conditionStartValue": 1, + "conditionValue": "0", + "fieldName": "base_quantity", + "fieldType": "number", + "palateName": "", + "styleEndLabel": "", + "styleEndValue": "", + "styleKey": "background-color", + "styleLabel": "Vertex color", + "styleStartLabel": "", + "styleStartValue": "", + "styleType": "color", + "styleValue": "#ff0000" + } + ], + "Material": [ + { + "condition": "=", + "conditionEndValue": 0, + "conditionStartValue": 0, + "conditionValue": true, + "fieldName": "@isSrc", + "fieldType": "boolean", + "palateName": "", + "styleEndLabel": "", + "styleEndValue": "", + "styleKey": "background-color", + "styleLabel": "Vertex color", + "styleStartLabel": "", + "styleStartValue": "", + "styleType": "color", + "styleValue": "#4973f3" + }, + { + "condition": "=", + "conditionEndValue": 0, + "conditionStartValue": 0, + "conditionValue": true, + "fieldName": "is_product", + "fieldType": "boolean", + "palateName": "", + "styleEndLabel": "", + "styleEndValue": "", + "styleKey": "node-radius", + "styleLabel": "2x", + "styleStartLabel": "", + "styleStartValue": "", + "styleType": "numeric", + "styleValue": 2 + } + ] + } + }, + "graphName": "Supply_Chain_Management", + "hideWidgetName": false, + "id": "hx3rVgRRGfLEwkeabZwKKi", + "patternLimit": 5, + "query": "", + "queryType": "pattern", + "refreshRate": 0, + "searchPattern": [ + { + "data": "explore_BOM_insights", + "id": "explore_BOM_insights", + "type": "QUERY" + }, + { + "data": "vertType", + "id": "2e9b80fd-030e-49fb-9c68-754bf2efca37", + "paramGlobalInput": "vertType", + "paramName": "vertType", + "paramType": "STRING", + "paramTypeReadonly": true, + "type": "PARAM" + }, + { + "data": "vertexId", + "id": "70e1db0f-1025-4a86-8fc3-03da2d5c8338", + "paramGlobalInput": "vertexId", + "paramName": "id", + "paramType": "STRING", + "paramTypeReadonly": true, + "type": "PARAM" + }, + { + "data": "depth", + "id": "23681b01-c596-4f2a-8208-212236a966d4", + "paramGlobalInput": "depth", + "paramName": "depth", + "paramType": "INT", + "paramTypeReadonly": true, + "type": "PARAM" + }, + { + "data": "upstream", + "id": "f622d4ae-0db7-4da1-af9b-54317cb0bbb3", + "paramGlobalInput": "upstream", + "paramName": "upstream", + "paramType": "BOOL", + "paramTypeReadonly": true, + "type": "PARAM" + } + ], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "", + "type": "internal-graph", + "version": "1763928813910151148" + }, + "wGYxHtNz9dmfUBHvh8vi8a": { + "chartSettings": { + "inputStates": [ + { + "dataType": "string", + "id": "input_35dZipma5w14zcPa9srhP7", + "name": "vertType", + "settings": { + "graphName": "AntiFraud", + "open": false, + "options": [ + { + "isCreatable": true, + "label": "BOM", + "value": "BOM" + }, + { + "isCreatable": true, + "label": "Material", + "value": "Material" + } + ], + "patternLimit": 300, + "query": "", + "searchPattern": [], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "useQuery": false + }, + "widgetType": "Dropdown" + }, + { + "dataType": "string", + "id": "input_rGNXpbuD8xm2Jp2jqYA3TR", + "name": "vertexId", + "settings": { + "graphName": "Supply_Chain_Management", + "open": false, + "options": [], + "patternLimit": 300, + "query": "INTERPRET QUERY(STRING vertType) FOR GRAPH Supply_Chain_Management {\n ListAccum @@ids;\n res = SELECT s from (BOM|Material):s \n where s.type == vertType\n ACCUM @@ids += s.id \n ;\n PRINT @@ids;\n}", + "queryType": "interactive", + "searchPattern": [], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "useQuery": true + }, + "widgetType": "Dropdown" + }, + { + "dataType": "number", + "id": "input_rHJAj5bR83viw6oAHPtzh4", + "name": "depth", + "settings": { + "max": "20" + }, + "widgetType": "Input" + }, + { + "dataType": "bool", + "id": "input_gvFDPVUBoEs7WCmUUYvKiA", + "name": "upstream", + "settings": { + "options": [ + { + "isCreatable": true, + "label": "TRUE", + "value": true + }, + { + "isCreatable": true, + "label": "FALSE", + "value": false + } + ] + }, + "widgetType": "Dropdown" + } + ] + }, + "graphName": "AntiFraud", + "hideWidgetName": false, + "id": "wGYxHtNz9dmfUBHvh8vi8a", + "patternLimit": 5, + "query": "", + "queryType": "pattern", + "refreshRate": 0, + "searchPattern": [], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "BOM", + "type": "Inputs", + "version": "1763928099165046718" + } + }, + "globalParameters": { + "LineDepth": { + "id": "input_1cRTwt6EKyjHGDuhpK7XUT", + "name": "LineDepth", + "type": "NUMBER", + "value": 5 + }, + "LineUpstream": { + "id": "input_p7HJXg2VhZhgeF1pK8hv4S", + "name": "LineUpstream", + "type": "BOOL", + "value": "true" + }, + "LineVertex": { + "id": "input_rAjS1bcFu9NBPSX8wdUK4U", + "name": "LineVertex", + "type": "STRING", + "value": "mat_101_China_74_3931_3634" + }, + "depth": { + "id": "input_rHJAj5bR83viw6oAHPtzh4", + "name": "depth", + "type": "NUMBER", + "value": 3 + }, + "upstream": { + "id": "input_gvFDPVUBoEs7WCmUUYvKiA", + "name": "upstream", + "type": "BOOL", + "value": "true" + }, + "vertType": { + "id": "input_35dZipma5w14zcPa9srhP7", + "name": "vertType", + "type": "STRING", + "value": "BOM" + }, + "vertexId": { + "id": "input_rGNXpbuD8xm2Jp2jqYA3TR", + "name": "vertexId", + "type": "STRING", + "value": "mat_1330_China_3665" + } + }, + "iconURL": "/insights/static/media/library.d3f7f207c6bb1d7be8e64045a19991b2.svg", + "id": "xB74jXwxq3KbSxpMEXvnQH", + "isDetail": true, + "isNew": false, + "layouts": { + "md": [ + { + "h": 43, + "i": "5ZQJVEuiq5cAxVxZWVqoLS", + "moved": false, + "static": false, + "w": 6, + "x": 6, + "y": 10 + }, + { + "h": 10, + "i": "7GwzFLnQ5dDDVtUtVx7mPv", + "moved": false, + "static": false, + "w": 6, + "x": 6, + "y": 0 + }, + { + "h": 43, + "i": "hx3rVgRRGfLEwkeabZwKKi", + "moved": false, + "static": false, + "w": 6, + "x": 0, + "y": 10 + }, + { + "h": 10, + "i": "wGYxHtNz9dmfUBHvh8vi8a", + "moved": false, + "static": false, + "w": 6, + "x": 0, + "y": 0 + } + ], + "xs": [ + { + "h": 10, + "i": "wGYxHtNz9dmfUBHvh8vi8a", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 0 + }, + { + "h": 12, + "i": "7GwzFLnQ5dDDVtUtVx7mPv", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 10 + }, + { + "h": 43, + "i": "hx3rVgRRGfLEwkeabZwKKi", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 22 + }, + { + "h": 43, + "i": "5ZQJVEuiq5cAxVxZWVqoLS", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 65 + } + ] + }, + "title": "BOM", + "version": "1763928024253252036", + "weight": 10 + } + ], + "title": "Supply Chain Management", + "userRoleForApp": "owner", + "version": "1764009595079839773" +} \ No newline at end of file diff --git a/agile_operations/supply_chain_management/queries/add_purchase_to_inventory.gsql b/agile_operations/supply_chain_management/queries/add_purchase_to_inventory.gsql new file mode 100644 index 00000000..f934b46b --- /dev/null +++ b/agile_operations/supply_chain_management/queries/add_purchase_to_inventory.gsql @@ -0,0 +1,62 @@ +CREATE OR REPLACE QUERY add_purchase_to_inventory(VERTEX purchase_order, SET invs){ + /* + Description: + Simulates the goods-receipt (GR) posting of a Purchase Order and instantly + shows the resulting inventory impact for the affected materials in the + specified Inventory_Held locations (storage locations / bins). + + This is a "what-if" inventory projection query — ideal for MRP simulation, + ATP/CTP validation, and real-time inventory planning without touching + the actual ERP system. + + Parameters: + purchase_order (VERTEX): + The Purchase Order vertex representing the inbound receipt + invs (SET): + Set of Inventory_Held primary IDs (storage locations) to check/update. + Use empty set {} to include all relevant locations. + + Output: + • Previous inventory quantity + last_updated timestamp per material/location + • Projected new inventory quantity after GR posting + • Clear message if no inventory exists in the specified locations + */ + + SumAccum @quantity; + MinAccum @date; + MapAccum>>> @@prev_inv_map, @@curr_inv_map; + STRING err, prev, curr; + + Start = {purchase_order}; + + poi = SELECT p FROM Start:s -(Has_Line_Number:i)- Line_Number:p + ACCUM + p.@date += s.date; + + prod = SELECT pr FROM poi:p -(reverse_Used_For:r)- SFC_Material:pr + ACCUM + pr.@quantity += p.quantity, + pr.@date += p.@date; + + inventories = to_vertex_set(invs,"Inventory_Held"); + + updated_inventory_1 = SELECT i FROM inventories:i -(Inventory_Has_Material:c)- SFC_Material:p + ACCUM + @@prev_inv_map += (i-> (p -> (i.last_updated -> i.quantity))); + + updated_inventory = SELECT i FROM inventories:i -(Inventory_Has_Material:c)- SFC_Material:p + ACCUM + INT quant = i.quantity + p.@quantity, + @@curr_inv_map += (i-> (p -> (p.@date -> quant))); + + + IF @@prev_inv_map.size()== 0 THEN + PRINT("There is no inventory for this product at specified inventory id."); + ELSE + PRINT( "The previous inventory for the product was- "); + PRINT(@@prev_inv_map); + PRINT("The current inventory for the product is- "); + PRINT(@@curr_inv_map); + END; + +} diff --git a/agile_operations/supply_chain_management/queries/check_shipment_capacity.gsql b/agile_operations/supply_chain_management/queries/check_shipment_capacity.gsql new file mode 100644 index 00000000..5913d154 --- /dev/null +++ b/agile_operations/supply_chain_management/queries/check_shipment_capacity.gsql @@ -0,0 +1,45 @@ +CREATE OR REPLACE QUERY check_shipment_capacity(VERTEX so){ + /* + Description: + Instant Order Fulfillment Status & Shipment Completion Check. + Compares the ordered quantity on a Sales Order against the quantity + actually shipped (via Shipment_Item vertices) and immediately reports: + • Total ordered quantity per material + • Quantity already fulfilled (shipped) + • Remaining unfulfilled quantity + Parameters: + so (VERTEX): + The Sales Order vertex to analyze + + Output: + @@prod_total_quantity → Total ordered quantity per material + @@prod_fulfillment → Quantity already shipped/fulfilled + @@prod_unfulfillment → Remaining open quantity (backorder + */ + + + MapAccum @@prod_fulfillment, @@prod_unfulfillment, @@prod_total_quantity; + MaxAccum @my_quantity; + + Start = {so}; + + soi = SELECT si FROM Start:s -(Has_Sales_Order_Item:i)- Sales_Order_Item:si; + + prod = SELECT p FROM soi:si -(For_Material:s)- SFC_Material:p + ACCUM + p.@my_quantity += si.quantity; + + ship = SELECT p FROM prod:p -(Carries_Product:c)- Shipment_Item:sh + ACCUM + IF p.@my_quantity > sh.quantity THEN + INT total_unfulfilled = p.@my_quantity - sh.quantity, + @@prod_fulfillment += (p -> sh.quantity), + @@prod_unfulfillment += (p -> total_unfulfilled), + @@prod_total_quantity += (p-> p.@my_quantity) + END; + + PRINT @@prod_total_quantity; + PRINT @@prod_fulfillment; + PRINT @@prod_unfulfillment; + +} diff --git a/agile_operations/supply_chain_management/queries/datewise_product_availability.gsql b/agile_operations/supply_chain_management/queries/datewise_product_availability.gsql new file mode 100644 index 00000000..d8286928 --- /dev/null +++ b/agile_operations/supply_chain_management/queries/datewise_product_availability.gsql @@ -0,0 +1,65 @@ +CREATE OR REPLACE QUERY datewise_product_availability(VERTEX sales, INT inventory_interval_threshold){ +/* + Description: + Real-time Available-to-Promise (ATP) with Inventory Freshness Control. + Answers the critical business question: + "Can we fulfill this Sales Order using only inventory that has been + physically counted or received within the last X months?" + + This is a strict, audit-ready ATP check that prevents promising + customers based on stale or "ghost" inventory — a major cause of + delivery failures in traditional ERP systems. + + Parameters: + sales (VERTEX): + The Sales Order to validate + inventory_interval_threshold (INT): + Maximum allowed age of inventory in months. + Example: 3 → only use inventory updated in the last 3 months + + Output: + • Clear fulfillment decision message + • Detailed map of which materials pass/fail the freshness + quantity check + • Ready for integration into order management UIs or ATP engines + */ + + TYPEDEF TUPLE myTuple; + + HeapAccum(1,quant ASC, inv ASC) @invHeap; + MapAccum> @product_sales_quantity; + MapAccum>> @@product_inventory; + ListAccum @@print_message; + MinAccum @prod_sales_date; + + Start = {sales}; + + soi = SELECT s FROM Start:so-(Has_Sales_Order_Item:i)-Sales_Order_Item:s + ACCUM + s.@prod_sales_date += so.date; + + prod = SELECT p FROM soi:s-(For_Material:r)-SFC_Material:p + ACCUM + p.@product_sales_quantity += (p->s.quantity), + p.@prod_sales_date += soi.@prod_sales_date; + + product_result = SELECT p FROM prod:p-(Inventory_Has_Material:c)- Inventory_Held:i + ACCUM + IF i.quantity >= p.@product_sales_quantity.get(p) AND i.last_updated >= datetime_sub(p.@prod_sales_date, INTERVAL inventory_interval_threshold MONTH) THEN + p.@invHeap += myTuple(i,i.quantity) + END + POST-ACCUM + IF p.@invHeap.size() > 0 THEN + @@product_inventory += (p->(p.@invHeap.top().inv->p.@invHeap.top().quant)), + p.@invHeap.pop() + END; + + IF @@product_inventory.size() == prod.size() THEN + @@print_message = "Sales Order can be satisfied for all products since they are available in inventory"; + ELSE + @@print_message = "Sales Order can only be satisfied for " + to_string(@@product_inventory.size()) + " count of products."; + END; + + PRINT @@print_message; + PRINT @@product_inventory; + } + diff --git a/agile_operations/supply_chain_management/queries/explore_bom.gsql b/agile_operations/supply_chain_management/queries/explore_bom.gsql new file mode 100644 index 00000000..4b726294 --- /dev/null +++ b/agile_operations/supply_chain_management/queries/explore_bom.gsql @@ -0,0 +1,48 @@ +CREATE OR REPLACE DISTRIBUTED QUERY explore_BOM(VERTEX input_vert, INT depth, BOOL upstream) FOR GRAPH Supply_Chain_Management { + /* + Query Name: explore_BOM + Full multi-level BOM explosion (downstream) or where-used analysis (upstream) + from any Material, BOM header, or Supplier. + + Key Use Cases: + • Explode a finished good into all components (any depth, even 20–30 levels) + • Reverse trace: "Which finished goods or BOMs use this raw material?" + • Critical for demand planning, costing, engineering change impact, + single-source risk analysis, and compliance reporting. + + Parameters: + input_vert : Starting vertex (Material Or BOM) + depth : Maximum traversal depth (set to 30+ for full explosion) + upstream : TRUE → Where-Used (who consumes this material?) + FALSE → BOM Explosion (what does this material consist of?) + */ + + SetAccum @@edges; + OrAccum @visited; + + // get src + verts = {input_vert}; + + // while loop + WHILE verts.size() > 0 LIMIT depth DO + // traverse up or down + IF upstream == TRUE THEN + verts = + SELECT t FROM verts:s -((Supplies|reverse_Has_Component_Material|reverse_Produced_By):e)- (Material|BOM):t + WHERE t.@visited == FALSE + ACCUM @@edges += e + POST-ACCUM + s.@visited = TRUE; + ELSE + verts = + SELECT t FROM verts:s -((reverse_Supplies|Has_Component_Material|Produced_By):e)- (Material|BOM|Supplier):t + WHERE t.@visited == FALSE + ACCUM @@edges += e + POST-ACCUM + s.@visited = TRUE; + END; + END; + + // print edges + PRINT @@edges; +} diff --git a/agile_operations/supply_chain_management/queries/explore_bom_line.gsql b/agile_operations/supply_chain_management/queries/explore_bom_line.gsql new file mode 100644 index 00000000..e3bb7cde --- /dev/null +++ b/agile_operations/supply_chain_management/queries/explore_bom_line.gsql @@ -0,0 +1,67 @@ +CREATE OR REPLACE DISTRIBUTED QUERY explore_BOM_line(VERTEX input_vert, INT depth, BOOL upstream, BOOL use_date_range, DATETIME start_date, DATETIME end_date) FOR GRAPH Supply_Chain_Management { + /* + Description: + End-to-end transactional lineage traversal (order-to-cash & procure-to-pay) + across actual production instances (SFC), sales orders, purchase orders, + shipments, and customers/suppliers. + + This is the "digital thread" query — it connects the physical flow + (what was actually built and shipped) with the commercial flow + (who ordered it and who supplied the components). + + Parameters: + input_vert (VERTEX): + Starting vertex — typically SFC_Material (actual batch), Sales_Order, + Purchase_Order, Customer, or Supplier + depth (INT): + Maximum traversal depth (usually 10–20 is sufficient for full lineage) + upstream (BOOL): + FALSE → Follow the flow downstream (e.g. raw material → finished good → customer) + TRUE → Follow the flow upstream (e.g. finished good → raw materials → supplier) + use_date_range (BOOL): + If TRUE, only traverse through dated vertices (Sales_Order, Purchase_Order, + SFC_Assembly) that fall within the specified window + start_date / end_date (DATETIME): + Optional time fence for filtering transactional vertices + + */ + + SetAccum @@edges; + OrAccum @visited; + + // get src + verts = {input_vert}; + + // while loop + WHILE verts.size() > 0 LIMIT depth DO + // traverse up or down + IF upstream == TRUE THEN + verts = + SELECT t FROM verts:s -((Has_Purchase_Order|Has_Line_Number|reverse_Used_For| + reverse_Has_Component_SFC|reverse_To_Be_Produced_By|reverse_For_Material| + reverse_Has_Sales_Order_Item|reverse_Has_Sales_Order):e)- + (Purchase_Order|Line_Number|SFC_Material|SFC_Assembly|Sales_Order_Item|Sales_Order|Customer):t + WHERE t.@visited == FALSE + // relevant vertices in date range (SFC_Assembly,Sales_Order,Purchase_Order) + ACCUM @@edges += e + POST-ACCUM + s.@visited = TRUE; + ELSE + verts = + SELECT t FROM verts:s -((reverse_Has_Purchase_Order|reverse_Has_Line_Number|Used_For| + Has_Component_SFC|To_Be_Produced_By|For_Material| + Has_Sales_Order_Item|Has_Sales_Order):e)- + (Supplier|Purchase_Order|Line_Number|SFC_Material|SFC_Assembly|Sales_Order_Item|Sales_Order):t + WHERE t.@visited == FALSE + // relevant vertices in date range (SFC_Assembly,Sales_Order,Purchase_Order) + ACCUM @@edges += e + POST-ACCUM + s.@visited = TRUE; + END; + END; + + // print edges + PRINT @@edges; + PRINT "explore_BOMLine works!"; +} + diff --git a/agile_operations/supply_chain_management/queries/get_biggest_customers.gsql b/agile_operations/supply_chain_management/queries/get_biggest_customers.gsql new file mode 100644 index 00000000..91574f14 --- /dev/null +++ b/agile_operations/supply_chain_management/queries/get_biggest_customers.gsql @@ -0,0 +1,52 @@ +CREATE OR REPLACE DISTRIBUTED QUERY get_biggest_customers(INT top_k, DATETIME start_date, DATETIME end_date, SET input_nations) FOR GRAPH Supply_Chain_Management { + /* + Description: + Top-K Customers by Delivered Quantity (or Revenue Proxy) in a selected period + with optional geographic filtering (by Nation / Region). + + Instantly ranks customers based on actual shipped/delivered volume from + Sales Orders — far more accurate than booked revenue or invoice data, + because it reflects real physical fulfillment. + + Parameters: + top_k (INT): + Number of top customers to return (e.g., 10, 20, 50) + start_date / end_date (DATETIME): + Time window for analysis (e.g., last 12 months, current FY) + input_nations (SET>): + Optional filter — only include customers from these countries. + Pass empty set {} to include all nations globally. + + Output: + Ranked list of Customer vertices with accumulated delivered quantity + Ready for visualization in GraphStudio, BI tools, or REST API exposure. + */ + + SumAccum @total_quantity; + + // if no nations, take all nations + nations = {input_nations}; + IF nations.size() == 0 THEN + nations = {Nation.*}; + END; + // get all customers in nations + customers = + SELECT t FROM nations:s -(reverse_Belongs_To)- Customer:t; + // filter their sales orders by date range + sales_orders = + SELECT t FROM customers:s -(Has_Sales_Order)- Sales_Order:t + //WHERE t.date BETWEEN start_date AND end_date + ; + // sum up the quantities of sales order items + sales_orders = + SELECT s FROM sales_orders:s -(Has_Sales_Order_Item)- Sales_Order_Item:t + ACCUM s.@total_quantity += t.quantity; + // sum up sums on customers and sort customers + customers = + SELECT t FROM sales_orders:s -(reverse_Has_Sales_Order)- Customer:t + ACCUM t.@total_quantity += s.@total_quantity + ORDER BY t.@total_quantity DESC + LIMIT top_k; + PRINT customers[customers.@total_quantity]; +} + diff --git a/agile_operations/supply_chain_management/queries/get_biggest_suppliers.gsql b/agile_operations/supply_chain_management/queries/get_biggest_suppliers.gsql new file mode 100644 index 00000000..cd5bc1f8 --- /dev/null +++ b/agile_operations/supply_chain_management/queries/get_biggest_suppliers.gsql @@ -0,0 +1,51 @@ +CREATE OR REPLACE DISTRIBUTED QUERY get_biggest_suppliers(INT top_k, DATETIME start_date, DATETIME end_date, SET input_nations) FOR GRAPH Supply_Chain_Management { + /* + Description: + Top-K Suppliers by Purchased/Inbound Volume in a selected time period + with optional geographic filtering (by Nation / Region). + + Ranks suppliers based on actual received or ordered quantity from + Purchase Orders — the most accurate measure of supplier spend and + strategic importance in manufacturing and distribution environments. + Parameters: + top_k (INT): + Number of top suppliers to return (e.g., 10, 25, 50) + start_date / end_date (DATETIME): + Analysis period (e.g., trailing 12 months, current fiscal year) + input_nations (SET>): + Optional country/region filter. + Pass empty set {} to include all suppliers globally. + + Output: + Ranked list of Supplier vertices with total purchased quantity + Ready for GraphStudio visualization, Power BI, Tableau, or REST API. + */ + + SumAccum @total_quantity; + + // if no nations, take all nations + nations = {input_nations}; + IF nations.size() == 0 THEN + nations = {Nation.*}; + END; + // get all suppliers in nations + suppliers = + SELECT t FROM nations:s -(reverse_Belongs_To)- Supplier:t; + // filter their purchase orders by date range + purchase_orders = + SELECT t FROM suppliers:s -(Has_Purchase_Order)- Purchase_Order:t + //WHERE t.date BETWEEN start_date AND end_date + ; + // sum up the quantities of line numbers + purchase_orders = + SELECT s FROM purchase_orders:s -(Has_Line_Number)- Line_Number:t + ACCUM s.@total_quantity += t.quantity; + // sum up sums on suppliers and sort suppliers + suppliers = + SELECT t FROM purchase_orders:s -(reverse_Has_Purchase_Order)- Supplier:t + ACCUM t.@total_quantity += s.@total_quantity + ORDER BY t.@total_quantity DESC + LIMIT top_k; + PRINT suppliers[suppliers.@total_quantity]; +} + diff --git a/agile_operations/supply_chain_management/queries/plant_failure_impact_nodes.gsql b/agile_operations/supply_chain_management/queries/plant_failure_impact_nodes.gsql new file mode 100644 index 00000000..fd256a16 --- /dev/null +++ b/agile_operations/supply_chain_management/queries/plant_failure_impact_nodes.gsql @@ -0,0 +1,132 @@ + CREATE OR REPLACE QUERY plant_failure_impact_nodes (VERTEX start_inv,INT max_hops = 8) { + /* + Description: + Critical Business Continuity & Risk Impact Analysis Query. + Answers the mission-critical question: + "If this manufacturing plant goes down tomorrow (fire, strike, natural disaster), + which finished goods, customer orders, and end customers will be directly impacted?" + + Performs a constrained BFS from a Plant → through current inventory → required materials + → open sales order items → customers. Returns the full downstream impact path + within seconds — impossible in traditional ERP or BI systems. + Parameters: + start_plant (VERTEX): + The manufacturing or distribution plant to simulate failure of + max_hops (INT, default 8): + Maximum graph distance to traverse. 8 is sufficient for full global impact. + + Output: + • All reachable Customer vertices (affected end customers) + • Full path of edges showing exactly how disruption propagates + • Visualizable in GraphStudio as impact network + */ + + TYPEDEF TUPLE Edge_Info; + SumAccum @@num_hops_traversed; // current step + ListAccum @@error_message_list; + ListAccum @local_from_edges; + OrAccum @or_visited; + SetAccum @@edges_to_display; + + SetAccum @@end_set; + SetAccum @@vertices_to_display_set; + SetAccum @@next_traverse_set; + + SetAccum @@e_type_set; + + @@e_type_set += ( + "Plant_Has_Inventory", + "Inventory_Has_Material", + "reverse_For_Material", + "reverse_Has_Sales_Order_Item", + "Has_Sales_Order" + ); + + + start_vertices(ANY) = {start_inv}; + end_vertices(ANY) = {Customer.*}; + + // initialize the step + @@num_hops_traversed = 0; + + // start from the source node + start_vertices = SELECT s + FROM start_vertices:s + POST-ACCUM + s.@or_visited += TRUE, + @@vertices_to_display_set += s + ; + + // Select end vertex for visualization + end_vertices = SELECT s + FROM end_vertices:s + POST-ACCUM + @@vertices_to_display_set += s + ; + + // Check if end is visited. + visited_end = SELECT s + FROM end_vertices:s + WHERE s.@or_visited + ; + + // breadth-first search from source + WHILE (start_vertices.size() > 0 AND visited_end.size() == 0) LIMIT max_hops DO + @@num_hops_traversed += 1; + + start_vertices = SELECT t + FROM start_vertices:s -(@@e_type_set>:e)- :t + WHERE t.@or_visited == FALSE + ACCUM + t.@local_from_edges += Edge_Info(s, t, e) + POST-ACCUM + t.@or_visited += TRUE + ; + + visited_end = SELECT s + FROM end_vertices:s + WHERE s.@or_visited + ; + END; + + end_traverse_back = visited_end; + + // go from target to source to collect the edges + vertices visited + print end_traverse_back; + WHILE (end_traverse_back.size() > 0) DO + end_traverse_back = SELECT s + FROM end_traverse_back:s + POST-ACCUM + FOREACH e_info IN s.@local_from_edges DO + @@next_traverse_set += e_info.from_vertex, + @@edges_to_display += e_info.curr_edge + END + ; + + + end_traverse_back = {@@next_traverse_set}; + @@vertices_to_display_set += @@next_traverse_set; + @@next_traverse_set.clear(); + END; + + vertices_to_display = {@@vertices_to_display_set}; + + IF ( + @@end_set.size() == 1 AND + visited_end.size() == 0 + ) THEN + STRING not_found_error_str = ( + "Path "+ "not found within " + to_string(max_hops) + " hops. " + + to_string(@@num_hops_traversed) + " hops were traversed in total." + ); + + @@error_message_list += not_found_error_str; + END; + + PRINT + end_traverse_back, + @@error_message_list AS error_message_list + ; + PRINT vertices_to_display, @@edges_to_display AS edges_to_display; +} + diff --git a/agile_operations/supply_chain_management/queries/product_quantity_sales_order.gsql b/agile_operations/supply_chain_management/queries/product_quantity_sales_order.gsql new file mode 100644 index 00000000..4aab3a95 --- /dev/null +++ b/agile_operations/supply_chain_management/queries/product_quantity_sales_order.gsql @@ -0,0 +1,57 @@ +CREATE OR REPLACE QUERY product_quantity_sales_order(VERTEX sales){ + /* + Description: + Real-time Available-to-Promise (ATP) Check – Basic Edition. + Answers the fundamental order fulfillment question: + "Do we have enough on-hand inventory (any location, any batch) to fulfill this sales order today?" + + Compares required quantity per material on a Sales Order against current + global inventory (Inventory_Held) and instantly reports whether the order + can be fully satisfied. + + Parameters: + sales (VERTEX): + The Sales Order vertex to validate + + Output: + • Clear fulfillment decision message + • Detailed map of materials with sufficient inventory (location + quantity) + • Immediate visibility into which items block full fulfillment + + */ + + TYPEDEF TUPLE myTuple; + + HeapAccum(1,quant ASC, inv ASC) @invHeap; + MapAccum> @product_sales_quantity; + MapAccum>> @@product_inventory; + ListAccum @@print_message; + + Start = {sales}; + + soi = SELECT s FROM Start:so-(Has_Sales_Order_Item:i)-Sales_Order_Item:s; + + prod = SELECT p FROM soi:s-(For_Material:r)-SFC_Material:p + ACCUM + p.@product_sales_quantity += (p->s.quantity); + + product_result = SELECT p FROM prod:p-(Inventory_Has_Material:c)- Inventory_Held:i + ACCUM + IF i.quantity >= p.@product_sales_quantity.get(p) THEN + p.@invHeap += myTuple(i,i.quantity) + END + POST-ACCUM + @@product_inventory += (p->(p.@invHeap.top().inv->p.@invHeap.top().quant)), + p.@invHeap.pop(); + + IF @@product_inventory.size() == prod.size() THEN + @@print_message = "Sales Order can be satisfied for all products since they are available in inventory"; + ELSE + INT current = prod.size() -@@product_inventory.size(); + @@print_message = "Sales Order can only be satisfied for " + to_string(current) + "count of products."; + END; + + PRINT @@print_message; + PRINT @@product_inventory; + } + diff --git a/agile_operations/supply_chain_management/queries/queries.gsql b/agile_operations/supply_chain_management/queries/queries.gsql deleted file mode 100644 index dccf3ad1..00000000 --- a/agile_operations/supply_chain_management/queries/queries.gsql +++ /dev/null @@ -1,568 +0,0 @@ -CREATE DISTRIBUTED QUERY explore_BOM(VERTEX input_vert, INT depth, BOOL upstream) FOR GRAPH Supply_Chain_Management { - - SetAccum @@edges; - OrAccum @visited; - - // get src - verts = {input_vert}; - - // while loop - WHILE verts.size() > 0 LIMIT depth DO - // traverse up or down - IF upstream == TRUE THEN - verts = - SELECT t FROM verts:s -((Supplies|reverse_Has_Component_Material|reverse_Produced_By):e)- (Material|BOM):t - WHERE t.@visited == FALSE - ACCUM @@edges += e - POST-ACCUM - s.@visited = TRUE; - ELSE - verts = - SELECT t FROM verts:s -((reverse_Supplies|Has_Component_Material|Produced_By):e)- (Material|BOM|Supplier):t - WHERE t.@visited == FALSE - ACCUM @@edges += e - POST-ACCUM - s.@visited = TRUE; - END; - END; - - // print edges - PRINT @@edges; -} - -CREATE DISTRIBUTED QUERY explore_BOM_line( - VERTEX input_vert, - INT depth, - BOOL upstream, - BOOL use_date_range, - DATETIME start_date, - DATETIME end_date) FOR GRAPH Supply_Chain_Management { - - SetAccum @@edges; - OrAccum @visited; - - // get src - verts = {input_vert}; - - // while loop - WHILE verts.size() > 0 LIMIT depth DO - // traverse up or down - IF upstream == TRUE THEN - verts = - SELECT t FROM verts:s -((Has_Purchase_Order|Has_Line_Number|reverse_Used_For| - reverse_Has_Component_SFC|reverse_To_Be_Produced_By|reverse_For_Material| - reverse_Has_Sales_Order_Item|reverse_Has_Sales_Order):e)- - (Purchase_Order|Line_Number|SFC_Material|SFC_Assembly|Sales_Order_Item|Sales_Order|Customer):t - WHERE t.@visited == FALSE - // relevant vertices in date range (SFC_Assembly,Sales_Order,Purchase_Order) - ACCUM @@edges += e - POST-ACCUM - s.@visited = TRUE; - ELSE - verts = - SELECT t FROM verts:s -((reverse_Has_Purchase_Order|reverse_Has_Line_Number|Used_For| - Has_Component_SFC|To_Be_Produced_By|For_Material| - Has_Sales_Order_Item|Has_Sales_Order):e)- - (Supplier|Purchase_Order|Line_Number|SFC_Material|SFC_Assembly|Sales_Order_Item|Sales_Order):t - WHERE t.@visited == FALSE - // relevant vertices in date range (SFC_Assembly,Sales_Order,Purchase_Order) - ACCUM @@edges += e - POST-ACCUM - s.@visited = TRUE; - END; - END; - - // print edges - PRINT @@edges; - PRINT "explore_BOMLine works!"; -} - -CREATE DISTRIBUTED QUERY get_biggest_customers(INT top_k, DATETIME start_date, DATETIME end_date, SET input_nations) FOR GRAPH Supply_Chain_Management { - - SumAccum @total_quantity; - - // if no nations, take all nations - nations = {input_nations}; - IF nations.size() == 0 THEN - nations = {Nation.*}; - END; - // get all customers in nations - customers = - SELECT t FROM nations:s -(reverse_Belongs_To)- Customer:t; - // filter their sales orders by date range - sales_orders = - SELECT t FROM customers:s -(Has_Sales_Order)- Sales_Order:t - WHERE t.date BETWEEN start_date AND end_date; - // sum up the quantities of sales order items - sales_orders = - SELECT s FROM sales_orders:s -(Has_Sales_Order_Item)- Sales_Order_Item:t - ACCUM s.@total_quantity += t.quantity; - // sum up sums on customers and sort customers - customers = - SELECT t FROM sales_orders:s -(reverse_Has_Sales_Order)- Customer:t - ACCUM t.@total_quantity += s.@total_quantity - ORDER BY t.@total_quantity DESC - LIMIT top_k; - PRINT customers[customers.@total_quantity]; -} - -CREATE DISTRIBUTED QUERY get_biggest_suppliers(INT top_k, DATETIME start_date, DATETIME end_date, SET input_nations) FOR GRAPH Supply_Chain_Management { - - SumAccum @total_quantity; - - // if no nations, take all nations - nations = {input_nations}; - IF nations.size() == 0 THEN - nations = {Nation.*}; - END; - // get all suppliers in nations - suppliers = - SELECT t FROM nations:s -(reverse_Belongs_To)- Supplier:t; - // filter their purchase orders by date range - purchase_orders = - SELECT t FROM suppliers:s -(Has_Purchase_Order)- Purchase_Order:t - WHERE t.date BETWEEN start_date AND end_date; - // sum up the quantities of line numbers - purchase_orders = - SELECT s FROM purchase_orders:s -(Has_Line_Number)- Line_Number:t - ACCUM s.@total_quantity += t.quantity; - // sum up sums on suppliers and sort suppliers - suppliers = - SELECT t FROM purchase_orders:s -(reverse_Has_Purchase_Order)- Supplier:t - ACCUM t.@total_quantity += s.@total_quantity - ORDER BY t.@total_quantity DESC - LIMIT top_k; - PRINT suppliers[suppliers.@total_quantity]; -} - -CREATE DISTRIBUTED QUERY trace_BOM_line_nations(VERTEX input_fg) FOR GRAPH Supply_Chain_Management { - OrAccum @visited; - MaxAccum @@src_nation, @rm_nation; - SumAccum @@domestic_quantity, @@international_quantity; - - verts (ANY) = {input_fg}; - - sfc_assembly = - SELECT t FROM verts:s -(To_Be_Produced_By)- SFC_Assembly:t; - bom = - SELECT t FROM sfc_assembly:s -(Based_On)- BOM:t; - plant = - SELECT t FROM bom:s -(Happens_At)- Plant:t; - src_nation = - SELECT t FROM plant:s -(Belongs_To)- Nation:t - POST-ACCUM @@src_nation += t; - - raw_materials (ANY) = {}; - WHILE verts.size() > 0 DO - // traverse until suppliers - // categorize raw materials as domestic or international - verts = - SELECT t FROM verts:s -((Has_Component_SFC|To_Be_Produced_By|For_Material):e)- - (SFC_Material|SFC_Assembly):t - WHERE t.@visited == FALSE - POST-ACCUM - s.@visited = TRUE; - - // collect raw materials (sfc_materials with edge to line number) - temp_raw_materials = - SELECT s FROM verts:s WHERE s.outdegree("Used_For") > 0; - raw_materials = raw_materials UNION temp_raw_materials; - END; - - // traverse from raw materials -> line number -> purchase order -> supplier (pass in MaxAccum) - line_numbers = - SELECT t FROM raw_materials:s -(Used_For)- Line_Number:t - POST-ACCUM t.@visited += TRUE; - purchase_orders = - SELECT t FROM line_numbers:s -(reverse_Has_Line_Number)- Purchase_Order:t - ACCUM t.@visited += TRUE; - suppliers = - SELECT t FROM purchase_orders:s -(reverse_Has_Purchase_Order)- Supplier:t - POST-ACCUM t.@visited += TRUE; - suppliers = - SELECT s FROM suppliers:s -(Belongs_To)- Nation:t - POST-ACCUM t.@rm_nation += t; - purchase_orders = - SELECT s FROM purchase_orders:s -(reverse_Has_Purchase_Order)- Supplier:t - ACCUM s.@rm_nation += t.@rm_nation; - line_numbers = - SELECT s FROM line_numbers:s -(reverse_Has_Line_Number)- Purchase_Order:t - ACCUM s.@rm_nation += t.@rm_nation; - raw_materials = - SELECT s FROM raw_materials:s -(Used_For)- Line_Number:t - ACCUM - IF t.@rm_nation == @@src_nation THEN - @@domestic_quantity += s.quantity - ELSE - @@international_quantity += s.quantity - END; - - PRINT @@domestic_quantity, @@international_quantity; -} - -CREATE OR REPLACE QUERY add_purchase_to_inventory(VERTEX purchase_order, SET invs){ - /* This query updates the inventory for the product supplied by the supplier */ - - SumAccum @quantity; - MinAccum @date; - MapAccum>>> @@prev_inv_map, @@curr_inv_map; - STRING err, prev, curr; - - Start = {purchase_order}; - - poi = SELECT p FROM Start:s -(Has_Line_Number:i)- Line_Number:p - ACCUM - p.@date += s.date; - - prod = SELECT pr FROM poi:p -(reverse_Used_For:r)- SFC_Material:pr - ACCUM - pr.@quantity += p.quantity, - pr.@date += p.@date; - - inventories = to_vertex_set(invs,"Inventory_Held"); - - updated_inventory_1 = SELECT i FROM inventories:i -(Inventory_Has_Material:c)- SFC_Material:p - ACCUM - @@prev_inv_map += (i-> (p -> (i.last_updated -> i.quantity))); - - updated_inventory = SELECT i FROM inventories:i -(Inventory_Has_Material:c)- SFC_Material:p - ACCUM - INT quant = i.quantity + p.@quantity, - @@curr_inv_map += (i-> (p -> (p.@date -> quant))); - - - IF @@prev_inv_map.size()== 0 THEN - PRINT("There is no inventory for this product at specified inventory id."); - ELSE - PRINT( "The previous inventory for the product was- "); - PRINT(@@prev_inv_map); - PRINT("The current inventory for the product is- "); - PRINT(@@curr_inv_map); - END; - -} - -CREATE OR REPLACE QUERY check_shipment_capacity(VERTEX so){ - /* This query checks shipment capacity and sales order quantity and to check if the order has been fulfilled */ - - - MapAccum @@prod_fulfillment, @@prod_unfulfillment, @@prod_total_quantity; - MaxAccum @my_quantity; - - Start = {so}; - - soi = SELECT si FROM Start:s -(Has_Sales_Order_Item:i)- Sales_Order_Item:si; - - prod = SELECT p FROM soi:si -(For_Material:s)- SFC_Material:p - ACCUM - p.@my_quantity += si.quantity; - - ship = SELECT p FROM prod:p -(Carries_Product:c)- Shipment_Item:sh - ACCUM - IF p.@my_quantity > sh.quantity THEN - INT total_unfulfilled = p.@my_quantity - sh.quantity, - @@prod_fulfillment += (p -> sh.quantity), - @@prod_unfulfillment += (p -> total_unfulfilled), - @@prod_total_quantity += (p-> p.@my_quantity) - END; - - PRINT @@prod_total_quantity; - PRINT @@prod_fulfillment; - PRINT @@prod_unfulfillment; - -} - -CREATE OR REPLACE QUERY datewise_product_availability(VERTEX sales, INT inventory_interval_threshold){ - /* This query gets the product quantity required in a sales order and checks inventory for the product and checks the date when the - inventory was last updated. If inventory was updated more than inventory_interval_threshold ago, order will not be fulfilled */ - - TYPEDEF TUPLE myTuple; - - HeapAccum(1,quant ASC, inv ASC) @invHeap; - MapAccum> @product_sales_quantity; - MapAccum>> @@product_inventory; - ListAccum @@print_message; - MinAccum @prod_sales_date; - - Start = {sales}; - - soi = SELECT s FROM Start:so-(Has_Sales_Order_Item:i)-Sales_Order_Item:s - ACCUM - s.@prod_sales_date += so.date; - - prod = SELECT p FROM soi:s-(For_Material:r)-SFC_Material:p - ACCUM - p.@product_sales_quantity += (p->s.quantity), - p.@prod_sales_date += soi.@prod_sales_date; - - product_result = SELECT p FROM prod:p-(Inventory_Has_Material:c)- Inventory_Held:i - ACCUM - IF i.quantity >= p.@product_sales_quantity.get(p) AND i.last_updated >= datetime_sub(p.@prod_sales_date, INTERVAL inventory_interval_threshold MONTH) THEN - p.@invHeap += myTuple(i,i.quantity) - END - POST-ACCUM - IF p.@invHeap.size() > 0 THEN - @@product_inventory += (p->(p.@invHeap.top().inv->p.@invHeap.top().quant)), - p.@invHeap.pop() - END; - - IF @@product_inventory.size() == prod.size() THEN - @@print_message = "Sales Order can be satisfied for all products since they are available in inventory"; - ELSE - @@print_message = "Sales Order can only be satisfied for " + to_string(@@product_inventory.size()) + " count of products."; - END; - - PRINT @@print_message; - PRINT @@product_inventory; - } - - CREATE OR REPLACE QUERY plant_failure_impact_nodes (VERTEX start_inv,INT max_hops = 8) { - // BFS Shortest Path - base version that can traverse through all these edges: - // Stores, Contains, SO_Requests, Includes_SO, Places - // Adapted from the following BFS algorithm: - // https://github.com/tigergraph/gsql-graph-algorithms/blob/master/algorithms/Path/bfs/tg_bfs.gsql - - TYPEDEF TUPLE Edge_Info; - SumAccum @@num_hops_traversed; // current step - ListAccum @@error_message_list; - ListAccum @local_from_edges; - OrAccum @or_visited; - SetAccum @@edges_to_display; - - SetAccum @@end_set; - SetAccum @@vertices_to_display_set; - SetAccum @@next_traverse_set; - - SetAccum @@e_type_set; - - @@e_type_set += ( - "Plant_Has_Inventory", - "Inventory_Has_Material", - "reverse_For_Material", - "reverse_Has_Sales_Order_Item", - "Has_Sales_Order" - ); - - - start_vertices(ANY) = {start_inv}; - end_vertices(ANY) = {Customer.*}; - - // initialize the step - @@num_hops_traversed = 0; - - // start from the source node - start_vertices = SELECT s - FROM start_vertices:s - POST-ACCUM - s.@or_visited += TRUE, - @@vertices_to_display_set += s - ; - - // Select end vertex for visualization - end_vertices = SELECT s - FROM end_vertices:s - POST-ACCUM - @@vertices_to_display_set += s - ; - - // Check if end is visited. - visited_end = SELECT s - FROM end_vertices:s - WHERE s.@or_visited - ; - - // breadth-first search from source - WHILE (start_vertices.size() > 0 AND visited_end.size() == 0) LIMIT max_hops DO - @@num_hops_traversed += 1; - - start_vertices = SELECT t - FROM start_vertices:s -(@@e_type_set>:e)- :t - WHERE t.@or_visited == FALSE - ACCUM - t.@local_from_edges += Edge_Info(s, t, e) - POST-ACCUM - t.@or_visited += TRUE - ; - - visited_end = SELECT s - FROM end_vertices:s - WHERE s.@or_visited - ; - END; - - end_traverse_back = visited_end; - - // go from target to source to collect the edges + vertices visited - WHILE (end_traverse_back.size() > 0) DO - end_traverse_back = SELECT s - FROM end_traverse_back:s - POST-ACCUM - FOREACH e_info IN s.@local_from_edges DO - @@next_traverse_set += e_info.from_vertex, - @@edges_to_display += e_info.curr_edge - END - ; - - - end_traverse_back = {@@next_traverse_set}; - @@vertices_to_display_set += @@next_traverse_set; - @@next_traverse_set.clear(); - END; - - vertices_to_display = {@@vertices_to_display_set}; - - IF ( - @@end_set.size() == 1 AND - visited_end.size() == 0 - ) THEN - STRING not_found_error_str = ( - "Path "+ "not found within " + to_string(max_hops) + " hops. " + - to_string(@@num_hops_traversed) + " hops were traversed in total." - ); - - @@error_message_list += not_found_error_str; - END; - - PRINT - end_traverse_back, - @@error_message_list AS error_message_list - ; - PRINT vertices_to_display, @@edges_to_display AS edges_to_display; -} - -CREATE OR REPLACE QUERY product_quantity_sales_order(VERTEX sales){ - /* This query gets the product quantity required in a sales order and checks inventory for the product */ - - TYPEDEF TUPLE myTuple; - - HeapAccum(1,quant ASC, inv ASC) @invHeap; - MapAccum> @product_sales_quantity; - MapAccum>> @@product_inventory; - ListAccum @@print_message; - - Start = {sales}; - - soi = SELECT s FROM Start:so-(Has_Sales_Order_Item:i)-Sales_Order_Item:s; - - prod = SELECT p FROM soi:s-(For_Material:r)-SFC_Material:p - ACCUM - p.@product_sales_quantity += (p->s.quantity); - - product_result = SELECT p FROM prod:p-(Inventory_Has_Material:c)- Inventory_Held:i - ACCUM - IF i.quantity >= p.@product_sales_quantity.get(p) THEN - p.@invHeap += myTuple(i,i.quantity) - END - POST-ACCUM - @@product_inventory += (p->(p.@invHeap.top().inv->p.@invHeap.top().quant)), - p.@invHeap.pop(); - - IF @@product_inventory.size() == prod.size() THEN - @@print_message = "Sales Order can be satisfied for all products since they are available in inventory"; - ELSE - INT current = prod.size() -@@product_inventory.size(); - @@print_message = "Sales Order can only be satisfied for " + to_string(current) + "count of products."; - END; - - PRINT @@print_message; - PRINT @@product_inventory; - } - - CREATE OR REPLACE QUERY top_k_products_purchase_order(INT k = 10) { - /* Get the top purchased products from purchase orders */ - - TYPEDEF TUPLE prod_tuple; - - HeapAccum(k,quantity DESC, p ASC) @@prod_heap; - - Start = {Purchase_Order.*}; - - res = SELECT p FROM Start:s -(Has_Line_Number:i)- Line_Number:p; - - res = SELECT p FROM res:r -(reverse_Used_For:s)- SFC_Material:p - ACCUM - @@prod_heap += prod_tuple(p,r.quantity); - - PRINT @@prod_heap; -} - -CREATE OR REPLACE QUERY top_k_products_sales_order(INT k = 10) { - /* Get the top purchased products from sales orders */ - - TYPEDEF TUPLE prod_tuple; - - HeapAccum(k,quantity DESC, p ASC) @@prod_heap; - - Start = {Sales_Order.*}; - - res = SELECT p FROM Start:s -(Has_Sales_Order_Item:i)- Sales_Order_Item:p; - - res = SELECT p FROM res:r -(For_Material:s)- SFC_Material:p - ACCUM - @@prod_heap += prod_tuple(p,r.quantity); - - PRINT @@prod_heap; -} - -CREATE OR REPLACE QUERY unfulfilled_orders(Set> sales_set) { - /* This query gets multiple sales orders as parameters and checks inventory for - products in the sales order. It fulfills order and keeps track of inventory level. */ - - MapAccum> @prod_per_sales; - MapAccum>> @@prod_per_inv; - MapAccum> @@total_prod_inv; - ListAccum @@unfulfilled_orders; - - OrAccum @order_fulfilled = TRUE; - - Start = {sales_set}; - - res = SELECT si FROM Start:s-(Has_Sales_Order_Item:i)-Sales_Order_Item:si; - - res = SELECT si FROM res:si-(For_Material:r)-SFC_Material:p - ACCUM - si.@prod_per_sales += (p-> si.quantity); - - inv = SELECT i FROM Inventory_Held:i -(Inventory_Has_Material:c)- SFC_Material:p - ACCUM - @@prod_per_inv += (p->(i->i.quantity)), - @@total_prod_inv += (p->i.quantity); - - - res_1 = SELECT si FROM res:si-(For_Material:r)-SFC_Material:p - ACCUM - FOREACH (key,value) IN @@prod_per_inv.get(p) DO - IF @@total_prod_inv.get(p) >= si.@prod_per_sales.get(p) THEN - BREAK - END - END, - IF @@total_prod_inv.get(p) < si.@prod_per_sales.get(p) THEN - si.@order_fulfilled += FALSE - END; - - PRINT @@prod_per_inv AS Previous_Product_Inventory; - - res_2 = SELECT si FROM res:si-(For_Material:r)-SFC_Material:p - ACCUM - IF NOT si.@order_fulfilled THEN - @@unfulfilled_orders += si - ELSE - FOREACH (key,value) IN @@prod_per_inv.get(p) DO - IF value >= si.@prod_per_sales.get(p) THEN - INT val = value - si.@prod_per_sales.get(p), - @@prod_per_inv += (p->(key->val)), - BREAK - ELSE - si.@prod_per_sales += (p->si.@prod_per_sales.get(p)-value), - @@prod_per_inv += (p->(key->0)) - END - END - END; - - PRINT @@prod_per_inv AS Current_Product_Inventory; - - PRINT @@unfulfilled_orders AS Orders_that_cannot_be_fulfilled; - PRINT res_2; - - -} \ No newline at end of file diff --git a/agile_operations/supply_chain_management/queries/top_k_product_sales_order.gsql b/agile_operations/supply_chain_management/queries/top_k_product_sales_order.gsql new file mode 100644 index 00000000..a8df4518 --- /dev/null +++ b/agile_operations/supply_chain_management/queries/top_k_product_sales_order.gsql @@ -0,0 +1,32 @@ +CREATE OR REPLACE QUERY top_k_products_sales_order(INT k = 10) { + /* + Description: + Global Top-K Best-Selling / Most Demanded Materials (by total ordered quantity) + across all Sales Orders in the entire graph. + + This is the true "voice of the customer" — reveals which finished goods, + semi-finished items, or configurable materials drive real revenue and + demand volume. Essential for S&OP, demand planning, and product strategy. + Parameters: + k (INT, default 10): + Number of top-selling materials to return (e.g., 10, 20, 50, 100) + Output: + Ranked heap of SFC_Material vertices with total sold/ordered quantity + + */ + + TYPEDEF TUPLE prod_tuple; + + HeapAccum(k,quantity DESC, p ASC) @@prod_heap; + + Start = {Sales_Order.*}; + + res = SELECT p FROM Start:s -(Has_Sales_Order_Item:i)- Sales_Order_Item:p; + + res = SELECT p FROM res:r -(For_Material:s)- SFC_Material:p + ACCUM + @@prod_heap += prod_tuple(p,r.quantity); + + PRINT @@prod_heap; +} + diff --git a/agile_operations/supply_chain_management/queries/top_k_products_purchase_order.gsql b/agile_operations/supply_chain_management/queries/top_k_products_purchase_order.gsql new file mode 100644 index 00000000..f2af3392 --- /dev/null +++ b/agile_operations/supply_chain_management/queries/top_k_products_purchase_order.gsql @@ -0,0 +1,32 @@ + CREATE OR REPLACE QUERY top_k_products_purchase_order(INT k = 10) { + /* + Description: + Global Top-K Most Purchased Materials (by total ordered quantity) + across all Purchase Orders in the entire graph. + + Reveals true consumption drivers — the raw, semi-finished, or finished + materials that dominate procurement volume. This is the real "voice of + the supply chain" for demand and spend patterns. + + Parameters: + k (INT, default 10): + Number of top materials to return (e.g., 10, 25, 50, 100) + + Output: + Ranked heap of SFC_Material vertices with total purchased quantity + */ + + TYPEDEF TUPLE prod_tuple; + + HeapAccum(k,quantity DESC, p ASC) @@prod_heap; + + Start = {Purchase_Order.*}; + + res = SELECT p FROM Start:s -(Has_Line_Number:i)- Line_Number:p; + + res = SELECT p FROM res:r -(reverse_Used_For:s)- SFC_Material:p + ACCUM + @@prod_heap += prod_tuple(p,r.quantity); + + PRINT @@prod_heap; +} diff --git a/agile_operations/supply_chain_management/queries/trace_bom_line_nations.gsql b/agile_operations/supply_chain_management/queries/trace_bom_line_nations.gsql new file mode 100644 index 00000000..0899c415 --- /dev/null +++ b/agile_operations/supply_chain_management/queries/trace_bom_line_nations.gsql @@ -0,0 +1,86 @@ +CREATE OR REPLACE DISTRIBUTED QUERY trace_BOM_line_nations(VERTEX input_fg) FOR GRAPH Supply_Chain_Management { + /* + Description: + Domestic vs. International Sourcing Analysis for a Specific Finished Good Batch. + + Starting from a real produced instance (SFC_Material) of a finished good, + this query traces the full transactional BOM lineage upstream through + actual production and procurement events to determine: + • What quantity of input materials was sourced domestically + • What quantity was sourced internationally + + This is a true "Country of Origin" and supply chain sovereignty query — + critical for compliance, risk, ESG, trade policy, and cost analysis. + + Parameters: + input_fg (VERTEX): + A specific produced batch/instance of a finished good (actual SFC_Material vertex) + + Output: + @@domestic_quantity → Total quantity sourced from the same nation as the final assembly plant + @@international_quantity → Total quantity sourced from all other nations + */ + OrAccum @visited; + MaxAccum @@src_nation, @rm_nation; + SumAccum @@domestic_quantity, @@international_quantity; + + verts (ANY) = {input_fg}; + + sfc_assembly = + SELECT t FROM verts:s -(To_Be_Produced_By)- SFC_Assembly:t; + bom = + SELECT t FROM sfc_assembly:s -(Based_On)- BOM:t; + plant = + SELECT t FROM bom:s -(Happens_At)- Plant:t; + src_nation = + SELECT t FROM plant:s -(Belongs_To)- Nation:t + POST-ACCUM @@src_nation += t; + + raw_materials (ANY) = {}; + WHILE verts.size() > 0 DO + // traverse until suppliers + // categorize raw materials as domestic or international + verts = + SELECT t FROM verts:s -((Has_Component_SFC|To_Be_Produced_By|For_Material):e)- + (SFC_Material|SFC_Assembly):t + WHERE t.@visited == FALSE + POST-ACCUM + s.@visited = TRUE; + + // collect raw materials (sfc_materials with edge to line number) + temp_raw_materials = + SELECT s FROM verts:s WHERE s.outdegree("Used_For") > 0; + raw_materials = raw_materials UNION temp_raw_materials; + END; + + // traverse from raw materials -> line number -> purchase order -> supplier (pass in MaxAccum) + line_numbers = + SELECT t FROM raw_materials:s -(Used_For)- Line_Number:t + POST-ACCUM t.@visited += TRUE; + purchase_orders = + SELECT t FROM line_numbers:s -(reverse_Has_Line_Number)- Purchase_Order:t + ACCUM t.@visited += TRUE; + suppliers = + SELECT t FROM purchase_orders:s -(reverse_Has_Purchase_Order)- Supplier:t + POST-ACCUM t.@visited += TRUE; + suppliers = + SELECT s FROM suppliers:s -(Belongs_To)- Nation:t + POST-ACCUM t.@rm_nation += t; + purchase_orders = + SELECT s FROM purchase_orders:s -(reverse_Has_Purchase_Order)- Supplier:t + ACCUM s.@rm_nation += t.@rm_nation; + line_numbers = + SELECT s FROM line_numbers:s -(reverse_Has_Line_Number)- Purchase_Order:t + ACCUM s.@rm_nation += t.@rm_nation; + raw_materials = + SELECT s FROM raw_materials:s -(Used_For)- Line_Number:t + ACCUM + IF t.@rm_nation == @@src_nation THEN + @@domestic_quantity += s.quantity + ELSE + @@international_quantity += s.quantity + END; + + PRINT @@domestic_quantity, @@international_quantity; +} + diff --git a/agile_operations/supply_chain_management/queries/unfulfilled_orders.gsql b/agile_operations/supply_chain_management/queries/unfulfilled_orders.gsql new file mode 100644 index 00000000..e69de29b diff --git a/connected_customer/customer_360/README.md b/connected_customer/customer_360/README.md index 29513135..19aeb6bf 100644 --- a/connected_customer/customer_360/README.md +++ b/connected_customer/customer_360/README.md @@ -114,11 +114,11 @@ with `gsql` command installed. If you don't yet have the `gsql` command available, see the TigerGraph documentation: - **Local GSQL shell on the server** - [The GSQL Shell](https://docs.tigergraph.com/tigergraph-server/4.2/gsql-shell/) + [The GSQL Shell](https://docs.tigergraph.com/tigergraph-server/current/gsql-shell/) (explains how to run `gsql` directly on a TigerGraph server) - **Remote GSQL client (from your laptop or another machine)** - [Using a Remote GSQL Client](https://docs.tigergraph.com/tigergraph-server/4.2/gsql-shell/using-a-remote-gsql-client) + [Using a Remote GSQL Client](https://docs.tigergraph.com/tigergraph-server/current/gsql-shell/using-a-remote-gsql-client) (explains how to download the GSQL client JAR, configure SSL, and create a `gsql` alias) Ensure that the script is executable with: ```bash diff --git a/connected_customer/customer_360/queries/Application_Engagement_Individuals.gsql b/connected_customer/customer_360/queries/Application_Engagement_Individuals.gsql index 69cc8338..c73124d2 100644 --- a/connected_customer/customer_360/queries/Application_Engagement_Individuals.gsql +++ b/connected_customer/customer_360/queries/Application_Engagement_Individuals.gsql @@ -1,4 +1,30 @@ CREATE DISTRIBUTED QUERY Application_Engagement_Individuals(/* Parameters here */) FOR GRAPH Customer_360_Financial { + /* + Description: + Returns all Individual vertices that have engaged with a product + application, as evidenced by ApplicationEngagement events linked + from SessionID vertices. + + This query traverses the engagement path: + SessionID -(action)-> ApplicationEngagement -(created_by)-> Individual + + It is useful for identifying the full population of individuals + who have interacted with any product application, regardless of + specific product or time window. + + Parameters: + (None) + This query currently operates on the full graph: + - All SessionID vertices in the graph are used as the starting set. + - No temporal or product-level filters are applied. + Consider adding parameters (e.g., date range, product ID, channel) + if you need more targeted engagement slices in the future. + + Output: + A deduplicated list of Individual vertices that are reachable via + at least one ApplicationEngagement event originating from a SessionID. + Ready for use in downstream segmentation, analytics, or APIs. + */ start = {SessionID.*}; app_sessions = SELECT s FROM start:s - (action:e) - ApplicationEngagement:t; diff --git a/connected_customer/customer_360/queries/Application_Starts.gsql b/connected_customer/customer_360/queries/Application_Starts.gsql index 9485e119..99c39d2e 100644 --- a/connected_customer/customer_360/queries/Application_Starts.gsql +++ b/connected_customer/customer_360/queries/Application_Starts.gsql @@ -1,4 +1,43 @@ CREATE DISTRIBUTED QUERY Application_Starts(/* Parameters here */) FOR GRAPH Customer_360_Financial { + /* + Description: + Identifies all Individual vertices who started a product application + but did not complete (submit) it. + + The query compares two sets of sessions: + - Sessions with a "start_application" action + - Sessions with a "submit_application" action + + Any session present in the first set but not in the second is treated + as an unfinished application. These sessions are then mapped to + Individual vertices via the created_by edge. + + Parameters: + (None) + This query currently: + - Uses all SessionID vertices in the graph as the starting set. + - Relies on action_detail values: + "start_application" and "submit_application" + - Has no date, product, or channel filters. + Consider adding parameters (e.g., date range, product, channel) + if more targeted analysis is needed. + + Output: + A set of Individual vertices (unfinished_individuals) representing + people who started an application but did not submit it, based on + the presence of a start event and absence of a submit event for + their sessions. + + Useful for: + - Drop-off funnel analysis + - Re-engagement / remarketing campaigns + - UX and journey optimization + + Notes: + - @@app_starts and @@app_submits are global SetAccums that collect + sessions with start vs submit actions. + - The MINUS operator on these sets computes unfinished sessions. + */ start = {SessionID.*}; SetAccum @@app_starts, @@app_submits, @@unfinished; diff --git a/connected_customer/customer_360/queries/Application_Submissions.gsql b/connected_customer/customer_360/queries/Application_Submissions.gsql index fc727764..1a9984e8 100644 --- a/connected_customer/customer_360/queries/Application_Submissions.gsql +++ b/connected_customer/customer_360/queries/Application_Submissions.gsql @@ -1,4 +1,20 @@ CREATE DISTRIBUTED QUERY Application_Submissions(/* Parameters here */) FOR GRAPH Customer_360_Financial { +/* + Description: + Returns Individuals who both started and submitted an application, + based on sessions that have: + - "start_application" and + - "submit_application" actions. + + Parameters: + (None) + Uses all SessionID vertices and ApplicationEngagement edges + filtered by action_detail. + + Output: + - @@finished: Set of SessionID vertices with completed applications. + - finished_individuals: Individuals linked to those sessions. + */ start = {SessionID.*}; SetAccum @@app_starts, @@app_submits, @@finished; @@ -21,4 +37,4 @@ CREATE DISTRIBUTED QUERY Application_Submissions(/* Parameters here */) FOR GRAP PRINT finished_individuals; } -UPDATE DESCRIPTION OF QUERY Application_Submissions "Returns all individuals who finished an application and submitted one" \ No newline at end of file +UPDATE DESCRIPTION OF QUERY Application_Submissions "Returns all individuals who finished an application and submitted one" From 5b4178f19fa42201a311f701b77b51520303c553 Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Thu, 4 Dec 2025 13:28:43 +0530 Subject: [PATCH 20/55] standardized customer_360 query documentation --- .../Application_Engagement_Individuals.gsql | 28 ++++--------- .../queries/Application_Starts.gsql | 41 ++++--------------- .../queries/Customers_No_Engagement.gsql | 14 ++++++- .../queries/Customers_With_Product.gsql | 16 +++++++- .../queries/Customers_With_Sessions.gsql | 17 +++++++- .../queries/Email_Engagement_Accounts.gsql | 18 +++++++- .../queries/Email_Engagement_By_Product.gsql | 22 +++++++++- .../queries/Individual_WebSearch.gsql | 14 +++++++ .../Individuals_K_Most_Engagement.gsql | 16 ++++++++ .../queries/Individuals_No_Application.gsql | 15 ++++++- .../queries/Individuals_Product_Browse.gsql | 16 +++++++- .../customer_360/queries/delete_all.gsql | 13 ++++++ 12 files changed, 167 insertions(+), 63 deletions(-) diff --git a/connected_customer/customer_360/queries/Application_Engagement_Individuals.gsql b/connected_customer/customer_360/queries/Application_Engagement_Individuals.gsql index c73124d2..ed41f394 100644 --- a/connected_customer/customer_360/queries/Application_Engagement_Individuals.gsql +++ b/connected_customer/customer_360/queries/Application_Engagement_Individuals.gsql @@ -1,31 +1,17 @@ CREATE DISTRIBUTED QUERY Application_Engagement_Individuals(/* Parameters here */) FOR GRAPH Customer_360_Financial { - /* +/* Description: - Returns all Individual vertices that have engaged with a product - application, as evidenced by ApplicationEngagement events linked - from SessionID vertices. - - This query traverses the engagement path: - SessionID -(action)-> ApplicationEngagement -(created_by)-> Individual - - It is useful for identifying the full population of individuals - who have interacted with any product application, regardless of - specific product or time window. + Returns Individuals who have engaged with a product application, + via the path: SessionID -(action)-> ApplicationEngagement + -(created_by)-> Individual. Parameters: (None) - This query currently operates on the full graph: - - All SessionID vertices in the graph are used as the starting set. - - No temporal or product-level filters are applied. - Consider adding parameters (e.g., date range, product ID, channel) - if you need more targeted engagement slices in the future. + Uses all SessionID vertices; no date, product, or channel filters. Output: - A deduplicated list of Individual vertices that are reachable via - at least one ApplicationEngagement event originating from a SessionID. - Ready for use in downstream segmentation, analytics, or APIs. - */ - start = {SessionID.*}; + Set of Individual vertices with at least one application engagement. + */ start = {SessionID.*}; app_sessions = SELECT s FROM start:s - (action:e) - ApplicationEngagement:t; individuals = SELECT t FROM app_sessions:s - (created_by:e) - Individual:t; diff --git a/connected_customer/customer_360/queries/Application_Starts.gsql b/connected_customer/customer_360/queries/Application_Starts.gsql index 99c39d2e..6b722dde 100644 --- a/connected_customer/customer_360/queries/Application_Starts.gsql +++ b/connected_customer/customer_360/queries/Application_Starts.gsql @@ -1,44 +1,19 @@ CREATE DISTRIBUTED QUERY Application_Starts(/* Parameters here */) FOR GRAPH Customer_360_Financial { /* Description: - Identifies all Individual vertices who started a product application - but did not complete (submit) it. - - The query compares two sets of sessions: - - Sessions with a "start_application" action - - Sessions with a "submit_application" action - - Any session present in the first set but not in the second is treated - as an unfinished application. These sessions are then mapped to - Individual vertices via the created_by edge. + Returns Individuals who started an application but did not submit it, + by comparing sessions with "start_application" vs "submit_application" + actions. Parameters: (None) - This query currently: - - Uses all SessionID vertices in the graph as the starting set. - - Relies on action_detail values: - "start_application" and "submit_application" - - Has no date, product, or channel filters. - Consider adding parameters (e.g., date range, product, channel) - if more targeted analysis is needed. + Uses all SessionID vertices; relies on action_detail values + "start_application" and "submit_application". Output: - A set of Individual vertices (unfinished_individuals) representing - people who started an application but did not submit it, based on - the presence of a start event and absence of a submit event for - their sessions. - - Useful for: - - Drop-off funnel analysis - - Re-engagement / remarketing campaigns - - UX and journey optimization - - Notes: - - @@app_starts and @@app_submits are global SetAccums that collect - sessions with start vs submit actions. - - The MINUS operator on these sets computes unfinished sessions. - */ - start = {SessionID.*}; + unfinished_individuals: Individuals with started but unfinished + application sessions. + */ start = {SessionID.*}; SetAccum @@app_starts, @@app_submits, @@unfinished; app_starts = SELECT s FROM start:s - (action:e) - ApplicationEngagement:t diff --git a/connected_customer/customer_360/queries/Customers_No_Engagement.gsql b/connected_customer/customer_360/queries/Customers_No_Engagement.gsql index 0dff9398..f0698a81 100644 --- a/connected_customer/customer_360/queries/Customers_No_Engagement.gsql +++ b/connected_customer/customer_360/queries/Customers_No_Engagement.gsql @@ -1,5 +1,17 @@ CREATE DISTRIBUTED QUERY Customers_No_Engagement(/* Parameters here */) FOR GRAPH Customer_360_Financial { - + /* + Description: + Returns Individuals who have never had any recorded engagement, + defined as having no outgoing "created_session" edges. + + Parameters: + (None) + Uses all Individual vertices in the graph. + + Output: + not_engaged: + Individual vertices where outdegree("created_session") == 0. + */ start = {Individual.*}; not_engaged = SELECT s FROM start:s diff --git a/connected_customer/customer_360/queries/Customers_With_Product.gsql b/connected_customer/customer_360/queries/Customers_With_Product.gsql index 8714ea4b..3f6daf24 100644 --- a/connected_customer/customer_360/queries/Customers_With_Product.gsql +++ b/connected_customer/customer_360/queries/Customers_With_Product.gsql @@ -1,4 +1,18 @@ CREATE DISTRIBUTED QUERY Accounts_With_Product(STRING product_type) FOR GRAPH Customer_360_Financial { + /* + Description: + Returns Accounts that hold a specified product type, matching either + credit card or loan products linked via has_product edges. + + Parameters: + product_type (STRING): + The product category/type to match, e.g. a CardCategory for + CreditCard or a LoanType for Loan. + + Output: + @@accounts: + Set of Account vertices that hold the specified product_type. + */ SetAccum @@accounts; start = {Account.*}; @@ -17,4 +31,4 @@ CREATE DISTRIBUTED QUERY Accounts_With_Product(STRING product_type) FOR GRAPH Cu } UPDATE DESCRIPTION OF QUERY Accounts_With_Product "Returns Accounts that hold the specified product type" -UPDATE DESCRIPTION OF QUERY_PARAM Accounts_With_Product.product_type "The type of product held by returned accounts" \ No newline at end of file +UPDATE DESCRIPTION OF QUERY_PARAM Accounts_With_Product.product_type "The type of product held by returned accounts" diff --git a/connected_customer/customer_360/queries/Customers_With_Sessions.gsql b/connected_customer/customer_360/queries/Customers_With_Sessions.gsql index 99d71bfa..8eeccce8 100644 --- a/connected_customer/customer_360/queries/Customers_With_Sessions.gsql +++ b/connected_customer/customer_360/queries/Customers_With_Sessions.gsql @@ -1,8 +1,21 @@ CREATE DISTRIBUTED QUERY Customers_With_Sessions(/* Parameters here */) FOR GRAPH Customer_360_Financial { - + /* + Description: + Returns Individuals who have created at least one session, + i.e., those with an outgoing "created_session" edge to SessionID. + + Parameters: + (None) + Uses all Individual vertices in the graph. + + Output: + engaged: + Individual vertices that are connected to at least one SessionID + via created_session. + */ start = {Individual.*}; engaged = SELECT s FROM start:s - (created_session:e) - SessionID:t; PRINT engaged; } -UPDATE DESCRIPTION OF QUERY Customers_With_Sessions "Returns all individuals who have created a session where they have engagement" \ No newline at end of file +UPDATE DESCRIPTION OF QUERY Customers_With_Sessions "Returns all individuals who have created a session where they have engagement" diff --git a/connected_customer/customer_360/queries/Email_Engagement_Accounts.gsql b/connected_customer/customer_360/queries/Email_Engagement_Accounts.gsql index ef675107..6f45f833 100644 --- a/connected_customer/customer_360/queries/Email_Engagement_Accounts.gsql +++ b/connected_customer/customer_360/queries/Email_Engagement_Accounts.gsql @@ -1,5 +1,19 @@ CREATE DISTRIBUTED QUERY Email_Engagement_Accounts(/* Parameters here */) FOR GRAPH Customer_360_Financial { - + /* + Description: + Returns Accounts that have had email engagement, by traversing from + SessionID through EmailEngagement to Individual and then to Account. + + Parameters: + (None) + Uses all SessionID vertices in the graph. + + Output: + accounts: + Account vertices reachable via + SessionID -(action)-> EmailEngagement -(created_by)-> Individual + -(represents_account)-> Account. + */ start = {SessionID.*}; email_sessions = SELECT s FROM start:s - (action:e) - EmailEngagement:t; @@ -10,4 +24,4 @@ CREATE DISTRIBUTED QUERY Email_Engagement_Accounts(/* Parameters here */) FOR GR PRINT accounts; } -UPDATE DESCRIPTION OF QUERY Email_Engagement_Accounts "Returns accounts that have had email engagement" \ No newline at end of file +UPDATE DESCRIPTION OF QUERY Email_Engagement_Accounts "Returns accounts that have had email engagement" diff --git a/connected_customer/customer_360/queries/Email_Engagement_By_Product.gsql b/connected_customer/customer_360/queries/Email_Engagement_By_Product.gsql index 1fbcccf4..3b093196 100644 --- a/connected_customer/customer_360/queries/Email_Engagement_By_Product.gsql +++ b/connected_customer/customer_360/queries/Email_Engagement_By_Product.gsql @@ -1,4 +1,24 @@ CREATE DISTRIBUTED QUERY Email_Engagement_By_Product(/* Parameters here */) FOR GRAPH Customer_360_Financial { + /* + Description: + Calculates email engagement rates by product type, based on the share + of products held by accounts that have email engagement. + + Parameters: + (None) + Uses all SessionID vertices, EmailEngagement events, and products + linked to engaged accounts (CreditCard / Loan). + + Output: + @@product_total: + Map of product_type -> total number of such products. + @@account_prod_total: + Map of product_type -> number of such products held by + email-engaged accounts. + @@engagement_rate: + Map of product_type -> engagement rate + (email-engaged products / total products), rounded to 3 decimals. + */ # Get All Email Engagement Accounts start = {SessionID.*}; @@ -40,4 +60,4 @@ CREATE DISTRIBUTED QUERY Email_Engagement_By_Product(/* Parameters here */) FOR PRINT @@engagement_rate; } -UPDATE DESCRIPTION OF QUERY Email_Engagement_By_Product "Returns email engagement percentage broken down by product type" \ No newline at end of file +UPDATE DESCRIPTION OF QUERY Email_Engagement_By_Product "Returns email engagement percentage broken down by product type" diff --git a/connected_customer/customer_360/queries/Individual_WebSearch.gsql b/connected_customer/customer_360/queries/Individual_WebSearch.gsql index 6c57c713..25334aab 100644 --- a/connected_customer/customer_360/queries/Individual_WebSearch.gsql +++ b/connected_customer/customer_360/queries/Individual_WebSearch.gsql @@ -1,4 +1,18 @@ CREATE DISTRIBUTED QUERY Individual_WebSearch(/* Parameters here */) FOR GRAPH Customer_360_Financial { + /* + Description: + Returns Individuals who have performed a web search activity, + via WebSearch events linked from SessionID. + + Parameters: + (None) + Uses all SessionID vertices in the graph. + + Output: + individuals: + Individual vertices reachable through + SessionID -(action)-> WebSearch -(created_by)-> Individual. + */ start = {SessionID.*}; web_sessions = SELECT s FROM start:s - (action:e) - WebSearch:t; diff --git a/connected_customer/customer_360/queries/Individuals_K_Most_Engagement.gsql b/connected_customer/customer_360/queries/Individuals_K_Most_Engagement.gsql index 94ccb4ec..cfb9d3f3 100644 --- a/connected_customer/customer_360/queries/Individuals_K_Most_Engagement.gsql +++ b/connected_customer/customer_360/queries/Individuals_K_Most_Engagement.gsql @@ -1,4 +1,20 @@ CREATE DISTRIBUTED QUERY Individuals_K_Most_Engagement(INT k = 10) FOR GRAPH Customer_360_Financial { + /* + Description: + Returns the top-k Individuals with the highest total engagement, + where engagement is the number of action events across their + associated SessionID vertices. + + Parameters: + k (INT, default = 10): + Number of Individuals to return, ranked by total engagements. + + Output: + individuals: + Top-k Individual vertices ordered by n.@engagements, where + n.@engagements is the sum of outdegree("action") over all + sessions created by that Individual. + */ MaxAccum @engagements; sessions = {SessionID.*}; diff --git a/connected_customer/customer_360/queries/Individuals_No_Application.gsql b/connected_customer/customer_360/queries/Individuals_No_Application.gsql index a2a302d7..a563d1bd 100644 --- a/connected_customer/customer_360/queries/Individuals_No_Application.gsql +++ b/connected_customer/customer_360/queries/Individuals_No_Application.gsql @@ -1,5 +1,18 @@ CREATE DISTRIBUTED QUERY Individuals_No_Application(/* Parameters here */) FOR GRAPH Customer_360_Financial { - /* Find individuals who looked at a product but never started an application */ + /* + Description: + Find individuals who looked at a product but never started an application + + Parameters: + (None) + Uses all SessionID vertices in the graph. + + Output: + individuals: + Individual vertices reachable from sessions in 'interest', + where 'interest' are SessionID vertices with at least one + action to a non-ApplicationEngagement vertex. + */ sessions = {SessionID.*}; # Sessions with ProductBrowse or Websearch diff --git a/connected_customer/customer_360/queries/Individuals_Product_Browse.gsql b/connected_customer/customer_360/queries/Individuals_Product_Browse.gsql index 75a6380e..a9576871 100644 --- a/connected_customer/customer_360/queries/Individuals_Product_Browse.gsql +++ b/connected_customer/customer_360/queries/Individuals_Product_Browse.gsql @@ -1,4 +1,18 @@ CREATE DISTRIBUTED QUERY Individuals_Product_Browse(/* Parameters here */) FOR GRAPH Customer_360_Financial { + /* + Description: + Returns Individuals who have browsed products, via ProductBrowse + events linked from SessionID. + + Parameters: + (None) + Uses all SessionID vertices in the graph. + + Output: + individuals: + Individual vertices reachable through + SessionID -(action)-> ProductBrowse -(created_by)-> Individual. + */ start = {SessionID.*}; prod_sessions = SELECT s FROM start:s - (action:e) - ProductBrowse:t; @@ -7,4 +21,4 @@ CREATE DISTRIBUTED QUERY Individuals_Product_Browse(/* Parameters here */) FOR G PRINT individuals; } -UPDATE DESCRIPTION OF QUERY Individuals_Product_Browse "Return individuals who have looked at products" \ No newline at end of file +UPDATE DESCRIPTION OF QUERY Individuals_Product_Browse "Return individuals who have looked at products" diff --git a/connected_customer/customer_360/queries/delete_all.gsql b/connected_customer/customer_360/queries/delete_all.gsql index 9a390510..09a444c5 100644 --- a/connected_customer/customer_360/queries/delete_all.gsql +++ b/connected_customer/customer_360/queries/delete_all.gsql @@ -1,4 +1,17 @@ CREATE DISTRIBUTED QUERY delete_all(/* Parameters here */) FOR GRAPH Customer_360_Financial{ + /* + Description: + Deletes all vertices and edges from the Customer_360_Financial graph, + effectively clearing the graph. + + Parameters: + (None) + + Output: + (None) + The graph is emptied; all vertices and edges are removed. + Use with extreme caution in non-production or reset scenarios only. + */ all = {ANY}; results = SELECT a FROM all:a - (:e) - ANY From c5cea208a0f695db7f1b99b3366c5c6e3d077d9e Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Thu, 4 Dec 2025 14:07:02 +0530 Subject: [PATCH 21/55] standardized readme and query documentation for Entity Resolution solution kit --- .../entity_resolution/README.md | 136 ++++++++++++++++-- .../queries/delete_all_cc_connections.gsql | 16 ++- .../queries/delete_unused_cc_nodes.gsql | 18 ++- .../find_shared_piis_of_two_entities.gsql | 18 ++- .../queries/match_entities.gsql | 38 ++++- .../queries/output_entity_cc_to_file.gsql | 16 ++- .../queries/unify_entities.gsql | 18 ++- 7 files changed, 237 insertions(+), 23 deletions(-) diff --git a/connected_customer/entity_resolution/README.md b/connected_customer/entity_resolution/README.md index 7d7f826c..123c7f18 100644 --- a/connected_customer/entity_resolution/README.md +++ b/connected_customer/entity_resolution/README.md @@ -1,17 +1,76 @@ -# Narratives +# Entity Resolution – Connected Customer (TigerGraph Solution Kit) -Entity Resolution is the cornerstone of our approach to addressing the -challenges posed by the vast array of data in today's interconnected digital -landscape. Specifically tailored to the Connected Customer use-case, our -Entity Resolution solution excels at accurately identifying and linking -entities across disparate datasets, focusing on Personally Identifiable -Information (PII) such as email addresses, phone numbers, and physical -addresses. By seamlessly integrating PII from various sources, businesses can -create a unified view of each customer. This comprehensive understanding -empowers organizations to personalize marketing campaigns, tailor product -recommendations, and optimize customer service interactions, fostering stronger -customer relationships and driving business success. +# Narratives +Entity Resolution is the cornerstone of our approach to handling the vast array +of customer data in today’s interconnected digital landscape. Specifically +tailored to the **Connected Customer** use case, this TigerGraph solution kit +excels at accurately identifying and linking entities across disparate +datasets, focusing on **Personally Identifiable Information (PII)** such as +email addresses, phone numbers, and physical addresses. By seamlessly +integrating PII from multiple sources, businesses can create a **unified view +of each customer**. This comprehensive understanding enables personalized +marketing campaigns, tailored product recommendations, and optimized customer +service interactions—fostering stronger customer relationships and driving +business success. + +Under the hood, the kit provisions a complete graph environment—schema, sample +data, loading jobs, and GSQL queries—to **resolve, connect, and unify customer +records** at scale. Entities are grouped using a combination of deterministic +matching (shared PII) and fuzzy matching (MinHash + similarity scoring), then +organized into connected components that represent real-world customers. + +With this kit you can: + +- Build unified, cross-system customer profiles using shared and fuzzy-matched PII. +- Detect and link duplicate or overlapping customer records across data sources. +- Form connected components of matching entities for downstream analytics and activation. +- Export resolved entity clusters for use in MDM, CDP, or downstream data pipelines. + +## Contents + +- [Overview](#overview) +- [Features](#features) +- [Prerequisites](#prerequisites) +- [Quickstart](#quickstart) +- [Working-with-the-graph](#working-with-the-graph) + - [Entity-Resolution-workflow](#entity-resolution-workflow) + - [Additional-queries](#additional-queries) +- [Scalability](#scalability) +- [Further-reading](#further-reading) + +--- + +## Overview + +Modern customer data is fragmented across CRM systems, product platforms, +billing systems, marketing tools, and more. The same person may appear under +slightly different names, email addresses, phone numbers, or addresses. + +This solution kit demonstrates how to use **TigerGraph** to resolve those +fragments into unified entities: + +- **Entity** represents a person or customer instance as it appears in source data. +- **PII vertices** (Email, Phone, Name, Address components, Tax ID, etc.) represent + individual attributes used for matching. +- **Same_As** edges connect Entities that appear to represent the _same real-world individual_. +- **Connected_Component** vertices represent final unified clusters of Entities, + each cluster being a resolved “real-world entity”. + +The kit includes both: + +- A **deterministic** matching layer using shared PII. +- A **fuzzy** matching layer using **MinHash** and **Jaro-Winkler** similarity on + name, email, and phone. + +You can: + +- Run the full entity resolution workflow on sample data. +- Tune matching thresholds and weights. +- Re-run incremental matching as new Entities arrive. +- Export results to CSV for external consumption. + +--- # Components This repository includes multiple components: @@ -24,7 +83,58 @@ This repository includes multiple components: - `readme.md` - This usage guide. - `setup.sh` - Automated setup script. -# Instructions +--- + +## Prerequisites + +Before you run this solution kit, make sure you have: + +- **A running TigerGraph instance** + - TigerGraph installed and running, or use the prebuilt kit on TG cloud. + - You must have permission to create graphs and run GSQL commands. + +- **GSQL client access** + - The `gsql` command-line tool available on the same machine/container where you cloned this repo. + - Ability to connect to your TigerGraph service + +- **Network access for sample data** + - Outbound internet access from the TigerGraph machine to read the sample + CSV files from the public S3 bucket used in `loading_job/load_data.gsql`. + +- **Shell environment** + - A Unix-like shell (Linux, macOS, or WSL) to run `setup.sh` and `queries/install_queries.sh`. + - Executable permissions for the scripts: + ```bash + chmod +x setup.sh + chmod +x queries/install_queries.sh + ``` + +> **Graph name:** This kit creates and uses a graph named `Entity_Resolution`. + +# Setup Instructions +The following instructions assume that you are running the following scripts +with `gsql` command installed. + +If you don't yet have the `gsql` command available, see the TigerGraph documentation: + +- **Local GSQL shell on the server** + [The GSQL Shell](https://docs.tigergraph.com/tigergraph-server/current/gsql-shell/) + (explains how to run `gsql` directly on a TigerGraph server) + +- **Remote GSQL client (from your laptop or another machine)** + [Using a Remote GSQL Client](https://docs.tigergraph.com/tigergraph-server/current/gsql-shell/using-a-remote-gsql-client) + (explains how to download the GSQL client JAR, configure SSL, and create a `gsql` alias) +Ensure that the script is executable with: +```bash + +chmod +x setup.sh + +``` +Then, run the automated script using: +```bash +./setup.sh +``` + The `setup.sh` script is designed to streamline the initial setup process by sequentially executing the following steps: diff --git a/connected_customer/entity_resolution/queries/delete_all_cc_connections.gsql b/connected_customer/entity_resolution/queries/delete_all_cc_connections.gsql index 948f7954..2b7573b0 100644 --- a/connected_customer/entity_resolution/queries/delete_all_cc_connections.gsql +++ b/connected_customer/entity_resolution/queries/delete_all_cc_connections.gsql @@ -1,7 +1,21 @@ USE GRAPH Entity_Resolution CREATE OR REPLACE DISTRIBUTED QUERY delete_all_cc_connections(INT num_of_batches = 1, INT batch_id = 0) FOR GRAPH Entity_Resolution{ +/* + Description: + Deletes Entity_In_Ring edges between Entity and Connected_Component + vertices, optionally in batches for large graphs. + Parameters: + num_of_batches (INT, default = 1) + Total number of batches. Run once per batch_id in [0, num_of_batches - 1]. + batch_id (INT, default = 0) + 0-based index of the current batch to process. + + Output: + Prints a status message with the number of Entity_In_Ring edges deleted + for the current batch. +*/ SumAccum @@count; temp = SELECT t @@ -20,4 +34,4 @@ UPDATE DESCRIPTION OF QUERY delete_all_cc_connections "This query deletes all En UPDATE DESCRIPTION OF QUERY_PARAM delete_all_cc_connections.num_of_batches "Number of batches to partition the deletions for all Entity vertices in the graph. This query must be called the same number of times as batch_num with incrementing batch_id 0 through batch_num - 1 to process all Entity vertices in the graph. Defaults to 1." -UPDATE DESCRIPTION OF QUERY_PARAM delete_all_cc_connections.batch_id "Current batch partition of Entity vertices to process and must be called with batch_id 0 through batch_num - 1. Defaults to 0." \ No newline at end of file +UPDATE DESCRIPTION OF QUERY_PARAM delete_all_cc_connections.batch_id "Current batch partition of Entity vertices to process and must be called with batch_id 0 through batch_num - 1. Defaults to 0." diff --git a/connected_customer/entity_resolution/queries/delete_unused_cc_nodes.gsql b/connected_customer/entity_resolution/queries/delete_unused_cc_nodes.gsql index cc78f589..06ae6241 100644 --- a/connected_customer/entity_resolution/queries/delete_unused_cc_nodes.gsql +++ b/connected_customer/entity_resolution/queries/delete_unused_cc_nodes.gsql @@ -1,7 +1,21 @@ USE GRAPH Entity_Resolution CREATE OR REPLACE DISTRIBUTED QUERY delete_unused_cc_nodes(INT num_of_batches = 1, INT batch_id = 0) FOR GRAPH Entity_Resolution { - + /* + Description: + Deletes unused Connected_Component vertices (those with outdegree = 0), + optionally in batches for large graphs. + + Parameters: + num_of_batches (INT, default = 1) + Total number of batches. Run once per batch_id in [0, num_of_batches - 1]. + batch_id (INT, default = 0) + 0-based index of the current batch to process. + + Output: + Prints a status message with the number of unused Connected_Component + vertices deleted for the current batch. +*/ SumAccum @@count; start = {Connected_Component.*}; @@ -22,4 +36,4 @@ UPDATE DESCRIPTION OF QUERY delete_unused_cc_nodes "This query deletes all unuse UPDATE DESCRIPTION OF QUERY_PARAM delete_unused_cc_nodes.num_of_batches "Number of batches to partition the deletions for all Connected_Component vertices in the graph. This query must be called the same number of times as batch_num with incrementing batch_id 0 through batch_num - 1 to process all Connected_Component vertices in the graph. Defaults to 1." -UPDATE DESCRIPTION OF QUERY_PARAM delete_unused_cc_nodes.batch_id "Current batch partition of Connected_Component vertices to process and must be called with batch_id 0 through batch_num - 1. Defaults to 0." \ No newline at end of file +UPDATE DESCRIPTION OF QUERY_PARAM delete_unused_cc_nodes.batch_id "Current batch partition of Connected_Component vertices to process and must be called with batch_id 0 through batch_num - 1. Defaults to 0." diff --git a/connected_customer/entity_resolution/queries/find_shared_piis_of_two_entities.gsql b/connected_customer/entity_resolution/queries/find_shared_piis_of_two_entities.gsql index cc2eec3f..1e998502 100644 --- a/connected_customer/entity_resolution/queries/find_shared_piis_of_two_entities.gsql +++ b/connected_customer/entity_resolution/queries/find_shared_piis_of_two_entities.gsql @@ -1,7 +1,21 @@ USE GRAPH Entity_Resolution CREATE OR REPLACE QUERY find_shared_piis_of_two_entities(VERTEX entity_1, VERTEX entity_2) { - + /* + Description: + Finds all PII vertices shared by two Entity vertices and returns their + type, value, and degree (outdegree of each shared PII vertex). + + Parameters: + entity_1 (VERTEX) + First Entity vertex to compare. + entity_2 (VERTEX) + Second Entity vertex to compare. + + Output: + A list of tuples (pii_type, pii_value, degree) for each shared PII vertex + between entity_1 and entity_2. +*/ TYPEDEF TUPLE pii_info; ListAccum @@degrees_of_shared_piis; @@ -31,4 +45,4 @@ UPDATE DESCRIPTION OF QUERY find_shared_piis_of_two_entities "This query returns UPDATE DESCRIPTION OF QUERY_PARAM find_shared_piis_of_two_entities.entity_1 "First Entity vertex used to search for shared PII vertices." -UPDATE DESCRIPTION OF QUERY_PARAM find_shared_piis_of_two_entities.entity_2 "Second Entity vertex used to search for shared PII vertices." \ No newline at end of file +UPDATE DESCRIPTION OF QUERY_PARAM find_shared_piis_of_two_entities.entity_2 "Second Entity vertex used to search for shared PII vertices." diff --git a/connected_customer/entity_resolution/queries/match_entities.gsql b/connected_customer/entity_resolution/queries/match_entities.gsql index c586ba36..48beb0f2 100644 --- a/connected_customer/entity_resolution/queries/match_entities.gsql +++ b/connected_customer/entity_resolution/queries/match_entities.gsql @@ -1,6 +1,42 @@ USE GRAPH Entity_Resolution CREATE OR REPLACE DISTRIBUTED QUERY match_entities( + +/* + Description: + Computes similarity scores between Entity vertices using shared PII + (including fuzzy MinHash matches) and inserts Same_As edges for pairs + whose accumulated score meets or exceeds the threshold. Supports batching, + degree limits, and incremental runs. + + Parameters: + customer_has_*_weight (FLOAT, various defaults) + Weights for each PII type (birthdate, email, name, phone, address parts, + tax ID, source customer ID) contributing to the match score. + num_of_source_batches (INT, default = 10) + Number of source batches for splitting the Entity set to control memory. + num_of_target_batches (INT, default = 1) + Number of target batches; nested batching for large-scale matching. + threshold (FLOAT, default = 1.0) + Minimum total similarity score required to create a Same_As edge. + pii_low_connections_limit (INT, default = 100) + Max outdegree for PII considered as a low-connectivity attribute when + seeding candidate pairs. + pii_high_connections_limit (INT, default = 25000) + Max outdegree for PII included in scoring; higher values increase recall + but can add runtime. + compute_entities_after_date (DATETIME, default = 1970-01-01 00:00:00) + Only entities with created_at after this timestamp are computed as new; + they are compared with each other and with existing entities. + + Output: + Prints: + - pairs_matched_count, max/min/avg match scores across all evaluated + pairs. + - execution_time_in_seconds for the full run. + Inserts Same_As edges with a score attribute between matching Entity pairs. +*/ + FLOAT customer_has_birthdate_weight = 0.5, FLOAT customer_has_email_address_weight = 0.5, FLOAT customer_has_name_weight = 0.2, @@ -336,4 +372,4 @@ UPDATE DESCRIPTION OF QUERY_PARAM match_entities.pii_low_connections_limit "Maxi UPDATE DESCRIPTION OF QUERY_PARAM match_entities.pii_high_connections_limit "Maximum outdegree of PII vertex considered for 'high connection' matching. The case will be skipped if the same vertex is connected to too many entities. Defaults to 25000." -UPDATE DESCRIPTION OF QUERY_PARAM match_entities.compute_entities_after_date "All Entity vertices with created_at after this date will be computed to find similarity edges related to these new entities. Defaults to 1970-01-01 00:00:00 (which is the earliest possible created_at and great for the initial running of this query)." \ No newline at end of file +UPDATE DESCRIPTION OF QUERY_PARAM match_entities.compute_entities_after_date "All Entity vertices with created_at after this date will be computed to find similarity edges related to these new entities. Defaults to 1970-01-01 00:00:00 (which is the earliest possible created_at and great for the initial running of this query)." diff --git a/connected_customer/entity_resolution/queries/output_entity_cc_to_file.gsql b/connected_customer/entity_resolution/queries/output_entity_cc_to_file.gsql index 8971a9ff..aa7cd964 100644 --- a/connected_customer/entity_resolution/queries/output_entity_cc_to_file.gsql +++ b/connected_customer/entity_resolution/queries/output_entity_cc_to_file.gsql @@ -1,7 +1,19 @@ USE GRAPH Entity_Resolution CREATE OR REPLACE QUERY output_entity_cc_to_file(STRING output_file_path = "/home/tigergraph/gsql_output/entity_cc_output.csv") { - +/* + Description: + Writes a CSV file mapping each Entity vertex to its Connected_Component + based on Entity_In_Ring edges. + + Parameters: + output_file_path (STRING, default = "/home/tigergraph/gsql_output/entity_cc_output.csv") + Absolute path for the CSV file on the TigerGraph server. + + Output: + Creates a CSV file with header "Entity,Connected_Component" and one row per + Entity–Connected_Component pair, then prints a confirmation message. +*/ FILE f (output_file_path); f.println("Entity", "Connected_Component"); @@ -19,4 +31,4 @@ CREATE OR REPLACE QUERY output_entity_cc_to_file(STRING output_file_path = "/hom UPDATE DESCRIPTION OF QUERY output_entity_cc_to_file "This query outputs a file containing a mapping of all Entity vertices to their respective Connected_Component community vertex." -UPDATE DESCRIPTION OF QUERY_PARAM output_entity_cc_to_file.output_file_path "File path location containing the output Entity vertex to Connected_Component vertex in CSV format. Defaults to /home/tigergraph/gsql_output/entity_cc_output.csv" \ No newline at end of file +UPDATE DESCRIPTION OF QUERY_PARAM output_entity_cc_to_file.output_file_path "File path location containing the output Entity vertex to Connected_Component vertex in CSV format. Defaults to /home/tigergraph/gsql_output/entity_cc_output.csv" diff --git a/connected_customer/entity_resolution/queries/unify_entities.gsql b/connected_customer/entity_resolution/queries/unify_entities.gsql index 8c44ae54..6bfc844e 100644 --- a/connected_customer/entity_resolution/queries/unify_entities.gsql +++ b/connected_customer/entity_resolution/queries/unify_entities.gsql @@ -1,7 +1,21 @@ USE GRAPH Entity_Resolution CREATE OR REPLACE DISTRIBUTED QUERY unify_entities() FOR GRAPH Entity_Resolution { - +/* + Description: + Builds Connected_Component communities from Same_As edges by propagating + the smallest internal ID across each connected group of Entity vertices, + then linking each Entity to its component via Entity_In_Ring. + + Parameters: + (None) + + Output: + Inserts: + - One Connected_Component vertex per connected group of Entities. + - One Entity_In_Ring edge from each Entity to its component. + Prints execution_time_in_seconds and a timestamp of completion. +*/ MinAccum @cc_id; // Each vertex's tentative component id DATETIME start_time = now(); @@ -41,4 +55,4 @@ CREATE OR REPLACE DISTRIBUTED QUERY unify_entities() FOR GRAPH Entity_Resolution } -UPDATE DESCRIPTION OF QUERY unify_entities "This query associates all Entity vertices in the graph to a Connected_Component vertex using Same_As edge previously inserted by match_entities query." \ No newline at end of file +UPDATE DESCRIPTION OF QUERY unify_entities "This query associates all Entity vertices in the graph to a Connected_Component vertex using Same_As edge previously inserted by match_entities query." From 30e25ce09acff348aac3da5c97c89a5cbd844ee7 Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Thu, 4 Dec 2025 17:28:05 +0530 Subject: [PATCH 22/55] fixed some errors resolving my earlier commit --- .../product_recommendations/README.md | 10 +++++-- .../queries/combine_features.gsql | 28 +++++++++++++++++++ .../queries/k_means.gsql | 21 ++++++++++++++ .../queries/unify_application_entities.gsql | 2 +- 4 files changed, 58 insertions(+), 3 deletions(-) diff --git a/connected_customer/product_recommendations/README.md b/connected_customer/product_recommendations/README.md index 94e36244..254c6261 100644 --- a/connected_customer/product_recommendations/README.md +++ b/connected_customer/product_recommendations/README.md @@ -62,8 +62,9 @@ and interaction types. - **Sample product recommendation dataset** Mock CSV data for customers, products, categories, styles, and purchases is - provided in the `data/` directory for inspection and column mapping, while the - actual data used by the loading job is pulled from a public S3 bucket. + provided in the `data/` directory for inspection and column mapping. The + actual data used by the loading job is pulled from a public S3 bucket but it + contains the same data as the CSV files. - **End-to-end loading job** A single loading job in `loading_job/load_jobs.gsql` that: @@ -107,6 +108,11 @@ Before you run this solution kit, make sure you have: - **(Optional) Python environment for local mock data** - Python 3.x if you want to run `data/data_generator.py` to regenerate local mock CSVs. + + Note that regenerating the data by itself does NOT change the data in the + graph. You may also want to be careful if there's already some data being + loaded if there's a new mock data you want to put in. + - Python packages: - `numpy` - `scipy` diff --git a/connected_customer/product_recommendations/queries/combine_features.gsql b/connected_customer/product_recommendations/queries/combine_features.gsql index 43842034..2d363ee8 100644 --- a/connected_customer/product_recommendations/queries/combine_features.gsql +++ b/connected_customer/product_recommendations/queries/combine_features.gsql @@ -6,6 +6,34 @@ CREATE QUERY combine_features( INT hub_threshold, INT split_threshold) SYNTAX V2 { +/* + Description: + Builds Combined_Feature vertices from frequently co-occurring feature + vertices. Treats high-degree feature vertices as hubs, finds other + features that share a moderate number of target vertices with each hub, + and replaces those direct edges with Has_Attribute / Linked edges + to Combined_Feature vertices. + + Parameters: + hub_v_type (SET, required) + Feature vertex types considered as hubs (e.g., Cluster, Product_Attribute). + e_type (SET, required) + Edge types used to traverse between hubs, targets, and features. + target_v_type (STRING, default "Customer") + Vertex type that connects pairs of feature vertices (the “context” node). + feature_v_type (SET, required) + Feature vertex types reachable via target_v_type to be paired with hubs. + hub_threshold (INT, required) + Minimum degree on e_type for a feature vertex to be treated as a hub. + split_threshold (INT, required) + Upper bound on the number of shared target vertices between feature pairs + to be combined into a Combined_Feature. + + Output: + Inserts Combined_Feature vertices, connects targets to them via + Has_Attribute, links original feature vertices to them via Linked, + and marks original hub/feature edges (ignore_edge = TRUE) to reduce noise. +*/ TYPEDEF TUPLE Vertex_Tuple; MapAccum> @intersection_size_map; OrAccum @hub; diff --git a/connected_customer/product_recommendations/queries/k_means.gsql b/connected_customer/product_recommendations/queries/k_means.gsql index 7e394bf8..9473c42a 100644 --- a/connected_customer/product_recommendations/queries/k_means.gsql +++ b/connected_customer/product_recommendations/queries/k_means.gsql @@ -11,6 +11,27 @@ CREATE QUERY k_means( BOOL use_custom_timestamp=FALSE, DATETIME custom_timestamp=to_datetime("1970-01-01")) SYNTAX V1 { +/* + Description: + Clusters vertices of type v_type using K-Means on numeric attributes + in attr_map, sweeping K from min_cluster_count to max_cluster_count and + choosing the best K by SSE. + + Parameters: + v_type (STRING, default "Customer") + Vertex type to cluster. + attr_set (SET, required) + JSON strings like "[idx,\"attr_key\"]" referring to keys in attr_map. + min_cluster_count, max_cluster_count (INT, required) + Range of K values to try; max must be >= min. + cluster_inc, random_iter_count, conv_iter_limit, conv_threshold, + random_seed, use_custom_timestamp, custom_timestamp + Standard K-Means and timestamp controls (see README for details). + + Output: + Inserts Cluster vertices (centroids) and In_Cluster edges from each + input vertex to its assigned Cluster; prints the number of clusters. +*/ TYPEDEF TUPLE Centroid_Distance; TYPEDEF TUPLE Cluster_Count_Tuple; // accum to store the smallest distance diff --git a/financial_crime/application_fraud/queries/unify_application_entities.gsql b/financial_crime/application_fraud/queries/unify_application_entities.gsql index 735dc484..7ff9a340 100644 --- a/financial_crime/application_fraud/queries/unify_application_entities.gsql +++ b/financial_crime/application_fraud/queries/unify_application_entities.gsql @@ -18,7 +18,7 @@ CREATE OR REPLACE DISTRIBUTED QUERY unify_application_entities() { S = SELECT t FROM S:s -(Same_Application:e)- :t ACCUM t.@cc_id += s.@cc_id // If s has smaller id than t, copy the id to t - HAVING t.@cc_id != t.@cc_id; + HAVING t.@cc_id != t.@cc_id'; END; result = SELECT t From ae17b72e81fb4db9ceff112377beba1b724eea65 Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Fri, 5 Dec 2025 20:48:49 +0530 Subject: [PATCH 23/55] standardized query descriptions for application fraud --- .../Supply_Chain_Management_Schema_Image.png | Bin 0 -> 276875 bytes .../Application_Engagement_Individuals.gsql | 3 +- .../queries/Application_Starts.gsql | 3 +- .../queries/delete_all_cc_connections.gsql | 4 -- .../queries/delete_unused_cc_nodes.gsql | 4 -- .../queries/match_entities.gsql | 58 ++++++++---------- .../queries/output_entity_cc_to_file.gsql | 2 +- ... => Insights_Product_Recommendations.json} | 0 .../images/Product_Recommendation_Schema.png | Bin 0 -> 202271 bytes .../queries/combine_features.gsql | 39 ++++++------ .../queries/recommend_products.gsql | 37 ++++++++++- .../batch_application_cc_features.gsql | 16 ++++- .../batch_application_distance_and_path.gsql | 15 +++++ ...delete_all_application_cc_connections.gsql | 15 +++++ .../queries/delete_unused_cc_nodes.gsql | 17 ++++- ...istance_and_path_to_fraud_application.gsql | 18 +++++- ...nce_and_path_to_fraud_application_vis.gsql | 18 ++++++ .../find_shared_piis_of_two_applications.gsql | 14 +++++ .../queries/get_application_cc_features.gsql | 16 ++++- .../get_num_applications_by_app_status.gsql | 11 ++++ .../get_num_applications_by_fraud_status.gsql | 5 ++ ...nected_components_by_num_applications.gsql | 15 ++++- ...et_top_k_products_by_num_applications.gsql | 19 +++++- ...oducts_by_num_applications_with_other.gsql | 18 +++++- .../incremental_application_match.gsql | 20 +++++- .../incremental_application_unify.gsql | 10 +++ ...cation_count_by_binary_classification.gsql | 7 ++- ...ghts_get_binary_classification_ratios.gsql | 6 +- .../insights_get_detected_fraud_amount.gsql | 7 ++- .../insights_get_missed_fraud_amount.gsql | 6 +- .../insights_get_net_benefit_amount.gsql | 8 ++- ..._get_net_gain_numbers_and_percentages.gsql | 10 ++- ...ts_get_relative_precision_recall_gain.gsql | 10 ++- .../queries/match_application_entities.gsql | 26 ++++++++ .../output_application_cc_to_file.gsql | 14 +++++ .../queries/set_application_fraud_status.gsql | 2 +- .../queries/unify_application_entities.gsql | 17 ++++- .../utils_get_negative_predictive_value.gsql | 2 +- 38 files changed, 407 insertions(+), 85 deletions(-) create mode 100644 agile_operations/supply_chain_management/meta/images/Supply_Chain_Management_Schema_Image.png rename connected_customer/product_recommendations/meta/{insights.json => Insights_Product_Recommendations.json} (100%) create mode 100644 connected_customer/product_recommendations/meta/images/Product_Recommendation_Schema.png diff --git a/agile_operations/supply_chain_management/meta/images/Supply_Chain_Management_Schema_Image.png b/agile_operations/supply_chain_management/meta/images/Supply_Chain_Management_Schema_Image.png new file mode 100644 index 0000000000000000000000000000000000000000..1adba384a12d404b53c4148e61f6a523acd13218 GIT binary patch literal 276875 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

#lz<^-7nOY7_fdT&>7a-$N##KmB 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%0A>Wzzqd-o{*h$9Aivfx$ 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 @@app_starts, @@app_submits, @@unfinished; app_starts = SELECT s FROM start:s - (action:e) - ApplicationEngagement:t diff --git a/connected_customer/entity_resolution/queries/delete_all_cc_connections.gsql b/connected_customer/entity_resolution/queries/delete_all_cc_connections.gsql index 2b7573b0..437743e3 100644 --- a/connected_customer/entity_resolution/queries/delete_all_cc_connections.gsql +++ b/connected_customer/entity_resolution/queries/delete_all_cc_connections.gsql @@ -11,10 +11,6 @@ CREATE OR REPLACE DISTRIBUTED QUERY delete_all_cc_connections(INT num_of_batches Total number of batches. Run once per batch_id in [0, num_of_batches - 1]. batch_id (INT, default = 0) 0-based index of the current batch to process. - - Output: - Prints a status message with the number of Entity_In_Ring edges deleted - for the current batch. */ SumAccum @@count; diff --git a/connected_customer/entity_resolution/queries/delete_unused_cc_nodes.gsql b/connected_customer/entity_resolution/queries/delete_unused_cc_nodes.gsql index 06ae6241..9aa97400 100644 --- a/connected_customer/entity_resolution/queries/delete_unused_cc_nodes.gsql +++ b/connected_customer/entity_resolution/queries/delete_unused_cc_nodes.gsql @@ -11,10 +11,6 @@ CREATE OR REPLACE DISTRIBUTED QUERY delete_unused_cc_nodes(INT num_of_batches = Total number of batches. Run once per batch_id in [0, num_of_batches - 1]. batch_id (INT, default = 0) 0-based index of the current batch to process. - - Output: - Prints a status message with the number of unused Connected_Component - vertices deleted for the current batch. */ SumAccum @@count; diff --git a/connected_customer/entity_resolution/queries/match_entities.gsql b/connected_customer/entity_resolution/queries/match_entities.gsql index 48beb0f2..2a211688 100644 --- a/connected_customer/entity_resolution/queries/match_entities.gsql +++ b/connected_customer/entity_resolution/queries/match_entities.gsql @@ -1,18 +1,32 @@ USE GRAPH Entity_Resolution CREATE OR REPLACE DISTRIBUTED QUERY match_entities( + FLOAT customer_has_birthdate_weight = 0.5, + FLOAT customer_has_email_address_weight = 0.5, + FLOAT customer_has_name_weight = 0.2, + FLOAT customer_has_phone_weight = 0.5, + FLOAT customer_has_std_city_weight = 0.2, + FLOAT customer_has_std_postcode_weight = 0.2, + FLOAT customer_has_std_state_weight = 0.2, + FLOAT customer_has_std_street_address_weight = 0.2, + FLOAT customer_has_tax_id_number_weight = 0.5, + FLOAT customer_has_source_customer_id_weight = 0.5, + INT num_of_source_batches = 10, // number of source batches + INT num_of_target_batches = 1, // number of target batches + FLOAT threshold = 1, // if a total matching score between two entities exceed the threshold, a similarity edge will be created between the matching entities + INT pii_low_connections_limit = 100, // maximum out degree of an attribute. The case will be skipped if the same attribute is connected to too many entities + INT pii_high_connections_limit = 25000, // maximum out degree of an attribute. The case will be skipped if the same attribute is connected to too many entities + DATETIME compute_entities_after_date = to_datetime("1970-01-01 00:00:00") // All entities that were created in TG after this date, will be computed to find similarity edges related to these new entities. The default value is the earliest time possible (great for the first time running this query) +) FOR GRAPH Entity_Resolution { /* Description: Computes similarity scores between Entity vertices using shared PII - (including fuzzy MinHash matches) and inserts Same_As edges for pairs - whose accumulated score meets or exceeds the threshold. Supports batching, - degree limits, and incremental runs. + (including fuzzy MinHash matches) and inserts Same_As edges for pairs. Parameters: customer_has_*_weight (FLOAT, various defaults) - Weights for each PII type (birthdate, email, name, phone, address parts, - tax ID, source customer ID) contributing to the match score. + Weights for each PII type contributing to the match score. num_of_source_batches (INT, default = 10) Number of source batches for splitting the Entity set to control memory. num_of_target_batches (INT, default = 1) @@ -20,41 +34,17 @@ CREATE OR REPLACE DISTRIBUTED QUERY match_entities( threshold (FLOAT, default = 1.0) Minimum total similarity score required to create a Same_As edge. pii_low_connections_limit (INT, default = 100) - Max outdegree for PII considered as a low-connectivity attribute when - seeding candidate pairs. + Max outdegree for PII considered as a low-connectivity attribute pii_high_connections_limit (INT, default = 25000) - Max outdegree for PII included in scoring; higher values increase recall - but can add runtime. + Max outdegree for PII included in scoring; compute_entities_after_date (DATETIME, default = 1970-01-01 00:00:00) Only entities with created_at after this timestamp are computed as new; - they are compared with each other and with existing entities. Output: - Prints: - - pairs_matched_count, max/min/avg match scores across all evaluated - pairs. - - execution_time_in_seconds for the full run. - Inserts Same_As edges with a score attribute between matching Entity pairs. + For each source Customer, a list of up to `recommendation_count` + Product_Variant items with normalized recommendation scores, sorted in + descending order of score. */ - - FLOAT customer_has_birthdate_weight = 0.5, - FLOAT customer_has_email_address_weight = 0.5, - FLOAT customer_has_name_weight = 0.2, - FLOAT customer_has_phone_weight = 0.5, - FLOAT customer_has_std_city_weight = 0.2, - FLOAT customer_has_std_postcode_weight = 0.2, - FLOAT customer_has_std_state_weight = 0.2, - FLOAT customer_has_std_street_address_weight = 0.2, - FLOAT customer_has_tax_id_number_weight = 0.5, - FLOAT customer_has_source_customer_id_weight = 0.5, - INT num_of_source_batches = 10, // number of source batches - INT num_of_target_batches = 1, // number of target batches - FLOAT threshold = 1, // if a total matching score between two entities exceed the threshold, a similarity edge will be created between the matching entities - INT pii_low_connections_limit = 100, // maximum out degree of an attribute. The case will be skipped if the same attribute is connected to too many entities - INT pii_high_connections_limit = 25000, // maximum out degree of an attribute. The case will be skipped if the same attribute is connected to too many entities - DATETIME compute_entities_after_date = to_datetime("1970-01-01 00:00:00") // All entities that were created in TG after this date, will be computed to find similarity edges related to these new entities. The default value is the earliest time possible (great for the first time running this query) -) FOR GRAPH Entity_Resolution { - TYPEDEF TUPLE, str STRING, created_at DATETIME> entity_fuzzy_vertex_info; ListAccum @entity_email_address_list; ListAccum @entity_name_list; diff --git a/connected_customer/entity_resolution/queries/output_entity_cc_to_file.gsql b/connected_customer/entity_resolution/queries/output_entity_cc_to_file.gsql index aa7cd964..dc9ef3ac 100644 --- a/connected_customer/entity_resolution/queries/output_entity_cc_to_file.gsql +++ b/connected_customer/entity_resolution/queries/output_entity_cc_to_file.gsql @@ -7,7 +7,7 @@ CREATE OR REPLACE QUERY output_entity_cc_to_file(STRING output_file_path = "/hom based on Entity_In_Ring edges. Parameters: - output_file_path (STRING, default = "/home/tigergraph/gsql_output/entity_cc_output.csv") + output_file_path Absolute path for the CSV file on the TigerGraph server. Output: diff --git a/connected_customer/product_recommendations/meta/insights.json b/connected_customer/product_recommendations/meta/Insights_Product_Recommendations.json similarity index 100% rename from connected_customer/product_recommendations/meta/insights.json rename to connected_customer/product_recommendations/meta/Insights_Product_Recommendations.json diff --git a/connected_customer/product_recommendations/meta/images/Product_Recommendation_Schema.png b/connected_customer/product_recommendations/meta/images/Product_Recommendation_Schema.png new file mode 100644 index 0000000000000000000000000000000000000000..135742b24e91dfabdfddc34700423b254f21eac4 GIT binary patch literal 202271 zcmeFY=T}t0(=KX2G6DjUvt%VoPLe@D1SCirV3dp`$zdc9SwLinBUvO2IWr?!vINOF z4LJ=kz%aw*ea~6zoZtK5uKNevFMI9XtGl{acXjQmr=E&=`$mI|n1T4-y?bO@nyLo( z?&0&@yZ7Lb5dW@azU2Jty@xbgTB=I#{Id74)9Dr_-?xZZe8s#f>c33gQ+hI)*+FRc zCvo6;veE3UeA#Y=@$hbRKxs)pCejLJGHE7OR^rKDE6S;2h*$rVP3is%rq24Qfq>hK zdZ4gx6CRWMlM^i6)5pH8K1a>S?7-oD%*c|gB>(^=Qzm4Op!h!zH01OD_4QxvGx+1K z2M0?3*=@}#71sztAs+q5U}bQi?%1@w;7{(z{}gg4tp=n@73KTCXHq8g808ya_@9DV zQXl`H)%^F{{1+YK|D48!JpEwspNp_H|El$$x&d}o`k&hO{~~As`CL#i%0+^ao144y^0G<3 zS6NL>?PqD}IdrE^;R!K29OKTqE+QsIWoK`{k29hd&CvN@t6X+FTy`s*U5K9PaXBr+ z@|sKNQ7C@5&0U2|8bdkFc*b(zIDx$ zet7K7O&iEdHVL4!=1c+rH0j>t5MYi&=I&id*XpiAmkHJ(WQ~dMgLFT^1D2zUf@=#8 zmIzk28nWmMXNG#RP>DG-C7yjtEASMr{&Njej0|Z{yvbM!3LyIUij2fJg)wR2Je4&r zZLVhN^67rk_op<`H_g2sv1Frwq{#Jedu~U2YhhP(N$Hk8ht1A)4n-#bix4dLZ91ad zWIKAp?73MWj%H-F)Bjx+qEmwQ!Y~BmS5YCM*ENpdntnYF)vlbN*$P+{A@1`mIzXWh zzRDu&5B(!u&6n(lq*+I+E`mi{_vg@k7NdIZE7|-flUf>6dq2#C%xw{sT3ToKHuTJa0`jY2!hT zM55zwE+UcD1MTI~Sf==Iqj+~O zFjIx5*^T5?R9-eUZu~=uqfu8ok#Dm{h;}ag{cLPRC;{;`Eh%r8-fksDW<&~$zho}P zx9dFH)`38?NlM2*Yo>Eqra|t-;X0%ZlONOQZsrYHnV1=-C?0n1hq3zBfcSOD-e8vF z#JH`#6@CZHLWtrTU?~H2^?A?V`5MnDm{<{Qu>R>oc3#%U2z!2; z(yH>xX~g|nHfMA~Pd53re}Bc4_aZzz+{VE{n3S9x8PU1ccG*pvo|2;T^H=5L7cVN{ zonn<=ZU0dVydv1w?*Gvq^5H|b3F!OxZxQc4(eT1b5U)kK@`@WI>KYoNdVAGDpX8N3 ze6n5Yb=m!nLR6fvS}Qi6G&ZO6TT0;Kv{=8t+vo*p6e*5Moy_ehId}ch;jNi2)?P>+ zAb-`nrQ_c|96Ck3EOgwGu}U9!@EOtooxO75trpo-!tiGaD7OTm(&fDT^6f=+&fZ+# zlrI`Fzdp$9{|2jddcos4{=Jl4HhtAmb~&5Zf*KZ@HEmPOPsN`(sN!v2l+a}XVace+ zWt~ooJyx)<5c1nVC;hPTVg?orR7S=Y*Ss}0XP9DMwpbp~9SOYsxOVxJA#`e)%V=se zp+RqNPsd*diNH!LcMYq0=IhYx|+^cZ_a`^{(55F zv7;{{zA%0DD0Jt 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�==}#}c^zTO;>9y`!=+BB+X+E66c(6zO%b=!CCa#_F{%(i& zKW@)OYe$Eask`0yd>-g6yaSL;Bjt$Ff%)mo_;5Jm?H@{)MAYv~xK3m^62E-?C(Oo3 zD!+YVyYW(~uHKtcAa?ELT+rCyT%7$pyK#$wKsyyo5#C0fbwAI?)N?h%xwVM zv^$y4IPm#naKs(@^&~2dMdp(H<8sdI^YBGl^oJ$WNIXR97}5-sr`RvdyW}G667UTi z=@$E;jy;G-u`;nh{lYt>>MrwE3djs9fD}8Ujc^wm_U2emZ+#jb3uA(@7R0V2Y7A?d zz@RVq@-gT#RS_e2QX;!t;0ZMgtEG7$?1>_@RjQGe|PY5(XA0)EF5=+{Y-jBq>tOnDif!2gR14xl^MErMRo`E z#bm8Amxx=OBUCg&h-3AK#o%hf#&AN@*V{;HFy?7M(Qq!1qZXMvJhk6uhJ#dnnZ1R2 zy=Sr1N!T80zm|RLkTM0F+ZHy3L;Pyd0)iZ#$@62!dmyID4aRvtT8{YVQ_vWG31owB zsr}E?)(_FyjXW{@R4)YURt%CpUpHj6%5{1h&&?QfnCP52=KS$=Az3+#z)@oRdy{5=w{=5D_3d|m z1*h6Q#m{DTR+^?NLLrBlf)6haXJ^Djtol`P!}2Nw0eh62RuZW+_mD99<66G_amqdf zV~MM`xUnA(#<5C_a7M`+KR^(1XF9J?9?(EoB$gd(iIZ5<6W80u{?h3xNUo7_3@ASb zuBF969%^;k@`RGsHY3f1iSkwzw<8Gtk&*C zmr98Q-N|&jPHbn?<(Wz0X$9?(x5UslpdXPIr3)=zpM^-ej+iOWC@nY|B|P34bxOT-{uERH z=I3@6ZA&fsGNrGJjwIx(kgxgi*&vl5;U`MA&HJ#r-j&okm4e(vGEsY(w5+=s7HkTB z!kQI3EsJ`uBEw;Qeh&j*xL6*l$bT*|x6T5bJv}PTR1Otsl=5-dYY|%6Y#4FsJyN8! zx1lB3qG|IDyFq&l@L`lN;%z#Y-x8-OhOL%2uN(~&zCQ_L}WqR|mXAMXyXitqyA zjp6#HNo6S~Y>+26E`rlG z8e!e5a_bIh4Mbe$u9L)MyevXlUmxA>%k|LCWaLEqGc*smIa-uUm z_DcxddVGtS$h~q#o-y3@!G^8aJU4OtTgIV2tDOF^8fqo=7#i4Ur| zQVzb8CFUG`~M!yr~S?kzJ3BSjP44&z;m+nl2)yJ#WDAfwo@Ov)EVs{ugHfV zgl0AG)|Q8@p2wOKassW9eXAbW>DoM^EBZMbK{r!hBPvKM3+!Zxd-q8Bs(Y0)obi0SUo|iR>@uQ zCZvn83sWBuM~PcebsIKr+TY}DYn}!bCBI*==o%Ju=vV!2tg!ggjmIy(K%#LO`hJ5r zyH^`H`hkB~F3h~_vT$4_96-6@jD!JI;Ps3d`slDw#a7&;S6t|wCLhv|o zqEgFTp_!Z;zb68#Ay%X-`Njs2gHLyzPy6Ebgcodj+f9aIqmtZsn1psk4KR5ufrT5_ z$qWx)w*>s>p+1|T*Ekqalk^3`uhnyP0`cmPPPK5@EMVO3v)<}sdhhkGnis=>Hf4!> zPxbB9mH?X(N1hWN|Jj4VVW4Og=(}EQmwS3{WJfd=?954uj&f@E-+q@-x2~vhEB{WH zeEKJS;zMp19x^Khym`yMc+~%gq|Ez^eq6~rLtG4q2~mi@c{%@R zV#(&%rqa$WqX9nYBR|W6h5qZoSPd5}rB2KO_RY~*_x7AtkF@wF?2js`7m$YE2n;vz z2JWoo`HM0BVvkHkoq7_+HjXV`aLtCk^1|w3FD@6R`h3$S#yRVj>IvzgU2UL-WiHp1 z&yN$24GFQ5jzN}y#(eIFtNG+r&OFkhhm&vY zDiedmgKas;PkbMK{-ezA69G6I>LYut-Xt$!=6f6-+RHjo1|!&$V;djC^BLvI1m)QD zXTaW$g((TxtbR$d6-j3n%VV*L52SPlg}ddx%cox8rI+&!$9*)!(_|oOhHw&uQrjh( z@rTPJY5h4*-n9YwU2IL(&Rus7*O7nbOAF)xub=n2?+*E0#4c-#6XVQbDZ~Xq3>xrS z?)-&0ShdVM*?wASGhPmrAHM7nvn1^it#ub zzAV<@8P~m=DH$831uvo5aMIoRZ=?7FtDbL)Ox*ubg{yz|S- zhj8{6ocI6ja`d@(vqu}ryLQE<54egRh2;oJpD90s;dh7>yUs0-mILY_^4NKllpi^g zG|yV?DhGGHz7`dGaF(KonozqBM5V5hGr#k+ert*Quv2%i=>G#K)DB5cD=A$0qh%5o zCP&C?8=NnW@3=TREY2>rEGD;^8cqUX7edw=Iysm@yguWK>}({0ln|R^@kh-Bd3+|R zrL(_HDR)#q*ILeoEg$Ah)yzBpenIRE4!R7|V~G~=G_X%m4@~9HH>_F&T{yc9fqDE+ zm{cw3X@az9>DVuIKP@D)BN*2&#C;^;+ z-aVOz?&f^UEHf#UZ|>rQKwbQB*hMGa6PGgZ>00BKF-0_r>T#*^n+hxLs&VR|$T$&L ztJp$b$YLkJJG0vXy|-}7-I8O;7Q0JNM%9cjx{ds@LL1(J8*3R(MxCL{q&dB^8jlzT zpV(c>HCqiD=5v%=@lrz~VZFDwY#9Re?42!4MBRBPB6)~an3fYK#UEap0g&qCVYA0eaF$b^aT4u?QHXPw; z{$rESAA{x}i$@EhBB^SgmR@1bENvpt0CopRRnKZsL{eX@QGcJtABXjgJ+&hh{|}R1 z?;!@UAQ+$Ln^0QwmP%5@AW~~pla_-!Wx_Mg444!6=A9fwtD66luFhg=_)b&?`MlHP zjH~moo~V9RSVjb_abGiB-7b{(j=hQbFuY@s5yDxs>QMzm5kxpIvNedfW>iLEM{gAk zdZDvl=H5MSDpX$Tu0Ppc?QOh~+PA;tHyA`068vDCd?bm!&21}EopDgLpDR8#=%kpp z`l(TU55mB*W1M{b$HqHT`7g6r>HJJWIL|eN)_XF{G!ce8^A(@a@VQ zC&71NXt+Pg;-)-?k0?}P8?pC!s-Yg6KQ|=#*r1t}bfv5y73kRe<@m&v97%C!@J!Gu zQ@L)J+oS(VoDqULZby=G^`UQ}4;fu?LW=@Xe3eqTO6Ur>^qwGGHcS^?7?#-`2#ZwE z4z-nT4hMPT!+X!p?sR}ppTz{ZJ%IP2oq8v}rCN;O-)>7dejBvIIg(X~3_jX$1)}7f zPD_yxnGgbIeQrpOM-jy90(njSakdU03|50k%DCI%OX8~KernD@oA#Gat|pZR7ueEM zY4Iw9z9)8aq#L>ST_^R>Zeg&0eKS0KcJh#eeBp`2EfFVDupV%Nxf&|UB-`U+W8sFP znev64pOY@It><%c_kU^1w5K0_&VF^Qd@N(5CgCw97e%hw1A2dVn~X$RPXf&5yM3q`m?(t21r7`bqJgus1R22Pw!=@EpKCd2Av+3~Lv9~deAWiH7B%G{qb=3grDP@E^9bB$v^-+PEoojrGy?nXBi+sVC> z6qW?ZQA&t6oG&ay=b;EDrB}#LDz&rb$7fhbxz%z(u`{eiA@d@v<(xEsh1At_3lAF; z!GnjKJ%`KYEz5uS|NT0|n_m@ix_)|cT7>nT^^Ke0WxEooePWd=6$zLvPx8v?Ok!G_ zF5LH(+t?FYu8uifjr44BN{S!{v^gt)*z-jy2h07(-tkjLfu+v`TT!8M0^j$AaLoo; zKA;9k_UYs01-ZXH1Am1?u_v1^d{r{-i*>d;y}dU(j5SVtfi05oraDq*yLja@*N)Xl zj;8gRP3ceRkJB^ZHl97hg2CAri&_ z+eu$?^B!{};lLUDJm-1!TEL;EoBq=KpI*hUXfxF#`X@Mo`p)L_p%gDcw6b%^+LS4B zf;SpG3nj{8&O7p!gSK!+JJT^-pHTE|34@6sG$=n?r2})AcFKww1 zNME4g&EgxG3HJPslfj_293R#wcDjVR{<^npbm{&I!}v^)W3KAr5`HhW{$Rq7a-g~y z6j&-|<*?s6jmbecgSTwE#EhTgV^9$t+iOmJ`9X^U>l)gKCklMMsA#@f5nGAv*n?>e zMpe=^8#*UnQKRs<8Kkzodxa@#3sT_3n_^k!j-2-(4p zkn`8=f)>uL%lPTvMLQXB_t{+Hrf?+Z;j;>!sdTYkLd~u@b8K^bty8=A^sF;<<*udj-N{a1Y+E%zR~dP6sG7s2RtlH~O9K6}D&FP4NKdJnIYzX~V& zS_bU0erx$GIwf%UHOC!37-U54VX+3g;Vb9*t5HjrADY_1)Sd7@I zNNViSh*VqQ-LNJ|lM$TF^wE?ck_0{;1W^DecmyT@Ad9cgqRuzo@u_No@;)bF7m}&P z-Zw*pu*#CIsiHZwK1=Mreih-gVFb;($9h-z#IZ8zaPI?`9NB{S4x>HI|I9JDaYC^fX`J3#-?4-kki>B|NpsLlm>H$ET1|e3 z(`$P`9VsT5h6`3e`M^~5!g2qT;+ncz#N&e_%at?PL?D>=+Oo33qhv~Zk(}R^@K8q< z+l$p%{--k$|r|FESXKz8AwueuDTS+15+Fob=D{C(#(aM*tfHvVDCf{ z*qM^4fAFYeUs>4Nc>K0=LTE1RFNE<9di%OC`sr;FmU698{vQ*~x z2n<`~pmYsxpH~k{!T?-wuk-dAb^>Ms_jVY95I*~>cl4W*(wyAyN^;L+)>?pltWJz> ziqnr>a)BH$H1IcOA?d^!&TL_c=6Q~oakkU=s)bYhnR380z% z2G^3-)Nt zu!M2pBbJSoB}C0K3nXihZ`4O{FV=2Aake1&IVK*T3Ze$pYe%QfLb4mlF~@{g?W@IP z3;#?;F5y9_wjpSg(1jP6pF7s+05xay%wN!BzhW;N>*BCa~LJ;!cisjNAVEmyUuR9 z_IT}(9~Al4(`F(C2;=ALnu-p1eRgd(s67Ws_O+YDZ~EPnZl)^Dl6WUJj5j0@PywBq zkO>Mwv-)#1zp9w@Tju=ZeQ*toc%th757nRWGlth(Y{I_e-mGYz^=8HFDhm%D7>)%Z zA$M4#y?fnR>h_;+Og>9j#(33A>G0-|j2ul2#^Kzdc$|6uX~znuS>SOs7e-HiV1JfT zPW*%HdmJ%+Bed$#?`Isl;W_cB3Nah=o#fq#I$;QQ#q*Tk!#RqIe|KgZ*^Wa_TF(%T zb2f?*M^qJas(_snadY)pYU{yS-9h7nzbWod{YlfUt(h({dV1XU8U5HK$BJpr34K#s z)QZ<8t9GBstpCEK`O2LtB1^eGNjNe4LT;2S$C=~!lBtJYI+r2%E^3$9zf*@P8r`?} zk^Qx*C1LNz%Dlc}OmqeI1bC5bLUEC3LNN!mrn4@8+cYNREa|z1wYLr>4*T_@KTyWc z`=9fJ|5g9Xii2f}1&dsWy&kFsZvujcEA7Qj>D$JnrYhje*RG9-(+*CWs0`){Wa-<+ zSWgF?;e{nMh^14v39`SorZ5m<_cD3obG=|G{5Y-GEfz>Wr-1xt4lWEg+6Zv1Hr*0A zl&JU~X#BhM7A$f&u}mL=RRE?ub$p#Ve`$l~iRE!>M3olQ#JLv2UK)YD#Mj=Y#g`;D zNF1UYQMaK0W5)78*|YgA6f{DRW4p0|Wh6@o8f2=~I!&g`5RfUpfTckW^Aq}g+SGL_ zp3d~2z41UHU@+L=q;f`dj3lqIV8OZ%QYH8-lrXAqutHglEfcm?ov>-nLOGl`LEBRe za%u*Mvl2d3VzO4XzbGwLLl~z-nmml9OmpNEI8y2_;Nk%izU=-=WSSrXpSJmPzL z+%9>r%8&kg3yCD|Z?XN~cmK?UkJduSNKKE-&p4Z8`)|*qlgWQqTRZi?mfxkR@qHuu z@5O`x@u1wluKVAa0*TKBdB$!3dbxicjG+nvqj0MK=l6eAFZ_Q@_4uEA|9g}FzrQt! z{hpqn|CZDIt0frND(vp=Duw;8T6+WA<^R=sKu$G7DXFRd4M+ZWpXe_!7q7|r=f@0` z)CKY*`v!5Lx_*pOblM}w#85rdu}wrkQIlp zgKOqTTVU_|o2N=4>QSNL=Inz}r)&Fy8Gzqy?ar$zq$46`tP~>R#vdn!SX_rTBpKv&J@wgYHwu z?0-o@Z4_wB+5emi@!mqZiC)9Ti=O}$lc>Yp=3a@z21=wN+k*C7AIK4?GK<&oE5Zh zJ(l0_1{QqCCbUgLTZU+KujqbD!o!0$gk(^JFxhR5EBT(6t#7id7Us5$P;D$KEti*s zQlc3f$Qrbkw35(XS$LMd`u^pG*8#Ae0GosoE0TSJ*(W!r8EU+VKi#8Q38~wTwk=ld znG8>1}Dw>f0`#&f2c@2%isOoG4q{)0`sP&D~g(kdc1AY&eG_l=E&W?@!C8d znWq1bIVVCkam~F_10*3D4}Iw8|Cr8mE0%j9K4qFMtn(Va5($Bo|(DXE)#+GEUv8Ao34(3%9D;= zYc6u^8lPgvju8{svd(vt99IvG5IkhCW*M|~cXH6V3b`Ob`D@uC6$-0I&odY{N@0=1 zui7|LxMMgLT+cV+xF z*8ctJG~R!uwsjK2$0W`>IGH|J-pz+DiK;}9h<4>+8#^=e%gyzVKXz_rV==1&YcbT9 z)8?E|X zu3Qf2JSBO*A%rCWm9_pU-oKm8=gaC#$k2^s@E664Xqf_=wxj7zeN3x z48I6VAT zLdS+{Hj|N&QFDB3PeM-K*xbxNF&O|ezH1O(3VZVo;JhHBp`m${6-7iKvx%mS-1#nR zXlh#6+1bqkuZXF^&AkN-0Hv(Mv5L!TtV^_hka3>*Qxi?b7J z)Ef-~HkCo2Ys6&fOt2+CA^bs!Fd$lP#Q*Z;i|xhE*o&z5$?t4!x8FAoFqmFQ-A3WP z`|WWuQ*y*ZiL{E1cn|c_5h*(ji4BP`e_7NW?QK5=15vjEbth;zm)Fw#dhNzyRq>O<$QGzUncyq z>gT=ewq&O}AIBM3f0sA2)&hk>ABB6I5$!d2C;OXutgp(25O2 zIv%<){o9?wD;PoOH^l;LM%oba^{>r!(vEvz z+(m(cilN6XXzBwMF?^YuQ_C1&=pT;B3Xk=r}VTI{? z0qp8;kLSDHiwS-EW?Fqo$}L&>fS>-W9lreN2#q?-ZI@GuXZMHScWr*y|Nb&97O%(; zu(~2D_r%giB3#Zl9eiXviUMCt(NC2VC`A6nE8OXI$PNXV2DIe=6r&RNd^dZu>MzP< z2NuOg1>33$4b~zsVOP>vF?9fRjvMD@OfmsnKu7idnVc5UI^m^xIU<4eZi)^plo~Yr zsS4+&ND&QrnVZaON9WJOQ2v{Fz_lLPn9Z^fsy-d|ZnxwMS73b_iW6b1kUw>+SkLQq z=RicnGagk5EEUsDyX=eZj_r<*0t_mz%%~aI9hX9)=#xRa1l+jW@O9Yr(qJalhat9tx-f>Yd1> z?;J0r^tx1VL7>myJD=~f*sP8FYfO5>Y^Bctyi-45c`G#7b@16F;dj8L_8CO#M@gl9 ze>be{ECR|0Soup63u>t$D=QghGM5zO zqq*Iiy_1dK_-B9r#3r#fBp|Urj&3w$nQY6aVoYN1bjFT?H{eHik1=2OAANc1%lz%M z*SuMCcZ^>1ToT)aJ6im*p7XKIU&!m`W?;Tc>TR5o>`CBrI$@R^anvn)JntMmq6~&} zcGd1GdiE86Nq-8$6Exktilb5O4Tsx`=We!;N;P-S7Z57ZzsIA$-^FM{a0)ImE-vV_ zhFJ_I)-hY-H|V;iS}Y*W6($w4$wQ(kG7palA>?eicNM$kP>L@SHAso(mfkMp0rU{D zoyUjMWF=7@*N>nOscqy*JK#fUSG6q?(hPD}V|2`2W?QEYf4s$gi1*j9PiA)RM0nit z4(a!qP+@+Jiy3gf;&u3q-;SAU5VIJ;byM#=->?aZ_H&+Hb^4kfE;!Ofd?{eVnfaAP zL0gewja1UBDQ&d)y2LN=X|k=Nbu_K+0?)(cdtN_y0U!OqT0{ch^FcZD)~eg;PSZY9 zYoWE7kl94!jv&& z4PER$t;{>o9V2a}lO%ERf94$vVyYOPO5yoV#2bx#uV)tI)YD{E$?=0w^dEyox(zB5 z1NAs)sK!)m0R~K?s=6kDT!GZF>%Po{?L&MBpG;EwPRt|LIViWOQXD4gl-ve68Ka3y zA~64&PBa+{WRJbJ;fFlO3$klY&3)a6a}$WO14aU4Zud#Q>v#6W8>86{NXCDB7w>p3 z0TbJG>my+nGL{^52^LJHuSeG+P#?@1*$9Lbi!`-uJQs`k=>*}zC$VXDlG<#Pd?n?D zyHP%xbCo^mc&;=i~ z6V`BZIwu+}qMgDLj*vHlaYE3!BtDg8@Htbpi_+%KpAF=Z&p9{Vb*_EgrLlCO{lM;O?48H~!Iam-Jq~ zz9OkBu=rp~$D|Efj`=0UA;+VT^mXxT8TBja14yONhf>QlgIDLR$s;fdAk(i{KH2=z zorQgp`NgX-{}}S-=;l7H*&;k={ejX>qq4PO{hd|+EH*Hqu3`t6@vGrh(Y#HW3OXS` zS=m-cwm?uosbVqeRHf>3-zxlsrysLFv|;1}(CsH8gP$I5 zEb}j1CWO-r4Y4;j0bGbytZ$&I7uAf0eTke6!yr1o0??ss!?I-U7?3{8th3j7LWt*h zr4D0LNv-bZ6)kvGf!zaqCHQ=QNm<`|Tl?Wz!n@WYtTLN#QL}feY_Ep-ctn!U%OB<9 zZM1WDT|`j@<(iE5KRQv8jh=irvI8?6Ktj8+ML(T*UJ#8^@!7yx_yTJK zTAmy%fQ1za(nJh`o(E#i;zk8Z1llxHgd*Y-s_QUIRi)ih^2vCrqlKZPERU@cppvqr z38di+i)CV6ehFCUJD`L)S7d8X{zg-sNC}3ZeC|)it;a+`I)49g%@(ZZ!~81frW>bM z7darFq5_^7PZc50QEYGZ%1s5$Jx1oHwbVuRpm?gSoZ$-uvhzqUTWWf{B{xGG+U?B3 zq$=3Dax#LvL@n-O@8VtV1Csyum@xo0rZRwE(BBh>)Mx%J{pwm zHR<4&$P+njN zP1B$69HS@320n^;yr0%dVqsRe8u|H4b9A6o?j zV+g$%d|Zjt3|uPhfQNUwZQkTaKc*MEP4(uLSpr(81H86Y@IbiP2WQBSfq}^Qj?I=E ze-Fx*6@EiEOxK81I7BGakVs1b<$9ctMGxzr(_57=V^q+s4Lb70c53;ao3)1rpMRtR z$}+y)N*n(`LRZ5i)AzQ-O-Wq%w=Qh^lp)gjPD^$_ETo|}-`i7xQ|`7=cZ$}5FzypM z!n${221p1G!SE$8LlkNJU~zN1K)8J16C*4lrEK3QcW0)pmf&~$ zqf^C;LKLL83IxRbAl~5+#e8OdKup)VdJ=>4-8IdS0*{&YcInU7+vw}){+ZiPu{@JM z7aYa!jE5XIu8s)>INB;7_L6FzBYY?#;Y{V2*#ZZ}JJsyba*4Ph}F{m-(} zB7|pcz26hr(}U5LCV|)v9%o~vcL2)<0F#LV!^~W5B-Rz{9FVk@1t*&jpa@PzZ>nDR z69J>Dn#a>s#mK=ILw7}PnmquoPMtnOGVxZO(Z?@g?2WEvGLX096y`@IG5mpz~fVR7x%x0q*Bqq#c5Tj@fwJPZ#HGC+1Dh5~# zEGS|A%+il`QxG6NDZxL8DUFPRJS;)35(eV4{YO67j56A!mQsbwU;-PLv^(T~VM zi%1p&`|VF0$x|7mIgtw)3+bd^Mm=MTxl8Cv%|6z1-eb~QXxx(oejDLU-4y%OIhUD? z7QDb2C4 zqU=un>u#Q@v;c@sxb3W>zd#Ra@t_7JP2hVRc4tLbn0Fv@5wgaUz*h!Q#ckHIv!vM9dmQBQIf z6!=Niu%M47^dBK8<3qxNl3s+@~+ z1UZNK=khsG>qq4we|Lh#Y?_$mhi@z z_iD0X#t?CRqueoz$1}#mUj?s)3x>VDxh~3^zZ;yjl9ms&=q{hE6ym#{8e%S@zr8$r zvo}C2$w6O~Csr*w#g&S9^EloB#&yh|Y3&qcAp77q2Sk9a;!9GT*`FYAiVC6!H_DQo?3B7S18M7#MyN?z(n$l&BrnyXHTs` z*f{Z-NdHH00jyb*Q#UsKzXefJj*bc)0pyCHwczif876N@wjnx`4GodOB z^h~ps?Sz#Zf)Kg5PHjk?CyB|r{zBl5AhhF7Igv(U60%K z@ChB!Q{ICBI0R`}6nI?pElJVBwXWB}_h$}OyWS}Th^Q|zs+COyTfP7oM@mISr4c-t zE-v4F1xm96LLxSW`Z*Re6kuXXHSkK2;{%td(8g%M>7`3_F`h9OX8ql6L^ zYwH`}NYO3ajsFukX9=mnPogsErU#Znv3S27y$}C5n~AWXfqZO+(E_t+L0x$4a?< zh}&#w?JrsHud=>2oU5VG7|9d)H_ zI^xD;ffm80Mvg50vhtJ_=pDPnQ>iC@h=e=zcN(n6@crIhN*Fiskh%OfYsAlCHuUF| zzQfX~Fs;4>T~+&m~e&q@W^3;nF8(_~5?4Szx#(vgdRru+AA?*QET$Q|jBY zTCl)Tzp9PzFlbCyT5P_rh0ANHp=08z<>}jc>vqA-qwe-2XXSm;47I)wNH0O6PwTfpq z``U88sQ~*mek3(npQT}83hsvr1->1tzQWs>ltXNn?G}ybKdc`+{?yBBD!^OF+i8r+ z)^;t1=#YO*S}4z`B zg74qi=;-JQ^7E;#|197%G&Iy007?Fh%l&CjdrpeE@9%T{XJ=;m^h~_G1c0ov3)$UU zB_(M9V`~5`0CwBcASGiYQ3kC}ztYm^=w9L!gnNLCSS?&`5n z_>Ro4-Sr6>+n@R90SPs{8y{kF^4Q7ANh^E%ahKnR`5fAqZ4;;K3&Y_&#~*!i{_B%A zV0rfxE=5=uu&}?7o|7I?o-mY7xWR=DR3C8gK=7u`9KqZpQG4qCFe+mG-8Vl6#&=Zr8m^J^6 zy#6aI`V4?zxhFKH!~#^jzvB@~0$=7CaALTBGftFT^lnJy+#M4jZ+f2iJ{9dm$tuN!$b;?UulG-yRRo7z3IVC9 zsXpk3xZ43yh81P-6L2DrnJrmLWqwuX`QW_hG?ZbeUhhSOC- zqv{-w?^+3N6EtA{NCJIzxZdIW;JnaAPVC|u_&V7V*@oFgFA}Qw&2~nd>f1>rL#2vd z#ea3TS;od9c8cJ%^_q?|LLm-PU!+&2r?;xnS1M9=E=tJQ~S!iL&A**TX<6YUg{tX|7JE^ucvrnXlI2XB6M<(4r zd!boME7;lC8N4)SGpt^_H1K=EaYBi_61Gq*pIUjzCLn|MV`oAy{Osj?gl(Y4Y~UREip0K&>PHm6 zAh-cbiyVmj9QFW;^RZ9CV&*>QC_VBhHcThL8P|moZ#4vggskG91GqkXH*{|A+mU7C8`L zUPQ)tPl?0+nwd%7#V_?JM>UfTa+_sb+yPulF@~*fhRLsqx?g~hE@=)hjL`!v>hVHi z*qwpueKM8KH3#I`5rD{*=DRji@s`=>xlpmeqxm1IwM#Fn z(O-xzX?LS_sk_)VXcPI0b~T|v7eK;zE!vn#m8lz zkRrma4dc^3xh#)%CXB}ot4p7sB!>=01mE;C3#gC}i}TX6bD|QC6W*{h=;K$mcCZ{I z_T)W0t?)gLp3T2p^iH-lbNH_2ggjoUWnAKN$$PSMaAsVT+m*-s&i$Q$i(kxq$@a_7 zV;F0DKyJb+%j27%a{Dhbv(kYk+V}cuni&@ZErDzmNdVR4$+uUDY%)UxtgdPtjj1ek z^8C#AC}hF^T0{QqXH!1ams#Gemb_28qtro4$SAQ-oqVq;9-q4R{B}Rtw)y(ndONyw zgmcdhXILnEh`u~t=nh`~l*F{4MeLawA0PM8a|c#ccQ-Z`W>Tv(1{=w&nGzwNwA8c6 zLXJz7vxm(-YdnE4v9D_)Iv17EX#z?_K-t<;uCFhU*3bUN^M*oH?>Y$GPFAu*YeApc zG)XS(1B_E3>-3(68nJoS+GhT89n8CVuZ5AE%JslokIXM~^H%PK=pVkx}^cT#LnDe}@2;xCz?$FQ)ljla8@#zCeCR80syY4`2o zp4HFHb7`WbO4xeF`vK=E=l(}F??+N!5op*iX4Wo$npX0xe7qeH9bcsx+z2^R->_gt zx=(q;YGck)iF_z%h|DEu)k+{oAv@@uxQMB6NjXt3Xt|(z+1{yeD)y>Mu2yprz_Kb3 zGR3IDFHlXsN^1U8ZL}_fNn5;8puqUj&=cGO!icu^VTO$lNd>YkFaoeF=b;` z4qv~CXp0hx>3`eXcFdlu=JQ_eZTSUNF-EwEYFVv}NqczhGfwVph@l9c@mr_(pc@Eu zOhJ#6_;0xAI+%(J^$EH33&+YK5DvLQKRl>|EAZ9Uw{5mGvu%DT>}gFc0__R^%uPWD zV-Y76ibWHn<->E`?f z85jBF2I?JmG6GBKI99)X9HT3)x^y{-GpZ$iBu_EeO~tUd5;dFulA-GdRm@PnKi^(ocJ2?Frx8@<*qJO zta?;lWjUo9xn%sNJHG6N#2)|SkMb_y&Z;4Hsqpa7~i zK+V^`TxY+r`Q4Gy%M>d}u9|!em)w;KH}pbxvF)AG4@bRo)@>MMCVrt^Z2Z!G zKj!-hsE){0n}?62O&;nCHz7ZdUhmcWL<`zvfLFkS;%uUh#Qj+DV)`B@q{teYC{Y70 z6SP6PqYT;Qf2)7MZ$2E(z!wtm>&?jZ5;ahY6ODR=d@!EkKHMGdws zpEOF_RO7a@jn0Tzu#FH$i+HQfcS%7Uo#gPz(kAMVVu^^?=d7iO)GGF%B@R=!l1?>? zwAsS&Q>VAU%JUMY>F=EDz4>eifARS%z%#*_QbQ}_1vSlc1~X{B+UPs{UGvmE#t)I1 z+{Ov4Q8_t^3M`b=<>z#?1*ci^Q!Yz-^uLNuvtodEg~MG>ef;KvPVyQ;AwV?Ji7n)m ztNi*x*m@|f-Jl@>zlR~)QESa|nLIu=#Cw^k0(r_TNs|1LBoO_Wl`qhPL9I*oT$()# z@UD?Rn$v`-Mhh?(KQ*PUHphPA=Qn(tbYEk=E^zRz(M+;X32iQcd`@mpK@U`Rmpx{! zMp)6+oE%lZWoh7<7+cA98|(GeKaewl!lI<|+){`h4TcnpY3Uk<92@}pcMUPUkEg$oCF<)@h!H8?L z^uh2|J>J~LeJP*iR@(ck0^xKTftpH=H}0<&XP9nj=cbI3VYN{}YPW4?P!e63m@cW( z%I36sx$sMFzb5S`*<6B)ym8{rE@9APIfeDN;A;gt#SHEp zp43}I*Gbsu31A#ML%{6_3qk>BjS5?Mp)8QHc)76H5+!2r|T zffNV*M;^=>d}pU_zT%wv@ArXcV$YB5^K*||?u&*&?ETL_kw3&d^fPm%>WM%pJ*Qu` z`rRY%;xS>1IukJNrrqm~Dp0Zswz*+&rYNzbCjT=Q`JvpifFkwwQih^gYr=aYC%h zQmLrpG79kHiF+Sp2R`B^8_p#*?hu37TY~~}5APIPh+P=JLoqFWHYjL6iQQk4P*kPx z(7SH*`sy7rFkQ`Z1@b@t@PGJWnNwk);(hLLUKE>LH?Qo4v6sS-qr=NUW0uJ$@TjD)6}q<|)^3H`>-cSjN&J}I zT6?kF&!Cte2E$s&gn`^^K0&@|Rt| zUK6O~sy0o}U`Cpn9bv7Zs~#P+&v`{(z|X*0U0Y^TF$UX05R?eZgv3mLkAcybK%fku zA-jFpZp17!rDu5N%nxH*W)}(f$z!U1c$@kdi%@#lnJ^YP19rF{y!6|$6CE8EObi~L zskwfT?`@wH2fI)qY*Qe%d2TAgZCs$J)u@)gCUgW2!mvCb6ROb-Quf^6-4dX0@u=C6 zCcSW&+T7NcSOy}*Z31^l_&Mxbe#hV*Xk?q`5Yz(M-V2NS!d;oNs(vJqx*oYI@OJ1` zs8T!vbLaD6XU^{$s|%;bYsvJp(_T!=m**KpREyU63jc2qoQj)1{40w55r?k#o1t9 z35xsRTTT#?m4j}C(9N=oC)$+u7m9jx7Z4))FlaICEWaV>fhei=t1-En-+@vt^9R?S z+Ybg)jkd>XrZtUngEAx>P~@3WdA++oX+JRofBr=ibL~&MCnxa1Mbq5J*f3C76f@5# zZpQH-Qn9-vc*?F$rp@fD=XWjEbJ&eF$<9Lo7gXu;Oy&vUMUzb7k>-cRG}q`3(`Y#l zh1?DNV#13cr^;v3Y|HEdbh$W<$G+4xdb2>c((n6B_c0ws@0-HhN)55lIJNe<&SxWb z3V8a}M+TM)JN{-3$kRqA{m)gN<;R0Nw*7Xn?W6>K?x%CdpX7`{DLMd#5Lt%y(ZbLF zx-M6^5J7}sCiFf5WBUQ{6Z!2Vvuw&K$nPMF?8rZueq*qrnDxF7bNhf^C|bn*;kb@2 zUewjX=OU|=tqf-~okLu}K=ICsZHHnrjKAAUzGWuZt*EmofJO=NizR6y?*z|*tY>=o zCDM~b$EXYPMaJCa4ME3g=yF^=(Ftv`^u+sNxi3u}>qhq>97_GtswPp4TP;Taw2U%up^E2ptxu_zvEO05~u$@>$n2o<#`&572WdK$G9N!rCS3$*c= zD>N)eJ(3VK_8jmkzlwXbmRyXxwrIPk5^L5L_zXG0n2GU9f|%V2uHGrL(z~YAI&+x+ z9`-O1Qa~QN?^PL&;z7MgY?d{uN1s#oXSi&Nv#|ykS3mu>CKmfTtx4{20cFM>2^7nQ z>S^L+_4)e8#eGRb7)M`v-h{`hSxlaAO$ea$Hr!z``%-TOe=s_q|5B}LCc+MsLgzj{ zIXv$;ICoCEwPjAmMk5Y)lkW0&amBSHQrxJ~kNHA5%z|A5YYMM2-Ic1gpMp6L4IgZu zjDh4*I%=u;YRryA2666qcctQ0ahY{x?Bc-}+XPjins!3ZUJ-7h!u^#>Iji!RMmrWu zT*T!Se{=AC9EP-otHsRsiV=m+{cr|RjLfud_2$KWIwv}4*`z~SIAF>Er`RAGq-P^- z3J3q2;L4wrIf!%IcHmuM;f#>C!AbldpfiJ?KCS%3Rys^`-_W&3^2oQCe!F5j>cKyU zES`P3i?-}udrW+baU)q3k}%S|l%67g(5Mn|Q}df{j25vb3^9-oCA-+Bhi+T?;|S$|MhA-|8+2QzGA9 z@BM&?6>oJR?IrN7e}~iru16HYpVhc2vY3WWR(iaL3elNK;x6O8k`hxSnkSr$yQDA+ zcm7JgCAnyuJP#gu9khzQ^LX|C#nt}(!N|^+%RQAvOmVC^MX`x=aYvUL1r$hJJ@!?J z59n)Dko$cyCW(58p}`0=-ezXJ9^JL%n-v9Q#Q$-?KkK`sqqt{t+_N~P^eQ7IXQnbT zl9eU20oN%xl-ufk2-fSaeL*PN(S(d4eHBlsmF%M2rbOxOp388LZ*s8T#_HD46G(+; zu8g3EbTOBIkJ0TqGUb`i*e1t~#}f~!OC4`+CuhSHx~ zJ0Dt^g)CU$7ICDJ?tbpnw=cWw%`nHz=uN?9-^=`zAZrS{0v5(NY0;>Q5uS190S)?_ zQMHO)p;(~;hws>Iu5UVDp14xA>lv=pSMd?4)z7Ko&9Ww|E}7tPJ1j@1=Vsn3I3;5f zbB)rNe*8HRt^X{uRGXuUaeu%IoWOJ;nz>eF$*oM+47l&91#H^zpw-^UpA3 zWq5_=nrHhM!=tL8a-PJVaIaQ+fgH~Lya%6CQxCgoxEWUyAQsgSe`$pjlZ`4JI3OOl z2+T|OCgbb_j^eLo;+$*A8BUoZrN6xh(VJiOrx#3EY0nbxygBM=h4&3|j29_Vv7(=a zoqE@0I19T*4n?yIrbV83Gd$$3Zs3f0$5CZ|RI9p03? zTbevsG|{&18|*_!QCaRYv09#{96fDRkL+sMz9V=p8Q4`9b3-w#%wG&6rmM10Mqz_@ zH{G!%{*w38)b?kGI{V&JYFmbex^W_bMLrO=A1P&&V_KM^1K~(o8&bUJpnCkJ$BJUP z;$1U&C=^FUC}AnxHRqfi!|MGGkp(qd&QNkNB!x5hZ15ud4C;GHbTxJHE>cu{r5?xS zOHJ>W@kd>G+R$svnLvKU_iFw-f>+z^L0$)U1|eRmtU1E<`PbxL+C?lD-+J`Kb8A+( zI+{{j)Ma%|xPIJapUHJjK}O%}ddO4BW~xKTIw)E04Z}rmmjF=zolsc)^E5@>^q}^DzF3ojKX?>ZPCD!-3bZ z(&_TA#lm_;++O@{2+Uwy#24X1-|IqAgNv19Ib(PX*vi6?B95Q0tkO^ys)A~4og z?7qQ0H`K|ug~9@vF|p>^*{@De^q`QW0xigc!tdGxW7PNdZj*nt1l>X!;pVb!H4Trr zsqm{^`4~$4c2^WDTfZ%?$K*~3C7b}n+!plmIPvm0imYYy({pmWh(-b~dj^1|3}UN4 zt|5sIl4xfC5g!~23!WBj+j2qoqzXvz#x>ensCR0KPP2w!gQ@46D1RE8O?-$Va5*+h z>QcNb4jRK|d#N?QeOi5aGLRh}3Jd78E-by?lzfrhn13OvHsw-99J%ubVT<_>B6(Ht+n$$=~$VOs&AS;keL?uXK zgea-tj-FtzeGmChD3nQaB)J=@iKX)cKsNP_S~BjV20@rqYFr|AR4yU z3p!KdpoV}kQnM4E%mXehr6@3I$`6FGXpOrMtc_{Q@~Q-j(GbgJD$1W95<6=aik86( zU|!GR`+(-Iuq2yuiaXyE?^Qvw?Q@dOGZNkEzLS?j|EKRl^j4_KsUKN?=K2s4T;g18 z{}mCsyw9&R>s2P1-gRyblR)Ukyh`kP`Fn_D`|)V?`G}dYz;9G>hrCC5ic$rN{WQc^ znvKoPZh4o(FVg|~sD*N%xU`Qeye-=#^-?GDlRjE-kqq|DwneX!zdU=?TI#*a*DSYf z0TJi@g6VVvN%B#G9V7{~gK~{H5Y;EW?0ZgG+FOx;a4_>wxRX&mf>}(WAeH!+ zN=>lZwn8K)Wu8PjBNeZmq13UIYBgo@{nDX#(#d= zN5kHS)(kJo-jxZyl(1lbeer);eM#E|PI3rvbm;Cza`X}E3-Ru$95ba7AX3}=(jHM16Lqcr*MOp=Hhb3ChES6zTx(P%VM4oh-I;|G?l1+mO( zR(1j_jU3%do_aFt8uCHfoqjQ4Fqeh#D5uOG(CczvTyl6Xvzd83Ro3phh;RQ+sh&#H z7wO(Q4|@*7!-RQu9`1M9t9RMU9kmBrhG91QNX_YH7|*#c7yh+kgotccMWXwbIck@9 zzTGZyJ`6ZV;< zh0*a-@6+L)}|{wH390n}tG4p%e=mC{PL{I4$n3 z#R5fx28z48x5X{EyA&v1+^x73E5)@Gcb~)eotgJ}X3ZZk^9w60taXwc*=L`7f9~tb z{!UtWo4!Toef)^>8nz4pmDUJu6xhITa2{5K1svIAS+EF&CCWv@D$2*|!lhdht6SOV z-B|wq(cXVXYU!Y`C7uB+jtu1k(S_=_1$_mMe!C?w$x{ll{-p?~h~6U&#h$awCBofu z+pua<<*b)H*WU={dSH?tCDok{DasS-{x>BYz;VD%jNS58aT zuGLi(Nuudw=Y4fwoJ5}d0cg@>234|6FHIx|njwnq*RKdFR|=df&svW^SpmUYavv5b zi?&6MiH^J)x0oJL*kz66&X5x>i#Jv-}Ia61;`u0o~FtuwJ`MLQiXHBJC7*KrL6Sp6E0_6eA2KB-F8=DyqwY~|LUC?FHq zYSb0X7Er|id2@A`{nCNBOX;r!{{S(IuQX&(>3EIouY_PjCWrH?d^yQwdYurleg+0vhu28b4HzG6ZooLRS3ae&HHA@ilI7AxFdC-9TiLm^ zCi91kSsd`S0!RiV|7e}$q~kre9CL6ePa-SuYjce=AH$<~tQq z`?WIrdXj#$N%ky;SO9DI!vZxOE1wuA`%h zrt2lvM!@T+{fx0=K2qF$XCEP-)E2+fd{Dv7UC8?5MjX=<$8-vLx=V|YWtm7CU9Ud8 z8cg{qI2Gd@%%dh43D3PC3Mx`jLrlfjlM-opi1(5t41f- zTd_E~EnJRZ$rhQs9P?xnCKXR{Y!+r8*&)!19owuLn7EeC?AP|U&rW(3aB7T>eM)FD z*)(A!hk5N(%+8oJ*^KpmhC{j-nCTPK79~o4oz@qfv*+(Ut9iaAznifT`ZM;ceF*xy zD_MV8;Q1?SFI$T-|B%GYmeZU>(JxB zx@lfT&&{J>Z)D?K$u>BfiH$O~P=dDJc}Kv$@V(OmnM&+_??v53U5l@|&=b9xMM=ihrf4mbN%UnJb7Gugm4#zdPTn+-R*Kom)VzNntQ?ABP;BXey^=i?Ox`-fXn?qv%1`5|=P^+*zzpa^2~G6(y#=2S-oix<#B{3g z-U`m|!o@JH#blqaFo3Cn0eT{nuwzQCKl|D$PVfh~UNG0f#%( zfN!I4ly(6@)8h%EecRL=H|K^;!YRKdzd|a&%#0r$BwQ8~rqPHYC%T>TN>Ytakp}|G6e<6$$ z?_7Y=Jy~#Mr{_#Qz%V6=!)P^;{QN|=ywEA&)GOdxUZu4sS|qB@ zG*U(C*kEze*MYDKxkYR})c6&qf8@u)(awA|9$8u#o*aB-Hq^gIo~DG-nIvH_IQuc$ zvte7+&a?Nqqul;1hx!%&u7Z7rg^d|S)z-@KOwWgCU1r%s{ib%@sO zt3f`w#vz)skbrVW>`~Ge4#@{dB_fI2^xCg3IeK~D8Z$$CN`EBxX-ez zu+V>EsiZ>)&Jy`qw2JxzcExbaAxW$U_yTQOu7n?6gCz7seJZ8=bx$g*LJue${z&Ne zV?*u~wFq^j9V3qdoc&%Ij!l`hOT%}Jnk~-@P?<*_Khg%YdeG~^Xo@1KJK@>>cU5ZBQqmGI%%%OB) z+gEbwSHJ+`>V!4(Y|Q*2zx{p41$ctSx5s9Dg-{3?b6}1$5x<&QHQ*fwXJjX;d3F?6 zx9S~pE}l%QQPo^HmH~6KE1On|U|sar3|q(lu$%hoEQ`m;>qKvq|JjW0%PRcoCMNfR zJE9HfzWYdUFUAGLt$A@yHts5adDr0%N}O3`7Gue$rR8XQytzi(`<=oZ{#o z-`V@WTFY}zJ|FkG9nRWKanSj9e*WyZC|_Z*z+2Wy+yInJkLEhdvC}?8?_y zu_#+!%CV|(NOWo;)_aVPc*y=Q zHS@pZ-;H2=huytEG))bmf;oUUDr=*WzqJaO3KD2lt4PI3+ocmYy}9JE<`_f#2}Uw zNp|u8iDH^eUuLBt{n>q~eMSdW22Lf-*XW)DejCt z8$zJ*VyNOak9`TKMZ17wJgCdbs?>}qC)f7MQ!%2@A;Mduarca(W52q?!{2w1h=@p1 z=7<;xWkx-Y5C*s7Uoe`IEir|df`d_CvP#x7xFQ%)&ZO$Y{d7j1I#LGQvdc*Ir$ogn z99uLj?`HVu{oh&AoteN_BPH1}WD4y>%Bh zrdVF``d-f$&OEPn0N)}2snS8Sop35f4qqbDSG@fWt`yljM~E_|&fYT5E34CxV;Dr} zVjRfHe}u6~yL~qlGGkUtqZ_8o3SZCf;^ZCCCwqFzI0Jn>B!Sk^4 zWnX6G1hs)52_|@L6GV8HH)#?@vJShKVaxyN+t&T@b+u;f0C2n&42J4dDk8sZegzRS znxk7j7PESwN5?8&_U}Kj%hi75J?~Cv;w~8d3m#fG^?~`pMr+LJ zl^cr@>_EV{w#<1h#i-u$ZR6rT5Y!nSZ*1AVicUDciU*rxY>c7S#%X;F$0=4))_Ru(1i2`WryBQ}Av^+8N*(daWB@YKzu2vDeSh44<{; z%z+^N9I0eMpSl$co^~g}Zp;x5b?1mlg_OKN+>i)RVK<%;qaJFf`*ClHWGWAriq$jP zQt>3erq98hTG^Aw$gs@$a8@g&R#O5Rl=G{gCs;QUsqAQaRtZG}CgNj7Rk3X!c8CKx zX5ldXTc62S3xiO8u6qa-k9+?o-%38eAfu{gJS#Zq5-d~}w{muN;stCX_Fb7<1k!FR z@P>m95~p}%*_E<3o@eh@QB0B>c)iN6GmY&)Uk6_)K@}$oCa^F)mL3a3{V+{|flAx2 zQ7iEKpg6z%nQlpK0Z#+}#Ly`Nju2fY_bu)meS^ACtMjW>!rOf&S(w$@Z~ zs?sB}@8Q@em)pNfn6MeN?<+7x9TmG*;N`q`$H^WFw?tbGYY^h2geB!3nv|Igv8Xj- zp=TvpW(UuJ&`aR*sf2_Fl4j!2c9US}x87jOl5@8_U9TuD^MpxONfOH`{|oL#C1VPx zhMoYzO*q$@IE=9(?RbZ@EQvYXb-J%-QDr1P^3}t3Zml8pR`n@fDHuYlob{6;7WuL& zV0mtszY6{%M>+AGnfoMU@KC>Ipfe3?^+T$0479ZTE%-{K%WKk8fFB>b2)xmI`>> zJWa#z9NwG@y0fKlbM`cX`Et0R=op#`3<_ZIL@oCC+2YQ>P;(0>h9-;EgE0S|E@cgT zo|QUilas~Q&v|A=n#Q?Fclh)@a$6a1+m|LtX#ODVmy2^{WmPEm_;nB$xxaJl*Mc+K zkS|T0K zE-L$84|ZA|Z@nd(sZ7t6Cks@x$*rlXGkzdc10h z(Ty#>WBqwTq=fyx07@B3(qJELpoMs9NQ#0Wr99=g4yg!b_diB8yn&4uoAy$7SV02^ zay{6ekvfLcM&R{!T4=J!W{Wo%uh4Xmw&gD8riC;6C7q2%9X|y;t<1kt^WjfY_v@NEu!?PkFiRT?odMm@arZQUA!(_tpyaun+y*GS; z_eH~pRd;<*j;T(33v@{j?*-x}UsMF!J3GD8V=)=|Jk78PMm;=#eVShvbw`7d95v_z2AhH>Hio*D>#;hr12Rq7&10U$*YQ=EmR^Lq^l}{zt(8>r z-%ldG_b~OBbNrYHXI*^wIwyH^l(v%Y$1Y8YTk5mIXZv`M8k8x=JjA9l`i8Xr0Reu0bZs*u*H|;d z6POjos~Vtn3)Rpj&qmx?NR@dEn6iPUVajI*J=GfhtGb{{g^4Koo7wN=h^%G#mF3a>AN>9fJcp&SxXn?l zc=Yo$gG+me8K?7^BKQqQE;cS`YzBXpRhn0ZBAMGELMAnBPlEDH!a$mIqg-_D-l0<} zwN@lRxPeHe!JJ_J^Hqm{;UbkWqO_K)sEwzt0=|#9Gy~fxoUQ2 zazlhP*>TFTvXaUKZ%wuHW6Mrab+h~z z<>Q=06U*sj2ups0Ut^n~TRXDQ2BRH&&`+|Lq7w{jbB$(Y3vV+bI0#&Z2vv< z#Qd{)VTp;PsE5zn{cbGfA=xJz-cORl&d+;key@3p8DLM)-gMKln3SFzMuB4+a-Jtp zY%S1~1<-I)g=g$7J&yQ<EYUG<5XC4UGtx$H^7|SM|&* zbxJqbEO!fZYWT#yqSQhv+fKv=qkvh0N->yl*$!ipuYsytdS2mzRT?1@-l;T@GwHW3 zeT5gqqT$Bq3J#?RV3Q1BsKsIDbUKc>RUxA4r&8kj<^=)I{7b=c=>MEKSpolYV2q9> z@Oy{%%0oy)0R3<8Ci%vQ5&aQ}gs6iA)jkN%su}NGR#OiI^s)Okp*p@y(17zi)`l+;%$ToxSd=Z zQ9`0n6^YOfPO$<==f4o-PpM!kP(KV|u#q4fxcvh@pfHdAGQ}((T*wpdS1lYir|Eqg zD%;Dq$B+nCwwkPd!2>D`5~$C-W>X@1BYXo>mho=}X&@4@2D-E*GTB>!gqT_ZZ%ouAD63Cy{4i&Z?qACOfJ@twt2gja}o#%Q+w0 zVFn6DaJKvYup@f?^Fc|Lz|cxxBJs-5Jfyyj?@vN%N+mEqqA8O-F4rcHh{{)1bww0w4!PY4xvK6glK6EAqMfLEofCWg{TjDMX5|G zYUG2Ba(j|G6(ei~kV3R3WJEn>sV#8{$+}Pv)x>rFR@B0$7roC$4KuJX^+w;TkA5-Y zAG@!i-OCe4$De0yPH5Ir-Qh0RSa>m!B?x_fp{f{EIi+i_aQY7^88RQd4FualqnK)OeqltFk+Qcm)3=MC#9 z#xw?c!gMXnWwNzdgDvv(!kQVaeX+Va^MkjMk4WM_0-U$nmryj@z z)a2y>LEAxfT(rN7C$l3Y`>!L!l_(=Nfs-roUSksONXLQBIw44T+6&+}dVpTAx_|X& zKWzGie}W~urpy>F`i)tH*%|3R(^X4Kd+S(RHTkMGUKl&IErE6R=aK>g2>Qg%wzPY? zfu7V6=gu=w0!>&$#sroi7@MorO3Hq+E38tnaBxA7p3Pk)Av!S;p||P$SBrJh0Vj*t z@`dU~pnBV&0@_Y>jB>lYUMn6`B_0s$GFund3cC-NwXM9ZZJ4SZBs`W)5+#MO?sa{Z z2~ZvwKJeJtI=~=Yk2S13pX?37CIY{EA?D`*dyeL0=S81!HVL_E= zzdAk!Kv}rvF)z!W)K(>o$=Y+(TFG1iYaCgFB-sjh#}mC6YHtlDWz~f+UR>HSR~pJ_ zlxxYTTK9ap{n)qh#u9(*r)>IaFCS_L4vVJpg36aw$BYIkQoLvEtd(zb6tR?SW1rLK zn!8Snh3o{Q@Dc|$;@79g9bsVHZ?xSeY8lF>z9~|T_FTvxQ$)r$6qCGN7j2S_&f^tt zR0feL`wkRm9ZOz{Le%)re)qKzwzNmhAdPsyG%$eOL`^PH&H2n$)5xs$u2S1 zdg0&qD_}Xt=aYA`OynJ$4o&j)yM0o~Zc`SkYJKP1uQ4;$+vC7;?v5;Gc$26sS#Le zEooTVn;ft*ktraYJhL#$C)@NsQ%x!2cQE3!m*#sCdh)8}tHJ8lt-`x1;Np!Ik*Z=V;^nqlEAqPjVS*Y9N|ullgotQ2AxI!ux}N?q zQjf(PNjZ!~;j~Qe-Aiu7G;SO22vB9bh&+X#Xc5by$m-~R4P%QAkQE(GCN3o%Ix~85 zl_Msn?uX1g8l0E%CLBuHI(-aNEktR)J6aUG4sSAo)9stSK4g(1gAbm3@raeO_xf2C zS{qkqDBLx>aizd3n_2sL>C8vk9rPu#*-U<36A^1Kl3siNH#xf{WLmlQuaxpz=$iqZ&N_}LwK-!%?)S6 z`$p_$>9JgGT3Dmz=XATRL_=h}!4%Jf+&GVi9b3S5Z&!)epA(qVh{S4uptt@lMB55Uy==Y4GO zfe!tNJMaZ(PT&e;pI3CEIv<;?`>E&%urV!hp;aYvb z6^MNfmcAds;!8C|!)Wdw%uBnK&9rpJKk_Q4A&P_^(_z0*mJK|RjdRH!3C$F}spMUL zOSdO|kM$eJkZ5WAnH8+o+*lCtzWdo{1+-4%7ud6q7$WnrS6+R+K@o%(6VI$H%yhVn zk-f( z4054Wy8h~*D$QkfN}nnd^m2jq;jo4ltS~R$+Er^WVGs~4V?K6oB}K?|WdxAXsI9+g z($5d=xWA!&7VtDk%IY`?XDXtyOyRBfPZAUI_tQnhjse$>!j$PHZ`+n`hnI%d0_wbO zkNQS686cMoJ3H%aEF`l*4itOk?Ypo5wz{M*H8>_&WhC-e7CQtJMuAZV7^0YK&;9YN z<@MH~+_LpkFbRwKSO`@tZNO9;#+}(X&FPQ$gnhgU{|G?8=#+@hPRX|L!AngHM>2m-OI;OBM`{@nxzusjyg?pAY@lbE0bx-GQKDerF4$TCqty#2RlD;AG^3!)WgSj zh+?un#2Y=q$bPpc$)~+*W;u=iw?BpI2lM*(>*~)iy)OzUsp(UK13IJv5kolPJPLXnlgm`(De+yGn~C6-d)Gy{!gYf0q))hGISrtcT#)`l zYFp!}vi;$v8t~=hk~EG1sz&g|`&aI)RNk9JCKK7u{|@m#u@?OeW0?;(rN01n=64)9 z+WA;6#LGA$^F#8D3dtkwY1GB^snKRiwpO0zb~1+GmlwzPVhd={I5Xjf?VMqN%u2aY zS114jOatX6X{Au5@EPB*sQu?)Xx6**Lq&oT=X6y~%8o#P$==*sx zz5uABT=+^op23mLc0g@LJ9SX^ck|`W7I_kXOY_Nb$)qN})REYT{Cn2=dHCV#usla9 zu3o=;_KZ0f*Eqh5>~>dJThP)i5cdL-u?fmw72dL5S#AD|8h&D4<-YEz*X-PwicMjT z;$F?3eY-AuKmQ?G*mr9~ukautR8d&vH5v&9<=dJqTRw%pPhTSrVZWm+0sUaf?80Hp zlL!Tpz0S|E3U!K_v7Z%(;^=|a15OzE7#%VuA_diAP!esgC~nGuQ$%-mYU11qe-3vS ztq}gHe;%LnlNQ#9ey{lM>wevp(s;l5~?b*Xg#nTujQe3lW)>OgrnPT(( z!d47f5C=&QN&q^5DZ^b)Ojck7o6bjHo5Q!enGwNUxz>c_aV{E7Khs~_i7x>;+#H_i z^-q%YFPs~JEISSfcq;DtXW5Gx3&&Asd?sb7aPgM6m%l<_#|gh_hA%dh1w>2hUxt&SVFQD|A3(Qiu`8e8))p_~ zc9w9a~;H*d=bwkz&R(Mctd-+j2jlxqlawfNibbx^&#+hhM9Hj?HEa1!WX zN!zNnGAooulXb&J0hmU@yk1^M88kxF836@ngIu9;BFc!c(Jq>b^D zTen{<#9ZdW#H*bdiW*{D!X8_0vPf zn_iy03_PU}T0G#s@pL1f6+&Y|YrT)K+6fX|+G&5%ruu1KCxK3TX~*%}9y;wVY3%&L z;}+bL!Q{AYIjw)4$twjW476q3B(-ilyz{GLYJ}tp5(-Po8G|ruu!+pW1oad7Y&2;| zwjkn=KqDiW5GHPko(q!2A0&*(kGp7BmIqY3q*>PMIkJ^Om#MwfK*r0YI30-G9|JD`$YuG`0-tZYYJXW4j>#2(Euf$u3=|(dx)n0KI&bsK$!WG#(<89*Yo?b8 z5%PIISBgpaIPr4}`>G^S%j4_6*EMjF@$FXQqu3R5a0*eh+*|{%H&8G!jZ_Z_dQfm* zdUC`YazJ0%(FXGuSfL7LKEp&SmkjY+ec}phV5(0hOBuR|IEysVQ21E8A)p-_n^RZI z)t5naP>Fm#jmN1VOA_0fMv^=1J;a|{<^*Qsczp;sb}d8~%>4ons6Yf0GH)^9p!)J9UZsyo($GnW$QeUKUjoAKzb z!k8o5Tp0s z{QC3yyfF)ySGd9Q1JKOj(_F6Ezc|ng<6jb2d0As=IbJE>j@utk}AhmHG4L_n45?-hXH1Ue=s%$jlONNFHT&ey8=ACO_Z`OqZ z-Lo1>TG643P}Ym+grHTv5Ejugc!08cgFwui@P=Mz?e{gU6C-3gX~!R17sM&w6|7&2 z?>}Sxq>IEoin`y9I!-wOWYBG+SFx>%*xx?fDz?e{X?xKzw8oT@)Vd5xMrNEHK9VYr zmhON14qjzNi(oLjLh&1qVp16=3WV2RmlyfX+^*>Lh6bhnwS+QtEi|GHaP` z2MHiP*iaHa`0WxZYY*h)9O|(!xtAZ?YB|Gv8lJ4%rr?w=t&4+|g*JgG+{L<MSp&K7Rp^CeYqNe)`Z}3B3)YolsLVi~D@&Pq&;3SMBYp6ihsWq}cbY zl{!qbH&byX(8wE#+mtabr*i;FiAtGt_$HZbTrAxtgO}gMSQ7}c5b?Fcu@+6iNIDx* z*HRvn!)=3eB%HBOeUl#Oug_ORN+0!|l54cvX-$kN0_w{aEtc0m#p^jin0#IP- z_lNzZyQcS{ryFl*(M_fts~i?LQa5vrLi?E+Og^@dSYnzf{DIH|9U^C5M%Xc6TIf&_ z%}gxLp2)-CyW;xkenQQoF(gtCX4K#Eb}ZB3g_hStuFVj3L)+x__vK@_e6q1@_65r3 z_xa2^gW<88Y$V!3?kofOznC*Jgre_^Cx(go2a*=yoxA8ZXa??CJ{*SZPbWHgV)@dD zU&Rzetp9L5!@BJk@>nY!QRrC+mN4Szg~#>Nv)a(Q#pp9wx$$(cineKiD}LUMxV92J z66j8(@P%8f7G^ay^n!HKEm0a?tr`o47HqT^OL8r5Vd;6Z;F2$IJ`qJj&B(!ZW%i@O zb;Vw~+jp&2-g!JJQk7J+iwsx@F|KfJ?)n}sDXT>YW}L!#8&;Y?S$UUn!dG!#p`^46 zGp*;fhaI^a9em-z0iZB>{|k~AV$Ktbr3%IkAv$>v_e&3+M8=LJfmg%)Wm>-v%Rj$) z%1E{fV>_s1>fFm<{$Eqyz79w@yS&ss=rB7h^I!fO-@`Pus#v2vzc;8G1YkEK&VK#o zRS7Dl>UJypb&DsyL&A4EN{PcsDNmeyU>1RCOKlF}-oem0x$%1dtr8eF2^#XjZHQ~~K~{0|7pq!Qp} zRvWviY>7vAZlujKJzL|n#8O1Q;n*^!Mxm;#P!ZYL=w@!1hA`Y#eA0Y1SgR~L7Vr%f z9sJP?7tJyBOGSb*m2(g##Ka8oU2xMOd%5;9TAEtsC5BN|76=aQAS!GkT|374eyM^1 z!^1YZ4XA&ep+EB~l(4h0sNW}CP0@`j+_m_{6r*n~NcfctAeAkvc4QS}`};)@Zi0<)W# zJ5m|i2)YUv!Rle27U(W+4>hO#!@lBz))m)mQkJwMR$Nx}69aZf3lH6$O2iwUr2)gQW>K(>p4X@F% zp@k?au;n@f=IdX*d}yvtF6^f2SZK+RpUOnbm`dy(>X@7FEcfp&mNIAHNUCs8B#nzW zVrYQshaN_8GQuYV?pa~O+Sl^P3qi0%hVn|!zXy<`s1P?s%1pi{2jyrR#o*QB7y0;| zL}+Z5l~G0X2wc(fzDzBJ3WM0x!uXn$HwvY{F4+oY6OULD!`1P@jz?^xNnMPBzW)PF z9a{Z=p_fXR0YB`qMEHOH`TsBCG~UtC@xS4$B#sTmY3b?M|NGZHzDgR5_#^nADBu6j z09ya=g8+QZPali^9UMd*9Uc8Qr#8GHO>O`84_0Nh|2SxmKMSrX_x}BRc5ZIs|2&+) ze-h&+%5|G^^7Co`bNi3y7(HQbZmuIIC#TBCkN>G}c-(SgSm@5q4lg4x-u~zI92=nj z4@&M3gK|kx5ys6NpaNz-A-G>Ttg8svxQkhEmBHyO9~hstUVm)3g`}Lml9SR?eqMZ3S1&+=y>Z;|G}qr}{uQ57$NNXWWMHTBL!+sKvCsi>`rilM?|(o4 zEt1U~I6Y|CQEOEzFRpY_xXxP#IJs|tN21)i;!T>3G=&*OrJ;4pVgi#Q8yWY)chV-3 z;TKBld>ZQBHR08h#c9>m4L0%dc^WKKzNyJ65!dI9r^PLw`u=CO@_+5!BzbZ3PoL=I z@+~K`Pz~?LLSnXk4xCP#XTUAbpI0rnww7^0F$M8UDgtS6|<2Z>B#Q#GDbow4i>ds69O&8r$6E9RbwF>)-poK>*@!6yv5Zc4B#b zS9koxM;~Z?{2G$|(`;c37i_Z=9`VDaz;f5f$nKru!LGc?zu~Jajv~wAklzm-5tRVI z&lE`q;gN}QDv}>vY)z710wVr`+CP362lusRz8-O(`au^g4l9y?iDenD2qE7Nr;-V) zMGnRPHqG|ouU`pkf;p~eLSc)$U-ZlphT?kw3Jp3g`W}(3jPp_^k@}@f5aCS*5Q13T33*Ji0Qku9 z`f_UhFjNBpD7we_IlZK~FHjeJbbgK3w^DCK;UfT@iDJtif-jl8ukWOv8BJ1vtKJxU zcroOwI2L$8#Lpl8*@e@Y$T%YaPYQ?Bqe70j`kyPwInvy=pB^$Srm4DjVG*e&Hj&Roum%T%;2Yg)u-(#8FJ4nwbHg? zng<*7p5VZDc4lS}MzqwFl>g#(e}KV^FC=ClzhvE(GLV>#?@hR6zhnp{5Mp@xSZi3- zo9J@X%$P`aJy&`iifH4*%WYX>=Y2;((e(S+8EmVY7<{LDkP*2k{uBW4F;||%lvM)I zOsgI?Ae9|R|7ESE?}DDTyRyi-WaMi9{$J#6wGfolMWC6pS23!ueLNd^uPFztWnG{B zQJGOsWqxN@4Lb37TPozi;Gs%Nx7LqEpX0KagNpSEt`M`>`+G+Eq@bX5D(QpC(n+xH z+&d#4C^@h-TB!10#BXt(oT{8RpPK+wOh_Ii`E};cjrsfbO{A&!2#)1T4gy$75*EV6 zg-c3GYIT@8- zv{>uNm$upKy6~dsHqrqK^DSZX2?y)IP4a|JHP?DLigI znmO3wA$*=GloHSET*j%QqE%^TXNkmyu$PoDR*9Ju+cB4a*RJD8pZqo1-7QfE+CDhI z>ETIENg3GF(9j?*D=QmcSV;Tc(?UX7-gAd{0sB<`s|7%xk1sC1#>O@=`MJGq_7VqY zYqz1Gq~z}hJ}*9ge&CKuqtDO9MFdc^8w4gNCwXffk>;>)SZ5NJ`OXGE zL%)V=N!dM%aq0P9e5p)npFjZ`zpH6`tUPsL#{MHk0U}wX1FENSc5d8Xxh)b<<#8>&l); z50m??7mA3mkDeub)$TvF+EdMc(oUcm*AOZ$6f51{@3a*QYzBNRio* zi(Ah>jm}3qO1^yMP5X_dY&>3mA1}k&FF;wNCd<#AiO#}GV zr&UOsi?g@KFDKhkN&4SqcLI6W>S1-?+IARy?n!UttdowZTCL9lET^QA z!8bv^xEwU>yfuTM{@Rz0m++jo?QOI4nD5`I;?jDNV!!s+z>+&w&>5ihfU65x%~nwB z5E}Tmdt=<<^)C6`>;X#B;EqAoHu6z{lMk(w=om9^lIWRN1?-qRz0q!N?{g{dAo_h@ zI8>~Zxj?TL0`bxEF_v7NoqPVr$3=R71PEVD|4Lj#l$9}}Sy@sbj0u$MC$}e~Tf52@ zS>!SKPfZG&A)3l8(ZcG)>+(Ku>8mT{PptqGf5vf68E&A}AqYEGW+|0};8>pk+Tflj z-`lm))9l3=hh@+II-iwZpfHN5oY)Ukl7FhsF+9Aqdljp@q>_kGSspO|9$5;Fb;vE)PquBFjUV5(#LZut+7oUK2TM;%e; z(oZ|;c1u`1Z!vmt0-Q=o?L>pZ-Y{YL9EIr{e|w5{3wxE7bonS%EqiV^dHNeT8vtCs zC!D4QV3uA91s!YBHQ$~8*2?y$?PeY>js2tt2 zJ>1NZJso_8d=9^n_C1x>+HNx$On}oNF(676&C8xt`XCZbI60pagb}hH8G4|SW}xLC zkG|0YTszgNkn(JsDB*jz+s6tX;$X4G9>B-*pviV^F5TU&m6(!HxIE6Se0OaU-&bIrZI7p_x1jkf6PBz~W7 z){|`K4-7b@y$twdD?bnlnEMb9qfzUAvzppj5)Em%-vI?2C4kd?d1#CFS5wTuy>keQwGk%Tt?_BMxzq~qg}Ll6 zB>VPFpyp#|t?lo&T)Gc*vS&nV6+cU5)PPFq_^c=(WOs=x{RF361}k?h5?QieeC zAMlFJiw=QMG!Rx5!xJPSq+3~Uo2(Rz=$SV_l+l#18*PE`)e~U_1tXC5VP~jvs7L}$ zOFNty7N=kdp#hw4=<$~MF_uL}H)+@)TutPH+ge0J8BqGmG?`J-^jl{5TaXIK5@{?5 z{$r>K*RnL0#|-*`@j<~N`Wp6V`kJN~9v{;K7R0%&&Auiu4+BOaPKy+xA3;=X&cNfy zzgHG(W$APH2nQ>MSF}AHfK8C+{J7Zc_jpP?2IGeIH@^5<6H7<+41g3E((*WeWz86p z3ysMbFS7j}CjUar4T+;kM`a+LG$QM}fWJ}~QV)`Jm8d70{Bm%`WQB#qg0b0A{2$!C z^;eu-*R2`BgC@9Jg1dWgw?cvwC^Wb`1PJc#RPYcixI=*8t`*!13lLliIXCYar@!ZU zyZaC5{*5shgQ~lVz4qF3%{{NZuRqpWL!gY$>W6|h0#CoT>Fgp!_@snZwrVoh2QXma zlk3O;(=1uX`!5$LUA++nCn;6FZ(BXOYyQ<|&k+l}>%CF&HCFuk_4mUB?6)uUwmf|x z@Ov3!ZEMNUr!V_Ess#Z3mO((z^epE93=MM58pR7IekPxkDDG z&9us>P`8UGN*Afux>@$hB4ks#JY86Ld$y@Vl!@`FZ|`sW=?%V~u^JgQ(908&2v}mi zI6pW0p!iGe{WsQOQbgaeUL`T7MX8T=OB_xjR|zued1{*Yjv`0d*Fs&jPIw`pittKH zbeaK6;Wr*HUZEHDsG3T}(^*#-cRncDzG7ekY(`r6x)J4t8xrQTVRtCmqn#1v;@Pom zzU!|}DjfWnp0*9!w-VVH$#~yiMv`qf$l-ty3;t28VV{aYfdQjKL24a&wWM68%NK_n zLOdj45TVqU25;C*?GcwTw4fhk&r*wmx#YVakq8u(9m1I{LyB;kcK(i1~enxGmC`3;5M>Tre`0g@>H<_DkrAxPZ9h zA#gz0QF@7m@+wnpktFPa*gFL3K@61N2CZyarJ7H@*{L zpqI@BJE*NbF-^$5ck@W2f?7T%NC-6T02GRoC$#fZx*_RfaE*~dqt-vcFov1&qH2Uk;U$f|3@~FYd zJyKjB;Gip$6!cjY;J?h!Zi~Y!8-Ba)+&*2W_g*=Op#f6-5B#$7pUg48C;T+P8Ul}l z38jD?d^E6$sf-D1?=8BlnLC)EIo35u>X6@1`tJKDMaw1C1So*D?4>+mGun&Xn=GxJ zBT>XW&CA}ret(s&P+80iz_C(X#4!FYI8&3RYvR3bfY z7HJN7OOYs+R3H(LBpJ&b^|4r3M_>4KfJRq02&nN`8xrjJO>uQ77BQgD2RG#AFYsw* zg0axB7>K_S%#kO{MrN4CJ60ChqAiSG=*WJJ|4IA;fQ$D4XV2_2S^1vT^sJ`9eBU~< zqp$v_NswOvpPT3@kkv)VwAxgW{vWk*WL9C8;;xJ0F3d)2AfmoY?n)=yw_PZA*NG{KaSlLQ7FJw$DbR|Up?#ax!F#ur6dc4Umh;jCVo7F zjouv7KW}=7;aeF{cxB1~j6+dB2fgS;?DqX~i2`@J`C=N(-HdGW;#5LBD2u@`YJ!3( zxZUR}R~{1JvJ7zK37#0i?p0q-pGhNnnfe8GKa+lD4dU&fVN3SZAyv0G&;$|zJBQHT z;3CuG$VJ_%HoZsS!iQQ(5$FQJ;bs|4)@{{xql%85#MuPfrKD=5B53_~fTGH%C07 z&*bHi23z{!PeB`JWe(<20o25|#24GoQwe~;#{9>LkNCIHMyRw)3&A#mHVVQrOla*Y zvpBORMMBG5Q#0?*DjWoo`uLV(PAs8V`6rC9K8&_9dT|Xr_V4PMO)%xl31fWlef_%&Qo)9U(o(brgBMMkU(*UpM zQ+QWGGO;qrzKcPui};F;2Ic3Sl5z|+T*?Vq>z}K<_Dkt31disvu2ANHTul^-fFntc zz>8bohk5xuQD0? zPZ9z2`v^V8kIbL1CFY2d)wgBW-+R-kWb?mS3~DRIQou_lqqmT=fmRyoGSCocCck1r z>+M_fNG36m6;M|jK*ZhN&dsuKVGspCTNXB0@xye7!tN`T##v+uUAR{Gb-mXfy2mg?(M zfDa$UOgL1TS8TfW_hSd9heBagV!o|92aq5z&5>2f^fpAo&_-EX=I<5JgD8K7AxK$O zg)T&1OB*yp7lO3MHp8}FiKd8QAfr`i+IVO+c;#ETB2Oi;gf^8!oHbTi{Nwz6LePbj zo8)&NkT$cXeKy9IW0VDTYRa#&RV0+*{mJKRyC0K zAEzM?+z+R0kDMLnMQr9Dbm)QL{C^Hh(#sM63bo0|?291+1MzkIS81wGF6&KzEH$R^ zy8mAQK!XbA!*6ZC)Ft69l9*Fgkq&V1`ajF&X#tJQMgj~$1H`q{h~ET})8e*ROLG65 zA&tyN14AVjysw`Z>EDG{{A&(n^Bgd4Hluya%>PFDqk-jL6BV2P2gcyFT*LpY*{7fW zKMNu>8D(H7E-p4THKmr3l?B_{GC#Hg`q&e5u%?C+U=9AWfdh7xGF5APdwXTnBXt#5 zKvHhd2gQ7WL3SYT}gh;fsw5qk=uXl$D zUR+)E*N%&lC@U*hKS8dVT}u870Xc#vu?^r5%ij_c1H1oRh106vRWHEXAlHc`74_v` z0i+N@A|lj(y-c40QMo|v3xM;ywonQPcb{x*zyO6>B}Wi1DJkh+-+5m;Q}C1?CT$F` z{&97pu*+URwg0a={GV^PnUlVv$P;%5fM)*JFP!Otp-i8iw0Pa~w;_1{9?XrTNN(%l zFM;1q0m|f~@`UY@sIL0IU)g|2^L2?y1=%jVfh&ObmfZ7NE98FoE^Z)$6 zj&9cbmYtn#Q2V?Hz%ywuOVdoZf)ms`ryJpmP`;$Q0uU~pkG!r}1R34az&K|x|$qDJYbHt?jQwwxAn8EEc}|+w-Jx=>2CH|36P*@H~0W8GA1w zA;DH&U0odqfTl&=-rkZ*{U@pEfBtlt>RSn)FV{8+rKP2|%FkI?Skw&-+bs@0{bP#+ z=BRebz`sO(aq&Cg*}}0YNqP}jbIZT}v#H1_EDKi z@xQ>J1_GKJ`_?Q-NvUNe5fKseDGHM_GiK8No~ODXsyAzcc}B*@`mV1%(b3VR0DPnE zd`++YzX$c{OyN^{8fr=^D!J+^=NA{!Ha0eY08;IL^waWxzuS=%@m^B@ggI<_x|Wuf z4FKKk^yhaqzu&Fv)5hoYMEI4IlytxUui@;hC9r&fkwgt*Z>dI!DX@C?4-UYUs~iJF<4lL0<%fUg?*^z4ul}JAZ+FY5y(7fD(fC zrChCx3-y$h<2g<*VUDq##Wv3NY3*V`jjJ**VImhE&VFJs5kJ^`&t3WVvf%^laY`_L z&wntsFk%ZvyA41*Cj@%lVMuIj?C&R89Vi3;yUL`Or8Rq*{~XAZW>oLw$-44>#!A+dRN05NVfI$CNNpl7;$Gg)k42Qr#^dU|etnYp>S z8H_%0`(}1smC#lDw$!{C2(!jkq1@WC0627pdseEH)q1LoF}Q#hfV*<=<1c>UfX(G3NVWJJzekL8X?m%eMK*D5B6oIm&Rt_F~z(uVqW02aBVPI06s zoj4=9`XM42W~QV%Qb^F<{H?q)`ulmtu&j=!ypos(r>}9&QJI~*7w=qQjh2-3cC)b# zJ)IkgC{N;am0e}*ZS3tgo14Fy2ws;IG0dXjX)H~-a~Icjyd)H;AE+0<=|Z&D_-eX z-AlLey-86nmz%5a3!qPnpd+!VQ8pL6ZDG2wVrS!N+37da0(Q4w6TP)HvjsSQjRS(Y z!T6lrsF$$j7?diFVVg(vy|L<6z)nX|mW9i3_(5)PIJvtbiITnzt zQ8oV>H|&-%Y9}McS6-!hXq3`PGH$0v4)$W!9KDj3_KF6eEeu7xAERfBA7-xyHf`&K z?df3sg64Vl(Jl*7b=UUU!br^)jA_B#&Z09_HFpkunBX6b7gR2mO*Y))bd~G5OWAwS z(wVXCV@78YImN>P>Dh}SVLHa?Rp+ciESG416W<-L^xLG>VMDI8L#+@=O=F!(z(Y{5$@n7s&y4Z!=!~lb<*S@ zJUmO-YdiKH1Em0)<&{L3xRwCK1&?i6Qh$dd;nu*K!w6u8$)5Aw)F5rSn<>j(yezX$Se4jrUA-d|Lz+tNK48XHqoSifusIt6x_fuS9MJnTiv5!zropSY8(D{{)k!@#GD z%rkB|C6j!!4A5txBBH%KaR>GOHSQ|3c|>Qq2kG#;mD$-8DApXp9D2o@a*e{p)Gx|< zHsign3IXT4lg#lF>>rX&Cp?N^ux~1zXX4=lSJS2BC=L8x`YCf~XonFx5dOgI(=sLV zHC*m)1C{5*F7;hHxw{8Z4~;94Gyu4!y!G*+9OWZxd1_)%nheCWCF~kkjkG%qOy5dtKhPFo*$F1Bz&`;4 zVcvFd1>Rf4k{U}88Qm0|Y3}_+7T*pJ#2q4smf7*aI3{-o5*Kv)eZndcqVpKSJ0(BLL>`ETepX2$*T3&ODaQ@JUs>1z`=x`|Y6Zas;8ydm2? zI#L`)cSoO!T$x4Mo{q!DcU2bHY8a$Mk(C~*{iTfntGlD)?!Hf72>f*W_FkDxH{3`U z!Yi^DfYOB&i`iS+=(Dj!BEm7n^E&Hzg)r+#hl0djm!M?j&){CR5@b*uVbEo=re5IH zCeA~R-6y4Po2jF=YMO-xzMZs~t~WGJ=O5@!+h6vFnhK)EXYuk%fh|Q`xGgjUaB>rj z++H*CIoL*Glt$<9HONW=N{?KwqI%%7%B!F6+u-+p9KO!{JATWSjr|Qid}$#?>iD^x z@aqo#lTRPG3w>+X_+M4*)I9SB*v$Co)V@_V6@%|OQ2z9J_ZNUY^#9y#%@GO;y!Jyz z-q^~bn4_TTOvVOsmTn8-R`Af+mL@pGD*j=s9s!F7zDx=4@$xDKozXEMy5|g;i$7b4 zPhpgQZ+qA`8#q{pI44!Olc2BDxZlX`URgl6^c?X~-Hr1td!S)^x{ z8n>EsMb9D$7Q%$nvFO>cbCWQ^UrvI)@~x)zahwI$t;mCIkV2$d-bc3Y&G7nJFP*%R z)^^c;k&Fk{P0!vRy9fLvXTJpj|B-9wVivsBPF7K&)1V=CMs_qSt7a|Gn_B5e#U<5C zXi6kC<+W}^ps-L1PM46m{rnV=t6=~+HXosNQzlp4>LNV!XS3b~E^Ye|l{7wFVpMNe zVp!rTZt-~9@_UQ!#vIK+0Y%Wu$X3pLArU6Bxlulnl7gO`!u$0&R71Cv?~tzdt-18T z^6&Zhp-f(?Rz2rV*iFhpg@ZF;q+x3}eb3zxGOI5aNRQauIiGgW7(71lAR10h zd>>Yh?~R_s=rr2SGNREk#d(S#e-jZeVDP;$ON+A6b30%Z#B&UsRE8k=i9v3G7iaZQ zqo}LZ05K*-z>xt=x6efArjRb5GJ1^OJVQjf`62ql{y&IemTh?ESrf|I`JWEpX2Cak z8x>i-4}u_a6tt`R6AIZyGLNmKk;6S76=JO78uX5P_nR3z#+O;J1uyf`rJ`ruy)Y8{ zaP8zq`Gls^;bz+Csf!$cCzgm7_X5JD@vtop-JeYjF~F(v4Gnur!JdHa^o!`E^bn_V~qn z=Ptpy#JSOqI+)74ZQK|4ORF{r3oi8S1g>wp=!SN4R_`q4bF^RM^BD*Dp_lMbY)iY> zyAdre8|aRtsLtl<+I%?8h{`WN00`IJ>$m4dKaM)!r-Bx+ymlw5Y}~>&mkVLIEqd{l zjKNw(2#-I{p3im)|0y(pjO=QK#I9|p*yE_u_>Nko^RI+||Mqg))pA+NYq~6pxdBRX zl+bt|e$*CB5f|p+KR9MoU(0lw$hY4dCm?p+hauJcY&{E&t-;QGoKpRgNS8E$*+ta7 zPLjkf0%=kSko_9T^D^je8L~*k6SMth2=9z;@8&`0wZGH8cRyXjmb0d#g73=fXI*PL zkB+a=u*^)Beol`4KAC`7sdWF&46W6#kA5fpBFOSf&AmK*#K#&Q2DyfnT83Bl;D;HX z)h=3LRrB{vB^o1KN#<|6ki|r?7s;2h@i;FI>vey~kx(^B4-BayBqTLIESEm?2*lKY z3Vgk7c_E&V(TlvhV0lV8pPY7=jp&r;6bS8x@;{?Z1D&}mdq8=%<`TMUf@+vwCJH-? ze7I4Q__jE=Vm$VRZd=4qbK`^qgqeix?<+`Z-U~sI%_SA8OpB>yGz636!=tps8&c`( zw!2E=ibZ!-gJvhonFkd5Ywc@0*!_Dc`+P^Uc|7**TdCE&2WDm?NU}D{C=Vvl zDn0$Bs2BZ$`@4|w(VUwZLB|d9E`gf~f4;kgNN?ondvQJ z&7(4ALT|o^IX}|tr~l={MNfY7{B({sy)v^gG%xl$@(^Yp0{E-QY>M+&%5ugOcyf)k zEeD?(d$}swkI1vOGWOKIX_5ZKzoh2);1{pn>6XheXbWf&flF1@Q%#lYt1S&d>~>>N zi|>pzZqIFc*10VhgoeuYLkj_8>S^@{Ww1N%{mT%=26VZhn&u1_2lBn7j+sm3059fo zt)axlnhsYBRJLS$lzGgNci;B2nWeke3aReJQvJy6}&V zbTz(PVMp%<4b0TvKDD0}?IR3!8P;I-R%5oUC_WjNs~y~Y+_==r0ImOSVe}*QHkDWK zc4}~`adBF2wuYwy!sT+7s-7|rimu_buX?;2`#rC%-{3n07G;Qv!`iOq;fUvbCtp38 z0fYYOcF%rHX%I@hb$0=Ma^i(YyNyt9P1o&hrzS1S#*xR+<$6*B&J-e`6b3Z#zjp-4 zeyIpLblFO5m4HW5m#}d`?I;ka+V2NyA)Fv16|n-^wP5NZ7`5M*?>X3NHRRP zERiU`PkN&QqSHtBEUFAa5Nx$9?qY^d)s9 z4GwG$@i^Lr!q@tbSq?G|Lzcs79k_1Rk}*Q~Mp&n%OLd`f5J5ai`E|4+?#mxzO=?go zlu>Nr4Vs*!G}r=yI7Sfo%lB(orOx_=3)-u^b8)pXogT(K|II!trOijs?F+Ft;1`I- znPOVyvzCKcfm9b&(&-S`C%2YVUfoxel(JSvkU6F=u4xO-rc~Ma<`t5sE9b& zwXv9OaQm4eOH^>ICDIU8Iis2{M=s;x#$Kkm{R)VQf?sa4DEab}7mGpZ`E)k1JY0OK znWMABp+jIqR$M+$5+R9#a#_ueIev}vwu@;tBG>UGsx?Mj} zEsP*P@O2woSz6l#QxA;s?^e!J4r;Y}!VnqL_JS!WM!LtR1XO%1Z31Ut2cTts>xYLp z%&qCgCqHs-zk6S$ssmFApT7He;hRY~2#!ulHu7Q@wBM;*YD~Ovko2>i$2d&w{v5e| zUU2<3d8`4Mx8$LWF_{fSLNQd*;r0~|wBw5i%sXrL1#YRUMhV3^MVa^y+ZhQIBT@OF`FJg_oY>4Bu z=f>oMX(Q#(ibF8wG>eQ(T|Kv4RF%l@?~;;8f=2?NRU*kaavAxy5A_`!zU)Ky%K_K^ z#A42)UsgCHr-&x`sFewY_R>(;Wb!y=#Ea*Di=@FnCx5u?Uj$Cg)qGp9yo4$s4_d zwp@mz7y>XRqkR2;l$n*KNn)t5mw$GTeCT6GzIU7`#@`lDHq`G2!`dInCOm~}~4 z?ZBW^XNl082 zu(*8(8afMV5MbvfJ_wp*J{l#(otg}Dc_Ev_#J*rwXe2zY5H&!UMa0do6uYw&LUEdz z7$q0E>=1;Eyi#?R;p3|c`)e#E)#s8s!%I!-UO}A`f79wGGm2I?(~)&qS@=UY9-r*cwv6syPaUORBGhOs4wm7e`8L%YuQk}cBl-zc4;tPp-h##VEef& zH9o`v0dJQcq8Guo9xOD{+u*eQtv?a9Nj|~R9uFQ5TM4xBs>1%%F|4h!7?Ctsq?p~u z60gSx48D*NY1V}~(Ik8ElaPQ&*DLNt;O=kbhc@q~PL4axND;rhrFOhR^17dv^mh96 zZEe1CnHFpF5hsM{5VI$YTXUb#(f#28B9bce+}d#DjU=Y2mZmHWy2>>w4>>eb_^!Bf zdr8sirZ>IzqXl!?CR>bV1D2xXaP~9oXmp0Rt!q@v9bD*Vx+RgXav(kOw%Lah{l$Qh zb+^4AzfnW*NSZeDf#4tS!*wgYGgAhQ6?V06>hCdKyZi67?ESzAuS>S#c{GA1L`elN zT(P^T^{^j@3*JeMZexV?U(4E|emS%?#C{y}rjvvcv1q&UFT^K3-W-!*N%nJDTS?5V z5Eq^$#P=&-bzW|?`T7ei=0g?|rWgjdKUYgs)rd_ zNN+M(4~EG5I*R;GJ|#E)xY;z462FsULpX0`KaIpzHT!9k=)LLJgoOcLbK?)6K+Hw< zXIqVQ?3^ORNKqkbvU@GjVmrHQxid+IlWcv{Jy)bkFZ{F}u;KaYBWKuyn;o&qbJVKS+b$uQsJp<0?S>xt{Z$n?nus|LEbVSsFePoD##Lvd~Xzzb$Ylsg^ zxLV6i@y7NERT}k}gU$2JD%){>y7rh0o^Nady+j^}$cs`UkU4zg$H7K~y0PkmUm`PD z&HDWvBDluOBt1h$Gh3yn=xiWkU2T~-!7*Ap`t2oyw?6jh<(-GI0!{5DAwOY8nRdV4 z`;i;UZ7EVh3g?)eAA*>*7r2Zbg0DMJ4yF#udOvOW)bbv9`fV58Rmb@;U*PF_vdE1j zChaD2e!1qmp=7WObUha-7|SE7u`hGh8%Fst*W5R(p^qC8j^m;&aK_g%=U&&jB2c#2 ziQiJa46St8_hc9+%o9C{P~9@1$!MHr@A1jS*%CSSQk6mT1q{NSi0qF)5)&2cw2S;RL;T(c z82O&`-WdwgBTWk)rRv)cx&mR8GYBsB!qKnM*{NAX@7A##7Q|%GwgNiZq~V{GMmi?U zaiz}tJ@(d1=6+4QHA^Ffg`=~@3bQ8<^L`aTSJ~I#|0H$-0-*Z4%vh3y8cCyc>Z$ z>}|oYd_Ou*$KC%fQl4R`-HCBeqvHzn=jt?c9#axxvMYBQu~jvO!UQ*)3r8zH8hDHr zKtF*ioa|l4WDd3z;tZF=V*JZiEju7e+T^d;+JD*VZ(gT$!j}iR0v>jfghX#Aqj}#u zj#fCer#$A^DnFp(=HsXUR9BT;_}va2yeFJF$h2mqxppsux1|r?-1A|kbnwK%m&Q#5 znny3c$6B?v^0&pmqGc`>&Qpt|y|7T8qjgqq0#A#o7`StWj_d7=NL3rlQ?T8v4ldZ3 zD*6n|bQ-Sf)f-_Vi2GU!PcIA;^yjv-{O&gDNL8`3_C2UOCaLExq^b**UJXadXrC*O zt}47QU&lEdHXr}#647{pS6RSw)uIJ$-)dx9)@SvAT=*i>Z?h zma3fX9>g<5j06cyon4m(>{Y`u!550agSQOku^BVI z`bO%OE0@gcubRwHv%_Q})hp|LNHco}*@)>TyeGZnVZcM|%u41hI|z3ms+@2L`#jEn zJtDgJXpB_9EkD$7r_8 z{1MrF3n4z7ajpSnly*ht63@=wyBp!vU1ou$Xso8)>ZDbG!thM)JyaZhu73H7X%&8l z`DI!A6ET!G6}Y};)q6%fI7pL)>^4X{O&i5$8R?lBfmMQlWrFC3m3{7kr z*nRWb;CqzWnj-n-iZk#P!Xw(R1*uo|e=BqkHe&$eWF*xKIFq+iV}(AiIUzB881B3q z<|bVdMPc`opr>e2ICk_qpA0B$IM@%{MTKg;kuk$0s+9im!4PlL$$!@Jo2#AlRuY4k z;GEOkq^(GpLfF0z`1?iCNA8Rqp)0-2VZkuw5hj6zR(G-kK2Gki%40s4P07xOdlaOi}+YjvocyU-O2X*vXj6&u8s!T5~QyD)6 zG*5^nn4!Do50^iGbYIq`qi-=dv$o&+&OvYvm!!B=+cy_C9#qh;EGQmh(w9S|aSn2# z78xRp4wK!_JvnW#A!KNHVJA=W)-C*Ijf|DxR$)I`oR`|d7-Lz9ZI)T0F}d$GTjH3T zU#I*lCtqCG6z_dULWVY_VsSHE-;kFk-_*AjFAP=nl4|n^gke$ zR71B1Rmm+kYT2m~YLH%9<`A--vb3Z08-+}y77z2pl!5BDzz*4#Bf?r5DwDibI%c_> zbl^iG0uYcgTX3IjbDkjVIhFfLSNVHtxSzY{YnqqSYq+aI@63tqNXl+$(DD;gy#A69 z9mh7yJHMaw$ZgpqbqRHhj|^?BIRjluSU9IzniyNpOR%25$FznLOuA6@#nr7n_bNIa z*EF3}#lf8tZNv6Ip?pV($<@8QzV;gew-^G zx_2#OYuE0-C2e_lD>tMVjS9-;#BE+q-H0De&>aMenxBVp^ty3s#dGvF&v`t&%lF)E z^50sDNL}7=Np?SnHWfmsXf`>kE;F(Lu3Fl`?4i!IhN zllx=HU3nh$UUZk!94^tc*loOZmqb94Z&assIG5!luoV5rjq^S@Q_25|Pr1PFZ{5Q-!0#_R9FO7_EedkOP+|3jZ( z;STjfjo8U!Uj|}#g_qAq17lZEF}Z=XT;hjAvh#&pE+0c1foaoG!ugAk+-ax&*=);S zCQCL`pNSuNSN`~rS_b^^AB%I@4HFph(ezctJg4g~MM`pYp}EkNX^-}cK|e1w6Ck>E zSe;d=@aUi5yA|>AAJw?ff^Ui2N5ARsysRJGqsvWS3G3IJiVJaz)~m1jjWr;Ar3o^v$x#ucD?k*A{XqTvV`Jj5hTu z)h|Lm9doJp_!VX7PX}5TZr8HGRtQ=W;hqC?on??Y60#K%`1BqAC#P9|n~vR39s{_j z7b}!pq%Z4OeZ^AZ$|XQ+b@FU>sV)|@GF;&`eo;-~jj2Z*(&!>~YQ%jsS(!DCJ?Q~Y z5E$RdA&le6A-t+gF7nyHNAVN%kRNKRwixWcXS-q_Zv{gtI|?7J?Kzjl&IIUguTD4P zcKjh1hMO+=e;ELWzjUE+T2|AVTia`o>93t8Wn4gA(a3uf*Vsr@MPf zFW-qQYJOl7k!55=O!S)Lx_eSpj9#xHA9=DYFQ(>Ioj~f$Kmi>4Iv&%zq0MgQCb@)Y zq*YDLXK8&c3+OO|y0MOUR4`TZSQWT%pnHAQ%fy*+0r^zS^{UUL7#$B?1O1Kp-C_Xz z;lT1KS;0?$TyMNe%6Yz|VPjB#l4r*#1HXtMMx2jR&r0U1oX5JE`~ewDrQ@trw)>;2 z@WJQwBp)$H590gtz{!ISNC(%n@L`9O4>j6FLO&blNeBE$@f0iGEmA&UPtiE@ZXR;4 zn-~wu;V1VBUy=@|-oQnanK>cckv4QLmDP z#mlPEe8B0VxZjmcB4yEyPjHDD*THEDH#|*YqhLh#m+rncs(AV32eSDuR>?r{_h)~F zWMb!;tSR;?a)Vs8VT8@)WSvyz(kOR2@yW1}bTQyY0!(=7H>P@pWeWawHP%!P!M+`n?IsZu zVcpE!99X(7N^_lQ`=pV{=yKGE<-7LMdVr&9?hv|xxNxW7^uMoQw{u&&VdhMgaH2OO z=}rU)erMTguWm_grXlgB+W#p^vmp(U7EGRuQDL_T5Blrjw$-C8{xBtE9UQCo3e8$y zs8Z{g$V}WgI>cVzruLW;Hm_%tliEk_Wzyy=?%iu1f#NgMs z_DaBMZY{T;iEMdH+Iy*Uvo`wMibNqFwODh-Q=kWMXiH1uymcw~%%Xey*eZ4sQJNQo!@89(hISVT znygYV{=psn5-oL=0=~#8sa|IyOpWMCOTBisos*D3MQ7%|!}eeb+m!WBJS+XpH8Od} z&l^M3lOFPGmB1%3?Uwd8*elUp5xr1RbL+3)=t2o<)EfD4gbEJxlI7^l3m<+`yfRLQ0hnIHW9 zh$G!E>48mo{%1mHwb{8`wBz+DuLWnZsw+8>TikAw4zx9lDgl!`MuY*Ctx&@kTYnDG z(9BlyU5=Y-q*FUC-1<^r!Ax57Ad-Y zdvhjctSdI4wwW*K5kh*&p$WL?xr>xXLKe{bn%n9?9gqJvkFEC@A~gkgKdQ=mL6Qh(x9x8jjMmyU}K#*Nrhg*Vr`~k zsO?Qx6c0Vt@L+?+lF5L+Id1LH4nxw=FnusY2pdlJVv)S!c&>?CHf)pVS2liWtmC-f zyUUk?mXP9|2Rr7uWtJ24Eu-v~vQWrL?}IJmyI=Mlc9ofvOL_HD)~3KthNnzXmN~zZ zFR_s@Ti@-(2bLnn85hiEvV%W5@rhqSze0)3gPYzj+II+f`b=TyyxGUt_)6}w#A>cEH>Y^gIQ zp>k@zSv)&!@<^?`U%vn{cic^U>^0oOf1npV4}0%q<2RYxK=dNvIs@qp>TmZwfTX;N zlbxjX!n)H?YE9O7G(~_Lxi-7>v4l?b0rI$We#0nwQ@)nL)aRV$qy7iab=qR|AtpL4Umx1 z|50Uq0U^FCHr>F5B$(s^OW(Zi%dY~7 z4~JclW`|Qy^vMl7zC4@7YTMcIa&BJ%f&X97fUPkx``(;;CCe8}B0LLJzCwc{7I8vq zn3q+W$@9SQ5qD!&i5U)*jn3FG^DkVRp~sQwvi8)`2_D;qG2C0D(T*0+;`xlS!j1YV zafqa_GYD91Szb7{ii?I@6c$$}U-=KBibvlec?bWM>PSSD$Yzf2zgEEC`ksCRrfC$O z(iGZ0G0xpgaYq_ZK@gYbPG{Syut|b+BqwY*FG3x_cW0jRPGo1yF&G+<@s%{HEN zH}O%;E{12?3DoDkSAF-dI{&_sKNYOnH=-=Le@hn&sU=^gY8YUHid z|LU1UjscOuF2#%Na-8c9*-V-#vZ!cxj7=JNq!K%C%O7HR!+KY`5zUHW(#XJPBd)20 zK+^@-K-+R#q-WkVY#;0KFP6rUR?k;h=p9keKnq#`7io);^n}%P=iuNq*|6NRIqJ<9 z?Hop-xl>qn>{2a9Z1+=a73GS4<`ZAbDs^$BV5+rJ&KAF=;w z65ZVWMAPdX0*zU18W>vf`1ncyg7YFg^3+wL)9tu?IVn!H{QY0zxBDYcoe#3yv;SbHUf zi&1%U4~JoA6}I0?*+aCaJ@o-Yb#0`I;w(6uiVF!ZK_OfqG?ZHtlMR|C8@LEu7XDtJ z{_Ax&tLx52cp*1A?R*)^fewW$ntvhF0JdHVq@$Nef5Oq4RPnxx`VnI}6}9?m z%sRD7s$KYIV^nV>oZ=P-DiNyhp;eg0stYdJc<@@Pl{t@4w}XbiqkepIgi|e+SR?=p z{A56s-*C!$G%~ZJ=d%UJPEsnzMMu&Bu>7Z}V6MowlSF=8_(&1Vv7}d4p1@^GI5fO%r+48SiZq2^n~BuV?Um@JIILi9ft!)I zObweZo{_lfrp{v$Kcfh%h8-No8gppi^8W>^ zKvch6CHIUzwU5VNa?U-CLr3%{`xI?t^vckE&qDSN_c+EPt&vYX>ts~PWSWZ0ILhE} zjGo=xJ8D0(KDV+rR%DHP9OLk0JZsRA-O;{&Wwb3PkGKwlh&j%^QuL6>n`lGSqUVQ- ztg64(pf&5+hOXL6(^wP#CjS$|;o!C~u9cC3&M?B{Gwm>BaV*$1o3f!ZL^U3{lFmEcn2#wd*tkenQJa*qz13w8{@c}AG|3^#`9;U z1(*@pF9zYpqxz-?&#lx|*e)QI)f*6Tz>tBsJ^n~+$j`r$DWCNHS_0X-|JEC(s3{UP$8;-I7v@G3e%p4o~j? z;N4j#WV=8%Psk)>~QkeT%qUtD^$4XtcxOa1+Am#1YJqszXbFZAswX!Gg3%Vb|wrI+R54jJPljr?Z`V-+yPO4VxbY;;#f=DiDwePzD5ggG5C5 z@>HJ%;*b}$zdZ2~r=lW|;7C)XH3AgbiI7x~8=;3F)jkh?WaDh{X~Zq!62XZm)F$tG z#Q4AacmFPlZDgT#cy=Q@9ryeoe69z|vt7TCzF>^VbEKv|SHubUe`GEXa)h&f^9V^rlL0^#8u1`lC``&oTG!Jhbxrq_W3?z=PXcIb) zOc#}b&}Im64!uK;lao0zhV7_xMqflWX#1T(`W_ge%*MgvpJB*Y>Bwp$1$}26`ja48 z^y}jv|9G}hsfElLnV4tSA#buEn}708{z;C@`U8I%1e{||=>LPkRwQprI8K?iQ*;F%R3?)kx~Us5eM}` z$}1BI%`?~w1G0^0+)FWTFOCQ!VWo&=Gaks^q}xSnyRXXNl;Mta=onjr^7p~#J=MOp;0kT2^m zgQyHdr!hMYF|I#r${*Rkj{GYH1akgF%)RTA>1`uQM*F8rw?rICPZ#?t0?+)Xhj_^2 zMCdc}dxzm7y=!+* z>mVZktv4TD=ZG?7yEUYC2r|ezG8epgpNnG-Gfdud&txm0r{-HK65jLL_%ET)S>&9d zTnfKoz%uW*#Jt&eGHpw&A7gV)tsf*XVjGe5Y#6rg`qf{Zt_enr=xavHMB5j(Xr##W zU(vQm9DC>;aer}b1fRc8#xoF^BFo5%=e2px)Lt?7Nn}R9>Cuy6bTlRr0`|Q-;L$9pk+v6TCipUW!2Urs&Q zW8XD5?w77X4hU~XTm9n|z9`L8^owkLf1Btg`V(sn0sPqi`oB(|`lCO}@2tl(W{q@j z_9Oc&`FSovL}$7{DL&)+x5qt>&dtUB&hg~q-Y2rao|bEVo#!6qY8r$_s=qYcMEmKrzlM=wexzfwUmqBeICR>6={{Kx_G0;o>n>7&S^O^g{h=3t8vZh11kB45S zahj1o#%X@6FA^5Zq0^ILxU_$IZm~yt9?W|tCvWMhL@bV!lj+bgB0W5ZX`9>_1nKS4*b6+v zh~%J0o*m4ywXl+IaAE2!Pfm0-qdk7eIsPt2u?3X(_xX|HXeo%xqu+ccQ!D`zS%Kv} zSC}qD8X7eV5M{1&A;?!`ZUJeHkmX%}@$kh=|AOdKZ$bnH0$tjr=Yq&PbpOF^H&Pfk zuk`Qz>7!YY*i#%5T^b^}-!C)E)n$WxD#cF9=d66cm&%%K)FEJ~SXi9P`V!JT={Nbk z4zV}zzJB-@9|(eTU#7>B+yCWbXC_bl$D>JV9**yLT|f9U_hx}#a!-SeeEM^f*HRQ+ zr2{q+_u05M0v%z0_Qb1`mmWWvUgNj@*v*r-gn(e#o zITq-#wN4lGtqA|pC59#IpW%ppYO7L5&GvD&@U+jYvqMEW)H+>D{~7xjO6aHWN^h!7 z>%Y=unZK%CV*VHt=nEMf8R;Ij5}mLS^g)}_%Za)X6-NKaztV%zPr5^1j@@V8Zck`MIC_xfKJ_hW!~Y-?Q-2wv{v>NWdaGvV z3NM~sF1^Ipbu#C4{6FFd(U!!M`5rm!vv7CSys?(Okgo3I$S}_Q&$RFz) z;lkS(IYH*gl1i_TDN95QAf%%&MSL-wAibAIDu>xz|Fb$}-P zXg71`+v7xTjQSFl&%WTg^gDHG5F$wm$+I5Eogeqgtn)Dl!1+!C;cg5;Ba2Kh$+k-vcW@*RT;@-vMQ z2->(mR2q%Nk+P)f%+RfuhllQL4)Kk!IO>9AIwo^6Y8Q|}xD)rLN^_UzjQQ9~lmPj{8OCyb!wc%Pd7hA0_v>`@8USurd zaYfh1ew=kfLWdX<>{Yo}jD6XMtTLiVq{WC}KoDm{*NB9$7ThOZ87bV7Y_F8CqF+K6 zk>d=jOC!oTiQ`z47a8U38`hDugZ2YyYpe(DkLOx*!)RfBR3YcNk)CE*R8f3)S{*#8 zYD9I$ZxtzMyj8?E_YKdDo-I9lj(Rk;rt6-=$eE<|SR?eM*59s~LekbRLuJ(wS{@N1 z+9h-P#rR+8Z}cOh&7$8~_ar_D-|~*9lZ#caLi@2tGUAZAb(A#+h7q&w{j>Y*hN>`9 zJZ4>>kZ09bG0*y7oh^)dN7bK8?iX~o?z&of z`_l#xCE)&SZf@{CJNE5bN3}=K?v+`yDfC9_u$AYhv2UfL!icUaXA_YXQVTNXG4AF5 zFS;SlshSib4AMv?&C-ZEVMvmOvg*rtR?50UYeku?0(qwK#$G&o{|i~G-&1v7(!n*> zBBO}4y>Fyv+BKfBvj$K6zk68SH}u(_V(#6e7_qDNj(x!RtPw^E#ta5d^Igvnvq)J_ z-W+Hn#TI~IeC36+({w}6pGgnoOs{nE)yaJyxGM{?-tutVX!c#HIA^Vilh+Yuu5`HV=nH!nx8x18eVrJ^^VX10 z{hs{`qL#<{+`{r?;oQPX4T`lOebSz{9m%}^Y9k=A5ya|PAn=cd5$eRz)05q!z`2w# z%d4)rEkhf`de%&sn|j#us7K1*|AD*1fN;~~rng*q;b%Z(i`G!5;Rm04BFSmBPw16& zH%G$ICZ*KggWI3B>k{PrO$C>agUicXD+WFeTVA7`wXPu&;kI|(JbCNS2buHfN7A79 z#8;1ok>>W5sFLf34T? z{4k76_l~&kf8BkmU!OYk|p0meZPW#%bBI&+Ef+M$|8-?2tujZG5q3Ry29y9$Y z!$Oh0)#uVTpE8!{Z|Mh8+Vxx?A#8kQoGD%L|N2>lx(nK@>sCL?s8U7YuO+I^hyK%B zW1lSs&Vb<<#s!}B`cvxw7#@s~oUA8?p4svFf8RT&>NIGBApikBtCMBW(UwRE1}Agr zct(lYC=N1WxFEACf?hR0t`zz*Yp?_J?OJ58S-Q3?q!;HJGo2IFLnlO*Fj6pJ9ofPQ z+Eze*B0EHKm^(%kQ6#R#a754jMrNC(1gkm}=7!OO?u)+A{*Bw{HMU1!qabAw z8IVO5hbLa9mvOH^z9EI<<=Trq?_n>|(l%u37cbeH&A~M6d|H4u^zF>3b&!OUg1FL{ zjiS=7>jm63_6_gpSwdUiZX8hxd4JsOG}fhL6P)jELa%DUz222;B~VUi{1%DIF$VV{Lhov+aLNT?>3TV zyFAB>#G4z&7g!wWNGkmXERXp_sHZ#?))kQv|E@y2%jdT*DtB&>^kvLj9L0Dug~})Z zociQoHDN|Ed;OUV592g|aU2=Prh$QHa}ZOAE#Be0TSclQiG?(ZJt_;7ig1Z*XF98B z%Y2?2sZ|hESui?&yEuw^XTjR&$5zPlB6;<5VR&Qf+r>dtuIL8?sN(Rke$KA9enuJ_ z>j43`FxGQMEnVTWz4nplS=&JBmN59k#ZgZtiz-WLGWuJ_;7kw0!DbljtjJv>nr7v9 z^3^E3IhT`}#()8(Xt&b$7L)UWOor%^;gWDi7t6=f8@o5d1i=%M; zC41{k3cRJ6_Rr6x9IE{;<*$arFhbXw&6*b>>x?dnV~v@&JDiT|m0n&feVdVc)p}YO z_e=9%>vY91KE{|v9uW<(j?ySK3;_&*nLd!0xrmdv#^N}~{kKxbCDC5jE@S^NzCcEH z)V*w0epi6+s{is^ttq76gy6D`jN8E$2?>dg_;>s z9GCYZ_8{{^F*x8X*Q2Xw=wSOmhei?i7B}ORkryKwnBBhT!dp6g|M#%fK zNVm9GWg5mUq#R0GViZr$R9a&%&HMaF=~w%r_V&iwx-ssD_GWuBgG8-?+?S-d8wNLP z$uq&r>hZ;U(4KfUpvy>p2DXY)+^~P+-j|tl)Nrc%UtB-$f8$xEP;#e}X=t0_1FQ#f zeKnrTYVXSe+hZNrGc#4$SOXQ^LYA&&e=@(;(qzvy=InMDZ*C*S$SJ=OIWx6-O*SM2 zFOHNg>Kmko`-P<>*`x!KYUGxOj%4kG^$Of>25E0HLgV6cl6je*j zaB*@v3>A-kq4IQ(f_-7A*mH1q=6Of3>tEFg*bXQ-E!X|SAAcamd`s>oCx+2P8k~ha z+u*^l0jBb~Fy%KxK1jvYp3@8tysGbg-<{dt4E=lJ)Z~#Mi z7RID5fchX${Kuy=<fMx}ZHb?#eVd>V%YjZA$bGzB-Rubtu`YE%J_E zd1zYDIE)7`Kl94u#m7!ePCfH-rlrX_y$V9v`O|xf%!W!weQm>abc>x&pC0`=>&(kMqwnRh!LwW18<7P_ zbmTemQnBW9=QfYXxgJ2;B6Ow4sZ>?S`pV~D5e!>J$!Ybyy$nCbkH{v+O1*@XcYJxa z`<{FlBr4*d3TI1GBX!dGRS));(>{Wg_c)_L%|{W=;5Xw(+R~SeikH`TH{$H9MV*&;M$5s(;$8IM7YQOaETB3|Pv&G(L`^Qkiz(b~tagIk^mu$_A`6wgc z>V0N5YNY0=j4`X_?6}#v;n7cT$}ly%#vNDvsKVdo|7wgBYhm?VZv_3U)M5H06_scT zktPfXqD<^vv+}!ARD_R+6h<)yAag_qUDsdfu_`iVJy=ubkC8_TEd~$$R{&f;uXQBY zO+WDylY4&kSCc6ABJ1T@j%9?M7vNcf*h_Im161y5FSAHKKrZt}P0z;RQF=X)`oVCw zmE)|B8GXu>MWXy-zn0np@xj=VQ5s>W$aFu_`5?U3dAUaty)p_YBRR`c)5|&%@68xL#hsr@>`-}ru3&nDy9q9Ua-B~1K()-8y>`2K2$ z=gp8t3VXSIfWt?O{bcMV2t**0j2e;eeME z?iogk!q|VNw8`=njeBLzXWFRyX5L5RTBkVEZ~z*5W#19Gko8YQ9c`r7bKB>1N4M37 zv94Z_=Iby1BITd;8shnYQDD#TWWOSe`))o{Pp0#i^TQZ43;|i}dX4C_#bE?WBgB|r z#vSvP&JiPWz%|NQVc{yDL2IpUW&ZVn-t1qe|Eod&UJX4K#pYg@`}ElBq~^=9T)Z@M z2Hlk^4Pl>kA7cET&+N&@&xlbGrrMDXkWpEC&xg^`-%Mo~-zzTeId9iGQQ&p|6RCF0 z{cjk(wSUR|ulhIUHlH`7ezKP`V2u6E`SjjArUA$M8kuoo5k10rZ`7QbHaCsVoF5TX z_Kd8#aSivvHwxZ#MvB}U7p5tAq%lE8&ZbV9#qz8XV}muvi+A4*dnb4Q)E$$X-f}S0 zz{pR3!%cgW{6Z8i?7H%YT4{+yYA`}5!Yt*^Y<-K9Po7HSh=`3W$a~<@7s(qA$IH*Y z-C2JDu`O4-eE5rdwr6^gq1r>L8ZU9}IkY?LR49JCQY&EtukouvrEN-5RrEuUj;r(g zGwSf3KX})T8PTGTS>Kac|GNH_acZ(OH4Gsu5ySgpELoc&ZgiCh&Zrwk*^_@p14}Rp{6vouxvOD!{|Lyyx-u%}%I408# z?V6I~Qk;?D`7@gn{|tC3Z*?p}7jtfJh=-UBmK|f3=$L!ndwZ;(6KS9^S7gobvuFSA zP5Vl(TuXl$Dy0?M45pjTI+k(5`T8=qDy@}yr<>MP)#YGlP*9wKWiv1is?}ouSswDS zChbe>f#HOKjU4H{>(qWS_Zm-;h6MZ3h6dhPJKAx~G-7FOd6R1B~R}vYD*p;`N&6-$gIM+lgZXeiuDa|mwsPFwE7o+^hbY`>4K1s(n6KD z`>yLj9Bctd7ep-r|DKU905MNKmHG&Qza@k3I&fYU(Ut-Sxor;0fPnBs(yGbfdI)f2 zZPjLwMn)PZB&&WQzGunM%WWUg>KeX7{xS$iB~(PP^ANVY;wv4~Y+Zu&#<iXnP zHxRsvCfBtYPV}G7Y-Rx2?AqH6Q4qZS^;v2nhOAlWGrj-4-}}8ZQt-B?_p`FCXpgn} zJsT-e9{cMZI~&0Pj?F!9LJ!VW% zD@C-$)xfyTfw>S-qHwwB4TeBQ3hUw@{DXh6X-x@w$@r?Kh_$6=i@GUSV$i%2RN-SU zhDSmNy+EFh{vgQ|vqTw_j)6T>m!aEy*k-yUyX(vK1h?dE9Tt{2J=d5ua zFJQUMMHwJT&lHIeBrQTg?SZWMu}+#Wgut_(`&@b*dLfr4flL`rGGBQR3bWTu4_)4q z^J_dy%*|a&f)DTUxphdXxmCs)@=s6aH3-#9z8@s)HPNrjX)}&xBvStXIhgVvX*-bd zh)KE4HuL6l_p$g#ay3XpBXDmi9Nh!N>y&8Y)IS9sX&ioRY# zBoBR&^6QYo+F$ufl2xZhJqW~4)=uHQJofeFk$P)aw9P<-xSSiQNRk8|WG=$%YK&W} zbKB~5ma14_QBd;LFT{4y?%|5ua zS*?rpbbpO!TGwR=qidOpO%dnl1LBrJYBGQI&+O}AdB$T~ZK2G6A9|j(2w=>27KE0H zY@=wV4G434tB;5z^O!~ijtvZfL3&m(^o2okGM=fwBh5?JCX#}Zb4fhs)Bm;F&vaX( z&^-g3{iNErA7>xpy#-OaJPNea8}}%w&JG57n;3hdJ~}=<>^IxX)-h77b=J~oo9v~) z$=Wz03J#V>G(e7NbyUH6*WLyokCEW!TJ#zNWfldGcLm<_>ag7X%fCEX*g}1*b-TLO zm!22LC-;*z^qryAQ{~b~^=8ac3L)G@7uX{z`XHleIO|-^vt7w_i?UrB5e-{Suj$wq zzW()0vng`z@Gt@yyY(S`*J?R1;8>gX!ej9c=lN_;?BiKq+#FcMIYk`jOaA$--$|(SgBuBiJH?tH|>K6RiKYVy{+dFQ4U4d?- zHdU3oofrO#kDi>|bpOF^Gg7PuIU#y0HIVukNZD6Uoy%+P+ol4~#y*VzIFrS5%>hHn zt}PTV*2jH8*6w@H)MGCT@IHMyjTYKK)`|j=^PX{{h+X|pqX9DW^!dr({fF;mTk1CO zKG!DC@aMmKd~!AnCn6mX*c;W05GA865e$qW8`Un!sFl!N#Jaj4fA@#qn>_uWj)l?U z_B2*39J(^AJ+hY(L@F>5F*}7M{uif3%jr6Ac%=MB(LY&x;nfQn;eyyc{FXyWTrck4 zSij4th1h;2i1|w6B}&GeS!WyeH4UEgqloo^)NheCSr2Am{k~>utr!<$Ap_99IDe~a zaeo*M?tSl_NuqBC`+!tN+V^lzKGw>i4Qf3wVq}5#AnF-6jy!lMb-L0j&F`M3pHj2L zc-M=jNFzoTK%X9yQ7v0}cW|A#xlAoqpaW;)S_i{mW8Yi%yqHg^$=?1;4^EDY8jF1{ z=Q8H_!0r3iB`m<$99QjIXZ}5R4I+p1;ccU+7Vrq?sm*(M-xk7Nw~F#MMl>SYr9naJ zBJ_}sK6~E4p`g6G5%+-1WfU+beZCUUb8;tR2BIn;%?q1h0r`*kM0oSaMv^k*AZVow za_n#Y)^DZJplWwW>oaTQx!m?0U$q7hw{!v#eE9IyrS7{Nc~lxQa;`oOFh;5NJ z3XSWtXr)>BO*Di8&E^6DPWC0+N)N_JQ--AV3^7%wh0(_GW!zXR%Bv!2{H;hFx?9F9 z=T^F{s`IhhPF3pop?FOX93W-$Sp^R41 zKZF(%54m<#sPsBJ@FGfQcq(RL=TW2)fuIm)7UbkjF3p^xl{+%Qg#!AG%0(2wZxNXhMSC`r!{x3se{AmDGws#=8dN(DjHK$@LRP z6uNnE6l*;=?%&dXy&UB9tWl)M+lwPz+f_MyjCwrg7b$Mc(<}gMxA*E63vzFUtWgm9 z#EAJ)+?UK}5|%OkO1*T=3&D<*cp@h3@%Al-tL=n)1pQO6m*GLQ8A28L&8R@ntZk74 z)*j;wf|OUkLduAIMaBJprJ|a}-eFLaW{V!#FVwW)(e0Yn1;dZ;jPv}6-auwM=iocv znFYW%lF@ddYBXt+k%GLXT3g)4ccm*q7H^l<(0x$tl$ax4{tAh^7n&1$P1VEL4pgL9 z8ffC*7e`djy2p?DR8?r6aY=#k>k`$r0Wx}FM4N4z+Hf+x2a6a~ha+`Am`e*t9KFdVq~r~SI)O*RP;#JY>Rn16guS`Ye0nP3&Tj^!{}hm%n#Ym!oFm` zr2``L+>_n^@)=~*BYSoB{x>(R>tUTb$Nlldx4%7o29X9zN~bWEC;(pdkn)*k%v(K^ z7|+~r-pi<&@%%DZDb%j@Uib#Uo6bnViyj%f=b)54XC^Ov@5RYY4;%_Y@<0}vUhx#Z zu$;u@@%UYe6a?9|PFpmKsnSi0FdzTHOPQ+az#RuNtq_8ew>h#CQOL-W9(sEhrgaNe zL7JXhAD*V1I{n-$S@;V~X+7*B3)y9to29WEf*!4!vdET9{4YG}5r8eua_B zd@ZB_=Z>7mtpc&lz#m>NdfgV$Q=(`d{lZg|6ET;JNBS!2X?6M? zhSAfp?# zmFOG>H^t&_d)F;l(;+lJqYCUH3@g^hi6>5`5ziW!?mN@4?2KwTaW)MiQl2r`=*w0L zpwFTr*`EE|7beHP{let!pLlR`c;ChkSN2A8cgK&%`gs2AzEJazMiJ@Rr_&oGPUdqM4_;NQR1hSxTH|-n)eq+%8E)aW}WGy{Yj+5D^<5(8=!4QIkFKE@tGj@MIhP#Ru>S5 zN+Cw3ip_ui^Pf*SGORIp+%ibRvx5w)`W)9Ix~IGl87hzl2zulhZ&pN4<>W?+3j61A zspw&Pea*^Ssv5noy&s}aex$9 z+8~{v7ixromvP>LWhht~$L#(SbIE&sfrn$PecqH=M0C`3n0D&5DQhgr%8OXaI6Byq34x5dTzY@J4X60M0FPZm6pprV0fqIILKn;jjp;3EAuefMwyQ` zOa4ov(C6HY{BIzm=pRz$^d|DZr0p4*U(uQwIkS1pS254I5iM4E{MSvxv&MXHCy?6a zpI2&nU|T!G(=_F|#&r>o)-)nmDko%>`h z$8FY(G4IyT^2m!mqlxivFN}iW zmyCIv_ur+FlRoP`a9B~(!Cd5f$Hgrb{k|Gx%?5k%zWa$0a%UAC$S2}*FqNHmx zXN+S{$8%(+axvzZYxfsMDMqxVQ7gq<=!3m%aikWbBWuYdbM`Y-#J!WgihkNX3R}|) zMi&Mz(G^7~+fV6BrAfqeI;vLc>bDR@X|GWwa==xFKmKL zbr$o#z|kl4P~-&TiPTe%|L^~MMvt8zDbcnISr+Iv&%c9ir zta%^|8)TLD_qY~ASJreG&M=uyD)!RcQ@kg~y~6(QzN!$lD5bo#DN7L5S6e{! zav(hmlJAMH#0~1Nk7dLM@>MZd9@0og>1B}HjzuK$!e5IBL|#i7Bcg&)eDBX5k2!sL+Slst@^np`>qo*ErjE=FH|?1`__OzA%BiiQQgCg>5TzD{ zHGfCQQlt%Io&w>neSVdm<(?P1bL_vokUG2X=DnFd&ORv}RYod=-ZB2zhZshm4V``Z zub<1klfg8NFtJX|!&71CP_Jd*t^2}YGc~GSZd3w!>8JLs@$*|)8}>laCJdG$i=?+Z z`d80Qo{2q_G3$*8gexNjl6bpOWL6c&t{Mui<#R1k&0Gsq&4x;$bS3>J2Zjf^@EIA( z7y!s=qt7J)oV?r%4VgMdcx zBFB-?j4DX@+3{RWI-*K#RB5^p$X8nfMSsX0S?%9syjE0A)jnVtB2Vi}dZ^NW^MDlB zM|!33va#1#OZMi`{D>~lrnUNoFMJ^h@{7Y8b@o`s2*kU2b-XBnDufQMAu>Tf7&DOd z3>x}j9NKUVV=O}$nN{?~)uQIa^%+cNYm+cWSYHfs`bBnRM8;RT=GKF2kdf#v`bSRI z2gAi$5iRE!KUc7kC$$H3QrZI<%6o?(f1TSA6 ztX?L0yDbc_TcnQP7%IHbuDrO8G$t#A(jbN^{C{~Et-N$5u{iRF7hzPMbV-(vR4msL z+D9(%Xx0ZW#mHM^0K-fcfZl?KASk?^Yw zG+ylY1;Idu$a?7@7>Jfvt#9K%bY`lDI2Ms3&BMi!`#HxML=^HM3F^?%`H{~()0)J5 z>MtTO`#e&UtaylCjs7uI4>Bp&X+?!pzt)g{8OAC{|5n8v>zMWl0HhAgekDgwV6<`_ZCdlo4qWk{x*k-}$HO%!^8%(4!7x@SS< z)2Cm%{E)-7*o%sYPTg3wewJpgIX5C6M1t6#)Y*{k>f*?qzPx(>*#rn~`sR4+P!vz? zLn~vxM(IZ81}VKft81QB;+}a);pA(8@=9k^Wk5I;_o0_ZVcc1d@T(m^0@Aw9=mkV7 zLRp=N*jJ_3Lx#Kl+)T=s^1@yz(k^8ugU*@Pa{?kP&N8Tt-_oOeq|8HZ+CLGXMdG>+ zQrA8s1(g(iS-^Uxfcxd>&*gv-S%3D#co_nv`EvhUSyQun|5ZGzPHLYOov?*_7hTJH zdFa1;fv5`izlt8bSP>4}Nc>mvb!!j#PWLiOjqzdT*}7!mShb5B@4hD54Us*2Z&)GG zufwxaAq!H+oH86RI%N^>nS0x6BkQ!I%dZu4ANLn?RrS7B_kV8Gd~hA9$y_5-nZ>!D zPj=1JYjRD|4rDnQ^`1m9h_r=?+%&IDmDHPERb;SW5w zKa3gElt8PCH`71Ii)KLNF)}!pr}%TKHzs5_m+r@p3fWgT+0(%_uS=!i3E1jzjO zaUF8FL_TpJI(sVS?mNdPkAL}S$a!vZ-`~G$a^FwiH97LY;q0IOo1ZM+Jqjwn^u)LKqL!7C3n4v>pbsc4`b;$|xOrhP;-7i_YjP0|^^3XWCZ&5AAUa!Z?8-q`{GkMe@7lT zl=GZ2k2w;JL1(2U6RB1@L_g@;v}Vhc_@Z<6;i-{z>@kz+9_ZMUu{PAdNaNs;(d_=2 z>F{E0@4az%8VaTPx-%nq5oHK7#95{NDHKtHBrT79M4aNW{+4&M^O55Izy7UV zM5f=7p$riIU4{;1xnmKZyvC7vh(I3Yh)>5N_A(7voWnbrH?sNgZ`ao!{na1WFfQlR z^|i0xu5r0(s*FW@+R%O#Cq~dB>Sv*k`in&6{f-FMw<fwpGr1DG!`J_M~XlL?Ym|MUyFiu@LNP=&Ge+y9gC<+Lw~LHnXBeCP1AqO$zB<-%j`wfBk(cX;>QLVS!7Ig&C<>{Q^t%ix z#>KEgp4Gnd$&~!dI8()_>4tyPNn>y>9Wg$0NM^=fW7>-6T^WYRS36`$2aHkwZd{f7 zSzjG*{FNTa{JE~E2x|gC{;qg=F_zDM_Oq!gj2FgCp60;ZYu|A`uKj0!_GfwRT6^R~ zZsw`#&uCj8tT}zC=l}*RMhd!tlxGZ}Q`%$Pp#Ka;j4Hm@hl+9`Yk#dtaxMKYBZJ>o z>%Vq=r>`{+j00q&Z~DUsW3Imb^{?me#-YD-#vJ>9a@fC4(ZDMKS!nnD?|*-ehh8vH zNcFUqJ{Th%qoeAKNb#iZ2*aLntUjmGfRW$TdUhgtGH-W$ylke|ntd?ls=Ve9Nr-M9 zs8RwT#ud_3L^^Xo2YBZpHeMFmALB)2>>4kjUM6|KXQ9$jEK$MT^43KHmiKZ-3CI;5 z$9S>!U8R$HHP%NJ81*8XX*zsANQ{hLh`twjwJ@R=E{;Nrym7NoDcy-|Zz>4#$edBPjhvCn@#jUkBIjHY1hQShEeXM7x&dF|at z|LU(M=MV(3MxBF{<0UCo2G7TgavSM__QqO5lDruARWd*v=|>irRc}ai+L(v(0ZUn_z_1epT9u_lp2`cHpLcaWpn+BL4> z9PJ?YrOQC7rN_U*zq7hCh)6|aD_Wr}D(tql<} zXINSuq;3{eWf+csFN~tn*5~24KO*+%oHo{~D}gwYwnJJTV-Gdyw88} zi<1lSo4L3+B9773kl4BNS63;Bgsf}Z?*EzmYt7K|B5oYQh=)a=l!^q}3 z=WDMS#l=^2$sER9*xNEn!?WS|Ej^Xv-x)bVw`2ULVm|X&>nVQEniSTPdt=N+>R0@B zc3j_YzEAy=ifq)exG;=lSs*=Ry?aCrynfM4--pTZw0k_c#~y6Yu#aTgnUNMrzvyx5 zBHgmb8dJ;{BC%vqQ6aV0xi;NYUxqxixiC`9q!G%qZmboAz57evM?%Nx3ep#G&Y)qA zj4cZVPo_0Ya!rRG_-j8wYOWBW6}gM>9L0#8YfrPTbN>y!QK;P-wsq^23AMSW}64!t5O^`xw1(ugYkIQPh4h`jGb+5f^KWvkaj*SL8V6Dfg#nKMm&AmpyrAe_Dyy zSmXJt3R1i0AnNUznObZ(?1)}-e(uYoX!_ov-_DU5$=v6)Iab9vxd)8<=pua_j%Kw# ztJ~x^b(-=zGy6DM*Lurmx|qMEDmt%5k<@6RbL-ttGt!7mW4_aH8Eu`2XLvfHR@B9D z|GO~me{-Y1jNfNsO)ZRlqxwX?o+0&1>Nn3x_5nI|^LyTtPm&8U;iI7^{c^2Aq92@0$s4UQW;C zC&GY&)I9srw9u};GF133Ki@i*vXuIW7x$qcbETeHE7w1-Y4ty(-(q+<8s}8B2jWm9 zhW_!+emXwShTPSAKrY|&zB?yx{rUSRw}c@FiLaM(((9ur2IE1}B5VQs@zKD4QuW6$_Au zPlY}|6JvSx^!e<+6hI6d*4e_SV*rS2b8KG7AR`K5-7u^<2g!_#pHn*~{(bPSX^j#6 zH+RM<;^hT%5Z5)w^h6s-_4|JM&McygShp_eej4TCSVobokFa}Mtoo7y^V#PxETmEB zVnz~(xKd+g*CwfzaxQrA-*Q9NVKEPBek&2_Ca zH8D~jLzbtWdU<6}xe)!4eo0EPt)zLH#WW4vV$S{EgHyS_L>C`Fm3m<=j>cTjweNlA zku(fG@R9qnZ@0bUNX}mw4q!3na{sOSS0c7v4kO+RVPt#CUPuRGO)*Hi<}GhOGPy4d zKR4ZfXr zF4Fg};-$rh$8Y7n_x*R`rApc&A9?Z_4G?WeIJFGSm+!yyr7tD1h|H{%K*&mDo^!N^ zfI~QX5J5Wn-tW?gKF&wh`XFSHm`F;*WD&B6NhGR1AV3kW$W5duV+xWG zfr|J=%pzd*?WW7jyNWo~Po%sL4{q&!cTiJX*SDa81w@dNgkGc&7AX>n zg7gj|MWk2hAYCAUH0eYH0!TA}R0ZiJK!8vr5c1~U`+VQbb06;?-^@4j&b;%Uf6koj zv-diy?6vpK-fR8b-5)D^4!mD6I8c7}MP7TPe*h1lXc{S3yIL%4L%XO!JF6(e{_^O~ zrW1o5tNSG>ZKm0!k{yPYUjt#=1Bsf4G?`nH>n)K|54j(VyoU9AR_?IFgAQ}gV+ZyT z+mfw?0^u#k14Bo#rujo{r&SF~YjL+EBl$PTiL=PITB{XC#B-2nhE*DHvT*yN^JfSCy`?x{?sLzSC0N{BXRv zBhX|4Qc!nUabQq67$*<%kX<*xiaIA;B+=tI`8>p(FYU8J>&6o*43|J_1HWK*`V5O> z{q8CqOf6(-?`Y^BE>eU@k}C#)vu9@JH;!>c-%WSB*8~9#$kWD&2&j zEkzhn5GMO$pexx^wQrDh06ine&46@2@jktb%<-N327rXxjC2UL1fsG0V+j;NjjM(n zno|Z~)CDJLmQ(!laEX-(Qq8k;;zAW=B$TUHXJS045*>f$(maAPPi6W~0A+0ylF zCV|~9HI8!irB%AnD)Q(4ZeUMVa1MI4`GyNrq4yX0;)V)s%WBEOP3Ppdv9E+7P$j_& zEaT=%t04=W0^z{TPdb}mN+mTOoPvwrz6w7}((UGcLAEht)&z|jf^kHNF_&vSuxQK#x6oX4T& z%bo114Ib@btW1JrHo5^TFYE!1dhu$~C!5yyzLFM)TTERmIW#enOzDzZJLzqPBE@^c zqpn5LDkq~yG~9Y68F!#7;i-4X7HZe4dGIEcH{9$EdPyR0tk_U_xLGB)2=7jeeuZ#c z|LhTbdmF1Cd5>o}$2PG4t_TFssyx-uRj(i5$+%HQNC)`d6Oife;M-h^wBO*;3$Zrr z;BS4Tb(mU9^d`%HkqN~7d;@(Q;XTT~Z%Hf0ByJ6OG19}31I%kvp?=#}*_ga?*r^LA zLo(sLAEPkU2_ff=g&p-*AKB)fR<{RglZV}8Bj?HBnB~T{88HQ|v*N9y1L$!qJ}c;X z-R9}jOtc~#eH|TK~@#RKT4`?Knlf4e=sLWnsd{4-?c;)>!o48ziO;FF>gl&GZ zc(pi+uvq7^rhU+v8wi2`J$mJ;Y;PhY)c1Y5=608-5?LuZuEV)he`Wow12_3S3$}tz0#1tiuIf z(cA2#%O-$w&*HP$_Zy9FDNqn=Q9jlrKq@&|>#p@kC`)I6?xEe`uveu~(*Bt7l!U93 z8g+S&;|s<6Y0L8=RxsiP}-V5gW{A_wNGenPP*=oVWNVZNqy*=U={k%LL3+K&(-NF2#I zWo1$XwpQWaDC)FxJFbiV7Q8SkZwc{X7i^MAuGL{f0u*bwrujr$ zQ&fb!I;I&Bq!2rQFq!S1Lw26555dTaya(ml~bwIlNKAVV$9m?y7r#LCpWAe$*rVNK2aCb#w4f0YCIt_!CB&cW->{qWsTpr z05n_d56f-gw20hH6Z(RxOagH!H+CkQ7IGr8%dQ-qqY!KU7*-+F zwk7;n2b^Y*RXBW4+<#XCjT0m3G0`HC8fmtdZQgBXJE5Z+`{lMwIGNDOsz0)m7cZq5 zIHN^t71B36b4zNcjR*SydO@Yswfbt&Zi)r1!her=T4`*9#>4X>W5SV(Q#*3;=Va3l zF~5!V1uBu4^a%DdtEM>QousS%L3MU}whs3uCt7N)fM^$T_P${X;ducIX}nv`1|^c~ z*d!ojqdriWp-(^n{+2#-2;c!=0eTJBeB2peiyI2sn*Ho7?F>5(ZwgteYPkTo>-W)G z!6oMK75&Zdt=VY__YNVK;qkM^{o%X&(N|Qqa9cGOThTt?zRJH?hF2#^d-zA6FE|gnM%y@-^~*(GVfuMZr}(ZmRwE*;$B4A39X=TL-IV~Y zM2cYucQ~4Jy@cM%VsZ&+_2J`Adw85T&AU&yjx?EB& z23O*|R@=YNtF^0-WDtjp;T;whH1s1@Cf_Rd7g95XPPJZBMFcK9nRzAJ?*EwN?{aI8IYaR6EoYge7TYOT?SNZ{L@d!eCdyC*I}C? za`y;mf6fijW#fYthKqq%*=)TbFQ@jy3{7Z}#|AMD>br+~CUB<@uiVaZ#%E|aT!6=) z=HpKpge-F`eb^`f0u>pp=wVS)d@;jxQQok3sAi*U4Qo0f_>sU}J@bW4yPeD1te5;B zK_&G(8&($dek%)SwjD+qMQNV&9v*y-fZbmakp6TXq5RdEjq}Ub!W2e2MXWw>6*+jQ z=%-GJBwt2`tgFgrgPkZIlskuWGJBckDs6 z)|2opV}UH;4186|wp&jv=%c#kY^sJRmR#M6bMzk44BV5KL8Lsn)iG@I@kU$qYMjhF zMypW6@cXhORilZFdY5=w#N2g^^PY+zN$u22E0eThwn?nBL>ku1t-I9dZRf?+xO)^p zt_;Q^O|A(jrZ+fUJf#wbu(xOWAZ+E!tEz&syL1PJ%0S_l8D;>FcLG-M{$Gce()9f& zxF5~Xlttri%~H1Wn_F|xhMOoxSbl}TIU0~<5~`{jlIts#&Os8D{It`dyZ)E z!=934RBD@s2vU)snbAM*+)cy4D_jwFWU(e|J1Z>hjhT*Ju(_F89_Q!6?76QSI865} zK#0X3d84UFi1SroI#Q6m?19p|kIu4d*f;25c_m}eK;fwRgr8LudN&$(=kzI#Wj*;` ztdtQ~-@aE|9QF~ZhqjKg%p`F6hSW%0N8RMqGrY%$r0m`eCJxIcE}M)yf(BwxJ3$5Mq)7^)?S>Hr@Z=Zq%a=v|3`C73TLsAgEfl@*dLzZ{63>Oe5~9E%U4QF2AsEluFxi;1 z+^%+C=da$>)hgy+pBfyzEc$X-T}7$NkH=@j`pk;M3T0ruFnxnYZE!0arK4j`)Pnmn z_KpJCZ?1Mp$ne-&TSmrc+*C$GTHYD^Y+<#m1er66-)=g^k5t&64E5myg`I*uVlH+^3HQZq|xc32{ zxqU6fX=2SE?7l8xK7Q^GK1n*>^s1@u_MJ2V_)7x8SukBT?2mSp5NjjjDP9uUm@L9M z>OT5MG@?FvQnm8A2_0eWgGd`$yXO%O-MG16_4G<~-qY9?L>n|V4Q9Nnkh0nFskvX; znK&ZH1D~NDO0p({ZeqA>6Ed!)vIJwPek{b^Mnz} zZ#rl59Y1!-%Bxo9B-UTi3tuiiCN$E6BrC>s~owlnZ*H{iFXA z)?NvaS7TdPhl+AUw#2Bi#OUl$G>!x;=uP93oXqH#r9#o zHyQ_+V_cwWI`55Vsh^{`6mKP2mcQ)}gVr^O#nP@4jsB|Lz#86hax{ZYPYdPb`tK^0 z7Mj}Tr)Y6X46+FM#HFe^538=e8x<4WH_lI1gJcAB6xjJ`cnh&W`kFl!N6Dq~rSkJV zGPfKxesU4!cU>iGvV9<&(iS#e>z-C(jPS^4;ya^=Q-PYD#<4yAI8SAsGGpigayTw0 z>ro0#cSPY1*)_xVAl?NGI|cr0U#v?;@-Pi$alQ<^*XJe0t<{J`BDC)^ zS5c0KV@`1usJCw*yH8Sf2xG|=!zu(*egTUB^UflS!}2j`k@br$kB9W@6rJ{|T3O2zQf%XG$TCh$_n zP%C+8X6M!+K=m*i=}>t!{}Hrb>?9>-um%`_AS|I?Va9`=A}(ckh>sx*{p$jg+N|Ap z^M8y>`TfYNQ`s>85)7-M=@D1& zQL~uI?=yGS^+>nxB5Z_g8-_Z>q}!=e*z|gROK~k3%ShMfTI@@cQPJk4X-b`XD3+#} zpOBc0DznQ`edduq)gFB7ayHe3ZQ-y+Q=Z(!H~lIh<9yGG=M?hZsy2D>J&s+l(1sk7 zYm2N@;cHL^yFSc)Y*axB4KM>E!-vNCIHU6Hk7j@wFUy6~c%N$R0Y~e≪a^&qw_O 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 F{{ntXZruO? literal 0 HcmV?d00001 diff --git a/connected_customer/product_recommendations/queries/combine_features.gsql b/connected_customer/product_recommendations/queries/combine_features.gsql index 2d363ee8..0cafa1b4 100644 --- a/connected_customer/product_recommendations/queries/combine_features.gsql +++ b/connected_customer/product_recommendations/queries/combine_features.gsql @@ -8,31 +8,28 @@ CREATE QUERY combine_features( { /* Description: - Builds Combined_Feature vertices from frequently co-occurring feature - vertices. Treats high-degree feature vertices as hubs, finds other - features that share a moderate number of target vertices with each hub, - and replaces those direct edges with Has_Attribute / Linked edges - to Combined_Feature vertices. + Builds Combined_Feature vertices from frequently co-occurring + feature vertices, rerouting edges through Combined_Feature nodes + to reduce hub noise. Parameters: - hub_v_type (SET, required) - Feature vertex types considered as hubs (e.g., Cluster, Product_Attribute). - e_type (SET, required) - Edge types used to traverse between hubs, targets, and features. - target_v_type (STRING, default "Customer") - Vertex type that connects pairs of feature vertices (the “context” node). - feature_v_type (SET, required) - Feature vertex types reachable via target_v_type to be paired with hubs. - hub_threshold (INT, required) - Minimum degree on e_type for a feature vertex to be treated as a hub. - split_threshold (INT, required) - Upper bound on the number of shared target vertices between feature pairs - to be combined into a Combined_Feature. + hub_v_type: + Feature vertex types treated as potential hubs. + e_type: + Edge types used to traverse hubs, targets, and features. + target_v_type: + Context vertex type linking features (default: "Customer"). + feature_v_type: + Feature vertex types paired with hubs via the target vertices. + hub_threshold: + Minimum degree on e_type for a feature to be a hub. + split_threshold: + Maximum shared targets for a feature pair to be combined. Output: - Inserts Combined_Feature vertices, connects targets to them via - Has_Attribute, links original feature vertices to them via Linked, - and marks original hub/feature edges (ignore_edge = TRUE) to reduce noise. + Creates Combined_Feature vertices, connects targets via + Has_Attribute, links original features via Linked, and flags + original hub/feature edges as ignored. */ TYPEDEF TUPLE Vertex_Tuple; MapAccum> @intersection_size_map; diff --git a/connected_customer/product_recommendations/queries/recommend_products.gsql b/connected_customer/product_recommendations/queries/recommend_products.gsql index 213c3ebe..2bc327e7 100644 --- a/connected_customer/product_recommendations/queries/recommend_products.gsql +++ b/connected_customer/product_recommendations/queries/recommend_products.gsql @@ -11,7 +11,42 @@ CREATE DISTRIBUTED QUERY recommend_products( FLOAT customer_popularity_scale = 1, FLOAT item_popularity_scale = 1) SYNTAX V1 { - +/* + Description: + Generates personalized product recommendations for each customer using a + hybrid of customer–customer and item–item collaborative filtering. + + Parameters: + src_customer_input: + Optional set of Customer vertices to recommend for. + batch_index: + Zero-based index of the current customer batch + num_batches: + Total number of batches to partition Customer vertices into + target_batch: + Number of batches used when traversing target vertex + ignore_threshold: + Minimum per-customer popularity score for an item to be kept; + recommendation_count: + Maximum number of top recommended items to keep per source + customer. + data_types: + JSON string configuring which vertex and edge types are treated + as “Feature” vs “Target” for Customer and Item similarity. + edge_importance_factors: + JSON string mapping edge types to scalar importance factors for + customer- and item-side similarity calculations. + vertex_degree_scales: + Set of vertex types whose contribution is inversely scaled by their degree + customer_popularity_scale: + Global multiplier applied to scores from customer–customer similarity–based popularity. + item_popularity_scale: + Global multiplier applied to scores from item–item co-occurrence–based popularity. + + Output: + For each source Customer, a list of up to `recommendation_count` + Product_Variant items with normalized recommendation scores. +*/ TYPEDEF TUPLE Item_Tuple; HeapAccum (recommendation_count, score DESC, item DESC) @recommended_items; MapAccum> @sum_intersection_size, @@sum_set_size; diff --git a/financial_crime/application_fraud/queries/batch_application_cc_features.gsql b/financial_crime/application_fraud/queries/batch_application_cc_features.gsql index 3976d124..7419b919 100644 --- a/financial_crime/application_fraud/queries/batch_application_cc_features.gsql +++ b/financial_crime/application_fraud/queries/batch_application_cc_features.gsql @@ -1,5 +1,19 @@ CREATE OR REPLACE DISTRIBUTED QUERY batch_application_cc_features(INT connections=25000, STRING output_file_path = "/home/tigergraph/gsql_output/batch_application_cc_features.csv") { - +/* + Description: + Runs a batch export of connected-component PII connectivity + features for each Application vertex and writes them to a CSV + file for downstream fraud analytics. + + Parameters: + connections: + Maximum outdegree for a PII vertex when counting shared links; + output_file_path: + CSV Output file path + + Output: + CSV file of application connected components and PII stats +*/ MapAccum @@connect_component, @@distinct_name, @@distinct_dob, @@distinct_email, @@distinct_phone, @@distinct_address, @@distinct_ip, @@distinct_id, @@distinct_device, @@distinct_party, @@distinct_account, @@distinct_card, @@connected_via_name, @@connected_via_dob, @@connected_via_email, @@connected_via_phone, @@connected_via_address, @@connected_via_ip, @@connected_via_id, @@connected_via_device, @@connected_via_party, @@connected_via_account, @@connected_via_card, @@only_connected_via_name, @@only_connected_via_dob, @@only_connected_via_email, @@only_connected_via_phone, @@only_connected_via_address, @@only_connected_via_ip, @@only_connected_via_id, @@only_connected_via_device, @@only_connected_via_party, @@only_connected_via_account, @@only_connected_via_card; OrAccum @connected_via_name, @connected_via_dob, @connected_via_email, @connected_via_phone, @connected_via_address, @connected_via_ip, @connected_via_id, @connected_via_device, @connected_via_party, @connected_via_account, @connected_via_card; SetAccum @middle_type; diff --git a/financial_crime/application_fraud/queries/batch_application_distance_and_path.gsql b/financial_crime/application_fraud/queries/batch_application_distance_and_path.gsql index 8c4aa344..49b22e7c 100644 --- a/financial_crime/application_fraud/queries/batch_application_distance_and_path.gsql +++ b/financial_crime/application_fraud/queries/batch_application_distance_and_path.gsql @@ -1,5 +1,20 @@ CREATE DISTRIBUTED QUERY batch_application_distance_and_path(INT depth=5, STRING output_file_path = "/home/tigergraph/gsql_output/batch_application_distance_and_path_features.csv") { +/* + Description: + For each Application, exports to CSV the shortest-hop distance + and PII-based connection path to any reachable fraudulent + Applications, along with their connected-component ids. + Parameters: + depth: + Maximum number of hops from the source Application when + traversing via PII links. + output_file_path: + Absolute path of the CSV output file. + + Output: + CSV of app–fraud distances and PII paths +*/ MapAccum, VERTEX> @@cc_map; MaxAccum @dis; MaxAccum @visited; diff --git a/financial_crime/application_fraud/queries/delete_all_application_cc_connections.gsql b/financial_crime/application_fraud/queries/delete_all_application_cc_connections.gsql index 3ef93cd8..56d9a61c 100644 --- a/financial_crime/application_fraud/queries/delete_all_application_cc_connections.gsql +++ b/financial_crime/application_fraud/queries/delete_all_application_cc_connections.gsql @@ -1,5 +1,20 @@ CREATE OR REPLACE DISTRIBUTED QUERY delete_all_application_cc_connections(INT num_of_batches = 1, INT batch_id = 0) { +/* + Description: + Deletes Application_In_Ring edges linking Application vertices to + Connected_Component vertices in batches. + Parameters: + num_of_batches: + Total number of partitions to divide Application vertices into. + The query must be executed once per batch_id from 0 to + num_of_batches - 1 to cover all vertices. + batch_id: + Index of the current batch to process (0-based). + Output: + A summary message indicating how many Application_In_Ring + edges were deleted for the current batch. +*/ SumAccum @@count; temp = SELECT t diff --git a/financial_crime/application_fraud/queries/delete_unused_cc_nodes.gsql b/financial_crime/application_fraud/queries/delete_unused_cc_nodes.gsql index 04be56a2..e57b295f 100644 --- a/financial_crime/application_fraud/queries/delete_unused_cc_nodes.gsql +++ b/financial_crime/application_fraud/queries/delete_unused_cc_nodes.gsql @@ -1,5 +1,20 @@ CREATE OR REPLACE DISTRIBUTED QUERY delete_unused_cc_nodes(INT num_of_batches = 1, INT batch_id = 0) { - +/* + Description: + Deletes unused Connected_Component vertices in batches + + Parameters: + num_of_batches: + Total number of partitions to divide Connected_Component vertices + into. The query must be executed once per batch_id from 0 to + num_of_batches - 1 to cover all vertices. + batch_id: + Index of the current batch to process (0-based). + + Output: + Prints a summary message indicating how many unused + Connected_Component vertices were deleted for the current batch. +*/ SumAccum @@count; start = {Connected_Component.*}; diff --git a/financial_crime/application_fraud/queries/distance_and_path_to_fraud_application.gsql b/financial_crime/application_fraud/queries/distance_and_path_to_fraud_application.gsql index 88956c59..0b3dff1f 100644 --- a/financial_crime/application_fraud/queries/distance_and_path_to_fraud_application.gsql +++ b/financial_crime/application_fraud/queries/distance_and_path_to_fraud_application.gsql @@ -1,6 +1,22 @@ CREATE OR REPLACE QUERY distance_and_path_to_fraud_application(VERTEX input, INT depth=5) { +/* + Description: + From an input Application, finds fraudulent Applications reachable + within a given hop limit and returns distance, PII-based path, + and connected-component ids. - MapAccum> @@cc_map; + Parameters: + input: + Source Application vertex. + depth: + Max hop distance from input (default 5). + + Output: + One row per reachable fraud Application with + ids, hop distance, and PII path from input. +*/ + + MapAccum> @@cc_map; MinAccum @dis; OrAccum @visited; SetAccum> @@fraud_app; diff --git a/financial_crime/application_fraud/queries/distance_and_path_to_fraud_application_vis.gsql b/financial_crime/application_fraud/queries/distance_and_path_to_fraud_application_vis.gsql index 32182b2d..d1e986b9 100644 --- a/financial_crime/application_fraud/queries/distance_and_path_to_fraud_application_vis.gsql +++ b/financial_crime/application_fraud/queries/distance_and_path_to_fraud_application_vis.gsql @@ -1,4 +1,22 @@ CREATE OR REPLACE DISTRIBUTED QUERY distance_and_path_to_fraud_application_vis(VERTEX input, INT min_depth = 2, INT max_depth = 5) { + +/* + Description: + From an input Application, finds fraudulent Applications within a hop + range and returns both summary rows and a subgraph for visualization. + + Parameters: + input: + Source Application vertex. + min_depth: + Min hop distance for fraud matches. + max_depth: + Max hop distance from input (default 5). + + Output: + Result sets: fraud rows plus vertices/edges + for visualization. +*/ TYPEDEF TUPLE Edge_Info; MapAccum> @@cc_map; MinAccum @dis; diff --git a/financial_crime/application_fraud/queries/find_shared_piis_of_two_applications.gsql b/financial_crime/application_fraud/queries/find_shared_piis_of_two_applications.gsql index e4f92322..07b3e73c 100644 --- a/financial_crime/application_fraud/queries/find_shared_piis_of_two_applications.gsql +++ b/financial_crime/application_fraud/queries/find_shared_piis_of_two_applications.gsql @@ -1,4 +1,18 @@ CREATE OR REPLACE QUERY find_shared_piis_of_two_applications(VERTEX application_1, VERTEX application_2) { +/* + Description: + Given two Applications, finds shared PII vertices and returns their + type, value, and degree (outdegree). + + Parameters: + application_1: + First Application vertex used to search for shared PII vertices. + application_2: + Second Application vertex used to search for shared PII vertices. + + Output: + Result set of (pii_type, pii_value, degree) for PII shared by both apps. +*/ TYPEDEF TUPLE pii_info; ListAccum @@degrees_of_shared_piis; diff --git a/financial_crime/application_fraud/queries/get_application_cc_features.gsql b/financial_crime/application_fraud/queries/get_application_cc_features.gsql index 5eea27fb..63eaf365 100644 --- a/financial_crime/application_fraud/queries/get_application_cc_features.gsql +++ b/financial_crime/application_fraud/queries/get_application_cc_features.gsql @@ -1,5 +1,19 @@ CREATE OR REPLACE QUERY get_application_cc_features(VERTEX application, INT connections=25000) { - +/* + Description: + For an input Application, computes connected-component–level PII + connectivity features for use in near real-time fraud analysis. + + Parameters: + application: + Source Application whose CC features are + being computed. + connections: + Max PII vertex outdegree when traversing; + + Output: + Summary of application CC size, fraud count, and PII connectivity. + */ MapAccum @@connect_component, @@distinct_name, @@distinct_dob, @@distinct_email, @@distinct_phone, @@distinct_address, @@distinct_ip, @@distinct_id, @@distinct_device, @@distinct_party, @@distinct_account, @@distinct_card, @@connected_via_name, @@connected_via_dob, @@connected_via_email, @@connected_via_phone, @@connected_via_address, @@connected_via_ip, @@connected_via_id, @@connected_via_device, @@connected_via_party, @@connected_via_account, @@connected_via_card, @@only_connected_via_name, @@only_connected_via_dob, @@only_connected_via_email, @@only_connected_via_phone, @@only_connected_via_address, @@only_connected_via_ip, @@only_connected_via_id, @@only_connected_via_device, @@only_connected_via_party, @@only_connected_via_account, @@only_connected_via_card; OrAccum @connected_via_name, @connected_via_dob, @connected_via_email, @connected_via_phone, @connected_via_address, @connected_via_ip, @connected_via_id, @connected_via_device, @connected_via_party, @connected_via_account, @connected_via_card; SetAccum @middle_type; diff --git a/financial_crime/application_fraud/queries/get_num_applications_by_app_status.gsql b/financial_crime/application_fraud/queries/get_num_applications_by_app_status.gsql index 6039ab0a..e491f5a1 100644 --- a/financial_crime/application_fraud/queries/get_num_applications_by_app_status.gsql +++ b/financial_crime/application_fraud/queries/get_num_applications_by_app_status.gsql @@ -1,4 +1,15 @@ CREATE OR REPLACE DISTRIBUTED QUERY get_num_applications_by_app_status () { +/* + Description: + Counts Applications grouped by status + (e.g. PENDING, APPROVED). + + Parameters: + (None) + + Output: + Summary of application counts by status value. +*/ TYPEDEF TUPLE App_Status_Info; ListAccum @@app_status_info_list; MapAccum> @@app_status_count_map; diff --git a/financial_crime/application_fraud/queries/get_num_applications_by_fraud_status.gsql b/financial_crime/application_fraud/queries/get_num_applications_by_fraud_status.gsql index 4b0697c0..38131b2b 100644 --- a/financial_crime/application_fraud/queries/get_num_applications_by_fraud_status.gsql +++ b/financial_crime/application_fraud/queries/get_num_applications_by_fraud_status.gsql @@ -1,4 +1,9 @@ CREATE OR REPLACE DISTRIBUTED QUERY get_num_applications_by_fraud_status () { +/* + Description: + Counts all Application vertices by fraud status and returns the + total number of fraudulent and legitimate applications. +*/ TYPEDEF TUPLE Fraud_Status_Info; ListAccum @@fraud_status_info_list; SumAccum @@num_fraud_applications; diff --git a/financial_crime/application_fraud/queries/get_top_k_connected_components_by_num_applications.gsql b/financial_crime/application_fraud/queries/get_top_k_connected_components_by_num_applications.gsql index 6eaabf47..7cf0358c 100644 --- a/financial_crime/application_fraud/queries/get_top_k_connected_components_by_num_applications.gsql +++ b/financial_crime/application_fraud/queries/get_top_k_connected_components_by_num_applications.gsql @@ -1,6 +1,19 @@ CREATE OR REPLACE DISTRIBUTED QUERY get_top_k_connected_components_by_num_applications ( INT top_k = 10 ) { +/* + Description: + Finds top-k Connected_Component vertices by number of Applications and + returns components, their applications, and edges for visualization. + + Parameters: + top_k: + Number of top components to return + (default 10). + + Output: + Summary of top-k components, apps, and CC edges. +*/ TYPEDEF TUPLE cc_vertex, INT cc_id, INT num_applications> Connected_Component_Info; HeapAccum(top_k, num_applications DESC, cc_id ASC) @@top_cc_heap; SetAccum> @@top_k_connected_components_set; @@ -40,4 +53,4 @@ CREATE OR REPLACE DISTRIBUTED QUERY get_top_k_connected_components_by_num_applic UPDATE DESCRIPTION OF QUERY get_top_k_connected_components_by_num_applications "This query returns the top top_k connected components and their applications, ranked by the number of applications it connects to." -UPDATE DESCRIPTION OF QUERY_PARAM get_top_k_connected_components_by_num_applications.top_k "The number of top connected components we want to return for this query." \ No newline at end of file +UPDATE DESCRIPTION OF QUERY_PARAM get_top_k_connected_components_by_num_applications.top_k "The number of top connected components we want to return for this query." diff --git a/financial_crime/application_fraud/queries/get_top_k_products_by_num_applications.gsql b/financial_crime/application_fraud/queries/get_top_k_products_by_num_applications.gsql index e2664adb..0889d3fb 100644 --- a/financial_crime/application_fraud/queries/get_top_k_products_by_num_applications.gsql +++ b/financial_crime/application_fraud/queries/get_top_k_products_by_num_applications.gsql @@ -3,6 +3,23 @@ CREATE OR REPLACE DISTRIBUTED QUERY get_top_k_products_by_num_applications ( STRING input_application_fraud_status = "", STRING input_application_status = "" ) { +/* + Description: + Finds top-k Products by number of linked Applications, with optional + filters on application fraud status and status. + + Parameters: + top_k: + Number of top Products to return + (default 10). + input_application_fraud_status: + Optional fraud-status filter for + input_application_status: + Optional Application.status filter + + Output: + Summary of top-k Products with application counts and basic attributes. +*/ TYPEDEF TUPLE < VERTEX product_vertex, STRING product_id, @@ -86,4 +103,4 @@ UPDATE DESCRIPTION OF QUERY get_top_k_products_by_num_applications "This query r UPDATE DESCRIPTION OF QUERY_PARAM get_top_k_products_by_num_applications.top_k "The number of top products we want to return for this query." UPDATE DESCRIPTION OF QUERY_PARAM get_top_k_products_by_num_applications.input_application_fraud_status "(Optional) Filter for fraud status of an application. Defaults to '' (no filter)." -UPDATE DESCRIPTION OF QUERY_PARAM get_top_k_products_by_num_applications.input_application_status "(Optional) Filter for the status of an application (case-sensitive). Defaults to '' (no filter)." \ No newline at end of file +UPDATE DESCRIPTION OF QUERY_PARAM get_top_k_products_by_num_applications.input_application_status "(Optional) Filter for the status of an application (case-sensitive). Defaults to '' (no filter)." diff --git a/financial_crime/application_fraud/queries/get_top_k_products_by_num_applications_with_other.gsql b/financial_crime/application_fraud/queries/get_top_k_products_by_num_applications_with_other.gsql index 96bc34c9..c373924e 100644 --- a/financial_crime/application_fraud/queries/get_top_k_products_by_num_applications_with_other.gsql +++ b/financial_crime/application_fraud/queries/get_top_k_products_by_num_applications_with_other.gsql @@ -3,6 +3,22 @@ CREATE OR REPLACE DISTRIBUTED QUERY get_top_k_products_by_num_applications_with_ STRING input_application_fraud_status = "", STRING input_application_status = "" ) { +/* + Description: + Finds top-k Products by number of linked Applications and adds an + aggregated "Other" bucket for all remaining Products. + + Parameters: + top_k: + Number of top Products to return + input_application_fraud_status: + Optional fraud-status filter + input_application_status: + Optional Application.status filter + + Output: + Summary of top-k Products and application counts. +*/ TYPEDEF TUPLE < VERTEX product_vertex, STRING product_id, @@ -112,4 +128,4 @@ UPDATE DESCRIPTION OF QUERY get_top_k_products_by_num_applications_with_other "T UPDATE DESCRIPTION OF QUERY_PARAM get_top_k_products_by_num_applications_with_other.top_k "The number of top products we want to return for this query." UPDATE DESCRIPTION OF QUERY_PARAM get_top_k_products_by_num_applications_with_other.input_application_fraud_status "(Optional) Filter for fraud status of an application. Defaults to '' (no filter)." -UPDATE DESCRIPTION OF QUERY_PARAM get_top_k_products_by_num_applications_with_other.input_application_status "(Optional) Filter for the status of an application (case-sensitive). Defaults to '' (no filter)." \ No newline at end of file +UPDATE DESCRIPTION OF QUERY_PARAM get_top_k_products_by_num_applications_with_other.input_application_status "(Optional) Filter for the status of an application (case-sensitive). Defaults to '' (no filter)." diff --git a/financial_crime/application_fraud/queries/incremental_application_match.gsql b/financial_crime/application_fraud/queries/incremental_application_match.gsql index c68829d4..ab7320ba 100644 --- a/financial_crime/application_fraud/queries/incremental_application_match.gsql +++ b/financial_crime/application_fraud/queries/incremental_application_match.gsql @@ -13,7 +13,25 @@ CREATE OR REPLACE QUERY incremental_application_match( FLOAT Application_Card_weight = 1.0, FLOAT threshold = 1.0 // if a total matching score between two entities exceed the threshold, a similarity edge will be created between the matching entities ) { - +/* + Description: + Parses a JSON payload for one Application, upserts its PII into the + graph, and uses weighted PII similarity to match it into an + existing Connected_Component (if any). + + Parameters: + input: + JSON string for the Application and its PII attributes. + Application_*_weight: + Per-PII weight used when scoring matches + threshold: + Minimum total similarity score required to link into an existing + component. + + Output: + Summary of Application id and whether it was matched into an existing + component. +*/ TYPEDEF TUPLE ver> Connected_Component_heap_tuple; HeapAccum(1, score DESC) @@Connected_Component_ID_heap; SetAccum @@edge_type_set, @@application_vertex, @@name, @@dob, @@email, @@phone_numbers, @@addresses, @@ip, @@ids, @@device, @@party, @@accounts, @@cards; diff --git a/financial_crime/application_fraud/queries/incremental_application_unify.gsql b/financial_crime/application_fraud/queries/incremental_application_unify.gsql index 51566034..b34400f7 100644 --- a/financial_crime/application_fraud/queries/incremental_application_unify.gsql +++ b/financial_crime/application_fraud/queries/incremental_application_unify.gsql @@ -1,4 +1,14 @@ CREATE OR REPLACE QUERY incremental_application_unify(SET> applications) { +/* + Description: + Unifies a given set of Applications into connected components via + Same_Application, updating Connected_Component and Application_In_Ring + in near real time. + + Parameters: + applications: + Set of Application vertices to group into Connected_Components. +*/ MinAccum @cc_id; // Each vertex's tentative component id diff --git a/financial_crime/application_fraud/queries/insights_get_application_count_by_binary_classification.gsql b/financial_crime/application_fraud/queries/insights_get_application_count_by_binary_classification.gsql index a3dd4fde..ce305d1f 100644 --- a/financial_crime/application_fraud/queries/insights_get_application_count_by_binary_classification.gsql +++ b/financial_crime/application_fraud/queries/insights_get_application_count_by_binary_classification.gsql @@ -8,6 +8,11 @@ CREATE OR REPLACE QUERY insights_get_application_count_by_binary_classification INT tg_false_negative, INT tg_false_positive ) { +/* + Description: + Builds a comparison table of binary classification counts for + scenarios without vs. with TigerGraph. +*/ TYPEDEF TUPLE Table_Entry; ListAccum @@application_count_stats; SumAccum @@total_applications_tg; @@ -30,4 +35,4 @@ CREATE OR REPLACE QUERY insights_get_application_count_by_binary_classification @@application_count_stats += Table_Entry("TOTAL", @@total_applications_no_tg, @@total_applications_tg); PRINT @@application_count_stats; -} \ No newline at end of file +} diff --git a/financial_crime/application_fraud/queries/insights_get_binary_classification_ratios.gsql b/financial_crime/application_fraud/queries/insights_get_binary_classification_ratios.gsql index 1545a882..acec4814 100644 --- a/financial_crime/application_fraud/queries/insights_get_binary_classification_ratios.gsql +++ b/financial_crime/application_fraud/queries/insights_get_binary_classification_ratios.gsql @@ -8,6 +8,10 @@ CREATE OR REPLACE QUERY insights_get_binary_classification_ratios ( INT tg_false_negative, INT tg_false_positive ) { +/* + Description: + Computes binary classification metrics for insights application +*/ TYPEDEF TUPLE Table_Entry; ListAccum @@performance_metrics_stats; @@ -26,4 +30,4 @@ CREATE OR REPLACE QUERY insights_get_binary_classification_ratios ( @@performance_metrics_stats += Table_Entry("Negative Predictive Value", no_tg_npv * 100, tg_npv * 100); @@performance_metrics_stats += Table_Entry("True Negative Rate (Specificity)", no_tg_specificity * 100, tg_specificity * 100); PRINT @@performance_metrics_stats; -} \ No newline at end of file +} diff --git a/financial_crime/application_fraud/queries/insights_get_detected_fraud_amount.gsql b/financial_crime/application_fraud/queries/insights_get_detected_fraud_amount.gsql index 30463e6e..b63c9172 100644 --- a/financial_crime/application_fraud/queries/insights_get_detected_fraud_amount.gsql +++ b/financial_crime/application_fraud/queries/insights_get_detected_fraud_amount.gsql @@ -3,6 +3,11 @@ CREATE OR REPLACE QUERY insights_get_detected_fraud_amount ( INT tg_true_positive, DOUBLE avg_loss_per_app_fraud ) { +/* + Description: + Estimates total detected fraud loss for insights application + +*/ TYPEDEF TUPLE Table_Entry; ListAccum @@detected_fraud_stats; @@ -12,4 +17,4 @@ CREATE OR REPLACE QUERY insights_get_detected_fraud_amount ( @@detected_fraud_stats += Table_Entry("Total Detected Fraud (with TigerGraph)", tg_detected_fraud_amount, "Detected Fraud"); @@detected_fraud_stats += Table_Entry("Total Detected Fraud (w/o TigerGraph)", no_tg_detected_fraud_amount, "Detected Fraud"); PRINT @@detected_fraud_stats; -} \ No newline at end of file +} diff --git a/financial_crime/application_fraud/queries/insights_get_missed_fraud_amount.gsql b/financial_crime/application_fraud/queries/insights_get_missed_fraud_amount.gsql index 20641644..5eaaa98d 100644 --- a/financial_crime/application_fraud/queries/insights_get_missed_fraud_amount.gsql +++ b/financial_crime/application_fraud/queries/insights_get_missed_fraud_amount.gsql @@ -3,6 +3,10 @@ CREATE OR REPLACE QUERY insights_get_missed_fraud_amount ( INT tg_false_negative, DOUBLE avg_loss_per_app_fraud ) { +/* + Description: + Estimates total missed fraud loss (in dollars) for insigts application +*/ TYPEDEF TUPLE Table_Entry; ListAccum @@missed_fraud_stats; @@ -12,4 +16,4 @@ CREATE OR REPLACE QUERY insights_get_missed_fraud_amount ( @@missed_fraud_stats += Table_Entry("Total Missed Fraud (with TG)", tg_missed_fraud_amount, "Missed Fraud"); @@missed_fraud_stats += Table_Entry("Total Missed Fraud (w/o TG)", no_tg_missed_fraud_amount, "Missed Fraud"); PRINT @@missed_fraud_stats; -} \ No newline at end of file +} diff --git a/financial_crime/application_fraud/queries/insights_get_net_benefit_amount.gsql b/financial_crime/application_fraud/queries/insights_get_net_benefit_amount.gsql index fc1a757f..ef477ffc 100644 --- a/financial_crime/application_fraud/queries/insights_get_net_benefit_amount.gsql +++ b/financial_crime/application_fraud/queries/insights_get_net_benefit_amount.gsql @@ -5,6 +5,12 @@ CREATE OR REPLACE QUERY insights_get_net_benefit_amount ( INT tg_true_positive, DOUBLE avg_loss_per_app_fraud ) { +/* + Description: + Estimates the net financial benefit of using TigerGraph features + for fraud detection by comparing detected and missed fraud losses + Used in insights application +*/ TYPEDEF TUPLE Table_Entry; ListAccum @@net_benefit_stats; @@ -16,4 +22,4 @@ CREATE OR REPLACE QUERY insights_get_net_benefit_amount ( @@net_benefit_stats += Table_Entry("Net Deduction in Missed Fraud", net_deduction_missed_fraud_amount, "Net Benefit"); @@net_benefit_stats += Table_Entry("Total Net Gain", net_gain_amount, "Net Benefit"); PRINT @@net_benefit_stats; -} \ No newline at end of file +} diff --git a/financial_crime/application_fraud/queries/insights_get_net_gain_numbers_and_percentages.gsql b/financial_crime/application_fraud/queries/insights_get_net_gain_numbers_and_percentages.gsql index e4913641..6f671bdb 100644 --- a/financial_crime/application_fraud/queries/insights_get_net_gain_numbers_and_percentages.gsql +++ b/financial_crime/application_fraud/queries/insights_get_net_gain_numbers_and_percentages.gsql @@ -6,6 +6,14 @@ CREATE OR REPLACE QUERY insights_get_net_gain_numbers_and_percentages ( INT tg_false_positive, INT tg_false_negative ) { +/* + Description: + Compares fraud detection outcomes between scenarios without and + with TigerGraph features and reports: additional fraud blocked, + net reduction in missed fraud, and net reduction in false positives, + both as counts and as percentage improvements. + Used in insights application +*/ INT additional_fraud_blocked = tg_true_positive - no_tg_true_positive; DOUBLE additional_fraud_blocked_pct = (100.0 * additional_fraud_blocked) / no_tg_true_positive; STRING additional_fraud_blocked_pct_str = to_string(round(additional_fraud_blocked_pct, 3)) + " %"; @@ -26,4 +34,4 @@ CREATE OR REPLACE QUERY insights_get_net_gain_numbers_and_percentages ( net_deduction_false_positive, net_deduction_false_positive_pct_str ; -} \ No newline at end of file +} diff --git a/financial_crime/application_fraud/queries/insights_get_relative_precision_recall_gain.gsql b/financial_crime/application_fraud/queries/insights_get_relative_precision_recall_gain.gsql index 1e152630..f1985e7e 100644 --- a/financial_crime/application_fraud/queries/insights_get_relative_precision_recall_gain.gsql +++ b/financial_crime/application_fraud/queries/insights_get_relative_precision_recall_gain.gsql @@ -6,6 +6,14 @@ CREATE OR REPLACE QUERY insights_get_relative_precision_recall_gain ( INT tg_false_positive, INT tg_false_negative ) { +/* + Description: + Computes the relative percentage gain in recall and precision + when using TigerGraph features versus not using them, based on + binary classification outcomes. + Used in insights application + +*/ DOUBLE no_tg_recall = utils_get_recall(no_tg_true_positive, no_tg_false_negative); DOUBLE tg_recall = utils_get_recall(tg_true_positive, tg_false_negative); DOUBLE relative_recall_gain = (tg_recall - no_tg_recall) / no_tg_recall * 100.0; @@ -20,4 +28,4 @@ CREATE OR REPLACE QUERY insights_get_relative_precision_recall_gain ( relative_recall_gain_str, relative_precision_gain_str ; -} \ No newline at end of file +} diff --git a/financial_crime/application_fraud/queries/match_application_entities.gsql b/financial_crime/application_fraud/queries/match_application_entities.gsql index d13eb6eb..3c23012c 100644 --- a/financial_crime/application_fraud/queries/match_application_entities.gsql +++ b/financial_crime/application_fraud/queries/match_application_entities.gsql @@ -17,6 +17,32 @@ CREATE OR REPLACE DISTRIBUTED QUERY match_application_entities( INT pii_high_connections_limit = 25000, // maximum out degree of an attribute. The case will be skipped if the same attribute is connected to too many entities DATETIME compute_entities_after_date = to_datetime("1970-01-01 00:00:00") // All entities that were created in TG after this date, will be computed to find similarity edges related to these new entities. The default value is the earliest time possible (great for the first time running this query) ) { +/* + Description: + Runs batched entity resolution on all Applications using weighted PII + similarity and inserts Same_Application edges for pairs whose score + exceeds a threshold. + + Parameters: + Application_*_weight: + Weights for each PII type + num_of_source_batches: + Number of source-side batches for Applications. + num_of_target_batches: + Number of target-side batches for candidate matches. + threshold: + Minimum similarity score to create Same_Application. + pii_low_connections_limit: + Max total degree for “low-degree” PII candidates. + pii_high_connections_limit: + Max total degree for PII in high-degree phase. + compute_entities_after_date: + Only Applications created after this timestamp are + considered as “new” for similarity computation. + + Output: + Summary of matched pairs and score statistics plus total execution time. +*/ ListAccum> @entity_list; MapAccum,FLOAT> @entity_map; diff --git a/financial_crime/application_fraud/queries/output_application_cc_to_file.gsql b/financial_crime/application_fraud/queries/output_application_cc_to_file.gsql index 919a1f83..7fe1aef7 100644 --- a/financial_crime/application_fraud/queries/output_application_cc_to_file.gsql +++ b/financial_crime/application_fraud/queries/output_application_cc_to_file.gsql @@ -1,4 +1,18 @@ CREATE OR REPLACE DISTRIBUTED QUERY output_application_cc_to_file(STRING output_file_path = "/home/tigergraph/gsql_output/application_cc_output.csv") { +/* + Description: + Exports a CSV mapping each Application vertex to its + Connected_Component via Application_In_Ring edges. + + Parameters: + output_file_path: + Path to the output CSV file for Application → + Connected_Component mappings. + + Output: + CSV file listing Application id and Connected_Component id + for all mapped Applications, plus a status message. +*/ FILE f (output_file_path); diff --git a/financial_crime/application_fraud/queries/set_application_fraud_status.gsql b/financial_crime/application_fraud/queries/set_application_fraud_status.gsql index 21ad83eb..844680db 100644 --- a/financial_crime/application_fraud/queries/set_application_fraud_status.gsql +++ b/financial_crime/application_fraud/queries/set_application_fraud_status.gsql @@ -1,5 +1,5 @@ CREATE OR REPLACE QUERY set_application_fraud_status(VERTEX application, BOOL fraud_status) { - + start = {application}; start = select s from start:s post-accum s.is_fraud = fraud_status; diff --git a/financial_crime/application_fraud/queries/unify_application_entities.gsql b/financial_crime/application_fraud/queries/unify_application_entities.gsql index 7ff9a340..da503ca0 100644 --- a/financial_crime/application_fraud/queries/unify_application_entities.gsql +++ b/financial_crime/application_fraud/queries/unify_application_entities.gsql @@ -1,5 +1,20 @@ CREATE OR REPLACE DISTRIBUTED QUERY unify_application_entities() { - +/* + Description: + Groups all Application vertices in the graph into connected + components via Same_Application edges, creating or updating the + corresponding Connected_Component vertices and Application_In_Ring + edges. + + Parameters: + (None) + + Output: + A single row with: + - execution_time_in_seconds : total query runtime in seconds + - timestamp : end-time timestamp for the unify + operation. +*/ MinAccum @cc_id; // Each vertex's tentative component id DATETIME start_time = now(); diff --git a/financial_crime/application_fraud/queries/utils_get_negative_predictive_value.gsql b/financial_crime/application_fraud/queries/utils_get_negative_predictive_value.gsql index dbf33092..b2e985f3 100644 --- a/financial_crime/application_fraud/queries/utils_get_negative_predictive_value.gsql +++ b/financial_crime/application_fraud/queries/utils_get_negative_predictive_value.gsql @@ -3,4 +3,4 @@ CREATE OR REPLACE QUERY utils_get_negative_predictive_value ( INT false_negative ) RETURNS (DOUBLE) { RETURN (true_negative * 1.0) / (true_negative + false_negative); -} \ No newline at end of file +} From cbce01e74e4feb43b0a0852594c33a23f81bb49b Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Mon, 8 Dec 2025 11:28:09 +0530 Subject: [PATCH 24/55] removed non ASCII characters --- .../entity_resolution_kyc/README.md | 108 ++++ ...hts_Application_Entity_Resolution_kyc.json | 488 ++++++++++++++++++ 2 files changed, 596 insertions(+) create mode 100644 financial_crime/entity_resolution_kyc/README.md create mode 100644 financial_crime/entity_resolution_kyc/meta/Insights_Application_Entity_Resolution_kyc.json diff --git a/financial_crime/entity_resolution_kyc/README.md b/financial_crime/entity_resolution_kyc/README.md new file mode 100644 index 00000000..a0768478 --- /dev/null +++ b/financial_crime/entity_resolution_kyc/README.md @@ -0,0 +1,108 @@ +## Narratives: Enhanced KYC and Fraud Linkage + +Accurate **Entity Resolution (ER)** is the foundation of effective Know Your Customer (KYC) compliance and financial crime detection. Traditional systems often fail to link scattered customer records, leading to gaps in risk profiles. This TigerGraph solution addresses this by: + +* **Establishing a Single Customer View (SCV):** By clustering all PII-linked records (via the **`Same_As`** edge) into a single **`Connected_Component`**, the solution creates an authoritative view of the customer, crucial for compliance. +* **Weighted Weakly Connected Components (WCC):** We use an approximate **weighted matching algorithm** where shared attributes (Email, Phone, Name, etc.) contribute a customizable **score** to the total match. This provides a high-fidelity, tunable mechanism for linking parties. +* **Real-Time Fraud Linkage:** Any new Party is instantly matched against existing entities using `incremental_party_match`. If a match is found and the existing entity belongs to a component linked to a known fraudulent party, the new entity is immediately flagged, drastically reducing risk exposure. +* **Feature Engineering for Downstream ML:** The resulting `Connected_Component` structure allows for powerful feature extraction, providing machine learning models with metrics like "Count of Parties in Component" or "Shortest Path to Known Fraudulent Party." + +*** + +## Components + +This repository is structured to provide a comprehensive, ready-to-deploy graph solution. + +| Component | Description | +| :--- | :--- | +| `Schema` | Definition of the database schema, focusing on the **`Party`** vertex, PII, and the `Connected_Component` structure (18 core vertices). | +| `Queries` | A collection of **GSQL queries** for matching, unification, feature engineering, and real-time lookup. | +| `Mock Data` | Sample data for Party, PII, and relationship loading. | +| `Loading Jobs` | Two loading jobs: one for cloud integration and the other for on-prem local deployment. | +| `README.md` | This usage guide. | +| `setup.sh` | Automated setup script for schema creation, loading, and query installation. | + +*** + +## ML Features: ER-Derived Features for Risk Scoring + +The structure created by the Entity Resolution process generates powerful features for risk scoring and identifying high-risk entities. + +1. **Community Size :** + * **Feature:** The total count of **`Party`** vertices belonging to the same **`Connected_Component`**. + * **Insight:** Larger components may indicate potential fraud rings, as numerous distinct records share PII/attributes. + * **Query Source:** `batch_party_cc_features` or `get_party_cc_features`. + +2. **Shortest Path Length to Fraud:** + * **Feature:** The shortest path length from a given **`Party`** or **`Connected_Component`** to any known **fraudulent Party** (`Party.is_fraud = 1`). + * **Insight:** Direct proximity (short path) is a primary risk metric for linkage analysis. + * **Query Source:** `batch_party_distance_and_path` or `distance_and_path_to_fraud_party`. + +3. **PII Overlap Count:** + * **Feature:** The accumulated weighted score of matching PII attributes (Full Name, DOB, Email, etc.) between a target Party and other Parties in the graph. + * **Insight:** Quantifies the **strength** and **type** of common links that resulted in the entity match. + * **Query Source:** Calculated within `incremental_party_match` and `match_party_entities`. + +4. **PII Multi-Party Degree:** + * **Feature:** The out-degree of a critical PII vertex (e.g., `Email`, `Phone`) that is shared by multiple distinct parties. + * **Insight:** High-degree PIIs (like an IP address used by many accounts) are strong indicators of shared risk or a single fraud operation. + +5. **Linked Asset Count:** + * **Feature:** The number of unique `Account`, `Card`, and `Application` vertices linked to the Party's **`Connected_Component`**. + * **Insight:** Assesses the total scope and exposure of the consolidated customer view. + +*** + +## Instructions + +### Setup and Installation + +1. **Schema Creation:** Run the general schema creation script, followed by the specific ER/KYC schema script. +2. **Data Loading:** Load data into the schema by running the data loading job script (`local_loading_job.gsql`). +3. **Query Installation:** Install all **15 GSQL queries** by executing the `install_queries.sh` script. + +### Query Execution Order and Explanations + +The solution requires a sequential execution of the batch queries to populate the core ER structure. + +#### Step 1: Graph Cleanup (Maintenance) + +*(Optional, but highly recommended before a fresh batch run)* + +| Query | Purpose | +| :--- | :--- | +| **`delete_all_party_cc_connections`** | Deletes old `Entity_In_Ring` edges to clear previous clustering results. | +| **`delete_unused_cc_nodes`** | Removes `Connected_Component` vertices that no longer link to any `Party`. | + +#### Step 2: Batch Entity Resolution (Matching and Unification) + +This is the core process to establish the links and the clusters. + +| Query | Purpose | Resulting Edges/Vertices | +| :--- | :--- | :--- | +| **`match_party_entities`** | Calculates the weighted similarity score between all potential Party pairs and inserts the **`Same_As`** edge if the score exceeds the `threshold`. | Inserts **`Same_As`** edges | +| **`unify_party_entities`** | Runs the Connected Components algorithm based on all existing `Same_As` edges. | Creates **`Connected_Component`** vertices; Inserts **`Entity_In_Ring`** edges | + +#### Step 3: Feature Engineering (Batch Calculation) + +These queries run after Step 2 is complete. They calculate the ML features and output the results. + +| Query | Purpose | Output/Target | +| :--- | :--- | :--- | +| **`batch_party_cc_features`** | Calculates structural features (e.g., CC size) for every Party in the graph. | Writes results to output file. | +| **`batch_party_distance_and_path`** | Finds the shortest path and minimum distance from all parties to known fraud (`Party.is_fraud = 1`). | Writes results to output file. | +| **`output_party_cc_to_file`** | Exports the final Party-to-Connected\_Component mapping. | Writes mapping to CSV file. | + +#### Step 4: Real-Time / Incremental Queries + +These queries are used for real-time lookups and transactional updates. + +| Query | Purpose | Type | +| :--- | :--- | :--- | +| **`incremental_party_match`** | Inserts a new Party and attempts to match it to an existing `Connected_Component`. | Real-Time Insert/Match | +| **`incremental_party_unify`** | Creates a new `Connected_Component` for a Party that did not match any existing component. | Real-Time Unification | +| **`set_party_fraud_status`** | Updates the `is_fraud` attribute for a given Party. | Real-Time Update | +| **`get_party_cc_features`** | Retrieves pre-computed features for a single Party. | Real-Time Lookup | +| **`distance_and_path_to_fraud_party`** | Retrieves the path to fraud for a single Party. | Investigation/Lookup | +| **`find_shared_piis_of_two_parties`** | Finds the specific PII attributes that connect any two parties. | Investigation/Lookup | + diff --git a/financial_crime/entity_resolution_kyc/meta/Insights_Application_Entity_Resolution_kyc.json b/financial_crime/entity_resolution_kyc/meta/Insights_Application_Entity_Resolution_kyc.json new file mode 100644 index 00000000..c1740344 --- /dev/null +++ b/financial_crime/entity_resolution_kyc/meta/Insights_Application_Entity_Resolution_kyc.json @@ -0,0 +1,488 @@ +{ + "defaultGraph": "AntiFraud", + "iconURL": "/insights/static/media/atom.14f5dd297b1a450cae3413a44f69a75b.svg", + "id": "arDCiwHPNbgkqU51x1tBNY", + "pageConfigSeparated": true, + "pages": [ + { + "chartMap": { + "2g1ThadQg96eQw9vWvjWKc": { + "chartSettings": { + "rulesByType": { + "Party": [ + { + "condition": "=", + "conditionEndValue": 1, + "conditionStartValue": 0, + "conditionValue": "1", + "fieldName": "is_fraud", + "fieldType": "number", + "palateName": "", + "styleEndLabel": "", + "styleEndValue": "", + "styleKey": "background-color", + "styleLabel": "Vertex color", + "styleStartLabel": "", + "styleStartValue": "", + "styleType": "color", + "styleValue": "#ff0000" + } + ] + } + }, + "graphName": "Entity_Resolution_KYC", + "hideWidgetName": false, + "id": "2g1ThadQg96eQw9vWvjWKc", + "patternLimit": 5, + "query": "INTERPRET QUERY(VERTEX componentId) FOR GRAPH Entity_Resolution_KYC {\n src = {componentId};\n SetAccum @@edge;\n SetAccum @@node;\n res = \n SELECT s FROM\n src:s - (:e1) - Party:p - (:e2) - (Address|Phone):t \n ACCUM @@edge += e1,@@edge += e2,@@node += s,@@node += p,@@node += t\n ;\n PRINT @@edge;\n \n nodes = {@@node};\n PRINT nodes;\n}", + "queryType": "interactive", + "refreshRate": 0, + "searchPattern": [], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "Connected Component Graph", + "type": "internal-graph", + "version": "1764696139513871285" + }, + "2zfNGnX5qw4YvjKuguE776": { + "chartSettings": { + "values": [ + { + "fontColor": "#000000", + "fontSize": 32, + "iconColor": "#000000", + "iconPostion": "", + "iconSize": 24, + "id": "h48iV6XssVA3mGqRSn3dVa", + "key": "party", + "label": "Total Parties", + "labelColor": "#000000", + "labelPostion": "", + "labelSize": 20, + "styleRule": [] + }, + { + "fontColor": "#000000", + "fontSize": 32, + "iconColor": "#000000", + "iconPostion": "", + "iconSize": 24, + "id": "1D9rXhSo9qv3jjqj6S7Z59", + "key": "active_address", + "label": "Active Address", + "labelColor": "#000000", + "labelPostion": "", + "labelSize": 20, + "styleRule": [] + }, + { + "fontColor": "#000000", + "fontSize": 32, + "iconColor": "#000000", + "iconPostion": "", + "iconSize": 24, + "id": "jJhtv54FHCFSaoUZD7Bcy7", + "key": "connected_component", + "label": "Connected Component", + "labelColor": "#000000", + "labelPostion": "", + "labelSize": 20, + "styleRule": [] + }, + { + "fontColor": "#000000", + "fontSize": 32, + "iconColor": "#000000", + "iconPostion": "", + "iconSize": 24, + "id": "uh2uw457meuyBNT7TnVhyW", + "key": "risk_alert", + "label": "Risk Alert", + "labelColor": "#000000", + "labelPostion": "", + "labelSize": 20, + "styleRule": [] + } + ] + }, + "graphName": "Entity_Resolution_KYC", + "hideWidgetName": false, + "id": "2zfNGnX5qw4YvjKuguE776", + "patternLimit": 5, + "query": "INTERPRET QUERY() FOR GRAPH Entity_Resolution_KYC {\n res = \n SELECT s from Party:s\n ;\n PRINT res.size() as party;\n \n res = SELECT s from\n Address:s - () - Party:p;\n PRINT res.size() as active_address;\n \n res = SELECT s FROM Connected_Component:s - () - Party:p;\n PRINT res.size() as connected_component;\n \n res = SELECT s FROM Connected_Component:s - () - Party:p WHERE p.is_fraud > 0;\n PRINT res.size() as risk_alert;\n}", + "queryType": "interactive", + "refreshRate": 0, + "searchPattern": [], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "", + "type": "value", + "version": "1764693452951940468" + }, + "6Yj4WGLfhs5iobubJJAgza": { + "chartSettings": { + "category": [ + { + "id": "typ", + "type": "string" + } + ], + "radius": [ + 0, + 71 + ], + "tableHeaders": [ + "cnt", + "typ" + ], + "tableIndex": 0, + "value": [ + { + "id": "cnt", + "type": "number" + } + ] + }, + "graphName": "Entity_Resolution_KYC", + "hideWidgetName": false, + "id": "6Yj4WGLfhs5iobubJJAgza", + "patternLimit": 5, + "query": "INTERPRET QUERY() FOR GRAPH Entity_Resolution_KYC {\n GroupByAccum cnt> @@grp;\n SumAccum @connCount;\n \n parties = SELECT p FROM Party:p\n ACCUM p.@connCount = p.outdegree()\n POST-ACCUM\n CASE \n WHEN p.@connCount <= 1 THEN @@grp += (\"Single Entity\"->1)\n WHEN p.@connCount <= 5 THEN @@grp += (\"2-5 Connections\"->1)\n WHEN p.@connCount <= 10 THEN @@grp += (\"6-10 Connections\"->1)\n ELSE @@grp += (\"11+ Connections\"->1)\n END;\n \n PRINT @@grp;\n}", + "queryType": "interactive", + "refreshRate": 0, + "searchPattern": [], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "Connection Distribution", + "type": "pie", + "version": "1764696420610182024" + }, + "aZsdFDfkYXbLqore5CeS77": { + "chartSettings": { + "category": [ + { + "id": "entity", + "type": "string" + } + ], + "tableHeaders": [ + "cnt", + "entity" + ], + "tableIndex": 0, + "value": [ + { + "id": "cnt", + "type": "number" + } + ] + }, + "graphName": "Entity_Resolution_KYC", + "hideWidgetName": false, + "id": "aZsdFDfkYXbLqore5CeS77", + "patternLimit": 5, + "query": "INTERPRET QUERY() FOR GRAPH Entity_Resolution_KYC {\n GroupByAccum cnt> @@grp;\n \n partySrc = \n SELECT s FROM Party:s ACCUM @@grp += (\"Party\"->1)\n ;\n res = \n SELECT t from partySrc:s - () - (Address|Phone|Email|Device|IP):t \n POST-ACCUM @@grp += (t.type->1)\n ;\n PRINT @@grp;\n}", + "queryType": "interactive", + "refreshRate": 0, + "searchPattern": [], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "Entity Type Distribution", + "type": "bar", + "version": "1764695406058758862" + }, + "kSo7qKu14XSMP6mQCjryv1": { + "chartSettings": { + "showColumns": [ + { + "isChecked": false, + "name": "v_id" + }, + { + "isChecked": false, + "name": "v_type" + }, + { + "isChecked": true, + "name": "Address" + }, + { + "isChecked": true, + "name": "ComponentId" + }, + { + "isChecked": true, + "name": "Parties" + }, + { + "isChecked": true, + "name": "Phone" + }, + { + "isChecked": true, + "name": "Risk" + } + ], + "sortedColumns": [ + "ComponentId", + "Parties", + "Address", + "Phone", + "Risk", + "Action" + ], + "tableActions": [ + { + "pageID": "viZkpMCa5wFYgruGR9JSWy", + "params": [ + { + "isCreatable": false, + "name": "componentId", + "paramGlobalInput": "", + "value": "ComponentId" + } + ], + "text": "click", + "url": "", + "urlName": "New Page" + } + ], + "tableColumns": [ + { + "id": "Matched pattern", + "isChecked": "true", + "label": "" + }, + { + "children": [ + { + "children": [ + { + "id": "phone_number", + "isChecked": "true", + "type": "vertex" + } + ], + "id": "Phone", + "isChecked": "true", + "type": "vertex" + }, + { + "children": [ + { + "id": "address", + "isChecked": "true", + "type": "vertex" + } + ], + "id": "Address", + "isChecked": "true", + "type": "vertex" + } + ], + "id": "Vertices", + "isChecked": "true", + "isExpanded": true, + "label": "Vertices" + }, + { + "children": [], + "id": "Edges", + "isChecked": "true", + "isExpanded": false, + "label": "Edges" + } + ], + "tableHeaders": [ + "v_id", + "v_type", + "Address", + "ComponentId", + "Parties", + "Phone", + "Risk" + ] + }, + "graphName": "Entity_Resolution_KYC", + "hideWidgetName": false, + "id": "kSo7qKu14XSMP6mQCjryv1", + "patternLimit": 5, + "query": "INTERPRET QUERY() FOR GRAPH Entity_Resolution_KYC {\n SumAccum @parties,@address,@phone,@risk;\n res = \n SELECT s FROM\n Connected_Component:s - () - Party:p - () - (Address|Phone):t\n ACCUM\n CASE WHEN t.type == \"Address\" THEN s.@address += 1 ELSE t.@phone += 1 END\n //CASE WHEN p.is_fraud > 0 THEN s.@risk += 1 END\n POST-ACCUM s.@parties += s.outdegree(\"Entity_In_Ring\"),s.@risk += sum(s.neighborAttribute( \"Entity_In_Ring\", \"Party\", \"is_fraud\" ))\n\n\n \n ORDER BY s.@parties DESC\n ;\n PRINT res[res.id as ComponentId,res.@parties as Parties,res.@address as Address,res.@phone as Phone,res.@risk as Risk];\n}", + "queryType": "pattern", + "refreshRate": 0, + "searchPattern": [ + { + "data": "Top_Connected_Component", + "id": "Top_Connected_Component", + "type": "QUERY" + } + ], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "Top Connected Component", + "type": "table", + "version": "1764697101443443105" + }, + "pWVjbPx54GYQyYYTHe8Qpj": { + "chartSettings": { + "inputStates": [ + { + "dataType": "vertex", + "id": "input_rjFCLCFNBMSX1tGBx9rHuK", + "name": "componentId", + "selectedGraph": "Entity_Resolution_KYC", + "settings": { + "fixVertexType": true + }, + "vertexType": "Connected_Component", + "widgetType": "Input" + } + ] + }, + "graphName": "AntiFraud", + "hideWidgetName": false, + "id": "pWVjbPx54GYQyYYTHe8Qpj", + "patternLimit": 5, + "query": "", + "queryType": "pattern", + "refreshRate": 0, + "searchPattern": [], + "staticData": "[\n {\n \"key\": \"value\"\n }\n]", + "title": "New Widget", + "type": "Inputs", + "version": "1764696049224336494" + } + }, + "globalParameters": { + "componentId": { + "id": "input_rjFCLCFNBMSX1tGBx9rHuK", + "name": "componentId", + "type": "VERTEX", + "value": { + "vertexID": "1678770176", + "vertexType": "Connected_Component" + } + } + }, + "iconURL": "/insights/static/media/library.d3f7f207c6bb1d7be8e64045a19991b2.svg", + "id": "viZkpMCa5wFYgruGR9JSWy", + "isDetail": true, + "isNew": false, + "layouts": { + "md": [ + { + "h": 33, + "i": "2g1ThadQg96eQw9vWvjWKc", + "moved": false, + "static": false, + "w": 7, + "x": 5, + "y": 28 + }, + { + "h": 8, + "i": "2zfNGnX5qw4YvjKuguE776", + "moved": false, + "static": false, + "w": 12, + "x": 0, + "y": 0 + }, + { + "h": 20, + "i": "6Yj4WGLfhs5iobubJJAgza", + "moved": false, + "static": false, + "w": 5, + "x": 0, + "y": 8 + }, + { + "h": 20, + "i": "aZsdFDfkYXbLqore5CeS77", + "moved": false, + "static": false, + "w": 7, + "x": 5, + "y": 8 + }, + { + "h": 33, + "i": "kSo7qKu14XSMP6mQCjryv1", + "moved": false, + "static": false, + "w": 5, + "x": 0, + "y": 28 + }, + { + "h": 12, + "i": "pWVjbPx54GYQyYYTHe8Qpj", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 61 + } + ], + "xs": [ + { + "h": 8, + "i": "2zfNGnX5qw4YvjKuguE776", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 0 + }, + { + "h": 20, + "i": "6Yj4WGLfhs5iobubJJAgza", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 8 + }, + { + "h": 20, + "i": "aZsdFDfkYXbLqore5CeS77", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 28 + }, + { + "h": 33, + "i": "kSo7qKu14XSMP6mQCjryv1", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 48 + }, + { + "h": 25, + "i": "2g1ThadQg96eQw9vWvjWKc", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 81 + }, + { + "h": 12, + "i": "pWVjbPx54GYQyYYTHe8Qpj", + "moved": false, + "static": false, + "w": 4, + "x": 0, + "y": 106 + } + ] + }, + "title": "Entity Network Dashboard", + "version": "1764696199602489941", + "weight": 20 + } + ], + "title": "Entity_Resolution_KYC", + "userRoleForApp": "owner", + "version": "1764673492229672678" +} \ No newline at end of file From 60e72428bdcfddb4a4201170c378421a59252400 Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Mon, 8 Dec 2025 12:37:15 +0530 Subject: [PATCH 25/55] standardized query descriptions for connected customer and added some queries to entity_resolution_kyc --- .../queries/batch_party_cc_features.gsql | 21 +++++- .../batch_party_distance_and_path.gsql | 22 +++++- .../delete_all_party_cc_connections.gsql | 22 +++++- .../queries/delete_unused_cc_nodes.gsql | 22 +++++- .../distance_and_path_to_fraud_party.gsql | 21 +++++- ...ed_component_entities_as_same_as_edge.gsql | 51 ++++++++++++++ ..._connected_components_to_entities_vis.gsql | 64 ++++++++++++++++++ .../queries/explore_er_steps.gsql | 67 +++++++++++++++++++ .../find_shared_piis_of_two_parties.gsql | 23 ++++++- .../queries/get_party_cc_features.gsql | 21 +++++- .../queries/get_party_fraud_status.gsql | 21 +++++- .../queries/incremental_party_match.gsql | 21 +++++- .../queries/incremental_party_unify.gsql | 24 ++++++- .../queries/match_party_entities.gsql | 24 ++++++- .../queries/output_party_cc_to_file.gsql | 23 ++++++- .../queries/top_connected_component.gsql | 34 ++++++++++ .../queries/unify_party_entities.gsql | 23 ++++++- 17 files changed, 490 insertions(+), 14 deletions(-) create mode 100644 financial_crime/entity_resolution_kyc/queries/expand_connected_component_entities_as_same_as_edge.gsql create mode 100644 financial_crime/entity_resolution_kyc/queries/expand_connected_components_to_entities_vis.gsql create mode 100644 financial_crime/entity_resolution_kyc/queries/explore_er_steps.gsql create mode 100644 financial_crime/entity_resolution_kyc/queries/top_connected_component.gsql diff --git a/financial_crime/entity_resolution_kyc/queries/batch_party_cc_features.gsql b/financial_crime/entity_resolution_kyc/queries/batch_party_cc_features.gsql index f4acba91..c0bcc7d2 100644 --- a/financial_crime/entity_resolution_kyc/queries/batch_party_cc_features.gsql +++ b/financial_crime/entity_resolution_kyc/queries/batch_party_cc_features.gsql @@ -1,5 +1,24 @@ CREATE OR REPLACE DISTRIBUTED QUERY batch_party_cc_features(INT connections=25000, STRING output_file_path = "/home/tigergraph/gsql_output/batch_party_cc_features.csv") { + /* + Query Name: batch_party_cc_features + + Purpose: Generate Machine Learning Features for Fraud Detection + This query traverses the graph to extract aggregated features from the Connected Component (CC) + that each 'Party' belongs to, focusing on shared Personally Identifiable Information (PII) + and other related entities (Accounts, Cards, etc.). + + Key Use Cases: + • Feature Engineering: Create robust, graph-based variables to train a fraud prediction model. + • Ring Detection: Identify 'Parties' that are linked via shared PII, often indicative of fraud rings. + • Risk Profiling: Assess the risk of a Party based on the size and fraudulence of its community. + + Parameters: + connections: Upper limit for the out-degree of PII/Entity nodes (e.g., limit a shared IP address + to prevent overly broad, non-specific connections, often called 'Super-Node' filtering). + output_file_path: Location to save the resulting CSV file with all features. + */ + MapAccum @@connect_component, @@distinct_name, @@distinct_dob, @@distinct_email, @@distinct_phone, @@distinct_address, @@distinct_ip, @@distinct_id, @@distinct_device, @@distinct_party, @@distinct_account, @@distinct_card, @@connected_via_name, @@connected_via_dob, @@connected_via_email, @@connected_via_phone, @@connected_via_address, @@connected_via_ip, @@connected_via_id, @@connected_via_device, @@connected_via_party, @@connected_via_account, @@connected_via_card, @@only_connected_via_name, @@only_connected_via_dob, @@only_connected_via_email, @@only_connected_via_phone, @@only_connected_via_address, @@only_connected_via_ip, @@only_connected_via_id, @@only_connected_via_device, @@only_connected_via_party, @@only_connected_via_account, @@only_connected_via_card; OrAccum @connected_via_name, @connected_via_dob, @connected_via_email, @connected_via_phone, @connected_via_address, @connected_via_ip, @connected_via_id, @connected_via_device, @connected_via_party, @connected_via_account, @connected_via_card; SetAccum @middle_type; @@ -126,4 +145,4 @@ UPDATE DESCRIPTION OF QUERY batch_party_cc_features "This query outputs a file c UPDATE DESCRIPTION OF QUERY_PARAM batch_party_cc_features.connections "Maximum outdegree of each PII vertex allowed in search used to filter out hub nodes. Defaults to 25000." -UPDATE DESCRIPTION OF QUERY_PARAM batch_party_cc_features.output_file_path "File path location to containing the output graph features in CSV format. Defaults to /home/tigergraph/gsql_output/batch_party_cc_features.csv" \ No newline at end of file +UPDATE DESCRIPTION OF QUERY_PARAM batch_party_cc_features.output_file_path "File path location to containing the output graph features in CSV format. Defaults to /home/tigergraph/gsql_output/batch_party_cc_features.csv" diff --git a/financial_crime/entity_resolution_kyc/queries/batch_party_distance_and_path.gsql b/financial_crime/entity_resolution_kyc/queries/batch_party_distance_and_path.gsql index 2c1ff471..4d71c1b1 100644 --- a/financial_crime/entity_resolution_kyc/queries/batch_party_distance_and_path.gsql +++ b/financial_crime/entity_resolution_kyc/queries/batch_party_distance_and_path.gsql @@ -1,5 +1,25 @@ CREATE DISTRIBUTED QUERY batch_party_distance_and_path(INT depth=5, STRING output_file_path = "/home/tigergraph/gsql_output/batch_party_distance_and_path_features.csv") { + /* + Query Name: batch_party_distance_and_path + Fraudulent Party Linkage Analysis (Shortest Path & Distance) + + Purpose: Graph Linkage Feature Generation + This query is run for *every* 'Party' in the graph (a batch process) to: + 1. Find the shortest path and distance (in hops) to any other 'Party' that is marked as **fraudulent**. + 2. Record the precise path of shared PII/Entities that connects the two. + 3. Generate a feature set that shows the degree of separation between a Party and known fraud. + + Key Use Cases: + • Feature Engineering: Create "Distance-to-Fraud" and "Path-Type" features for ML models. + • Risk Prioritization: Identify non-fraudulent parties (is_fraud=0) that are only 1-2 hops away from known fraud. + • Investigation: Quickly trace the PII/Entity linkage for high-risk individuals. + + Parameters: + depth: Maximum traversal depth (number of hops) for the search. Limits search time and focuses on close connections. + output_file_path: Location to save the resulting CSV file. + */ + MapAccum, VERTEX> @@cc_map; MaxAccum @dis; MaxAccum @visited; @@ -73,4 +93,4 @@ UPDATE DESCRIPTION OF QUERY batch_party_distance_and_path "This query outputs a UPDATE DESCRIPTION OF QUERY_PARAM batch_party_distance_and_path.depth "Maximum number of hops from source Party vertex to traverse in the graph. Defaults to 5." -UPDATE DESCRIPTION OF QUERY_PARAM batch_party_distance_and_path.output_file_path "File path location to containing the output graph features in CSV format. Defaults to /home/tigergraph/gsql_output/batch_party_distance_and_path_features.csv" \ No newline at end of file +UPDATE DESCRIPTION OF QUERY_PARAM batch_party_distance_and_path.output_file_path "File path location to containing the output graph features in CSV format. Defaults to /home/tigergraph/gsql_output/batch_party_distance_and_path_features.csv" diff --git a/financial_crime/entity_resolution_kyc/queries/delete_all_party_cc_connections.gsql b/financial_crime/entity_resolution_kyc/queries/delete_all_party_cc_connections.gsql index 8c9cb643..47dcee6d 100644 --- a/financial_crime/entity_resolution_kyc/queries/delete_all_party_cc_connections.gsql +++ b/financial_crime/entity_resolution_kyc/queries/delete_all_party_cc_connections.gsql @@ -1,5 +1,25 @@ CREATE OR REPLACE DISTRIBUTED QUERY delete_all_party_cc_connections(INT num_of_batches = 1, INT batch_id = 0) { + /* + Query Name: delete_all_party_cc_connections + Graph Maintenance: Clear Connected Component (CC) Assignments + + Purpose: Preparation for Community Detection + This query's sole purpose is to efficiently **delete** all existing `Entity_In_Ring` edges + that link `Party` vertices to their associated `Connected_Component` vertices. This action is + a required precursor before running a fresh community detection algorithm (like a new Connected + Component run) to ensure the graph features are calculated based on the latest community structure. + + Key Concept: Batch Processing + The query is designed to run in distributed, manageable batches (`num_of_batches` and `batch_id`) + to prevent memory/resource exhaustion when deleting a massive number of edges (millions or billions) + in a single transaction. + + Parameters: + num_of_batches: Total number of jobs required to process all `Party` vertices. + batch_id: The specific partition of `Party` vertices to process in the current run (0 to num_of_batches - 1). + */ + SumAccum @@count; temp = SELECT t @@ -18,4 +38,4 @@ UPDATE DESCRIPTION OF QUERY delete_all_party_cc_connections "This query deletes UPDATE DESCRIPTION OF QUERY_PARAM delete_all_party_cc_connections.num_of_batches "Number of batches to partition the deletions for all Party vertices in the graph. This query must be called the same number of times as batch_num with incrementing batch_id 0 through batch_num - 1 to process all Party vertices in the graph. Defaults to 1." -UPDATE DESCRIPTION OF QUERY_PARAM delete_all_party_cc_connections.batch_id "Current batch partition of Party vertices to process and must be called with batch_id 0 through batch_num - 1. Defaults to 0." \ No newline at end of file +UPDATE DESCRIPTION OF QUERY_PARAM delete_all_party_cc_connections.batch_id "Current batch partition of Party vertices to process and must be called with batch_id 0 through batch_num - 1. Defaults to 0." diff --git a/financial_crime/entity_resolution_kyc/queries/delete_unused_cc_nodes.gsql b/financial_crime/entity_resolution_kyc/queries/delete_unused_cc_nodes.gsql index 98705745..75810467 100644 --- a/financial_crime/entity_resolution_kyc/queries/delete_unused_cc_nodes.gsql +++ b/financial_crime/entity_resolution_kyc/queries/delete_unused_cc_nodes.gsql @@ -1,4 +1,24 @@ CREATE OR REPLACE DISTRIBUTED QUERY delete_unused_cc_nodes(INT num_of_batches = 1, INT batch_id = 0) { + /* + Query Name: delete_unused_cc_nodes + Graph Maintenance: Prune Unused Connected Component (CC) Vertices + + Purpose: Cleanup and Optimization + This query is designed to clean up the graph by **deleting** any `Connected_Component` (CC) + vertex that no longer serves a purpose. After running a community detection algorithm and then + deleting the assignment edges (e.g., using `delete_all_party_cc_connections`), some CC nodes + might remain but have no connections to any `Party` vertices. Deleting these "orphaned" nodes + reduces graph size, improves query performance, and maintains graph integrity. + + Key Concept: Orphaned Node Removal + A CC node is considered 'unused' or 'orphaned' if its **out-degree is zero** (it has no outgoing edges). + Since the CC node is only supposed to have outgoing edges to `Party` vertices, an out-degree of zero + means it is not linked to any entities and can be safely removed. + + Parameters: + num_of_batches: Total number of jobs required to process all `Connected_Component` vertices. + batch_id: The specific partition of `Connected_Component` vertices to process in the current run. + */ SumAccum @@count; @@ -20,4 +40,4 @@ UPDATE DESCRIPTION OF QUERY delete_unused_cc_nodes "This query deletes all unuse UPDATE DESCRIPTION OF QUERY_PARAM delete_unused_cc_nodes.num_of_batches "Number of batches to partition the deletions for all Connected_Component vertices in the graph. This query must be called the same number of times as batch_num with incrementing batch_id 0 through batch_num - 1 to process all Connected_Component vertices in the graph. Defaults to 1." -UPDATE DESCRIPTION OF QUERY_PARAM delete_unused_cc_nodes.batch_id "Current batch partition of Connected_Component vertices to process and must be called with batch_id 0 through batch_num - 1. Defaults to 0." \ No newline at end of file +UPDATE DESCRIPTION OF QUERY_PARAM delete_unused_cc_nodes.batch_id "Current batch partition of Connected_Component vertices to process and must be called with batch_id 0 through batch_num - 1. Defaults to 0." diff --git a/financial_crime/entity_resolution_kyc/queries/distance_and_path_to_fraud_party.gsql b/financial_crime/entity_resolution_kyc/queries/distance_and_path_to_fraud_party.gsql index 5dee6779..15172301 100644 --- a/financial_crime/entity_resolution_kyc/queries/distance_and_path_to_fraud_party.gsql +++ b/financial_crime/entity_resolution_kyc/queries/distance_and_path_to_fraud_party.gsql @@ -1,4 +1,23 @@ CREATE OR REPLACE QUERY distance_and_path_to_fraud_party(VERTEX input, INT depth=5) { + /* + Query Name: distance_and_path_to_fraud_party + Real-Time Shortest Path to Fraudulent Parties + + Purpose: Real-Time Risk Assessment + This query is designed for near **real-time execution**, performing a single Breadth-First Search (BFS) + starting from a specific `Party` (e.g., a new applicant). Its goal is to quickly find the shortest + distance and the precise chain of shared PII/Entities linking the input Party to any known + `Fraud Party` within a specified hop `depth`. + + Key Use Cases: + • Transaction Monitoring: Identify if a customer is closely linked to fraud before approving a transaction. + • New Application Scoring: Immediately flag new applicants connected to high-risk communities. + • Investigation: Quickly map the shortest connection path between a suspect and known fraud entities. + + Parameters: + input: The specific Party vertex to start the search from (the target of the risk assessment). + depth: Maximum number of hops allowed. Keeps the search fast and focused on close-range risk. + */ MinAccum @dis; OrAccum @visited; @@ -48,4 +67,4 @@ UPDATE DESCRIPTION OF QUERY distance_and_path_to_fraud_party "This query returns UPDATE DESCRIPTION OF QUERY_PARAM distance_and_path_to_fraud_party.input "Source Party vertex from which to begin traversal searching for closely connected Fraud Party vertices." -UPDATE DESCRIPTION OF QUERY_PARAM distance_and_path_to_fraud_party.depth "Maximum number of hops from source Party vertex to traverse in the graph. Defaults to 5." \ No newline at end of file +UPDATE DESCRIPTION OF QUERY_PARAM distance_and_path_to_fraud_party.depth "Maximum number of hops from source Party vertex to traverse in the graph. Defaults to 5." diff --git a/financial_crime/entity_resolution_kyc/queries/expand_connected_component_entities_as_same_as_edge.gsql b/financial_crime/entity_resolution_kyc/queries/expand_connected_component_entities_as_same_as_edge.gsql new file mode 100644 index 00000000..096451b4 --- /dev/null +++ b/financial_crime/entity_resolution_kyc/queries/expand_connected_component_entities_as_same_as_edge.gsql @@ -0,0 +1,51 @@ +CREATE OR REPLACE DISTRIBUTED QUERY expand_connected_component_entities_same_as_edge ( + VERTEX input_connected_component +) { + /* + Query: expand_connected_component_entities_same_as_edge + Description: + Following Query is used to give an holistic view of the given input Connected_Component node using Same_As edge + + input: Connected_Component : `1678770178` + + output: connected edges + */ + OrAccum @visited; + SetAccum @@edges_to_display; + input_connected_component_set = {input_connected_component}; + + curr_linked_parties = SELECT t + FROM input_connected_component_set:s -(Entity_In_Ring:e)- Party:t + WHERE getvid(t) == s.id + ACCUM @@edges_to_display += e + + ; + + WHILE curr_linked_parties.size() > 0 DO + curr_linked_parties = SELECT t + FROM curr_linked_parties:s -(Same_As:e)- Party:t + ACCUM @@edges_to_display += e + HAVING NOT t.@visited + ; + + curr_linked_parties = SELECT s + FROM curr_linked_parties:s + POST-ACCUM s.@visited += True + ; + END; + + all_related_parties = SELECT s + FROM Party:s + WHERE s.@visited + ; + + PRINT + input_connected_component + ; + + PRINT + input_connected_component_set, + all_related_parties + ; + PRINT @@edges_to_display; +} diff --git a/financial_crime/entity_resolution_kyc/queries/expand_connected_components_to_entities_vis.gsql b/financial_crime/entity_resolution_kyc/queries/expand_connected_components_to_entities_vis.gsql new file mode 100644 index 00000000..d5a386e5 --- /dev/null +++ b/financial_crime/entity_resolution_kyc/queries/expand_connected_components_to_entities_vis.gsql @@ -0,0 +1,64 @@ +CREATE OR REPLACE DISTRIBUTED QUERY expand_connected_components_to_entities_vis ( + VERTEX input_connected_component, + BOOL show_only_shared_features = False +) { + + /* + Query: expand_connected_components_to_entities_vis + Description: + Following Query is used to give an holistic view of all connected edges for the Connected_Component node + input: Connected_Component : `1678770178` + + output: connected edges + */ + SumAccum @num_parties_linked_to_features; + SetAccum @@edges_to_display; + input_connected_component_set = {input_connected_component}; + + SetAccum @@edge_type_set = ( + "Has_Full_Name", + "Has_DOB", + "Has_Email", + "Has_Phone", + "Has_Address", + "Has_IP", + "Has_ID", + "Has_Device", + "Application_Has_Party", + "Party_Has_Account", + "Party_Has_Card" + ); + + linked_parties = SELECT t + FROM input_connected_component_set:s -(Entity_In_Ring:e)- Party:t + ACCUM @@edges_to_display += e + ; + + selected_features_to_display = SELECT t + FROM linked_parties:s -(@@edge_type_set:e)- :t + ACCUM t.@num_parties_linked_to_features += 1 + ; + + IF show_only_shared_features THEN + selected_features_to_display = SELECT s + FROM selected_features_to_display:s + WHERE s.@num_parties_linked_to_features > 1 + ; + END; + + selected_features_to_display = SELECT s + FROM selected_features_to_display:s -(@@edge_type_set:e)- linked_parties:t + ACCUM @@edges_to_display += e + ; + + PRINT + input_connected_component, + show_only_shared_features + ; + PRINT + input_connected_component_set, + linked_parties, + selected_features_to_display + ; + PRINT @@edges_to_display; +} diff --git a/financial_crime/entity_resolution_kyc/queries/explore_er_steps.gsql b/financial_crime/entity_resolution_kyc/queries/explore_er_steps.gsql new file mode 100644 index 00000000..c290126f --- /dev/null +++ b/financial_crime/entity_resolution_kyc/queries/explore_er_steps.gsql @@ -0,0 +1,67 @@ +CREATE OR REPLACE DISTRIBUTED QUERY explore_er_steps ( + SET> party_to_explore, + BOOL explore_step_01, + BOOL explore_step_02, + BOOL explore_step_03 +) { + // The visualization for this query will be more intuitive + // if all vertices in party_to_explore were all in the same vertex community + + // Step 01 = explore attribute edges - before match_party_entities + // Step 02 = status after match_party_entities was run + // Step 03 = status after unify_party_entities was run + SetAccum @@edges_to_display; + SetAccum @@attr_edges; + OrAccum @is_start_set; + + @@attr_edges += ( + "Has_Full_Name", + "Has_DOB", + "Has_Email", + "Has_Phone", + "Has_Address", + "Has_IP", + "Has_ID", + "Has_Device", + "Application_Has_Party", + "Party_Has_Account", + "Party_Has_Card" + ); + + party_to_explore_set = {party_to_explore}; + + party_to_explore_set = SELECT s + FROM party_to_explore_set:s + POST-ACCUM s.@is_start_set += TRUE + ; + + IF explore_step_01 THEN + attributes_explore_step_01 = SELECT t + FROM party_to_explore_set:s -(@@attr_edges:e)- :t + ACCUM @@edges_to_display += e + ; + + PRINT attributes_explore_step_01; + END; + + IF explore_step_02 THEN + party_with_same_as_edges = SELECT s + FROM party_to_explore_set:s -(Same_As:e)- Party:t + WHERE t.@is_start_set == TRUE + ACCUM @@edges_to_display += e + ; + END; + + IF explore_step_03 THEN + curr_party_cc_step_03 = SELECT t + FROM party_to_explore_set:s -(Entity_In_Ring:e)- Connected_Component:t + ACCUM @@edges_to_display += e + ; + + PRINT curr_party_cc_step_03; + END; + + + PRINT party_to_explore_set; + PRINT @@edges_to_display; +} diff --git a/financial_crime/entity_resolution_kyc/queries/find_shared_piis_of_two_parties.gsql b/financial_crime/entity_resolution_kyc/queries/find_shared_piis_of_two_parties.gsql index 80b69d73..5af73b39 100644 --- a/financial_crime/entity_resolution_kyc/queries/find_shared_piis_of_two_parties.gsql +++ b/financial_crime/entity_resolution_kyc/queries/find_shared_piis_of_two_parties.gsql @@ -1,5 +1,26 @@ CREATE OR REPLACE QUERY find_shared_piis_of_two_parties(VERTEX party_1, VERTEX party_2) { + /* + Query Name: find_shared_piis_of_two_parties + Real-Time PII Linkage Analysis between Two Parties + + Purpose: Investigative Link Discovery + The query's goal is to identify all **Personally Identifiable Information (PII)** nodes + (like Name, Address, IP, Device, etc.) that are **directly shared** by the two input + `Party` vertices. This immediately reveals the underlying connection, which is a critical + step in verifying potential fraud rings or suspicious collusion. + + Key Output Features: + • PII Type (e.g., 'Full_Name'): What type of entity is shared. + • PII Value (Vertex ID): The ID of the shared entity itself. + • Degree (Out-degree): How many *other* parties are also connected to this shared PII. + A high degree indicates a high-risk 'hub' entity. + + Parameters: + party_1: The first Party vertex ID. + party_2: The second Party vertex ID. + */ + TYPEDEF TUPLE pii_info; ListAccum @@degrees_of_shared_piis; @@ -29,4 +50,4 @@ UPDATE DESCRIPTION OF QUERY find_shared_piis_of_two_parties "This query returns UPDATE DESCRIPTION OF QUERY_PARAM find_shared_piis_of_two_parties.party_1 "First Party vertex used to search for shared PII vertices." -UPDATE DESCRIPTION OF QUERY_PARAM find_shared_piis_of_two_parties.party_2 "Second Party vertex used to search for shared PII vertices." \ No newline at end of file +UPDATE DESCRIPTION OF QUERY_PARAM find_shared_piis_of_two_parties.party_2 "Second Party vertex used to search for shared PII vertices." diff --git a/financial_crime/entity_resolution_kyc/queries/get_party_cc_features.gsql b/financial_crime/entity_resolution_kyc/queries/get_party_cc_features.gsql index 8c99f0a7..eac4fb10 100644 --- a/financial_crime/entity_resolution_kyc/queries/get_party_cc_features.gsql +++ b/financial_crime/entity_resolution_kyc/queries/get_party_cc_features.gsql @@ -1,5 +1,24 @@ CREATE OR REPLACE QUERY get_party_cc_features(VERTEX party, INT connections=25000) { + /* + Query Name: get_party_cc_features + Real-Time Connected Component (CC) Feature Extraction for a Single Party + + Purpose: Real-Time Feature Engineering for Single Entity Risk + This query quickly calculates detailed, graph-based features for a specific `Party` by analyzing + the aggregated properties of the entire `Connected_Component` (CC) it belongs to. This is ideal + for calculating a risk score for a single entity (e.g., a new loan application or login attempt). + + Key Features Calculated (for the CC): + • Size and Composition: Total Party nodes, distinct PII nodes (Name, IP, Device, etc.). + • Linkage Strength: Number of Parties connected via shared PII. + • Specificity: Number of Parties connected *only* by a single type of shared PII (e.g., only by DOB). + + Parameters: + party: The specific Party vertex ID to analyze. + connections: PII Super-Node filter. Maximum out-degree allowed for PII nodes to be considered valid connectors. + */ + MapAccum @@connect_component, @@distinct_name, @@distinct_dob, @@distinct_email, @@distinct_phone, @@distinct_address, @@distinct_ip, @@distinct_id, @@distinct_device, @@distinct_application, @@distinct_account, @@distinct_card, @@connected_via_name, @@connected_via_dob, @@connected_via_email, @@connected_via_phone, @@connected_via_address, @@connected_via_ip, @@connected_via_id, @@connected_via_device, @@connected_via_application, @@connected_via_account, @@connected_via_card, @@only_connected_via_name, @@only_connected_via_dob, @@only_connected_via_email, @@only_connected_via_phone, @@only_connected_via_address, @@only_connected_via_ip, @@only_connected_via_id, @@only_connected_via_device, @@only_connected_via_application, @@only_connected_via_account, @@only_connected_via_card; OrAccum @connected_via_name, @connected_via_dob, @connected_via_email, @connected_via_phone, @connected_via_address, @connected_via_ip, @connected_via_id, @connected_via_device, @connected_via_application, @connected_via_account, @connected_via_card; SetAccum @middle_type; @@ -166,4 +185,4 @@ UPDATE DESCRIPTION OF QUERY get_party_cc_features "This query returns the follow UPDATE DESCRIPTION OF QUERY_PARAM get_party_cc_features.party "Source Party vertex for generating graph features." -UPDATE DESCRIPTION OF QUERY_PARAM get_party_cc_features.connections "Maximum outdegree of each PII vertex allowed in search used to filter out hub nodes. Defaults to 25000." \ No newline at end of file +UPDATE DESCRIPTION OF QUERY_PARAM get_party_cc_features.connections "Maximum outdegree of each PII vertex allowed in search used to filter out hub nodes. Defaults to 25000." diff --git a/financial_crime/entity_resolution_kyc/queries/get_party_fraud_status.gsql b/financial_crime/entity_resolution_kyc/queries/get_party_fraud_status.gsql index 5b75a5a0..ed5ee8a1 100644 --- a/financial_crime/entity_resolution_kyc/queries/get_party_fraud_status.gsql +++ b/financial_crime/entity_resolution_kyc/queries/get_party_fraud_status.gsql @@ -1,4 +1,23 @@ CREATE DISTRIBUTED QUERY get_party_fraud_status(SET> parties) { + + /* + Query Name: get_party_fraud_status + Bulk Retrieval of Party Fraud Status + + Purpose: Data Lookup and Status Verification + This query takes a set of specific `Party` vertices as input and quickly returns + the value of their `is_fraud` attribute. It is typically used for external systems + to retrieve known labels for downstream analysis, model evaluation, or auditing. + It performs no graph traversal, making it extremely fast. + + Key Use Cases: + • Model Evaluation: Retrieve ground truth labels for a set of Parties for model performance testing. + • System Integration: Fetch current fraud flags for multiple entities efficiently. + • Audit and Reporting: Generate a quick report on the current status of specific accounts. + + Parameters: + parties: A set of Party vertex IDs whose fraud status needs to be retrieved. + */ MapAccum, INT> @@fraud_status; @@ -13,4 +32,4 @@ CREATE DISTRIBUTED QUERY get_party_fraud_status(SET> parties) { UPDATE DESCRIPTION OF QUERY get_party_fraud_status "This query returns the integer fraud status of each provided Party vertex." -UPDATE DESCRIPTION OF QUERY_PARAM get_party_fraud_status.parties "Party vertices for obtaining fraud status as stored in is_fraud integer attribute." \ No newline at end of file +UPDATE DESCRIPTION OF QUERY_PARAM get_party_fraud_status.parties "Party vertices for obtaining fraud status as stored in is_fraud integer attribute." diff --git a/financial_crime/entity_resolution_kyc/queries/incremental_party_match.gsql b/financial_crime/entity_resolution_kyc/queries/incremental_party_match.gsql index 67bfc9bc..22d48e58 100644 --- a/financial_crime/entity_resolution_kyc/queries/incremental_party_match.gsql +++ b/financial_crime/entity_resolution_kyc/queries/incremental_party_match.gsql @@ -14,6 +14,25 @@ CREATE OR REPLACE QUERY incremental_party_match( FLOAT threshold = 1.0 // if a total matching score between two entities exceed the threshold, a similarity edge will be created between the matching entities ) { + /* + Query Name: incremental_party_match + Real-Time Weighted Entity Resolution and Matching + + Purpose: Incremental Graph Update and De-duplication + This query handles the full lifecycle of a new (or updated) Party record: + 1. **Data Ingestion:** Parses a JSON payload and inserts the new Party and all associated PII/Entity nodes (like Address, IP, Account) into the graph. + 2. **Scoring:** Calculates a **weighted match score** between the new Party and *all existing Parties* by summing the weights of their shared PII/Entities. + 3. **Resolution:** If the score to an existing Party's community exceeds a defined `threshold`, the new Party is linked to that highest-scoring `Connected_Component` (CC), effectively merging it into an existing group. + + Key Concept: Weighted Matching + Different PII/Entity types carry different evidential weight for matching (e.g., a shared Device ID is a strong match, a shared Name is weaker). This is controlled by the input `weight` parameters. + + Parameters: + input: The JSON string containing the new Party and PII data. + Customer_[Entity]_weight: Weights defining the contribution of each shared PII/Entity type to the total score. + threshold: The minimum cumulative score required to declare a match and perform the merge. + */ + TYPEDEF TUPLE ver> Connected_Component_heap_tuple; HeapAccum(1, score DESC) @@Connected_Component_ID_heap; SetAccum @@edge_type_set, @@party_vertex, @@name, @@dob, @@email, @@phone_numbers, @@addresses, @@ip, @@ids, @@device, @@applications, @@accounts, @@cards; @@ -459,4 +478,4 @@ UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_match.Customer_Account_weigh UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_match.Customer_Card_weight "Weight accumulation for matching Card PII vertices between provided input Party and every other Party in the graph. Defaults to 1.0." -UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_match.threshold "Accumulated weight threshold required for matching Party vertices into a Connected_Component community using the provided weight for each PII vertex type. Defaults to 1.0." \ No newline at end of file +UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_match.threshold "Accumulated weight threshold required for matching Party vertices into a Connected_Component community using the provided weight for each PII vertex type. Defaults to 1.0." diff --git a/financial_crime/entity_resolution_kyc/queries/incremental_party_unify.gsql b/financial_crime/entity_resolution_kyc/queries/incremental_party_unify.gsql index ab029e0b..31a1bb59 100644 --- a/financial_crime/entity_resolution_kyc/queries/incremental_party_unify.gsql +++ b/financial_crime/entity_resolution_kyc/queries/incremental_party_unify.gsql @@ -1,5 +1,27 @@ CREATE OR REPLACE DISTRIBUTED QUERY incremental_party_unify(SET> parties) { + /* + Query Name: incremental_party_unify + Incremental Community Initialization (Connected Component Assignment) + + Purpose: Graph Structure Completion + This query takes a set of newly inserted `Party` vertices that were not successfully + matched to an existing `Connected_Component` (CC) community by the matching logic + (e.g., `incremental_party_match`). It then assigns each of these unmatched Parties + to a **new, unique CC** of its own. + + Core Logic: + 1. **Self-Assignment:** Initially treat each Party (or small group linked by `Same_As` edges) + as its own CC. + 2. **Propagation (Optional):** If the input Parties are linked by `Same_As` edges (a rare scenario + in a strictly incremental flow, but accounted for), this step uses the standard Connected + Component algorithm (specifically, label propagation) to group them. + 3. **Creation:** Inserts a new `Connected_Component` vertex and links the Party to it. + + Parameters: + parties: The set of new Party vertices that need to be assigned to a CC. + */ + MinAccum @cc_id; // Each vertex's tentative component id DATETIME start_time = now(); @@ -41,4 +63,4 @@ CREATE OR REPLACE DISTRIBUTED QUERY incremental_party_unify(SET> p UPDATE DESCRIPTION OF QUERY incremental_party_unify "This query associates Party vertices unable to be matched to any existing Party using incremental_party_match into its own respective Connected_Component community vertex so it can be eligible for weighted weakly connected components matching in near real time." -UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_unify.parties "Party vertices to put into a Connected_Component community." \ No newline at end of file +UPDATE DESCRIPTION OF QUERY_PARAM incremental_party_unify.parties "Party vertices to put into a Connected_Component community." diff --git a/financial_crime/entity_resolution_kyc/queries/match_party_entities.gsql b/financial_crime/entity_resolution_kyc/queries/match_party_entities.gsql index 3f69c364..599f6c35 100644 --- a/financial_crime/entity_resolution_kyc/queries/match_party_entities.gsql +++ b/financial_crime/entity_resolution_kyc/queries/match_party_entities.gsql @@ -18,6 +18,28 @@ CREATE OR REPLACE DISTRIBUTED QUERY match_party_entities( DATETIME compute_entities_after_date = to_datetime("1970-01-01 00:00:00") // All entities that were created in TG after this date, will be computed to find similarity edges related to these new entities. The default value is the earliest time possible (great for the first time running this query) ) { + /* + Query Name: match_party_entities + Batch-Based Weighted Entity Resolution (All-Pairs Similarity) + + Purpose: Full Graph De-Duplication + This query is an **offline batch job** designed to find and link similar `Party` vertices + across the entire graph based on shared PII/Entities. It calculates a weighted score + for every pair of parties connected by a shared PII, and if the score exceeds the + `threshold`, it creates a `Same_As` edge between them. + + Key Concepts: + • **Weighted Similarity:** Each shared PII type contributes a different weight to the final score. + • **Nested Batching:** The graph is divided into `source` and `target` batches to manage memory and parallelize the all-pairs comparison efficiently. + • **Super-Node Filtering:** PII nodes (e.g., a common IP address shared by millions of users) are filtered out using `pii_low_connections_limit` and `pii_high_connections_limit` to prevent false positives and poor performance. + + Parameters: + Customer_[Entity]_weight: Weights for each PII type defining match strength. + num_of_source_batches/num_of_target_batches: Control the size and number of batches for distributed processing. + threshold: Score required to create a `Same_As` link. + compute_entities_after_date: Incremental processing date filter, useful for re-running the job on recent data only. + */ + ListAccum> @entity_list; MapAccum,FLOAT> @entity_map; SumAccum @@count; @@ -265,4 +287,4 @@ UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.pii_low_connections_limit UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.pii_high_connections_limit "Maximum outdegree of PII vertex considered for 'high connection' matching. The case will be skipped if the same vertex is connected to too many entities. Defaults to 25000." -UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.compute_entities_after_date "All Party vertices with created_at after this date will be computed to find similarity edges related to these new entities. Defaults to 1970-01-01 00:00:00 (which is the earliest possible created_at and great for the initial running of this query)." \ No newline at end of file +UPDATE DESCRIPTION OF QUERY_PARAM match_party_entities.compute_entities_after_date "All Party vertices with created_at after this date will be computed to find similarity edges related to these new entities. Defaults to 1970-01-01 00:00:00 (which is the earliest possible created_at and great for the initial running of this query)." diff --git a/financial_crime/entity_resolution_kyc/queries/output_party_cc_to_file.gsql b/financial_crime/entity_resolution_kyc/queries/output_party_cc_to_file.gsql index 32543849..3d31fb90 100644 --- a/financial_crime/entity_resolution_kyc/queries/output_party_cc_to_file.gsql +++ b/financial_crime/entity_resolution_kyc/queries/output_party_cc_to_file.gsql @@ -1,5 +1,24 @@ CREATE OR REPLACE QUERY output_party_cc_to_file(STRING output_file_path = "/home/tigergraph/gsql_output/party_cc_output.txt") { - + /* + Query Name: output_party_cc_to_file + Export Party-to-Community Mapping to File + + Purpose: Data Export and Downstream Integration + The primary goal of this query is to **generate a plain text or CSV file** that maps + every `Party` vertex in the graph to the `Connected_Component` community vertex it + currently belongs to. This output is critical for: + • **Model Training:** Providing ground truth labels (community IDs) for machine learning models. + • **Reporting/Auditing:** Creating a comprehensive record of the current entity resolution state. + • **External System Integration:** Loading the community assignments into a data warehouse or external database. + + Key Concepts: + • FILE Object: TigerGraph's utility to write data directly to a file on the server. + • Simple Traversal: A single hop is used to link the Party to its assigned community. + + Parameters: + output_file_path: The full path where the output file (e.g., CSV) should be created on the TigerGraph server. + */ + FILE f (output_file_path); f.println("Party", "Connected_Component"); @@ -17,4 +36,4 @@ CREATE OR REPLACE QUERY output_party_cc_to_file(STRING output_file_path = "/home UPDATE DESCRIPTION OF QUERY output_party_cc_to_file "This query outputs a file containing a mapping of all Party vertices to their respective Connected_Component community vertex." -UPDATE DESCRIPTION OF QUERY_PARAM output_party_cc_to_file.output_file_path "File path location containing the output Party vertex to Connected_Component vertex in CSV format. Defaults to /home/tigergraph/gsql_output/party_cc_output.csv" \ No newline at end of file +UPDATE DESCRIPTION OF QUERY_PARAM output_party_cc_to_file.output_file_path "File path location containing the output Party vertex to Connected_Component vertex in CSV format. Defaults to /home/tigergraph/gsql_output/party_cc_output.csv" diff --git a/financial_crime/entity_resolution_kyc/queries/top_connected_component.gsql b/financial_crime/entity_resolution_kyc/queries/top_connected_component.gsql new file mode 100644 index 00000000..1e8ffe99 --- /dev/null +++ b/financial_crime/entity_resolution_kyc/queries/top_connected_component.gsql @@ -0,0 +1,34 @@ +CREATE OR REPLACE DISTRIBUTED QUERY Top_Connected_Component(/* Parameters here */) FOR GRAPH Entity_Resolution_KYC { + + /* + Query Name: Top_Connected_Component + Top Connected Component Summary Analysis + + Purpose: Community Profiling and Risk Prioritization + This query traverses the graph to aggregate key statistics for each `Connected_Component` (CC) + vertex. It focuses on gathering the total count of associated `Party` vertices, the number of + unique PII entities (specifically `Address` and `Phone`), and a cumulative risk score. The + results are then sorted to show the largest/most active communities first. + + Key Metrics Calculated per Connected_Component: + • Parties: Total number of Party vertices belonging to the CC. + • Address: Total number of unique Address PII vertices linked to the CC. + • Phone: Total number of unique Phone PII vertices linked to the CC. + • Risk: The sum of the `is_fraud` attribute from all associated Party vertices (cumulative risk score). + */ + + SumAccum @parties,@address,@phone,@risk; + res = + SELECT s FROM + Connected_Component:s - () - Party:p - () - (Address|Phone):t + ACCUM + CASE WHEN t.type == "Address" THEN s.@address += 1 ELSE t.@phone += 1 END + //CASE WHEN p.is_fraud > 0 THEN s.@risk += 1 END + POST-ACCUM s.@parties += s.outdegree("Entity_In_Ring"),s.@risk += sum(s.neighborAttribute( "Entity_In_Ring", "Party", "is_fraud" )) + + + + ORDER BY s.@parties DESC + ; + PRINT res[res.id as ComponentId,res.@parties as Parties,res.@address as Address,res.@phone as Phone,res.@risk as Risk]; +} diff --git a/financial_crime/entity_resolution_kyc/queries/unify_party_entities.gsql b/financial_crime/entity_resolution_kyc/queries/unify_party_entities.gsql index c469f5b9..6deb61e8 100644 --- a/financial_crime/entity_resolution_kyc/queries/unify_party_entities.gsql +++ b/financial_crime/entity_resolution_kyc/queries/unify_party_entities.gsql @@ -1,5 +1,26 @@ CREATE OR REPLACE DISTRIBUTED QUERY unify_party_entities() { + /* + Query Name: unify_party_entities + Finalization of Entity Resolution: Connected Component Assignment + + Purpose: Clustering and Community Creation + This query is the final, essential step in a batch Entity Resolution pipeline. It performs + the Connected Components (CC) algorithm, using the transitive links established by the + `Same_As` similarity edges (created by `match_party_entities`), to **group all connected + `Party` vertices** into single, distinct communities. + + Core Logic: Label Propagation (MinAccum) + 1. **Initialization:** Every Party vertex is initially labeled with its own unique ID. + 2. **Propagation:** Smaller IDs are iteratively passed across `Same_As` edges until every + Party in a connected cluster has adopted the smallest ID among them. This smallest ID + becomes the unique identifier for the entire community (the CC ID). + 3. **Creation:** New `Connected_Component` vertices are created using these final CC IDs, + and linking edges (`Entity_In_Ring`) are established. + + Key Outcome: Graph structure is finalized with explicit community assignments. + */ + MinAccum @cc_id; // Each vertex's tentative component id DATETIME start_time = now(); @@ -39,4 +60,4 @@ CREATE OR REPLACE DISTRIBUTED QUERY unify_party_entities() { } -UPDATE DESCRIPTION OF QUERY unify_party_entities "This query associates all Party vertices in the graph to a Connected_Component vertex using Same_As edge previously inserted by match_party_entities query." \ No newline at end of file +UPDATE DESCRIPTION OF QUERY unify_party_entities "This query associates all Party vertices in the graph to a Connected_Component vertex using Same_As edge previously inserted by match_party_entities query." From 0240e01605ffffd30a014bd66685a140816c5ec5 Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Mon, 8 Dec 2025 13:54:40 +0530 Subject: [PATCH 26/55] enhanced readability of credit card fraud narration --- .../network_infrastructure/README.md | 14 ++-- .../supply_chain_management/README.md | 44 ++++++------- connected_customer/customer_360/README.md | 36 +++++------ .../entity_resolution/README.md | 11 ++-- .../queries/delete_all_cc_connections.gsql | 32 +++++++--- .../queries/delete_unused_cc_nodes.gsql | 29 ++++++--- .../find_shared_piis_of_two_entities.gsql | 28 +++++--- .../queries/match_entities.gsql | 64 ++++++++++++------- .../queries/output_entity_cc_to_file.gsql | 31 ++++++--- .../queries/unify_entities.gsql | 35 ++++++---- .../product_recommendations/README.md | 38 +++++------ .../queries/combine_features.gsql | 59 ++++++++++------- .../queries/k_means.gsql | 61 ++++++++++++------ .../queries/recommend_products.gsql | 64 +++++++++---------- financial_crime/application_fraud/README.md | 31 +++++---- financial_crime/transaction_fraud/README.md | 16 +++-- 16 files changed, 353 insertions(+), 240 deletions(-) diff --git a/agile_operations/network_infrastructure/README.md b/agile_operations/network_infrastructure/README.md index 961166e9..c470e58c 100644 --- a/agile_operations/network_infrastructure/README.md +++ b/agile_operations/network_infrastructure/README.md @@ -1,11 +1,11 @@ -# Network Infrastructure – Cybersecurity (TigerGraph Solution Kit) +# Network Infrastructure - Cybersecurity (TigerGraph Solution Kit) Cybersecurity is a crucial aspect of large organizations. Enterprises operate their own data centers and network infrastructure involving many different devices. Cyberattacks and other incidents can lead to issues such as data -breaches, corrupted files, and loss of data — resulting in billions of +breaches, corrupted files, and loss of data - resulting in billions of dollars lost each year. One key capability for detecting and responding to -these threats is a deep understanding of your organization’s network +these threats is a deep understanding of your organization's network infrastructure and how devices are connected. TigerGraph allows you to connect data from multiple sources and load data @@ -14,10 +14,10 @@ topology of their network infrastructure, understand how components relate, and run graph algorithms at scale to discover related incidents and events based on device topology in near real time. -This solution kit provisions a complete graph environment — schema, sample -data, and GSQL queries — to help you: +This solution kit provisions a complete graph environment - schema, sample +data, and GSQL queries to help you: -- Visualize router–firewall–switch–server topologies. +- Visualize router-firewall-switch-server topologies. - Identify single points of failure and the blast radius of device failures. - Trace incidents and alerts through network paths and time. - Find unsecured routes and devices with frequent alerts and incidents. @@ -68,7 +68,7 @@ model and traverse it in real time. This solution kit combines: (`Has_Minute`, `Has_Hour`, `Has_Date`, `Has_Month`, `Has_Year`). You can use the included queries as building blocks for operations, -security analysis, and incident investigation — or extend the graph with +security analysis, and incident investigation - or extend the graph with your own device types, log sources, or analytics. > **Graph name:** This kit creates and uses a graph named `Network_Infrastructure`. diff --git a/agile_operations/supply_chain_management/README.md b/agile_operations/supply_chain_management/README.md index 41b7527e..581beb45 100644 --- a/agile_operations/supply_chain_management/README.md +++ b/agile_operations/supply_chain_management/README.md @@ -15,9 +15,9 @@ questions, this graph enables real-time, highly connected analytics, including: - Full **forward/backward traceability** of production batches. - Real-time **Available-to-Promise (ATP)**, with optional inventory freshness rules. - **Country-of-origin** and domestic vs. international sourcing analysis. -- **Demand–supply matching** and identification of bottlenecks or constraints. +- **Demand-supply matching** and identification of bottlenecks or constraints. -By leveraging TigerGraph’s native parallel engine and bi-directional traversals, +By leveraging TigerGraph-s native parallel engine and bi-directional traversals, analysts, planners, sourcing managers, and risk teams can get instant answers to complex multi-hop questions, improving agility, resilience, and decision quality across the supply chain. @@ -48,7 +48,7 @@ a manufacturing supply chain: - **Production & genealogy** - `SFC_Material` and `SFC_Assembly` represent actual production batches and assembly operations. - - Graph edges capture “produced at”, “used for”, and BOM relationships. + - Graph edges capture "produced at", "used for", and BOM relationships. - **Orders & pricing** - `Purchase_Order`, `Line_Number` for inbound procurement. - `Sales_Order`, `Sales_Order_Item` for outbound customer demand. @@ -66,7 +66,7 @@ a manufacturing supply chain: > **Graph name:** This kit creates and uses a graph named `Supply_Chain_Management`. You can use the included queries for operations, planning, risk, and analytics -— or extend the graph with your own entities, KPIs, and business rules. + or extend the graph with your own entities, KPIs, and business rules. --- # Components @@ -147,28 +147,28 @@ After the script finishes (typically within minutes), the graph is fully loaded The queries are independent and can be executed in any order for day-to-day analysis. There is no mandatory sequential workflow except when performing inventory simulation or batch allocation. ### Core Operational Queries (run on-demand) -- `product_quantity_sales_order` – Standard Available-to-Promise (ATP): checks if sufficient quantity exists for a Sales Order. -- `datewise_product_availability` – Smart ATP with freshness: only considers inventory updated within the last X months. -- `unfulfilled_orders` – Batch allocation engine: processes a set of Sales Orders and virtually reserves inventory. -- `check_shipment_capacity` – Compares ordered vs shipped quantities to find partially fulfilled items. -- `add_purchase_to_inventory` – Simulates the effect of receiving a Purchase Order on current stock levels. - -### Traceability & Risk Queries (recursive – run anytime) -- `explore_BOM` – Explodes a Bill of Materials downstream or traces where-used upstream to any depth. -- `explore_BOM_line` – Traces the actual physical genealogy of a specific batch (SFC) across production and consumption. -- `plant_failure_impact_nodes` – BFS impact analysis: returns all customers, orders, and finished goods affected if a Plant fails. -- `trace_BOM_line_nations` – Calculates the exact percentage of domestic vs international raw materials in a finished good. - -### Analytics & Reporting Queries (aggregation – run on-demand) -- `get_biggest_customers` – Top-K customers by ordered quantity (optional date range and nation filter). -- `get_biggest_suppliers` – Top-K suppliers by supplied quantity (optional date range and nation filter). -- `top_k_products_sales_order` – Most demanded finished goods across all Sales Orders. -- `top_k_products_purchase_order` – Most purchased raw/semi-finished materials. +- `product_quantity_sales_order` - Standard Available-to-Promise (ATP): checks if sufficient quantity exists for a Sales Order. +- `datewise_product_availability` - Smart ATP with freshness: only considers inventory updated within the last X months. +- `unfulfilled_orders` - Batch allocation engine: processes a set of Sales Orders and virtually reserves inventory. +- `check_shipment_capacity` - Compares ordered vs shipped quantities to find partially fulfilled items. +- `add_purchase_to_inventory` - Simulates the effect of receiving a Purchase Order on current stock levels. + +### Traceability & Risk Queries (recursive - run anytime) +- `explore_BOM` - Explodes a Bill of Materials downstream or traces where-used upstream to any depth. +- `explore_BOM_line` - Traces the actual physical genealogy of a specific batch (SFC) across production and consumption. +- `plant_failure_impact_nodes` - BFS impact analysis: returns all customers, orders, and finished goods affected if a Plant fails. +- `trace_BOM_line_nations` - Calculates the exact percentage of domestic vs international raw materials in a finished good. + +### Analytics & Reporting Queries (aggregation - run on-demand) +- `get_biggest_customers` - Top-K customers by ordered quantity (optional date range and nation filter). +- `get_biggest_suppliers` - Top-K suppliers by supplied quantity (optional date range and nation filter). +- `top_k_products_sales_order` - Most demanded finished goods across all Sales Orders. +- `top_k_products_purchase_order` - Most purchased raw/semi-finished materials. All queries are distributed, highly parameterized (depth, date ranges, top-K, nation filters, etc.), and complete in seconds even on datasets with millions of orders and multi-level BOMs. ## Scalability -The heaviest queries (`explore_BOM`, `explore_BOM_line`, `plant_failure_impact_nodes`, `unfulfilled_orders`) use TigerGraph’s native parallel traversal and recursion engines and scale linearly across the cluster. +The heaviest queries (`explore_BOM`, `explore_BOM_line`, `plant_failure_impact_nodes`, `unfulfilled_orders`) use TigerGraph's native parallel traversal and recursion engines and scale linearly across the cluster. For extremely large supply chains (hundreds of millions of SFC instances or deep BOMs): - Depth/hop limits are built into queries to guarantee predictable runtime. diff --git a/connected_customer/customer_360/README.md b/connected_customer/customer_360/README.md index 19aeb6bf..7bce69d3 100644 --- a/connected_customer/customer_360/README.md +++ b/connected_customer/customer_360/README.md @@ -1,9 +1,9 @@ -# Customer 360 – Financial (TigerGraph Solution Kit) +# Customer 360 - Financial (TigerGraph Solution Kit) -A TigerGraph solution kit that builds a Customer 360° view for financial institutions. +A TigerGraph solution kit that builds a Customer 360 degree view for financial institutions. -This project provisions a complete graph environment — schema, sample data, -and GSQL queries — to analyze customer behavior and engagement across accounts, +This project provisions a complete graph environment - schema, sample data, +and GSQL queries - to analyze customer behavior and engagement across accounts, products, and digital touchpoints. With this kit you can: @@ -31,11 +31,11 @@ With this kit you can: ## Overview -Customer 360° is about consolidating all customer interactions — accounts, -transactions, digital engagement, and product holdings — into a single, +Customer_360 is about consolidating all customer interactions - accounts, +transactions, digital engagement, and product holdings - into a single, connected view. Graph databases like TigerGraph are well-suited for this -because they can traverse deeply connected data (e.g., customers → sessions → -actions → products) in real time. +because they can traverse deeply connected data (e.g., customers -> sessions -> +actions -> products) in real time. This solution kit models a retail banking environment with: @@ -172,7 +172,7 @@ touchpoint captured in the graph. **Customers_With_Sessions** Returns all individuals who have created at least one session. This effectively -gives you your “digitally active” customer population and can be used to +gives you your "digitally active" customer population and can be used to segment customers into active versus inactive, or to measure adoption of your digital channels over time. @@ -205,7 +205,7 @@ products such as loans or credit cards. **Application_Submissions** Returns individuals who have started and successfully submitted at least one -application. You can use this as a “converted” cohort, analyze their profiles +application. You can use this as a "converted" cohort, analyze their profiles and behaviors, or compare them against abandoned-application segments to understand which attributes or journeys correlate with successful completion. @@ -277,19 +277,19 @@ If your data lives in cloud storage or another external system (for example, Amazon S3, Google Cloud Storage, Azure Blob Storage, or a data warehouse), you can keep the `CREATE DATA_SOURCE` pattern from the sample and adapt it to your environment. -If your data is stored on the TigerGraph server’s local filesystem instead, you can +If your data is stored on the TigerGraph server's local filesystem instead, you can skip `CREATE DATA_SOURCE` entirely and point `DEFINE FILENAME` directly to your local paths. TigerGraph supports multiple loading options. For detailed, up-to-date examples, please refer to the official documentation: -- **Data loading overview** – supported sources and general workflow +- **Data loading overview** - supported sources and general workflow https://docs.tigergraph.com/tigergraph-server/current/data-loading/data-loading-overview -- **Data loading entry page** – index of all loading methods (local files, cloud storage, warehouses, Spark, etc.) +- **Data loading entry page** - index of all loading methods (local files, cloud storage, warehouses, Spark, etc.) https://docs.tigergraph.com/tigergraph-server/current/data-loading/ -- **Load data from cloud storage** – step-by-step guide for Amazon S3, Google Cloud Storage, and Azure Blob Storage +- **Load data from cloud storage** - step-by-step guide for Amazon S3, Google Cloud Storage, and Azure Blob Storage https://docs.tigergraph.com/tigergraph-server/current/data-loading/load-from-cloud At a high level, the steps are: @@ -297,7 +297,7 @@ At a high level, the steps are: 1. For cloud or external systems, define a `DATA_SOURCE` object that matches your storage system and credentials. For local files on the TigerGraph server, you can omit `DATA_SOURCE`. 2. Update the `DEFINE FILENAME` lines in `load_all` so they point to your cloud URIs -3. Keep the `LOAD` blocks the same unless your column structure changes—if it does, +3. Keep the `LOAD` blocks the same unless your column structure changes and if it does, adjust the `VALUES(...)` mappings so each vertex/edge attribute receives the correct column. Once those changes are in place, you can reload your data with: @@ -309,7 +309,7 @@ gsql loading_job/load_data.gsql ## Resetting the Environment -Sometimes you’ll want to wipe the graph data and start fresh — for example, when +Sometimes you'll want to wipe the graph data and start fresh - for example, when switching to a new dataset or after updating the schema and loading logic. This repository provides a utility query to delete all data, and a separate @@ -354,8 +354,8 @@ removed. ### 2. Full reset using the `reset` script -If you need a complete reset of the solution kit — not just data, but also the -loading job, data source, queries, and the graph definition — you can use the +If you need a complete reset of the solution kit - not just data, but also the +loading job, data source, queries, and the graph definition - you can use the `reset/reset.gsql` script. This script performs the following operations on `Customer_360_Financial`: diff --git a/connected_customer/entity_resolution/README.md b/connected_customer/entity_resolution/README.md index 123c7f18..9e1c5543 100644 --- a/connected_customer/entity_resolution/README.md +++ b/connected_customer/entity_resolution/README.md @@ -1,9 +1,9 @@ -# Entity Resolution – Connected Customer (TigerGraph Solution Kit) +# Entity Resolution - Connected Customer (TigerGraph Solution Kit) # Narratives Entity Resolution is the cornerstone of our approach to handling the vast array -of customer data in today’s interconnected digital landscape. Specifically +of customer data in today's interconnected digital landscape. Specifically tailored to the **Connected Customer** use case, this TigerGraph solution kit excels at accurately identifying and linking entities across disparate datasets, focusing on **Personally Identifiable Information (PII)** such as @@ -11,11 +11,10 @@ email addresses, phone numbers, and physical addresses. By seamlessly integrating PII from multiple sources, businesses can create a **unified view of each customer**. This comprehensive understanding enables personalized marketing campaigns, tailored product recommendations, and optimized customer -service interactions—fostering stronger customer relationships and driving +service interactions, fostering stronger customer relationships and driving business success. -Under the hood, the kit provisions a complete graph environment—schema, sample -data, loading jobs, and GSQL queries—to **resolve, connect, and unify customer +Under the hood, the kit provisions a complete graph environment to **resolve, connect, and unify customer records** at scale. Entities are grouped using a combination of deterministic matching (shared PII) and fuzzy matching (MinHash + similarity scoring), then organized into connected components that represent real-world customers. @@ -55,7 +54,7 @@ fragments into unified entities: individual attributes used for matching. - **Same_As** edges connect Entities that appear to represent the _same real-world individual_. - **Connected_Component** vertices represent final unified clusters of Entities, - each cluster being a resolved “real-world entity”. + each cluster being a resolved "real-world entity". The kit includes both: diff --git a/connected_customer/entity_resolution/queries/delete_all_cc_connections.gsql b/connected_customer/entity_resolution/queries/delete_all_cc_connections.gsql index 437743e3..10d22c4e 100644 --- a/connected_customer/entity_resolution/queries/delete_all_cc_connections.gsql +++ b/connected_customer/entity_resolution/queries/delete_all_cc_connections.gsql @@ -1,17 +1,31 @@ USE GRAPH Entity_Resolution CREATE OR REPLACE DISTRIBUTED QUERY delete_all_cc_connections(INT num_of_batches = 1, INT batch_id = 0) FOR GRAPH Entity_Resolution{ -/* - Description: - Deletes Entity_In_Ring edges between Entity and Connected_Component - vertices, optionally in batches for large graphs. + /* + Query Name: delete_all_cc_connections + Graph Maintenance: Clear Connected Component (CC) Assignments for Entities + + Purpose: + Remove all `Entity_In_Ring` edges linking `Entity` vertices to + `Connected_Component` vertices. This is typically run before a new + community-detection or CC assignment step so that fresh connections can + be recomputed from scratch. + + Key Concept: + • Batch Deletion: + Uses `num_of_batches` and `batch_id` to partition the `Entity` vertex + space so that very large graphs can be processed across multiple jobs + without overloading resources. Parameters: - num_of_batches (INT, default = 1) - Total number of batches. Run once per batch_id in [0, num_of_batches - 1]. - batch_id (INT, default = 0) - 0-based index of the current batch to process. -*/ + num_of_batches – Total number of batches to divide the `Entity` set into. + batch_id – Zero-based index (0 … num_of_batches - 1) of the batch + to process in this execution. + + Output: + Deletes the matching `Entity_In_Ring` edges for the selected batch and + prints a summary string with the number of edges deleted. + */ SumAccum @@count; temp = SELECT t diff --git a/connected_customer/entity_resolution/queries/delete_unused_cc_nodes.gsql b/connected_customer/entity_resolution/queries/delete_unused_cc_nodes.gsql index 9aa97400..2db8f9dd 100644 --- a/connected_customer/entity_resolution/queries/delete_unused_cc_nodes.gsql +++ b/connected_customer/entity_resolution/queries/delete_unused_cc_nodes.gsql @@ -2,16 +2,29 @@ USE GRAPH Entity_Resolution CREATE OR REPLACE DISTRIBUTED QUERY delete_unused_cc_nodes(INT num_of_batches = 1, INT batch_id = 0) FOR GRAPH Entity_Resolution { /* - Description: - Deletes unused Connected_Component vertices (those with outdegree = 0), - optionally in batches for large graphs. + Query Name: delete_unused_cc_nodes + Graph Maintenance: Prune Unused Connected Component (CC) Vertices + + Purpose: + Clean up the graph by deleting `Connected_Component` vertices that are no + longer referenced by any entities. Typically run after clearing CC + assignment edges so that orphaned CC nodes are removed. + + Key Concept: + • Orphaned CC Nodes: + A `Connected_Component` vertex with `outdegree() == 0` is considered + unused and can be safely deleted. Parameters: - num_of_batches (INT, default = 1) - Total number of batches. Run once per batch_id in [0, num_of_batches - 1]. - batch_id (INT, default = 0) - 0-based index of the current batch to process. -*/ + num_of_batches – Total number of batches used to partition all + `Connected_Component` vertices for deletion. + batch_id – Zero-based index (0 … num_of_batches - 1) of the batch + processed in this execution. + + Output: + Deletes all unused `Connected_Component` vertices in the selected batch and + prints a summary with the number of vertices removed. + */ SumAccum @@count; start = {Connected_Component.*}; diff --git a/connected_customer/entity_resolution/queries/find_shared_piis_of_two_entities.gsql b/connected_customer/entity_resolution/queries/find_shared_piis_of_two_entities.gsql index 1e998502..2b043877 100644 --- a/connected_customer/entity_resolution/queries/find_shared_piis_of_two_entities.gsql +++ b/connected_customer/entity_resolution/queries/find_shared_piis_of_two_entities.gsql @@ -2,20 +2,28 @@ USE GRAPH Entity_Resolution CREATE OR REPLACE QUERY find_shared_piis_of_two_entities(VERTEX entity_1, VERTEX entity_2) { /* - Description: - Finds all PII vertices shared by two Entity vertices and returns their - type, value, and degree (outdegree of each shared PII vertex). + Query Name: find_shared_piis_of_two_entities + Real-Time PII Linkage Analysis between Two Entities + + Purpose: + Given two `Entity` vertices, identify all **PII vertices** that are directly + shared between them. This helps explain why two entities are connected or + suspected to be related (e.g., fraud review, ER explainability, data quality). + + Key Output Fields: + • pii_type – The type/label of the shared PII vertex. + • pii_value – The shared PII vertex instance (its ID/value). + • degree – Outdegree of the PII vertex, i.e., how many entities share it. + High-degree PII may indicate a common hub (e.g., shared email domain). Parameters: - entity_1 (VERTEX) - First Entity vertex to compare. - entity_2 (VERTEX) - Second Entity vertex to compare. + entity_1 – First `Entity` vertex to compare. + entity_2 – Second `Entity` vertex to compare. Output: - A list of tuples (pii_type, pii_value, degree) for each shared PII vertex - between entity_1 and entity_2. -*/ + A list of tuples (pii_type, pii_value, degree) for each PII vertex that is + connected to both `entity_1` and `entity_2`. + */ TYPEDEF TUPLE pii_info; ListAccum @@degrees_of_shared_piis; diff --git a/connected_customer/entity_resolution/queries/match_entities.gsql b/connected_customer/entity_resolution/queries/match_entities.gsql index 2a211688..ab136e7c 100644 --- a/connected_customer/entity_resolution/queries/match_entities.gsql +++ b/connected_customer/entity_resolution/queries/match_entities.gsql @@ -19,32 +19,48 @@ CREATE OR REPLACE DISTRIBUTED QUERY match_entities( DATETIME compute_entities_after_date = to_datetime("1970-01-01 00:00:00") // All entities that were created in TG after this date, will be computed to find similarity edges related to these new entities. The default value is the earliest time possible (great for the first time running this query) ) FOR GRAPH Entity_Resolution { -/* - Description: - Computes similarity scores between Entity vertices using shared PII - (including fuzzy MinHash matches) and inserts Same_As edges for pairs. - - Parameters: - customer_has_*_weight (FLOAT, various defaults) - Weights for each PII type contributing to the match score. - num_of_source_batches (INT, default = 10) - Number of source batches for splitting the Entity set to control memory. - num_of_target_batches (INT, default = 1) - Number of target batches; nested batching for large-scale matching. - threshold (FLOAT, default = 1.0) - Minimum total similarity score required to create a Same_As edge. - pii_low_connections_limit (INT, default = 100) - Max outdegree for PII considered as a low-connectivity attribute - pii_high_connections_limit (INT, default = 25000) - Max outdegree for PII included in scoring; - compute_entities_after_date (DATETIME, default = 1970-01-01 00:00:00) - Only entities with created_at after this timestamp are computed as new; +/ + /* + Query Name: match_entities + Batch-Based Weighted & Fuzzy Entity Resolution (All-Pairs Similarity) + + Purpose: + Offline batch job that finds and links similar `Entity` vertices based on shared PII. + Uses both exact matches and fuzzy string similarity (on hashed email, name, and phone) + to compute a weighted similarity score. When the score exceeds `threshold`, a `Same_As` + edge is created between the matching entities. + + Key Concepts: + • Weighted Similarity: + Each PII type contributes a configurable weight to the final match score. + • Fuzzy Matching: + Hashed email, name, and phone are compared with Jaro–Winkler–based scores + derived from the original values. + • Nested Batching: + Entities are divided into `source` and `target` batches to scale all-pairs + comparison and control memory usage. + • Super-Node Filtering: + Highly connected PII vertices are skipped using `pii_low_connections_limit` + and `pii_high_connections_limit` to prevent false positives and hot spots. + • Incremental Runs: + `compute_entities_after_date` allows focusing on recently created entities + for incremental/recurring execution. + + Parameters (high-level): + customer_has_*_weight + Controls how strongly each PII attribute influences the similarity score. + num_of_source_batches, num_of_target_batches + Control batching strategy and parallelism. + threshold + Minimum total similarity score required to create a `Same_As` edge. + compute_entities_after_date + Only `Entity` vertices created after this timestamp are treated as new + and considered for similarity edge creation. Output: - For each source Customer, a list of up to `recommendation_count` - Product_Variant items with normalized recommendation scores, sorted in - descending order of score. -*/ + Inserts `Same_As` edges between matched `Entity` pairs and prints aggregate + statistics (number of pairs matched and score distribution). + */ TYPEDEF TUPLE, str STRING, created_at DATETIME> entity_fuzzy_vertex_info; ListAccum @entity_email_address_list; ListAccum @entity_name_list; diff --git a/connected_customer/entity_resolution/queries/output_entity_cc_to_file.gsql b/connected_customer/entity_resolution/queries/output_entity_cc_to_file.gsql index dc9ef3ac..d2e7dcdf 100644 --- a/connected_customer/entity_resolution/queries/output_entity_cc_to_file.gsql +++ b/connected_customer/entity_resolution/queries/output_entity_cc_to_file.gsql @@ -1,19 +1,32 @@ USE GRAPH Entity_Resolution CREATE OR REPLACE QUERY output_entity_cc_to_file(STRING output_file_path = "/home/tigergraph/gsql_output/entity_cc_output.csv") { -/* - Description: - Writes a CSV file mapping each Entity vertex to its Connected_Component - based on Entity_In_Ring edges. + /* + Query Name: output_entity_cc_to_file + Export Entity-to-Community Mapping to File + + Purpose: + Generate a CSV file that maps every `Entity` vertex to the + `Connected_Component` vertex it is currently assigned to via + `Entity_In_Ring` edges. Useful for reporting, audits, and + downstream ML or warehousing workflows. + + Key Concepts: + • FILE output: + Uses TigerGraph’s `FILE` object to write directly to a server-side file. + • One-hop traversal: + Traverses from each `Entity` to its `Connected_Component` in a single hop. Parameters: - output_file_path - Absolute path for the CSV file on the TigerGraph server. + output_file_path – Absolute path where the CSV file will be created on + the TigerGraph server. Output: - Creates a CSV file with header "Entity,Connected_Component" and one row per - Entity–Connected_Component pair, then prints a confirmation message. -*/ + Creates a CSV file with header: + "Entity,Connected_Component" + and one row per `Entity`–`Connected_Component` pair, then prints a + confirmation message. + */ FILE f (output_file_path); f.println("Entity", "Connected_Component"); diff --git a/connected_customer/entity_resolution/queries/unify_entities.gsql b/connected_customer/entity_resolution/queries/unify_entities.gsql index 6bfc844e..24614a30 100644 --- a/connected_customer/entity_resolution/queries/unify_entities.gsql +++ b/connected_customer/entity_resolution/queries/unify_entities.gsql @@ -1,21 +1,32 @@ USE GRAPH Entity_Resolution CREATE OR REPLACE DISTRIBUTED QUERY unify_entities() FOR GRAPH Entity_Resolution { -/* - Description: - Builds Connected_Component communities from Same_As edges by propagating - the smallest internal ID across each connected group of Entity vertices, - then linking each Entity to its component via Entity_In_Ring. + /* + Query Name: unify_entities + Finalize Entity Resolution: Connected Component Assignment for Entities - Parameters: - (None) + Purpose: + Use existing `Same_As` similarity edges (e.g., created by `match_entities`) + to group all connected `Entity` vertices into communities, then materialize + each community as a `Connected_Component` vertex linked by `Entity_In_Ring` + edges. + + Core Logic (Label Propagation with MinAccum): + 1. Initialization: + Each `Entity` vertex is labeled with its own internal ID as a tentative + component ID. + 2. Propagation: + The smallest ID is iteratively propagated across `Same_As` edges until + all vertices in a connected group share the same ID. + 3. Creation: + For each final ID, a `Connected_Component` vertex is created and every + member `Entity` is connected to it via `Entity_In_Ring`. Output: - Inserts: - - One Connected_Component vertex per connected group of Entities. - - One Entity_In_Ring edge from each Entity to its component. - Prints execution_time_in_seconds and a timestamp of completion. -*/ + • One `Connected_Component` vertex per connected group of `Entity` vertices. + • One `Entity_In_Ring` edge from each `Entity` to its community. + • Printed execution time (in seconds) and a completion timestamp. + */ MinAccum @cc_id; // Each vertex's tentative component id DATETIME start_time = now(); diff --git a/connected_customer/product_recommendations/README.md b/connected_customer/product_recommendations/README.md index 254c6261..83cf3441 100644 --- a/connected_customer/product_recommendations/README.md +++ b/connected_customer/product_recommendations/README.md @@ -1,17 +1,17 @@ -# Product Recommendations – Retail (TigerGraph Solution Kit) +# Product Recommendations - Retail (TigerGraph Solution Kit) A TigerGraph solution kit that builds a graph-powered product recommendation engine for retail and e-commerce. -This kit provides a complete graph environment — schema, sample data, -and GSQL queries — to understand customer behavior and generate personalized -product recommendations based on purchases, attributes, and shared affinities. +This kit provides a complete graph environment to understand customer behavior +and generate personalized product recommendations based on purchases, +attributes, and shared affinities. With this kit you can: - Cluster customers by behavior and numeric attributes (e.g., age). - Discover products that are frequently co-purchased or share similar audiences. -- Build and explore combined “feature” nodes (clusters, styles, attributes). +- Build and explore combined "feature" nodes (clusters, styles, attributes). - Generate ranked product recommendations for one or many customers. --- @@ -33,10 +33,10 @@ With this kit you can: ## Overview Product recommendations are about connecting who your customers are with -what they buy and why they choose certain products — and then turning +what they buy and why they choose certain products - and then turning those insights into relevant suggestions in real time. Graph databases like TigerGraph are well-suited for this because they can traverse deeply connected -data (e.g., customers → purchases → products → attributes → clusters) efficiently. +data (e.g., customers -> purchases -> products -> attributes -> clusters) efficiently. This solution kit models a retail / e-commerce environment with: @@ -157,7 +157,7 @@ queries and what you can get from each of them, grouped by business purpose. ### 1. Customer Segmentation & Profiling -**k_means(v_type, attr_set, min_cluster_count, max_cluster_count, …)** +**k_means(v_type, attr_set, min_cluster_count, max_cluster_count, ")** This uses the K-Means algorithm to turn random data into groups of k clusters. Clusters customers (or any vertex type) are grouped into behaviorally similar groups based on numerical attributes. The query normalizes the selected @@ -174,7 +174,7 @@ A/B tests, or to enrich downstream analytics with stable cluster labels. **combine_features(hub_v_type, e_type, target_v_type, feature_v_type, hub_threshold, split_threshold)** This builds combined feature concepts from frequently co-occurring attributes. -The query first finds “hub” feature vertices whose degree is above hub_threshold. +The query first finds "hub" feature vertices whose degree is above hub_threshold. Hub vertices are those that have a very high degree - above our threshold. It then looks for other feature vertices that share a moderate number of common target vertices with each hub vertex, where the shared neighbor count is between 2 and @@ -186,7 +186,7 @@ We flag `e.ignore_edge = TRUE` on the Combined_Feature edges for future queries to avoid them and reduce noise. Use this to turn very popular but generic features into more informative -interaction features (e.g. “Nike × RunningShoes” instead of just “Nike”), +interaction features (e.g. "Nike x RunningShoes" instead of just "Nike"), to enrich recommendation or similarity queries with more meaningful combinations of customer and product attributes. @@ -199,7 +199,7 @@ ignore_threshold, recommendation_count, data_types, edge_importance_factors, vertex_degree_scales, customer_popularity_scale, item_popularity_scale)** This query generates personalized product recommendations for each customer by -combining customer–customer similarity and item–item co-purchasing patterns +combining customer-customer similarity and item-item co-purchasing patterns over the graph. It outputs a ranked list of Product_Variant vertices for each source @@ -227,7 +227,7 @@ RUN QUERY recommend_products({}, 0, 1) The first parameter (`{}`) leaves `src_customer_input` empty, so the query falls back to **batch mode** (controlled by `batch_index` and `num_batches`). -`batch_index = 0` and `num_batches = 1` mean “run for all customers in a single batch,” +`batch_index = 0` and `num_batches = 1` mean "run for all customers in a single batch," returning up to the default `recommendation_count` recommendations per customer. ## Using Your Own Data @@ -245,13 +245,13 @@ TigerGraph-hosted public S3 bucket, using the data source and loading job define TigerGraph supports multiple loading options. For detailed, up-to-date examples, please refer to the official documentation: -- **Data loading overview** – supported sources and general workflow +- **Data loading overview** - supported sources and general workflow https://docs.tigergraph.com/tigergraph-server/current/data-loading/data-loading-overview -- **Data loading entry page** – index of all loading methods (local files, cloud storage, warehouses, Spark, etc.) +- **Data loading entry page** - index of all loading methods (local files, cloud storage, warehouses, Spark, etc.) https://docs.tigergraph.com/tigergraph-server/current/data-loading/ -- **Load data from cloud storage** – step-by-step guide for Amazon S3, Google Cloud Storage, and Azure Blob Storage +- **Load data from cloud storage** - step-by-step guide for Amazon S3, Google Cloud Storage, and Azure Blob Storage https://docs.tigergraph.com/tigergraph-server/current/data-loading/load-from-cloud At a high level, the steps to use your own data are: @@ -260,7 +260,7 @@ At a high level, the steps to use your own data are: For local files on the TigerGraph server, you can omit `DATA_SOURCE`. 2. Update the `DEFINE FILENAME` lines in `load_all` so they point to your own URIs (for example, your customer, product, and transaction files). -3. Keep the `LOAD` blocks the same unless your column structure changes—if it +3. Keep the `LOAD` blocks the same unless your column structure changes-if it does, adjust the `VALUES(...)` mappings so each vertex/edge attribute (for example `Customer.id`, `Product_Variant.id`, `Interacted.timestamp`, `Has_Attribute.score`) receives the correct input column. @@ -273,7 +273,7 @@ gsql loading_job/load_data.gsql ## Resetting the Environment -Sometimes you’ll want to wipe the graph environment and start fresh — for example, +Sometimes you'll want to wipe the graph environment and start fresh - for example, when switching to a new dataset or after changing the schema, loading logic, or recommendation queries. @@ -286,8 +286,8 @@ This repository provides a reset script for this purpose. ### Full reset using the `reset` script -If you need a complete reset of the Product Recommendation solution kit — not just -data, but also the loading job, data source, queries, and the graph definition — +If you need a complete reset of the Product Recommendation solution kit - not just +data, but also the loading job, data source, queries, and the graph definition - you can use the `reset/reset.gsql` script. This script performs the following operations on the `Product_Recommendation` graph: diff --git a/connected_customer/product_recommendations/queries/combine_features.gsql b/connected_customer/product_recommendations/queries/combine_features.gsql index 0cafa1b4..55923692 100644 --- a/connected_customer/product_recommendations/queries/combine_features.gsql +++ b/connected_customer/product_recommendations/queries/combine_features.gsql @@ -6,31 +6,46 @@ CREATE QUERY combine_features( INT hub_threshold, INT split_threshold) SYNTAX V2 { -/* - Description: - Builds Combined_Feature vertices from frequently co-occurring - feature vertices, rerouting edges through Combined_Feature nodes - to reduce hub noise. - Parameters: - hub_v_type: - Feature vertex types treated as potential hubs. - e_type: - Edge types used to traverse hubs, targets, and features. - target_v_type: - Context vertex type linking features (default: "Customer"). - feature_v_type: - Feature vertex types paired with hubs via the target vertices. - hub_threshold: - Minimum degree on e_type for a feature to be a hub. - split_threshold: - Maximum shared targets for a feature pair to be combined. + /* + Query Name: combine_features + Feature Co-Occurrence Grouping + + Purpose: + Identify frequently co-occurring feature pairs around high-degree (“hub”) + feature vertices and collapse them into `Combined_Feature` vertices. This + reduces hub noise and creates more informative composite features for + downstream models or recommenders. + + High-Level Flow: + • Hub detection: + Select `hub_v_type` vertices whose degree over `e_type` exceeds + `hub_threshold` and mark them as hubs. + • Co-occurrence counting: + For each hub–feature pair connected through `target_v_type` vertices + (e.g., Customers), count how many targets they share. + • Pair selection: + Keep hub–feature pairs whose shared target count lies between 2 and + `split_threshold`, treating them as meaningful but not overly common + co-occurrences. + • Combined feature creation: + For each selected pair and its associated targets: + – Create/attach a `Combined_Feature` vertex. + – Flag original hub/feature edges as `ignore_edge = TRUE`. + – Connect targets to the combined feature via `Has_Attribute`. + – Link original feature vertices to the combined feature via `Linked`. + + Configuration: + `hub_v_type`, `feature_v_type`, `target_v_type`, and `e_type` define the + schema context (which vertices/edges are treated as hubs, features, and + targets), while `hub_threshold` and `split_threshold` control how aggressively + features are combined. Output: - Creates Combined_Feature vertices, connects targets via - Has_Attribute, links original features via Linked, and flags - original hub/feature edges as ignored. -*/ + Inserts `Combined_Feature` vertices, `Has_Attribute` edges from targets to + combined features, `Linked` edges from original features to combined features, + and marks affected original edges as ignored. + */ TYPEDEF TUPLE Vertex_Tuple; MapAccum> @intersection_size_map; OrAccum @hub; diff --git a/connected_customer/product_recommendations/queries/k_means.gsql b/connected_customer/product_recommendations/queries/k_means.gsql index 9473c42a..e8f4cc28 100644 --- a/connected_customer/product_recommendations/queries/k_means.gsql +++ b/connected_customer/product_recommendations/queries/k_means.gsql @@ -11,27 +11,52 @@ CREATE QUERY k_means( BOOL use_custom_timestamp=FALSE, DATETIME custom_timestamp=to_datetime("1970-01-01")) SYNTAX V1 { -/* - Description: - Clusters vertices of type v_type using K-Means on numeric attributes - in attr_map, sweeping K from min_cluster_count to max_cluster_count and - choosing the best K by SSE. - Parameters: - v_type (STRING, default "Customer") - Vertex type to cluster. - attr_set (SET, required) - JSON strings like "[idx,\"attr_key\"]" referring to keys in attr_map. - min_cluster_count, max_cluster_count (INT, required) - Range of K values to try; max must be >= min. - cluster_inc, random_iter_count, conv_iter_limit, conv_threshold, - random_seed, use_custom_timestamp, custom_timestamp - Standard K-Means and timestamp controls (see README for details). + /* + Query Name: k_means + K-Means Clustering over Vertex Attribute Vectors + + Purpose: + Cluster vertices of type `v_type` using K-Means on normalized numeric + features taken from each vertex’s `attr_map`. The query sweeps K from + `min_cluster_count` to `max_cluster_count` and picks the “best” cluster + count based on changes in sum of squared errors (SSE), approximating the + elbow point. + + High-Level Flow: + • Build feature vectors: + Parse `attr_set` to select a subset of numeric keys from `attr_map`, + normalize each feature across all vertices, and construct per-vertex + vectors. + • K sweep & initialization: + For each candidate K in [min_cluster_count, max_cluster_count] (step + `cluster_inc`), run up to `random_iter_count` random initializations of + centroids using a seeded PRNG (`random_seed`). + • K-Means iterations: + Assign each vertex to its closest centroid, recompute centroids from + assigned members, and repeat until convergence (change in SSE below + `conv_threshold` or `conv_iter_limit` reached). + • Model selection & merge: + Track SSE across K values and iterations, approximate the elbow via + second differences in SSE, and optionally merge nearly identical + centroids. + • Materialization: + For the chosen K and iteration, insert one `Cluster` vertex per + centroid and an `In_Cluster` edge from each vertex to its cluster, + stamped with either `now()` or `custom_timestamp`. + + Configuration: + Parameters control the vertex type (`v_type`), feature subset (`attr_set`), + K range and convergence behavior (min/max_cluster_count, cluster_inc, + random_iter_count, conv_iter_limit, conv_threshold, random_seed), and + the timestamp used on `Cluster`/`In_Cluster` (`use_custom_timestamp`, + `custom_timestamp`). Output: - Inserts Cluster vertices (centroids) and In_Cluster edges from each - input vertex to its assigned Cluster; prints the number of clusters. -*/ + Inserts `Cluster` vertices and `In_Cluster` edges for all clustered + vertices, then prints the number of clusters created. + */ + TYPEDEF TUPLE Centroid_Distance; TYPEDEF TUPLE Cluster_Count_Tuple; // accum to store the smallest distance diff --git a/connected_customer/product_recommendations/queries/recommend_products.gsql b/connected_customer/product_recommendations/queries/recommend_products.gsql index 2bc327e7..b47ca174 100644 --- a/connected_customer/product_recommendations/queries/recommend_products.gsql +++ b/connected_customer/product_recommendations/queries/recommend_products.gsql @@ -11,42 +11,38 @@ CREATE DISTRIBUTED QUERY recommend_products( FLOAT customer_popularity_scale = 1, FLOAT item_popularity_scale = 1) SYNTAX V1 { -/* - Description: - Generates personalized product recommendations for each customer using a - hybrid of customer–customer and item–item collaborative filtering. - - Parameters: - src_customer_input: - Optional set of Customer vertices to recommend for. - batch_index: - Zero-based index of the current customer batch - num_batches: - Total number of batches to partition Customer vertices into - target_batch: - Number of batches used when traversing target vertex - ignore_threshold: - Minimum per-customer popularity score for an item to be kept; - recommendation_count: - Maximum number of top recommended items to keep per source - customer. - data_types: - JSON string configuring which vertex and edge types are treated - as “Feature” vs “Target” for Customer and Item similarity. - edge_importance_factors: - JSON string mapping edge types to scalar importance factors for - customer- and item-side similarity calculations. - vertex_degree_scales: - Set of vertex types whose contribution is inversely scaled by their degree - customer_popularity_scale: - Global multiplier applied to scores from customer–customer similarity–based popularity. - item_popularity_scale: - Global multiplier applied to scores from item–item co-occurrence–based popularity. + + /* + Query Name: recommend_products + Hybrid Customer–Item Collaborative Filtering Recommender + + Purpose: + For each source `Customer`, generate a ranked list of `Product_Variant` + recommendations using a combination of: + • customer–customer similarity (shared features/interactions) + • item–item co-occurrence (co-purchasing behavior). + + High-Level Flow: + • Select source customers (explicit input set or batched over all customers). + • Build customer–customer similarity from feature overlaps, weighted by + interaction strength, edge importance, and feature popularity. + • Derive item popularity per customer from similar customers and + co-occurring items. + • Optionally reweight by category importance and vertex-degree scaling. + • Keep up to `recommendation_count` best items per customer. + + Configuration: + Key parameters control which customers are processed (src_customer_input, + batch_index/num_batches), filtering and list length (ignore_threshold, + recommendation_count), and how schema/weights are applied + (data_types, edge_importance_factors, vertex_degree_scales, + customer_popularity_scale, item_popularity_scale). Output: - For each source Customer, a list of up to `recommendation_count` - Product_Variant items with normalized recommendation scores. -*/ + For each source `Customer`, returns a list of up to `recommendation_count` + `Product_Variant` recommendations with normalized scores, ordered by + descending score. + */ TYPEDEF TUPLE Item_Tuple; HeapAccum (recommendation_count, score DESC, item DESC) @recommended_items; MapAccum> @sum_intersection_size, @@sum_set_size; diff --git a/financial_crime/application_fraud/README.md b/financial_crime/application_fraud/README.md index ed83909b..515e2db5 100644 --- a/financial_crime/application_fraud/README.md +++ b/financial_crime/application_fraud/README.md @@ -3,8 +3,7 @@ A TigerGraph solution kit for detecting **application fraud** and uncovering fraud rings using shared Personally Identifiable Information (PII) and **entity resolution**. -This project provisions a complete graph environment — schema, sample data, -and GSQL queries — to analyze how applications are connected via devices, IPs, +This project provisions a complete graph environment to analyze how applications are connected via devices, IPs, addresses, and other identity signals. With this kit you can: @@ -39,10 +38,10 @@ before they are approved, when: - Multiple applications share the same PII. - Applications are part of a larger, tightly connected ring. -- New applications appear “close” in the graph to known fraud. +- New applications appear "close" in the graph to known fraud. Relational databases struggle to track these many-to-many, multi-hop -relationships at scale. TigerGraph’s native graph engine is well suited +relationships at scale. TigerGraph's native graph engine is well suited because it can: **Fast traversals.** Move from an application through its PII and back to other @@ -290,7 +289,7 @@ This keeps the graph lean and avoids clutter from obsolete components. --- **output_application_cc_to_file(output_file_path)** -Exports a simple mapping of `Application` → `Connected_Component` into a CSV +Exports a simple mapping of `Application` -> `Connected_Component` into a CSV file. This is useful for joining graph-based communities back into downstream systems such as your warehouse, feature store, or BI dashboards. @@ -300,7 +299,7 @@ systems such as your warehouse, feature store, or BI dashboards. **batch_application_cc_features(connections, output_file_path)** Generates a CSV of connected-component features for every `Application`. -For each application’s community, it counts distinct PII nodes and how many +For each application's community, it counts distinct PII nodes and how many applications are connected via each PII type. Use this to build offline training datasets capturing the structure and density of each fraud ring. @@ -383,8 +382,8 @@ optionally filtered by application fraud status and/or application status. --- **get_top_k_products_by_num_applications_with_other(top_k, input_application_fraud_status, input_application_status)** -Provides a “top-k + Other” breakdown: the top products by number of applications -plus an aggregated “Other Products” bucket. +Provides a "top-k + Other" breakdown: the top products by number of applications +plus an aggregated "Other Products" bucket. --- @@ -398,8 +397,8 @@ into uplift and dollar amounts. --- **insights_get_application_count_by_binary_classification(...)** -Takes confusion-matrix counts for two model runs—typically a baseline model -and a model augmented with graph features—and returns a side-by-side table +Takes confusion-matrix counts for two model runs- a baseline model +and a model augmented with graph features-and returns a side-by-side table plus totals. --- @@ -412,7 +411,7 @@ negative predictive value) for both scenarios and expresses them as percentages. **insights_get_detected_fraud_amount(no_tg_true_positive, tg_true_positive, avg_loss_per_app_fraud)** Given an average monetary loss per fraudulent application, this query converts -true positives into “detected fraud dollars” with and without graph features. +true positives into "detected fraud dollars" with and without graph features. --- @@ -450,7 +449,7 @@ gsql 'USE GRAPH Application_Fraud RUN QUERY get_application_cc_features("7da7048 ``` -This returns a JSON-style record with the application’s connected-component +This returns a JSON-style record with the application's connected-component ID, fraud count in its ring, and PII-based graph feature that you can feed directly into an ML model or rules engine. @@ -466,20 +465,20 @@ TigerGraph-hosted public S3 bucket, using the loading job defined in TigerGraph supports multiple loading options. For detailed, up-to-date examples, please refer to the official documentation: -- **Data loading overview** – supported sources and general workflow +- **Data loading overview** - supported sources and general workflow https://docs.tigergraph.com/tigergraph-server/4.2/data-loading/data-loading-overview -- **Data loading entry page** – index of all loading methods (local files, cloud storage, warehouses, Spark, etc.) +- **Data loading entry page** - index of all loading methods (local files, cloud storage, warehouses, Spark, etc.) https://docs.tigergraph.com/tigergraph-server/4.2/data-loading/ -- **Load data from cloud storage** – step-by-step guide for Amazon S3, Google Cloud Storage, and Azure Blob Storage +- **Load data from cloud storage** - step-by-step guide for Amazon S3, Google Cloud Storage, and Azure Blob Storage https://docs.tigergraph.com/tigergraph-server/4.2/data-loading/load-from-cloud ## Resetting the Environment -Sometimes you’ll want to wipe the graph data and start fresh — for example, when +Sometimes you'll want to wipe the graph data and start fresh - for example, when switching to a new dataset or after updating the schema and loading logic. This repository provides a utility query to delete all data, and a separate diff --git a/financial_crime/transaction_fraud/README.md b/financial_crime/transaction_fraud/README.md index d61b4c46..2e5a7ee2 100644 --- a/financial_crime/transaction_fraud/README.md +++ b/financial_crime/transaction_fraud/README.md @@ -1,10 +1,14 @@ # Narratives - -Credit card transaction fraud detection identifies and prevents unauthorized or deceptive transactions in real-time. It analyzes transaction data, including cardholder information, transaction details, and historical patterns, to detect anomalies and suspicious activity indicative of fraud. - -TigerGraph models complex relationships and patterns among entities such as cardholders, merchants, transactions, and geographic locations. This enable detection of fraudulent networks and patterns that may be difficult to uncover using traditional relational databases. - -Using graph algorithms enable organizations to detect and respond to fraudulent transactions quickly and efficiently, ultimately reducing financial losses and protecting consumers from fraudulent activity. +Credit card transaction fraud detection identifies and prevents unauthorized +or deceptive transactions in real-time. It analyzes transaction data, +including cardholder information, transaction details, and historical patterns, +to detect anomalies and suspicious activity indicative of fraud. TigerGraph +models complex relationships and patterns among entities such as cardholders, +merchants, transactions, and geographic locations. This enable detection of +fraudulent networks and patterns that may be difficult to uncover using traditional +relational databases. Using graph algorithms enable organizations to detect and +respond to fraudulent transactions quickly and efficiently, ultimately reducing +financial losses and protecting consumers from fraudulent activity. # Components From 90b983b80afff6b47a922f5dbed1846fc367ecca Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Mon, 8 Dec 2025 16:25:17 +0530 Subject: [PATCH 27/55] mule detection documentation standardized --- .../mule_account_detection/README.md | 273 ++++++++++++++++-- 1 file changed, 244 insertions(+), 29 deletions(-) diff --git a/financial_crime/mule_account_detection/README.md b/financial_crime/mule_account_detection/README.md index c661cf12..9061b225 100644 --- a/financial_crime/mule_account_detection/README.md +++ b/financial_crime/mule_account_detection/README.md @@ -1,23 +1,53 @@ -# Narratives - -Mule account detection in financial crime is a critical endeavor aimed at identifying bank accounts that are used to receive and disperse money from illicit activities. These mule accounts, which may be involved either knowingly or unknowingly in these operations, present a significant challenge for financial institutions. The rapid movement of funds through an extensive and seemingly unconnected network of accounts, spread across numerous financial institutions, complicates the tracking and halting of such illicit transactions. Financial institutions are therefore tasked with detecting this activity promptly to prevent further fund transfer and ensure the return of assets to their rightful owners. - -TigerGraph's solutions address this challenge through a suite of sophisticated graph algorithms that enable real-time monitoring, feature engineering for machine learning, and anomaly detection to trace illegal funds effectively. - -Community Detection is employed to unveil clusters within the transaction network, highlighting groups of accounts that work in concert to move illicit funds. The Centrality (PageRank) algorithm identifies key accounts that act as central nodes in the distribution network, crucial for disrupting the flow of illicit money. Closeness (Shortest Path) analysis reveals the most direct routes for money laundering, aiding in the trace back to the source. Lastly, Deep Link Analysis uncovers hidden connections between accounts, providing a comprehensive understanding of the network's structure and operation. Together, these algorithms form the backbone of TigerGraph's approach to dismantling the complex networks of mule accounts, safeguarding the financial system against the movement of illicit funds. - -# Components - -This repository includes multiple components: - -- `Schema` - Definition of database schema. -- `Queries` - Collection of GSQL queries. -- `Mock Data` - Sample data. -- `Loading Jobs` - two loading jobs: one for cloud integration and the other for on-prem local deployment. -- `Insights Applications` - ROI Dashboard and Analytical Insights Applicaitions. -- `README.md` - This usage guide. -- `setup.sh` - Automated setup script. - +# Mule Account Detection Solution +**A graph-based analytics solution for identifying mule accounts, suspicious transactions, and hidden fraud patterns in real-time.** + +--- + +## Contents + +- [Overview](#overview) +- [ML Features](#ml-features) +- [Queries Included](#queries-included) +- [Components](#components) +- [Prerequisites](#prerequisites) +- [Quickstart Guide](#quickstart-guide) +- [Query Execution Order and Explanations](#query-execution-order-and-explanations) +- [Load Sample Data](#load-sample-data) +- [Graph Schema](#graph-schema) +- [Sample Dataset](#sample-dataset) +- [Using Your Own Data](#using-your-own-data) +- [Cleanup (Optional)](#cleanup-optional) +- [Insights Applications](#insights-applications) + +--- + +## Overview + +Mule account detection in financial crime is a critical endeavor aimed at +identifying bank accounts that are used to receive and disperse money from +illicit activities. These mule accounts, which may be involved either knowingly +or unknowingly in these operations, present a significant challenge for financial +institutions. The rapid movement of funds through an extensive and seemingly +unconnected network of accounts, spread across numerous financial institutions, +complicates the tracking and halting of such illicit transactions. Financial +institutions are therefore tasked with detecting this activity promptly to +prevent further fund transfer and ensure the return of assets to their rightful +owners. TigerGraph's solutions address this challenge through a suite of +sophisticated graph algorithms that enable real-time monitoring, feature engineering +for machine learning, and anomaly detection to trace illegal funds effectively. +Community Detection is employed to unveil clusters within the transaction network, +highlighting groups of accounts that work in concert to move illicit funds. +The Centrality (PageRank) algorithm identifies key accounts that act as +central nodes in the distribution network, crucial for disrupting the flow +of illicit money. Closeness (Shortest Path) analysis reveals the most direct +routes for money laundering, aiding in the trace back to the source. Lastly, +Deep Link Analysis uncovers hidden connections between accounts, providing a +comprehensive understanding of the network's structure and operation. Together, +these algorithms form the backbone of TigerGraph's approach to dismantling the +complex networks of mule accounts, safeguarding the financial system against +the movement of illicit funds. + +--- # ML Features ### Here is a list of graph features we provide in the solution kit: @@ -49,11 +79,89 @@ This repository includes multiple components: 9. **Multi-Hop Mule Account Analysis**: We calculate the total number of mule accounts within a specified number of hops from each account. This helps assess the proximity and density of mule accounts in the network. -# Instructions +## Queries Included + +Below is a list of queries included in this solution. + +### **1. Identify Potential Mule Accounts** +Scores accounts based on suspicious behavior patterns such as high-velocity flows, layering, quick pass-through transactions, etc. + +### **2. Multi-hop Transaction Tracing** +Finds the complete chain of fund movement starting from a given account up to N hops. + +### **3. Circular Transaction Finder** +Detects loops where money moves between accounts and eventually returns to the origin. + +### **4. Suspicious Cluster Detection** +Uses graph connectivity to identify tightly linked groups behaving in coordinated ways. + +### **5. Behavioral Anomaly Detection** +Compares current account transaction behavior against its own historical patterns. + +### **6. Account Profile Explorer** +Retrieves everything related to a specific account: customer, devices, IPs, merchants, inflow/outflow, partner accounts, and more. + +### **7. Counterparty Risk Aggregation** +Measures the risk level of counterparties transacting with a given account. + +### **8. Velocity-based Red Flags** +Detects spikes in incoming or outgoing amounts over short windows. + + +# Components +This repository includes multiple components: + +- `Schema` - Definition of database schema. +- `Queries` - Collection of GSQL queries. +- `Mock Data` - Sample data. +- `Loading Jobs` - two loading jobs: one for cloud integration and the other for on-prem local deployment. +- `Insights Applications` - ROI Dashboard and Analytical Insights Applicaitions. +- `README.md` - This usage guide. +- `setup.sh` - Automated setup script. + + +## Prerequisites + +You will need: + +- **TigerGraph 3.x or 4.x** +- GSQL client access +- RESTPP endpoint enabled +- Ability to install schema + queries +- Linux shell or Windows WSL for running scripts + +--- + +## Quickstart Guide + + +### 1. Create the schema + +This schema is a subset of the Super Schema in `financial_crime/library/schema`. +First create the global vertex and edge types, then create the graph for the +mule-account detection solution: + +```bash +gsql /home/tigergraph/solution_kits/financial_crime/library/schema/general_global_financial_crime_super_schema.gsql + +gsql /home/tigergraph/solution_kits/financial_crime/mule_account_detection/schema/create_schema_bottomup.gsql### **3. Install queries** +``` + + +### 2. Load data + +Load data into the schema by running the local loading job script: + +```bash + gsql /home/tigergraph/solution_kits/financial_crime/mule_account_detection/load/local_loading_job.gsql +``` + + +### 3. Install queries +```bash +./install_queries.sh +``` -1. **Schema Creation**: This schema is a subset of the Super Schema in financial_crime/library/schema. To create the schema, first run `gsql /home/tigergraph/solution_kits/financial_crime/library/schema/general_global_financial_crime_super_schema.gsql` to generate global vertex and edge types. Then, user can run `/home/tigergraph/solution_kits/financial_crime/mule_account_detection/schema/create_schema_bottomup.gsql` to create the graph. -2. **Data Loading**: Load data into the schema by running the data loading job with the `local_loading_job.gsql` script. -3. **Query Installation**: Completes the setup by installing necessary queries through the `install_queries.sh` script. ## Query Execution Order and Explanations @@ -73,8 +181,12 @@ Subsequent queries can be run following the completion of the aforementioned two ### Step 3: Feature Engineering Queries -The feature engineering queries generate feature values to feed the downstream ML model. These queries aggregate the values and propagate the features as attributes of the `Account` vertex. To use the ML model documented in the `model` folder, the following feature engineering queries need to be executed. The right column lists the attribute name corresponding to the query. -| Query | Attribute Name(Feature) | Type | +The feature engineering queries generate feature values to feed the downstream ML model. +These queries aggregate the values and propagate the features as attributes of the `Account` vertex. +To use the ML model documented in the `model` folder, the following feature engineering queries need +to be executed. The right column lists the attribute name corresponding to the query. + +| Query | Attribute Name(Feature) | Type | |-------------------------------------|----------------------------|--------| | `tg_wcc_account_with_weights` | `com_size` & `com_id` | INT | | `tg_pagerank_wt_account` | `pagerank` | FLOAT | @@ -88,15 +200,117 @@ The feature engineering queries generate feature values to feed the downstream M ### Step 4: Investigation Queries and Information Retrieval Queries -These queries can be executed at any time and have no dependencies on the previous queries. The investigation queries are designed to help investigate certain accounts, facilitating deep analysis. These queries include: +These queries can be executed at any time and have no dependencies on the previous queries. +The investigation queries are designed to help investigate certain accounts, facilitating deep analysis. These queries include: - `single_party_PII`: Provides personally identifiable information for a party involved in transactions. - `attributes_to_party_traversal`: Traverses from transaction attributes to the parties involved. - `party_full_address`: Retrieves the full address details for a party involved in transactions. -## Mock Data +## Load Sample Data + + +You can load the provided CSV files via: + +```bash +gsql -g MuleDetection run loading_job load_sample +``` + +This loads: + +- Customers +- Accounts +- Transactions +- Devices +- IPs +- Merchants +- Geolocation data + +--- + +## Graph Schema + +The sample schema includes the following **vertex types**: + +- **Customer** — account owner +- **Account** — bank accounts under the customer +- **Transaction** — money movement events +- **Device** — login devices +- **IP_Address** — IP used for login +- **Merchant** — merchant receiving funds +- **Location** — geographical tag + +### **Edges** + +- `OWNS` — Customer → Account +- `HAS_TRANSACTION` — Account → Transaction +- `SENT_TO` — Transaction → Account (target) +- `USED_DEVICE` — Customer → Device +- `USED_IP` — Device → IP_Address +- `VISITED` — Customer → Location +- `RECEIVED_BY` — Transaction → Merchant + +This structure enables deep multi-hop investigative analytics. + +--- + +## Sample Dataset + +The `data` folder is populated with sample data files. These files are crafted +to closely mimic real-world scenarios, providing a realistic context for testing and demonstration purposes. + +This dataset includes: + +- Customers with IDs and profiles +- Accounts linked to customers +- Synthetic but realistic transactions +- Complex money movement chains +- Suspicious clusters intentionally injected +- Devices, IPs, and merchants for contextual enrichment + +This dataset allows you to test every query in the solution end-to-end. + +--- +-- + +## Using Your Own Data + +Update your mapping in: + +``` +/data/your_sources/ +``` + +Then modify: + +``` +load.gsql +``` + +to match your column names. + +You may also adjust: + +- scoring weights +- anomaly thresholds +- cycle detection sensitivity + +--- + +## Cleanup (Optional) + +Remove all vertices, edges, and data: + +```bash +gsql -g MuleDetection drop all +``` + +Or: + +```bash +gsql drop graph MuleDetection +``` -The `data` folder is populated with sample data files. These files are crafted to closely mimic real-world scenarios, providing a realistic context for testing and demonstration purposes. ## Insights Applications @@ -116,3 +330,4 @@ There are two insights applications: - Shortest Path. - IP Sharing Analysis. - Device Sharing Analysis. + From b6c1a18e5ebf04aac1069ec8fc3f93391946a318 Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Mon, 8 Dec 2025 16:29:34 +0530 Subject: [PATCH 28/55] removed non ASCII characters from mule detection --- .../mule_account_detection/README.md | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/financial_crime/mule_account_detection/README.md b/financial_crime/mule_account_detection/README.md index 9061b225..b7eb64ec 100644 --- a/financial_crime/mule_account_detection/README.md +++ b/financial_crime/mule_account_detection/README.md @@ -232,23 +232,23 @@ This loads: The sample schema includes the following **vertex types**: -- **Customer** — account owner -- **Account** — bank accounts under the customer -- **Transaction** — money movement events -- **Device** — login devices -- **IP_Address** — IP used for login -- **Merchant** — merchant receiving funds -- **Location** — geographical tag +- **Customer** - account owner +- **Account** - bank accounts under the customer +- **Transaction** - money movement events +- **Device** - login devices +- **IP_Address** - IP used for login +- **Merchant** - merchant receiving funds +- **Location** - geographical tag ### **Edges** -- `OWNS` — Customer → Account -- `HAS_TRANSACTION` — Account → Transaction -- `SENT_TO` — Transaction → Account (target) -- `USED_DEVICE` — Customer → Device -- `USED_IP` — Device → IP_Address -- `VISITED` — Customer → Location -- `RECEIVED_BY` — Transaction → Merchant +- `OWNS` - Customer -> Account +- `HAS_TRANSACTION` -> Account -> Transaction +- `SENT_TO` - Transaction -> Account (target) +- `USED_DEVICE` - Customer -> Device +- `USED_IP` - Device -> IP_Address +- `VISITED` - Customer -> Location +- `RECEIVED_BY` - Transaction -> Merchant This structure enables deep multi-hop investigative analytics. From d21967b9ab5b5bdfb98499c2d60087382baf6ce1 Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Tue, 9 Dec 2025 13:20:28 +0530 Subject: [PATCH 29/55] enchanced README.md for transaction fraud kit --- financial_crime/transaction_fraud/README.md | 140 +++++++++++++++----- 1 file changed, 109 insertions(+), 31 deletions(-) diff --git a/financial_crime/transaction_fraud/README.md b/financial_crime/transaction_fraud/README.md index 2e5a7ee2..ad793b5d 100644 --- a/financial_crime/transaction_fraud/README.md +++ b/financial_crime/transaction_fraud/README.md @@ -1,4 +1,24 @@ -# Narratives +# Credit Card Transaction Fraud Detection + +A real-time, graph-powered solution for detecting credit card fraud rings, +busting coordinated attacks, and preventing financial losses using TigerGraph. + +## Contents +- [Overview](#Overview) +- [ML Features](#ML-Features) +- [Queries Included](#Queries-Included) +- [Components](#Components) +- [Prerequisites](#Prerequisites) +- [Quickstart Guide](#Quickstart-Guide) +- [Query Execution Order and Explanations](#Query-Execution-Order-and-Explanations) +- [Load Sample Data](#Load-Sample-Data) +- [Graph Schema](#Graph-Schema) +- [Sample Dataset](#Sample-Dataset) +- [Using Your Own Data](#Using-Your-Own-Data) +- [Cleanup (Optional)](#Cleanup-(Optional)) +- [Insights Applications](#Insights-Applications) +## Overview + Credit card transaction fraud detection identifies and prevents unauthorized or deceptive transactions in real-time. It analyzes transaction data, including cardholder information, transaction details, and historical patterns, @@ -10,36 +30,40 @@ relational databases. Using graph algorithms enable organizations to detect and respond to fraudulent transactions quickly and efficiently, ultimately reducing financial losses and protecting consumers from fraudulent activity. -# Components - -This repository includes multiple components: - -- `Schema` - Definition of database schema. -- `Queries` - Collection of GSQL queries. -- `Query Installation Automation Script` - Scripts for automating the installation of queries. -- `Mock Data` - Sample data. -- `Loading Jobs` - Scripts for data loading tasks. -- `Insights Applications (JSON)` - Applications for data analysis and visualization, configured in JSON. -- `Machine Learning Model` - Source code for the machine learning model. -- `Unit Test` - Test cases for ensuring code reliability and correctness. - -# Instructions - -The `setup.sh` script is designed to streamline the initial setup process by sequentially executing the following steps: - -1. **Schema Creation**: This schema is a subset of the Super Schema in financial_crime/library/schema. To create the schema, first run `gsql /home/tigergraph/solution_kits/financial_crime/library/schema/general_global_financial_crime_super_schema.gsql` to generate global vertex and edge types. Then, user can run `/home/tigergraph/solution_kits/financial_crime/transaction_fraud/schema/create_schema_bottomup.gsql` to create the graph. -2. **Data Loading**: Load data into the schema by running the data loading job with the `2_load_data.gsql` script. -3. **Query Installation**: Completes the setup by installing necessary queries through the `3_install_queries.sh` script. - -## Installation Note for Queries - -When installing the queries, there is **one specific query** that requires special attention: - -- `mer_shortest_path_length` is a subquery of `all_shortest_path_length`. - -To successfully install `all_shortest_path_length`, the `mer_shortest_path_length` query **must be installed first**. - -The script `3_install_queries.sh` has already been configured to handle this dependency. This note is intended for users who wish to **manually install** these queries. +## Components +- Schema - Graph schema definition +- Queries - Complete GSQL query library +- Query Installation Script - 3_install_queries.sh (handles dependencies automatically) +- Mock Data - Realistic synthetic dataset +- Loading Job - loading_job/load_data.gsql +- Insights Applications - Pre-built JSON dashboards +- Machine Learning Model - Training code & docs in ./model +- Unit Tests - Validation suite + +## Prerequisites +- TigerGraph 3.x or 4.x (Cloud or Enterprise) +- GSQL client access +- RESTPP endpoint enabled +- Linux shell or Windows WSL + +## Quickstart Guide +One-command full setup (recommended): +```bash +./setup.sh +``` +This would run the following commands + +```bash +# 1. Create global vertex/edge types (run once per instance) +gsql /home/tigergraph/solution_kits/financial_crime/library/schema/general_global_financial_crime_super_schema.gsql +# 2. Create the TransactionFraud graph and local schema +gsql /home/tigergraph/solution_kits/financial_crime/transaction_fraud/schema/create_schema_bottomup.gsql +# 3. Load sample data +gsql -g TransactionFraud run loading_job load_data +# 4. Install all queries (automatically respects mer_shortest_path_length dependency) +./3_install_queries.sh +``` +You are now ready to explore fraud patterns and launch the Insights apps! ## Query Execution Order and Explanations @@ -111,6 +135,60 @@ These queries can be executed at any time and have no dependencies on the previo - `card_transactions_stats`: Offers statistical analysis of transactions per card. - `merchant_category_transaction_stats`: Delivers transaction statistics categorized by merchant types. +## ML Features +Graph-derived features automatically attached to each Payment_Transaction vertex for the included machine learning model: +Feature | Query | Attribute on Payment_Transaction | Type +-------------------------------------------|--------------------------------------------|----------------------------------------|------- +Community Size (Merchant & Card) | tg_wcc_*_weight_based | mer_com_size, cd_com_size | INT +PageRank (Merchant & Card) | tg_pagerank_wt_* | mer_pagerank, cd_pagerank | FLOAT +Shortest Path to Known Fraud | all_shortest_path_length | shortest_path_length | INT +Max Amount in Recent Interval | card_merchant_max_amount_within_interval | max_txn_amt_interval | FLOAT +Max Tx Count in Interval | card_merchant_max_txn_count_in_interval | max_txn_cnt_interval | INT +Repeated Card Count | number_of_repeated_card | cnt_repeated_card | INT +Community Tx Stats (count,total,avg,max,min)| community_transaction_* | com_mer_* / com_cd_* | INT/FLOAT +Merchant Category Stats (count,total,avg,max,min) | merchant_category_* | mer_cat_* | INT/FLOAT +In/Out Degree | degrees | indegree, outdegree | INT +Demographics & Context | direct attributes | gender, age, city_pop, occupation, unix_time, mer_cat | STRING/INT + +## Graph Schema +Main vertices: Cardholder, Credit_Card, Payment_Transaction, Merchant, Merchant_Category, Location +Full schema available in ./schema/ + +## Sample Dataset +Realistic synthetic data with intentionally injected fraud patterns: +- Multi-card / multi-merchant fraud rings +- Single large transactions +- High-frequency attacks +- Geographic and demographic enrichment +Ideal for testing all queries, ML model, and visualizations end-to-end. + +## Using Your Own Data +1. Place your files in **/data/your_sources/** +2. Modify column mappings in **loading_job/load_data.gsql** +3. Adjust thresholds or feature logic as needed + +## Cleanup (Optional) +```bash +gsql drop graph TransactionFraud +# or +gsql -g TransactionFraud drop all +``` + +## Insights Applications +**ROI Dashboard** +1. Shows the transaction fraud losses. +2. Live total fraud losses and amount saved by TigerGraph. +3. Live fraud distribution. +4. ML performance. +**Transaction Fraud Analytics** +1. Merchant network pagerank. +2. Shortest path to frauds with user-specified limit. +3. Community. +4. Card With Large Total Transaction Amount. +5. Card with high transaction frequency. +**Note**: For ```Card With Large Total Transaction Amount``` and ```Card with high transaction frequency,``` the graph only shows fraudulent transactions to avoid overcrowded visualizations. + + ## Mock Data The `data` folder is populated with sample data files. These files are crafted to closely mimic real-world scenarios, providing a realistic context for testing and demonstration purposes. From 3bc392612d0b673d443c066e5ec8711db770e6c6 Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Tue, 9 Dec 2025 15:01:34 +0530 Subject: [PATCH 30/55] removed non ASCII characters from querries --- ...nstream_device_topology_visualization.gsql | 10 +-- ...xplore_topology_from_multiple_routers.gsql | 16 ++-- .../explore_topology_from_one_router.gsql | 20 ++--- ...nts_by_impacted_device_and_time_range.gsql | 30 +++---- ...d_events_by_time_range_and_event_type.gsql | 18 ++-- ...tial_incident_source_of_event_by_time.gsql | 26 +++--- ..._related_events_from_incident_by_time.gsql | 30 +++---- .../find_unsecured_servers_visualization.gsql | 18 ++-- .../incident_impact_by_max_radius.gsql | 10 +-- .../top_k_devices_with_most_alerts.gsql | 4 +- .../top_k_devices_with_most_incidents.gsql | 4 +- .../queries/add_purchase_to_inventory.gsql | 8 +- .../queries/check_shipment_capacity.gsql | 12 +-- .../datewise_product_availability.gsql | 10 +-- .../queries/explore_bom.gsql | 10 +-- .../queries/explore_bom_line.gsql | 10 +-- .../queries/get_biggest_customers.gsql | 4 +- .../queries/get_biggest_suppliers.gsql | 2 +- .../queries/plant_failure_impact_nodes.gsql | 12 +-- .../queries/product_quantity_sales_order.gsql | 8 +- .../queries/top_k_product_sales_order.gsql | 2 +- .../top_k_products_purchase_order.gsql | 2 +- .../queries/trace_bom_line_nations.gsql | 10 +-- .../queries/unfulfilled_orders.gsql | 84 +++++++++++++++++++ .../queries/delete_all_cc_connections.gsql | 6 +- .../queries/delete_unused_cc_nodes.gsql | 6 +- .../find_shared_piis_of_two_entities.gsql | 10 +-- .../queries/match_entities.gsql | 12 +-- .../queries/output_entity_cc_to_file.gsql | 10 +-- .../queries/unify_entities.gsql | 6 +- .../queries/combine_features.gsql | 22 ++--- .../queries/k_means.gsql | 14 ++-- .../queries/recommend_products.gsql | 16 ++-- .../batch_application_distance_and_path.gsql | 2 +- .../queries/get_application_cc_features.gsql | 2 +- .../queries/match_application_entities.gsql | 17 ---- .../output_application_cc_to_file.gsql | 5 -- .../queries/batch_party_cc_features.gsql | 6 +- .../batch_party_distance_and_path.gsql | 6 +- .../distance_and_path_to_fraud_party.gsql | 6 +- .../find_shared_piis_of_two_parties.gsql | 6 +- .../queries/get_party_cc_features.gsql | 6 +- .../queries/get_party_fraud_status.gsql | 6 +- .../queries/match_party_entities.gsql | 6 +- .../queries/output_party_cc_to_file.gsql | 10 +-- .../queries/top_connected_component.gsql | 8 +- .../model/model_training.ipynb | 2 +- .../model/training/model_training.md | 2 +- 48 files changed, 307 insertions(+), 245 deletions(-) diff --git a/agile_operations/network_infrastructure/queries/downstream_device_topology_visualization.gsql b/agile_operations/network_infrastructure/queries/downstream_device_topology_visualization.gsql index 2d52a4f0..4a8c8237 100644 --- a/agile_operations/network_infrastructure/queries/downstream_device_topology_visualization.gsql +++ b/agile_operations/network_infrastructure/queries/downstream_device_topology_visualization.gsql @@ -8,14 +8,14 @@ CREATE OR REPLACE QUERY downstream_device_topology_visualization ( Purpose: Visualize the downstream topology from a given device, following the device hierarchy: - Router → Firewall → Switch → Server + Router -> Firewall -> Switch -> Server Also explores multiple downstream Switch layers (k-hop depth traversal). Key Features: - ✔ Identifies all downstream devices classified by device type. - ✔ Follows device hierarchy dynamically based on input device type. - ✔ Limits multi-hop Switch traversal using k_hop_switch_limit. - ✔ Returns devices and connecting edges for visualization. + . Identifies all downstream devices classified by device type. + . Follows device hierarchy dynamically based on input device type. + . Limits multi-hop Switch traversal using k_hop_switch_limit. + . Returns devices and connecting edges for visualization. Inputs: - device: Starting device. diff --git a/agile_operations/network_infrastructure/queries/explore_topology_from_multiple_routers.gsql b/agile_operations/network_infrastructure/queries/explore_topology_from_multiple_routers.gsql index acf6cafc..169defad 100644 --- a/agile_operations/network_infrastructure/queries/explore_topology_from_multiple_routers.gsql +++ b/agile_operations/network_infrastructure/queries/explore_topology_from_multiple_routers.gsql @@ -6,21 +6,21 @@ CREATE OR REPLACE QUERY explore_topology_from_multiple_routers (SET Firewall -> Switch -> Server Key Features: - ✔ Uses BFS to exhaustively discover downstream switches. + Uses BFS to exhaustively discover downstream switches. Inputs: - starter_router_set — Set of router vertices to start the topology exploration. + starter_router_set - Set of router vertices to start the topology exploration. If empty, all routers in the graph are selected automatically. Outputs: - - all_visited_router_devices — Routers connected to input routers via Device_Has_Type - - all_visited_firewalls — First downstream stage (Firewalls) - - all_visited_switches — All connected Switches (multi-hop via BFS) - - all_visited_servers — Downstream Servers - - @@edges_to_display — All edges to display full topology + - all_visited_router_devices - Routers connected to input routers via Device_Has_Type + - all_visited_firewalls - First downstream stage (Firewalls) + - all_visited_switches - All connected Switches (multi-hop via BFS) + - all_visited_servers - Downstream Servers + - @@edges_to_display - All edges to display full topology */ OrAccum @visited; SetAccum @@edges_to_display; diff --git a/agile_operations/network_infrastructure/queries/explore_topology_from_one_router.gsql b/agile_operations/network_infrastructure/queries/explore_topology_from_one_router.gsql index b4db0562..1ae34e46 100644 --- a/agile_operations/network_infrastructure/queries/explore_topology_from_one_router.gsql +++ b/agile_operations/network_infrastructure/queries/explore_topology_from_one_router.gsql @@ -6,22 +6,22 @@ CREATE OR REPLACE QUERY explore_topology_from_one_router (VERTEX starter Purpose: Visualizes the downstream network topology starting from a single router. Traverses devices in this structured order: - Router → Firewall → Switch → Server + Router -> Firewall -> Switch -> Server Key Features: - ✔ Discovers all downstream Firewalls and Switches (including bypass paths) - ✔ Uses BFS to find all connected Switches (multi-hop exploration) - ✔ Captures all edges forming the full topology view + . Discovers all downstream Firewalls and Switches (including bypass paths) + . Uses BFS to find all connected Switches (multi-hop exploration) + . Captures all edges forming the full topology view Input: - starter_router — A single Router vertex that acts as the exploration starting point. + starter_router - A single Router vertex that acts as the exploration starting point. Outputs: - - all_visited_router_devices — Devices directly connected to starter router via Device_Has_Type - - all_visited_firewalls — Firewalls downstream of the router - - all_visited_switches — All discovered Switches (including BFS expansion) - - all_visited_servers — Servers connected downstream of switches - - @@edges_to_display — Complete collection of edges forming the explored topology + - all_visited_router_devices - Devices directly connected to starter router via Device_Has_Type + - all_visited_firewalls - Firewalls downstream of the router + - all_visited_switches - All discovered Switches (including BFS expansion) + - all_visited_servers - Servers connected downstream of switches + - @@edges_to_display - Complete collection of edges forming the explored topology */ OrAccum @visited; SetAccum @@edges_to_display; diff --git a/agile_operations/network_infrastructure/queries/find_events_by_impacted_device_and_time_range.gsql b/agile_operations/network_infrastructure/queries/find_events_by_impacted_device_and_time_range.gsql index 9945fbd3..badf7cc1 100644 --- a/agile_operations/network_infrastructure/queries/find_events_by_impacted_device_and_time_range.gsql +++ b/agile_operations/network_infrastructure/queries/find_events_by_impacted_device_and_time_range.gsql @@ -10,28 +10,28 @@ CREATE OR REPLACE QUERY find_events_by_impacted_device_and_time_range ( Purpose: Finds and visualizes events linked to a specific device within a given time range. - Also traces time hierarchy (Minute → Hour → Date → Month → Year) for chronological visualization. + Also traces time hierarchy (Minute -> Hour -> Date -> Month -> Year) for chronological visualization. Optionally includes detailed event type information (Alert, Incident, and their classifications). Key Features: - ✔ Filters events by impacted device and time window. - ✔ Visualizes full time-based hierarchy for each event. - ✔ Option to include related event type, alert, and incident classifications. - ✔ Collects all traversal edges for easy visualization. + . Filters events by impacted device and time window. + . Visualizes full time-based hierarchy for each event. + . Option to include related event type, alert, and incident classifications. + . Collects all traversal edges for easy visualization. Inputs: - input_device — The impacted device to search events for. - start_time — Minimum timestamp of events to include. - end_time — Maximum timestamp of events to include. - show_event_types_vis — (Optional) If TRUE, includes event/alert/incident types. + input_device - The impacted device to search events for. + start_time - Minimum timestamp of events to include. + end_time - Maximum timestamp of events to include. + show_event_types_vis - (Optional) If TRUE, includes event/alert/incident types. Outputs: - - linked_events_within_time — Events impacting the input device within the time range. - - linked_time_date_minute / hour / date / month / year — Chronologically related time vertices. - - linked_event_types — Event classification (if enabled). - - linked_alerts_within_time, linked_incidents_within_time — Associated alerts and incidents. - - linked_alert_types, linked_incident_types — Alert/Incident categories. - - @@edges_to_display — All edges used to visualize event and time relationships. + - linked_events_within_time - Events impacting the input device within the time range. + - linked_time_date_minute / hour / date / month / year - Chronologically related time vertices. + - linked_event_types - Event classification (if enabled). + - linked_alerts_within_time, linked_incidents_within_time - Associated alerts and incidents. + - linked_alert_types, linked_incident_types - Alert/Incident categories. + - @@edges_to_display - All edges used to visualize event and time relationships. */ SetAccum @@edges_to_display; input_device_set = {input_device}; diff --git a/agile_operations/network_infrastructure/queries/find_events_by_time_range_and_event_type.gsql b/agile_operations/network_infrastructure/queries/find_events_by_time_range_and_event_type.gsql index 5be011a7..be815431 100644 --- a/agile_operations/network_infrastructure/queries/find_events_by_time_range_and_event_type.gsql +++ b/agile_operations/network_infrastructure/queries/find_events_by_time_range_and_event_type.gsql @@ -13,18 +13,18 @@ CREATE OR REPLACE QUERY find_events_by_time_range_and_event_type ( and incident types for comprehensive event analysis. Inputs: - start_time — Minimum datetime filter for event retrieval. - end_time — Maximum datetime filter for event retrieval. - input_event_type_filter — (Optional) Filters by event type. If empty, all types are included. + start_time - Minimum datetime filter for event retrieval. + end_time - Maximum datetime filter for event retrieval. + input_event_type_filter - (Optional) Filters by event type. If empty, all types are included. Outputs: - selected_events_with_info: - • event_id — Event identifier - • event_time — Timestamp of the event - • event_type — Type classification (Security, System, Network, etc.) - • event_alert_type — Enriched alert type data (if any) - • event_incident_type — Enriched incident type data (if any) - • impacted_devices_list — Devices affected by this event + . event_id - Event identifier + . event_time - Timestamp of the event + . event_type . Type classification (Security, System, Network, etc.) + . event_alert_type - Enriched alert type data (if any) + . event_incident_type - Enriched incident type data (if any) + . impacted_devices_list - Devices affected by this event */ MaxAccum @event_type; diff --git a/agile_operations/network_infrastructure/queries/find_potential_incident_source_of_event_by_time.gsql b/agile_operations/network_infrastructure/queries/find_potential_incident_source_of_event_by_time.gsql index 94730f7c..787d2052 100644 --- a/agile_operations/network_infrastructure/queries/find_potential_incident_source_of_event_by_time.gsql +++ b/agile_operations/network_infrastructure/queries/find_potential_incident_source_of_event_by_time.gsql @@ -9,23 +9,23 @@ CREATE OR REPLACE QUERY find_potential_incident_source_of_event_by_time ( Purpose: Identifies potential root-cause incidents for a given input event by: - • Retrieving impacted devices of the event - • Exploring connected devices within a specified hop radius - • Scanning for past incident events within a defined timeframe - • Linking discovered incidents with their incident types + . Retrieving impacted devices of the event + . Exploring connected devices within a specified hop radius + . Scanning for past incident events within a defined timeframe + . Linking discovered incidents with their incident types Inputs: - input_event — The event vertex to investigate potential source incidents for - max_radius — Max number of hops allowed for device connectivity exploration (default: 3) - num_seconds_before_event_start — Time window (in seconds) before input_event.timestamp to search for related incidents (default: 3600) + input_event - The event vertex to investigate potential source incidents for + max_radius - Max number of hops allowed for device connectivity exploration (default: 3) + num_seconds_before_event_start - Time window (in seconds) before input_event.timestamp to search for related incidents (default: 3600) Outputs: - • input_event_set — Original event input - • related_devices_within_radius — Connected devices marked by event radius - • incident_events_from_related_devices — Incident events found in time range - • incidents_from_related_devices — Linked incident entities - • incident_types_of_related_devices — Final categorized incident types - • @@edges_to_display — All traversal edges for visualization/UI mapping + . input_event_set - Original event input + . related_devices_within_radius - Connected devices marked by event radius + . incident_events_from_related_devices - Incident events found in time range + . incidents_from_related_devices - Linked incident entities + . incident_types_of_related_devices - Final categorized incident types + . @@edges_to_display - All traversal edges for visualization/UI mapping */ SetAccum @@related_devices_set; diff --git a/agile_operations/network_infrastructure/queries/find_potential_related_events_from_incident_by_time.gsql b/agile_operations/network_infrastructure/queries/find_potential_related_events_from_incident_by_time.gsql index c69c369d..aecdab4b 100644 --- a/agile_operations/network_infrastructure/queries/find_potential_related_events_from_incident_by_time.gsql +++ b/agile_operations/network_infrastructure/queries/find_potential_related_events_from_incident_by_time.gsql @@ -10,26 +10,26 @@ CREATE OR REPLACE QUERY find_potential_related_events_from_incident_by_time ( Purpose: Identifies events that could be potentially related to a given input incident. It does so by: - • Discovering all devices impacted by the incident - • Expanding outward through connected devices within max_radius hops - • Searching for events (Alerts, Incidents) that occurred within a time window + . Discovering all devices impacted by the incident + . Expanding outward through connected devices within max_radius hops + . Searching for events (Alerts, Incidents) that occurred within a time window starting from the incident occurrence time - • Classifying and linking detected related alerts and incidents with types + . Classifying and linking detected related alerts and incidents with types Inputs: - input_incident — Starting Incident vertex for event correlation analysis - max_radius — Max number of hops to discover connected impacted devices (default: 3) - num_seconds_from_incident_start — Time window (in seconds) after incident start to look for related events (default: 3600) + input_incident - Starting Incident vertex for event correlation analysis + max_radius - Max number of hops to discover connected impacted devices (default: 3) + num_seconds_from_incident_start - Time window (in seconds) after incident start to look for related events (default: 3600) Outputs: - • input_incident_set — Original input incident - • linked_event — Event directly linked to input incident - • impacted_devices_within_radius — All devices reached via radius traversal - • alerts_from_impacted_devices — Related Alerts discovered in time range - • incidents_from_impacted_devices — Related Incidents discovered - • alert_types_of_impacted_devices — Enriched alert category details - • incident_types_of_impacted_devices — Enriched incident category details - • @@edges_to_display — All collected edges for UI / Graph visualization + . input_incident_set - Original input incident + . linked_event - Event directly linked to input incident + . impacted_devices_within_radius - All devices reached via radius traversal + . alerts_from_impacted_devices - Related Alerts discovered in time range + . incidents_from_impacted_devices - Related Incidents discovered + . alert_types_of_impacted_devices - Enriched alert category details + . incident_types_of_impacted_devices - Enriched incident category details + . @@edges_to_display - All collected edges for UI / Graph visualization */ SetAccum @@impacted_devices_set; diff --git a/agile_operations/network_infrastructure/queries/find_unsecured_servers_visualization.gsql b/agile_operations/network_infrastructure/queries/find_unsecured_servers_visualization.gsql index 926d0dad..87ac40fe 100644 --- a/agile_operations/network_infrastructure/queries/find_unsecured_servers_visualization.gsql +++ b/agile_operations/network_infrastructure/queries/find_unsecured_servers_visualization.gsql @@ -7,19 +7,19 @@ CREATE OR REPLACE QUERY find_unsecured_servers_visualization (UINT k_hop_switch_ It identifies Servers that are reachable via Switches without passing through security devices (like Firewalls). What It Does: - • Finds all Routers, Switches, and Servers. - • Traverses paths from Routers → Switches → Servers using Connect_To edges. - • Expands through Switch-to-Switch connections up to 'k_hop_switch_limit' hops. - • Collects all involved vertices and edges for visualization. + . Finds all Routers, Switches, and Servers. + . Traverses paths from Routers -> Switches -> Servers using Connect_To edges. + . Expands through Switch-to-Switch connections up to 'k_hop_switch_limit' hops. + . Collects all involved vertices and edges for visualization. Key Outputs: - - routers_to_display → Starting routers - - switches_to_display → Switches on the unsecured path - - servers_to_display → Potentially unsecured servers - - edges_to_display → All traversal edges for graph visualization + - routers_to_display -> Starting routers + - switches_to_display -> Switches on the unsecured path + - servers_to_display -> Potentially unsecured servers + - edges_to_display -> All traversal edges for graph visualization Parameter: - k_hop_switch_limit → Maximum number of Switch-to-Switch traversal hops (default: 3) + k_hop_switch_limit -> Maximum number of Switch-to-Switch traversal hops (default: 3) */ diff --git a/agile_operations/network_infrastructure/queries/incident_impact_by_max_radius.gsql b/agile_operations/network_infrastructure/queries/incident_impact_by_max_radius.gsql index ff781930..4ccd3777 100644 --- a/agile_operations/network_infrastructure/queries/incident_impact_by_max_radius.gsql +++ b/agile_operations/network_infrastructure/queries/incident_impact_by_max_radius.gsql @@ -18,13 +18,13 @@ CREATE OR REPLACE QUERY incident_impact_by_max_radius ( 4. Collects all impacted devices and edges for visualization. Key Outputs: - - input_incident_set → The provided incident - - linked_event → Event associated with the incident - - impacted_devices_within_radius → All potentially impacted devices - - @@edges_to_display → Edges used during traversal + - input_incident_set -> The provided incident + - linked_event -> Event associated with the incident + - impacted_devices_within_radius -> All potentially impacted devices + - @@edges_to_display -> Edges used during traversal Parameters: - max_radius → Max number of hops to explore propagation (default: 4) + max_radius -> Max number of hops to explore propagation (default: 4) */ SetAccum @@impacted_devices_set; diff --git a/agile_operations/network_infrastructure/queries/top_k_devices_with_most_alerts.gsql b/agile_operations/network_infrastructure/queries/top_k_devices_with_most_alerts.gsql index c8794393..c2214304 100644 --- a/agile_operations/network_infrastructure/queries/top_k_devices_with_most_alerts.gsql +++ b/agile_operations/network_infrastructure/queries/top_k_devices_with_most_alerts.gsql @@ -13,10 +13,10 @@ CREATE OR REPLACE QUERY top_k_devices_with_most_alerts (INT k) { 4. Returns the top K most alert-prone devices. Key Outputs: - - devices → Top K devices with highest aggregated alert_count + - devices -> Top K devices with highest aggregated alert_count Parameter: - k → Number of devices to return (top K) + k -> Number of devices to return (top K) */ SumAccum @alert_count; diff --git a/agile_operations/network_infrastructure/queries/top_k_devices_with_most_incidents.gsql b/agile_operations/network_infrastructure/queries/top_k_devices_with_most_incidents.gsql index 44104d40..7320c9d3 100644 --- a/agile_operations/network_infrastructure/queries/top_k_devices_with_most_incidents.gsql +++ b/agile_operations/network_infrastructure/queries/top_k_devices_with_most_incidents.gsql @@ -13,10 +13,10 @@ CREATE OR REPLACE QUERY top_k_devices_with_most_incidents (INT k) { 4. Returns the top K devices with the most incidents. Key Output: - - devices → List of top K devices ranked by incident_count + - devices -> List of top K devices ranked by incident_count Parameter: - k → Number of top devices to return.. + k -> Number of top devices to return.. */ SumAccum @incident_count; diff --git a/agile_operations/supply_chain_management/queries/add_purchase_to_inventory.gsql b/agile_operations/supply_chain_management/queries/add_purchase_to_inventory.gsql index f934b46b..b530fc7c 100644 --- a/agile_operations/supply_chain_management/queries/add_purchase_to_inventory.gsql +++ b/agile_operations/supply_chain_management/queries/add_purchase_to_inventory.gsql @@ -5,7 +5,7 @@ CREATE OR REPLACE QUERY add_purchase_to_inventory(VERTEX purchas shows the resulting inventory impact for the affected materials in the specified Inventory_Held locations (storage locations / bins). - This is a "what-if" inventory projection query — ideal for MRP simulation, + This is a "what-if" inventory projection query - ideal for MRP simulation, ATP/CTP validation, and real-time inventory planning without touching the actual ERP system. @@ -17,9 +17,9 @@ CREATE OR REPLACE QUERY add_purchase_to_inventory(VERTEX purchas Use empty set {} to include all relevant locations. Output: - • Previous inventory quantity + last_updated timestamp per material/location - • Projected new inventory quantity after GR posting - • Clear message if no inventory exists in the specified locations + . Previous inventory quantity + last_updated timestamp per material/location + . Projected new inventory quantity after GR posting + . Clear message if no inventory exists in the specified locations */ SumAccum @quantity; diff --git a/agile_operations/supply_chain_management/queries/check_shipment_capacity.gsql b/agile_operations/supply_chain_management/queries/check_shipment_capacity.gsql index 5913d154..70b41da2 100644 --- a/agile_operations/supply_chain_management/queries/check_shipment_capacity.gsql +++ b/agile_operations/supply_chain_management/queries/check_shipment_capacity.gsql @@ -4,17 +4,17 @@ CREATE OR REPLACE QUERY check_shipment_capacity(VERTEX so){ Instant Order Fulfillment Status & Shipment Completion Check. Compares the ordered quantity on a Sales Order against the quantity actually shipped (via Shipment_Item vertices) and immediately reports: - • Total ordered quantity per material - • Quantity already fulfilled (shipped) - • Remaining unfulfilled quantity + . Total ordered quantity per material + . Quantity already fulfilled (shipped) + . Remaining unfulfilled quantity Parameters: so (VERTEX): The Sales Order vertex to analyze Output: - @@prod_total_quantity → Total ordered quantity per material - @@prod_fulfillment → Quantity already shipped/fulfilled - @@prod_unfulfillment → Remaining open quantity (backorder + @@prod_total_quantity -> Total ordered quantity per material + @@prod_fulfillment -> Quantity already shipped/fulfilled + @@prod_unfulfillment -> Remaining open quantity (backorder */ diff --git a/agile_operations/supply_chain_management/queries/datewise_product_availability.gsql b/agile_operations/supply_chain_management/queries/datewise_product_availability.gsql index d8286928..6221783f 100644 --- a/agile_operations/supply_chain_management/queries/datewise_product_availability.gsql +++ b/agile_operations/supply_chain_management/queries/datewise_product_availability.gsql @@ -7,7 +7,7 @@ CREATE OR REPLACE QUERY datewise_product_availability(VERTEX sales, physically counted or received within the last X months?" This is a strict, audit-ready ATP check that prevents promising - customers based on stale or "ghost" inventory — a major cause of + customers based on stale or "ghost" inventory - a major cause of delivery failures in traditional ERP systems. Parameters: @@ -15,12 +15,12 @@ CREATE OR REPLACE QUERY datewise_product_availability(VERTEX sales, The Sales Order to validate inventory_interval_threshold (INT): Maximum allowed age of inventory in months. - Example: 3 → only use inventory updated in the last 3 months + Example: 3 -> only use inventory updated in the last 3 months Output: - • Clear fulfillment decision message - • Detailed map of which materials pass/fail the freshness + quantity check - • Ready for integration into order management UIs or ATP engines + . Clear fulfillment decision message + . Detailed map of which materials pass/fail the freshness + quantity check + . Ready for integration into order management UIs or ATP engines */ TYPEDEF TUPLE myTuple; diff --git a/agile_operations/supply_chain_management/queries/explore_bom.gsql b/agile_operations/supply_chain_management/queries/explore_bom.gsql index 4b726294..1f124978 100644 --- a/agile_operations/supply_chain_management/queries/explore_bom.gsql +++ b/agile_operations/supply_chain_management/queries/explore_bom.gsql @@ -5,16 +5,16 @@ CREATE OR REPLACE DISTRIBUTED QUERY explore_BOM(VERTEX input_vert, INT depth, BO from any Material, BOM header, or Supplier. Key Use Cases: - • Explode a finished good into all components (any depth, even 20–30 levels) - • Reverse trace: "Which finished goods or BOMs use this raw material?" - • Critical for demand planning, costing, engineering change impact, + . Explode a finished good into all components (any depth, even 20-30 levels) + . Reverse trace: "Which finished goods or BOMs use this raw material?" + . Critical for demand planning, costing, engineering change impact, single-source risk analysis, and compliance reporting. Parameters: input_vert : Starting vertex (Material Or BOM) depth : Maximum traversal depth (set to 30+ for full explosion) - upstream : TRUE → Where-Used (who consumes this material?) - FALSE → BOM Explosion (what does this material consist of?) + upstream : TRUE -> Where-Used (who consumes this material?) + FALSE -> BOM Explosion (what does this material consist of?) */ SetAccum @@edges; diff --git a/agile_operations/supply_chain_management/queries/explore_bom_line.gsql b/agile_operations/supply_chain_management/queries/explore_bom_line.gsql index e3bb7cde..9c46ff12 100644 --- a/agile_operations/supply_chain_management/queries/explore_bom_line.gsql +++ b/agile_operations/supply_chain_management/queries/explore_bom_line.gsql @@ -5,19 +5,19 @@ CREATE OR REPLACE DISTRIBUTED QUERY explore_BOM_line(VERTEX input_vert, INT dept across actual production instances (SFC), sales orders, purchase orders, shipments, and customers/suppliers. - This is the "digital thread" query — it connects the physical flow + This is the "digital thread" query - it connects the physical flow (what was actually built and shipped) with the commercial flow (who ordered it and who supplied the components). Parameters: input_vert (VERTEX): - Starting vertex — typically SFC_Material (actual batch), Sales_Order, + Starting vertex - typically SFC_Material (actual batch), Sales_Order, Purchase_Order, Customer, or Supplier depth (INT): - Maximum traversal depth (usually 10–20 is sufficient for full lineage) + Maximum traversal depth (usually 10-20 is sufficient for full lineage) upstream (BOOL): - FALSE → Follow the flow downstream (e.g. raw material → finished good → customer) - TRUE → Follow the flow upstream (e.g. finished good → raw materials → supplier) + FALSE -> Follow the flow downstream (e.g. raw material -> finished good -> customer) + TRUE -> Follow the flow upstream (e.g. finished good -> raw materials -> supplier) use_date_range (BOOL): If TRUE, only traverse through dated vertices (Sales_Order, Purchase_Order, SFC_Assembly) that fall within the specified window diff --git a/agile_operations/supply_chain_management/queries/get_biggest_customers.gsql b/agile_operations/supply_chain_management/queries/get_biggest_customers.gsql index 91574f14..a5b342d9 100644 --- a/agile_operations/supply_chain_management/queries/get_biggest_customers.gsql +++ b/agile_operations/supply_chain_management/queries/get_biggest_customers.gsql @@ -5,7 +5,7 @@ CREATE OR REPLACE DISTRIBUTED QUERY get_biggest_customers(INT top_k, DATETIME st with optional geographic filtering (by Nation / Region). Instantly ranks customers based on actual shipped/delivered volume from - Sales Orders — far more accurate than booked revenue or invoice data, + Sales Orders - far more accurate than booked revenue or invoice data, because it reflects real physical fulfillment. Parameters: @@ -14,7 +14,7 @@ CREATE OR REPLACE DISTRIBUTED QUERY get_biggest_customers(INT top_k, DATETIME st start_date / end_date (DATETIME): Time window for analysis (e.g., last 12 months, current FY) input_nations (SET>): - Optional filter — only include customers from these countries. + Optional filter - only include customers from these countries. Pass empty set {} to include all nations globally. Output: diff --git a/agile_operations/supply_chain_management/queries/get_biggest_suppliers.gsql b/agile_operations/supply_chain_management/queries/get_biggest_suppliers.gsql index cd5bc1f8..3eeb843d 100644 --- a/agile_operations/supply_chain_management/queries/get_biggest_suppliers.gsql +++ b/agile_operations/supply_chain_management/queries/get_biggest_suppliers.gsql @@ -5,7 +5,7 @@ CREATE OR REPLACE DISTRIBUTED QUERY get_biggest_suppliers(INT top_k, DATETIME st with optional geographic filtering (by Nation / Region). Ranks suppliers based on actual received or ordered quantity from - Purchase Orders — the most accurate measure of supplier spend and + Purchase Orders - the most accurate measure of supplier spend and strategic importance in manufacturing and distribution environments. Parameters: top_k (INT): diff --git a/agile_operations/supply_chain_management/queries/plant_failure_impact_nodes.gsql b/agile_operations/supply_chain_management/queries/plant_failure_impact_nodes.gsql index fd256a16..64adb82f 100644 --- a/agile_operations/supply_chain_management/queries/plant_failure_impact_nodes.gsql +++ b/agile_operations/supply_chain_management/queries/plant_failure_impact_nodes.gsql @@ -6,9 +6,9 @@ "If this manufacturing plant goes down tomorrow (fire, strike, natural disaster), which finished goods, customer orders, and end customers will be directly impacted?" - Performs a constrained BFS from a Plant → through current inventory → required materials - → open sales order items → customers. Returns the full downstream impact path - within seconds — impossible in traditional ERP or BI systems. + Performs a constrained BFS from a Plant -> through current inventory -> required materials + -> open sales order items -> customers. Returns the full downstream impact path + within seconds - impossible in traditional ERP or BI systems. Parameters: start_plant (VERTEX): The manufacturing or distribution plant to simulate failure of @@ -16,9 +16,9 @@ Maximum graph distance to traverse. 8 is sufficient for full global impact. Output: - • All reachable Customer vertices (affected end customers) - • Full path of edges showing exactly how disruption propagates - • Visualizable in GraphStudio as impact network + . All reachable Customer vertices (affected end customers) + . Full path of edges showing exactly how disruption propagates + . Visualizable in GraphStudio as impact network */ TYPEDEF TUPLE Edge_Info; diff --git a/agile_operations/supply_chain_management/queries/product_quantity_sales_order.gsql b/agile_operations/supply_chain_management/queries/product_quantity_sales_order.gsql index 4aab3a95..0aa17672 100644 --- a/agile_operations/supply_chain_management/queries/product_quantity_sales_order.gsql +++ b/agile_operations/supply_chain_management/queries/product_quantity_sales_order.gsql @@ -1,7 +1,7 @@ CREATE OR REPLACE QUERY product_quantity_sales_order(VERTEX sales){ /* Description: - Real-time Available-to-Promise (ATP) Check – Basic Edition. + Real-time Available-to-Promise (ATP) Check - Basic Edition. Answers the fundamental order fulfillment question: "Do we have enough on-hand inventory (any location, any batch) to fulfill this sales order today?" @@ -14,9 +14,9 @@ CREATE OR REPLACE QUERY product_quantity_sales_order(VERTEX sales){ The Sales Order vertex to validate Output: - • Clear fulfillment decision message - • Detailed map of materials with sufficient inventory (location + quantity) - • Immediate visibility into which items block full fulfillment + . Clear fulfillment decision message + . Detailed map of materials with sufficient inventory (location + quantity) + . Immediate visibility into which items block full fulfillment */ diff --git a/agile_operations/supply_chain_management/queries/top_k_product_sales_order.gsql b/agile_operations/supply_chain_management/queries/top_k_product_sales_order.gsql index a8df4518..67bdff09 100644 --- a/agile_operations/supply_chain_management/queries/top_k_product_sales_order.gsql +++ b/agile_operations/supply_chain_management/queries/top_k_product_sales_order.gsql @@ -4,7 +4,7 @@ CREATE OR REPLACE QUERY top_k_products_sales_order(INT k = 10) { Global Top-K Best-Selling / Most Demanded Materials (by total ordered quantity) across all Sales Orders in the entire graph. - This is the true "voice of the customer" — reveals which finished goods, + This is the true "voice of the customer" - reveals which finished goods, semi-finished items, or configurable materials drive real revenue and demand volume. Essential for S&OP, demand planning, and product strategy. Parameters: diff --git a/agile_operations/supply_chain_management/queries/top_k_products_purchase_order.gsql b/agile_operations/supply_chain_management/queries/top_k_products_purchase_order.gsql index f2af3392..4d5d6ee8 100644 --- a/agile_operations/supply_chain_management/queries/top_k_products_purchase_order.gsql +++ b/agile_operations/supply_chain_management/queries/top_k_products_purchase_order.gsql @@ -4,7 +4,7 @@ Global Top-K Most Purchased Materials (by total ordered quantity) across all Purchase Orders in the entire graph. - Reveals true consumption drivers — the raw, semi-finished, or finished + Reveals true consumption drivers - the raw, semi-finished, or finished materials that dominate procurement volume. This is the real "voice of the supply chain" for demand and spend patterns. diff --git a/agile_operations/supply_chain_management/queries/trace_bom_line_nations.gsql b/agile_operations/supply_chain_management/queries/trace_bom_line_nations.gsql index 0899c415..97380664 100644 --- a/agile_operations/supply_chain_management/queries/trace_bom_line_nations.gsql +++ b/agile_operations/supply_chain_management/queries/trace_bom_line_nations.gsql @@ -6,10 +6,10 @@ CREATE OR REPLACE DISTRIBUTED QUERY trace_BOM_line_nations(VERTEX Starting from a real produced instance (SFC_Material) of a finished good, this query traces the full transactional BOM lineage upstream through actual production and procurement events to determine: - • What quantity of input materials was sourced domestically - • What quantity was sourced internationally + . What quantity of input materials was sourced domestically + . What quantity was sourced internationally - This is a true "Country of Origin" and supply chain sovereignty query — + This is a true "Country of Origin" and supply chain sovereignty query - critical for compliance, risk, ESG, trade policy, and cost analysis. Parameters: @@ -17,8 +17,8 @@ CREATE OR REPLACE DISTRIBUTED QUERY trace_BOM_line_nations(VERTEX A specific produced batch/instance of a finished good (actual SFC_Material vertex) Output: - @@domestic_quantity → Total quantity sourced from the same nation as the final assembly plant - @@international_quantity → Total quantity sourced from all other nations + @@domestic_quantity -> Total quantity sourced from the same nation as the final assembly plant + @@international_quantity -> Total quantity sourced from all other nations */ OrAccum @visited; MaxAccum @@src_nation, @rm_nation; diff --git a/agile_operations/supply_chain_management/queries/unfulfilled_orders.gsql b/agile_operations/supply_chain_management/queries/unfulfilled_orders.gsql index e69de29b..15ecf274 100644 --- a/agile_operations/supply_chain_management/queries/unfulfilled_orders.gsql +++ b/agile_operations/supply_chain_management/queries/unfulfilled_orders.gsql @@ -0,0 +1,84 @@ +CREATE OR REPLACE QUERY unfulfilled_orders(Set> sales_set) { + /* + Description: + Batch Available-to-Promise (ATP) with Real-Time Inventory Allocation. + + Takes a set of Sales Orders (any size) and performs a true multi-order, + multi-material ATP check using current global inventory. + Automatically allocates inventory to fulfill orders in the order they + appear in the input set and reports: + . Which orders can be fully fulfilled + . Which orders remain unfulfilled (or partially fulfilled) + . Updated inventory levels after allocation + + This is the production-grade "order promising engine" used by advanced + order management systems - but running natively on the graph in real time. + + Parameters: + sales_set (SET>): + Set of Sales Order vertices to evaluate and allocate (FIFO order) + + Output: + . Previous_Product_Inventory -> inventory before allocation + . Current_Product_Inventory -> inventory after allocation + . Orders_that_cannot_be_fulfilled -> list of fully/partially unfulfilled orders + */ + + MapAccum> @prod_per_sales; + MapAccum>> @@prod_per_inv; + MapAccum> @@total_prod_inv; + ListAccum @@unfulfilled_orders; + + OrAccum @order_fulfilled = TRUE; + + Start = {sales_set}; + + res = SELECT si FROM Start:s-(Has_Sales_Order_Item:i)-Sales_Order_Item:si; + + res = SELECT si FROM res:si-(For_Material:r)-SFC_Material:p + ACCUM + si.@prod_per_sales += (p-> si.quantity); + + inv = SELECT i FROM Inventory_Held:i -(Inventory_Has_Material:c)- SFC_Material:p + ACCUM + @@prod_per_inv += (p->(i->i.quantity)), + @@total_prod_inv += (p->i.quantity); + + + res_1 = SELECT si FROM res:si-(For_Material:r)-SFC_Material:p + ACCUM + FOREACH (key,value) IN @@prod_per_inv.get(p) DO + IF @@total_prod_inv.get(p) >= si.@prod_per_sales.get(p) THEN + BREAK + END + END, + IF @@total_prod_inv.get(p) < si.@prod_per_sales.get(p) THEN + si.@order_fulfilled += FALSE + END; + + PRINT @@prod_per_inv AS Previous_Product_Inventory; + + res_2 = SELECT si FROM res:si-(For_Material:r)-SFC_Material:p + ACCUM + IF NOT si.@order_fulfilled THEN + @@unfulfilled_orders += si + ELSE + FOREACH (key,value) IN @@prod_per_inv.get(p) DO + IF value >= si.@prod_per_sales.get(p) THEN + INT val = value - si.@prod_per_sales.get(p), + @@prod_per_inv += (p->(key->val)), + BREAK + ELSE + si.@prod_per_sales += (p->si.@prod_per_sales.get(p)-value), + @@prod_per_inv += (p->(key->0)) + END + END + END; + + PRINT @@prod_per_inv AS Current_Product_Inventory; + + PRINT @@unfulfilled_orders AS Orders_that_cannot_be_fulfilled; + PRINT res_2; + + +} diff --git a/connected_customer/entity_resolution/queries/delete_all_cc_connections.gsql b/connected_customer/entity_resolution/queries/delete_all_cc_connections.gsql index 10d22c4e..c471e6b8 100644 --- a/connected_customer/entity_resolution/queries/delete_all_cc_connections.gsql +++ b/connected_customer/entity_resolution/queries/delete_all_cc_connections.gsql @@ -12,14 +12,14 @@ CREATE OR REPLACE DISTRIBUTED QUERY delete_all_cc_connections(INT num_of_batches be recomputed from scratch. Key Concept: - • Batch Deletion: + . Batch Deletion: Uses `num_of_batches` and `batch_id` to partition the `Entity` vertex space so that very large graphs can be processed across multiple jobs without overloading resources. Parameters: - num_of_batches – Total number of batches to divide the `Entity` set into. - batch_id – Zero-based index (0 … num_of_batches - 1) of the batch + num_of_batches - Total number of batches to divide the `Entity` set into. + batch_id - Zero-based index (0 -> num_of_batches - 1) of the batch to process in this execution. Output: diff --git a/connected_customer/entity_resolution/queries/delete_unused_cc_nodes.gsql b/connected_customer/entity_resolution/queries/delete_unused_cc_nodes.gsql index 2db8f9dd..99dffa3b 100644 --- a/connected_customer/entity_resolution/queries/delete_unused_cc_nodes.gsql +++ b/connected_customer/entity_resolution/queries/delete_unused_cc_nodes.gsql @@ -11,14 +11,14 @@ CREATE OR REPLACE DISTRIBUTED QUERY delete_unused_cc_nodes(INT num_of_batches = assignment edges so that orphaned CC nodes are removed. Key Concept: - • Orphaned CC Nodes: + . Orphaned CC Nodes: A `Connected_Component` vertex with `outdegree() == 0` is considered unused and can be safely deleted. Parameters: - num_of_batches – Total number of batches used to partition all + num_of_batches - Total number of batches used to partition all `Connected_Component` vertices for deletion. - batch_id – Zero-based index (0 … num_of_batches - 1) of the batch + batch_id - Zero-based index (0 -> num_of_batches - 1) of the batch processed in this execution. Output: diff --git a/connected_customer/entity_resolution/queries/find_shared_piis_of_two_entities.gsql b/connected_customer/entity_resolution/queries/find_shared_piis_of_two_entities.gsql index 2b043877..75ac79d8 100644 --- a/connected_customer/entity_resolution/queries/find_shared_piis_of_two_entities.gsql +++ b/connected_customer/entity_resolution/queries/find_shared_piis_of_two_entities.gsql @@ -11,14 +11,14 @@ CREATE OR REPLACE QUERY find_shared_piis_of_two_entities(VERTEX entity_1 suspected to be related (e.g., fraud review, ER explainability, data quality). Key Output Fields: - • pii_type – The type/label of the shared PII vertex. - • pii_value – The shared PII vertex instance (its ID/value). - • degree – Outdegree of the PII vertex, i.e., how many entities share it. + . pii_type - The type/label of the shared PII vertex. + . pii_value - The shared PII vertex instance (its ID/value). + . degree - Outdegree of the PII vertex, i.e., how many entities share it. High-degree PII may indicate a common hub (e.g., shared email domain). Parameters: - entity_1 – First `Entity` vertex to compare. - entity_2 – Second `Entity` vertex to compare. + entity_1 - First `Entity` vertex to compare. + entity_2 - Second `Entity` vertex to compare. Output: A list of tuples (pii_type, pii_value, degree) for each PII vertex that is diff --git a/connected_customer/entity_resolution/queries/match_entities.gsql b/connected_customer/entity_resolution/queries/match_entities.gsql index ab136e7c..e4c9ca10 100644 --- a/connected_customer/entity_resolution/queries/match_entities.gsql +++ b/connected_customer/entity_resolution/queries/match_entities.gsql @@ -31,18 +31,18 @@ CREATE OR REPLACE DISTRIBUTED QUERY match_entities( edge is created between the matching entities. Key Concepts: - • Weighted Similarity: + . Weighted Similarity: Each PII type contributes a configurable weight to the final match score. - • Fuzzy Matching: - Hashed email, name, and phone are compared with Jaro–Winkler–based scores + . Fuzzy Matching: + Hashed email, name, and phone are compared with Jaro-Winkler-based scores derived from the original values. - • Nested Batching: + . Nested Batching: Entities are divided into `source` and `target` batches to scale all-pairs comparison and control memory usage. - • Super-Node Filtering: + . Super-Node Filtering: Highly connected PII vertices are skipped using `pii_low_connections_limit` and `pii_high_connections_limit` to prevent false positives and hot spots. - • Incremental Runs: + . Incremental Runs: `compute_entities_after_date` allows focusing on recently created entities for incremental/recurring execution. diff --git a/connected_customer/entity_resolution/queries/output_entity_cc_to_file.gsql b/connected_customer/entity_resolution/queries/output_entity_cc_to_file.gsql index d2e7dcdf..c6c0a314 100644 --- a/connected_customer/entity_resolution/queries/output_entity_cc_to_file.gsql +++ b/connected_customer/entity_resolution/queries/output_entity_cc_to_file.gsql @@ -12,19 +12,19 @@ CREATE OR REPLACE QUERY output_entity_cc_to_file(STRING output_file_path = "/hom downstream ML or warehousing workflows. Key Concepts: - • FILE output: - Uses TigerGraph’s `FILE` object to write directly to a server-side file. - • One-hop traversal: + . FILE output: + Uses TigerGraph's `FILE` object to write directly to a server-side file. + . One-hop traversal: Traverses from each `Entity` to its `Connected_Component` in a single hop. Parameters: - output_file_path – Absolute path where the CSV file will be created on + output_file_path - Absolute path where the CSV file will be created on the TigerGraph server. Output: Creates a CSV file with header: "Entity,Connected_Component" - and one row per `Entity`–`Connected_Component` pair, then prints a + and one row per `Entity`-`Connected_Component` pair, then prints a confirmation message. */ FILE f (output_file_path); diff --git a/connected_customer/entity_resolution/queries/unify_entities.gsql b/connected_customer/entity_resolution/queries/unify_entities.gsql index 24614a30..d2939766 100644 --- a/connected_customer/entity_resolution/queries/unify_entities.gsql +++ b/connected_customer/entity_resolution/queries/unify_entities.gsql @@ -23,9 +23,9 @@ CREATE OR REPLACE DISTRIBUTED QUERY unify_entities() FOR GRAPH Entity_Resolution member `Entity` is connected to it via `Entity_In_Ring`. Output: - • One `Connected_Component` vertex per connected group of `Entity` vertices. - • One `Entity_In_Ring` edge from each `Entity` to its community. - • Printed execution time (in seconds) and a completion timestamp. + . One `Connected_Component` vertex per connected group of `Entity` vertices. + . One `Entity_In_Ring` edge from each `Entity` to its community. + . Printed execution time (in seconds) and a completion timestamp. */ MinAccum @cc_id; // Each vertex's tentative component id diff --git a/connected_customer/product_recommendations/queries/combine_features.gsql b/connected_customer/product_recommendations/queries/combine_features.gsql index 55923692..63877e3b 100644 --- a/connected_customer/product_recommendations/queries/combine_features.gsql +++ b/connected_customer/product_recommendations/queries/combine_features.gsql @@ -12,28 +12,28 @@ CREATE QUERY combine_features( Feature Co-Occurrence Grouping Purpose: - Identify frequently co-occurring feature pairs around high-degree (“hub”) + Identify frequently co-occurring feature pairs around high-degree feature vertices and collapse them into `Combined_Feature` vertices. This reduces hub noise and creates more informative composite features for downstream models or recommenders. High-Level Flow: - • Hub detection: + . Hub detection: Select `hub_v_type` vertices whose degree over `e_type` exceeds `hub_threshold` and mark them as hubs. - • Co-occurrence counting: - For each hub–feature pair connected through `target_v_type` vertices + . Co-occurrence counting: + For each hub-feature pair connected through `target_v_type` vertices (e.g., Customers), count how many targets they share. - • Pair selection: - Keep hub–feature pairs whose shared target count lies between 2 and + . Pair selection: + Keep hub-feature pairs whose shared target count lies between 2 and `split_threshold`, treating them as meaningful but not overly common co-occurrences. - • Combined feature creation: + . Combined feature creation: For each selected pair and its associated targets: - – Create/attach a `Combined_Feature` vertex. - – Flag original hub/feature edges as `ignore_edge = TRUE`. - – Connect targets to the combined feature via `Has_Attribute`. - – Link original feature vertices to the combined feature via `Linked`. + - Create/attach a `Combined_Feature` vertex. + - Flag original hub/feature edges as `ignore_edge = TRUE`. + - Connect targets to the combined feature via `Has_Attribute`. + - Link original feature vertices to the combined feature via `Linked`. Configuration: `hub_v_type`, `feature_v_type`, `target_v_type`, and `e_type` define the diff --git a/connected_customer/product_recommendations/queries/k_means.gsql b/connected_customer/product_recommendations/queries/k_means.gsql index e8f4cc28..7a6c51c9 100644 --- a/connected_customer/product_recommendations/queries/k_means.gsql +++ b/connected_customer/product_recommendations/queries/k_means.gsql @@ -18,29 +18,29 @@ CREATE QUERY k_means( Purpose: Cluster vertices of type `v_type` using K-Means on normalized numeric - features taken from each vertex’s `attr_map`. The query sweeps K from - `min_cluster_count` to `max_cluster_count` and picks the “best” cluster + features taken from each vertex's `attr_map`. The query sweeps K from + `min_cluster_count` to `max_cluster_count` and picks the "best" cluster count based on changes in sum of squared errors (SSE), approximating the elbow point. High-Level Flow: - • Build feature vectors: + . Build feature vectors: Parse `attr_set` to select a subset of numeric keys from `attr_map`, normalize each feature across all vertices, and construct per-vertex vectors. - • K sweep & initialization: + . K sweep & initialization: For each candidate K in [min_cluster_count, max_cluster_count] (step `cluster_inc`), run up to `random_iter_count` random initializations of centroids using a seeded PRNG (`random_seed`). - • K-Means iterations: + . K-Means iterations: Assign each vertex to its closest centroid, recompute centroids from assigned members, and repeat until convergence (change in SSE below `conv_threshold` or `conv_iter_limit` reached). - • Model selection & merge: + . Model selection & merge: Track SSE across K values and iterations, approximate the elbow via second differences in SSE, and optionally merge nearly identical centroids. - • Materialization: + . Materialization: For the chosen K and iteration, insert one `Cluster` vertex per centroid and an `In_Cluster` edge from each vertex to its cluster, stamped with either `now()` or `custom_timestamp`. diff --git a/connected_customer/product_recommendations/queries/recommend_products.gsql b/connected_customer/product_recommendations/queries/recommend_products.gsql index b47ca174..f10903e4 100644 --- a/connected_customer/product_recommendations/queries/recommend_products.gsql +++ b/connected_customer/product_recommendations/queries/recommend_products.gsql @@ -14,22 +14,22 @@ CREATE DISTRIBUTED QUERY recommend_products( /* Query Name: recommend_products - Hybrid Customer–Item Collaborative Filtering Recommender + Hybrid Customer-Item Collaborative Filtering Recommender Purpose: For each source `Customer`, generate a ranked list of `Product_Variant` recommendations using a combination of: - • customer–customer similarity (shared features/interactions) - • item–item co-occurrence (co-purchasing behavior). + . customer-customer similarity (shared features/interactions) + . item-item co-occurrence (co-purchasing behavior). High-Level Flow: - • Select source customers (explicit input set or batched over all customers). - • Build customer–customer similarity from feature overlaps, weighted by + . Select source customers (explicit input set or batched over all customers). + . Build customer-customer similarity from feature overlaps, weighted by interaction strength, edge importance, and feature popularity. - • Derive item popularity per customer from similar customers and + . Derive item popularity per customer from similar customers and co-occurring items. - • Optionally reweight by category importance and vertex-degree scaling. - • Keep up to `recommendation_count` best items per customer. + . Optionally reweight by category importance and vertex-degree scaling. + . Keep up to `recommendation_count` best items per customer. Configuration: Key parameters control which customers are processed (src_customer_input, diff --git a/financial_crime/application_fraud/queries/batch_application_distance_and_path.gsql b/financial_crime/application_fraud/queries/batch_application_distance_and_path.gsql index 49b22e7c..0fa2729e 100644 --- a/financial_crime/application_fraud/queries/batch_application_distance_and_path.gsql +++ b/financial_crime/application_fraud/queries/batch_application_distance_and_path.gsql @@ -13,7 +13,7 @@ CREATE DISTRIBUTED QUERY batch_application_distance_and_path(INT depth=5, STRING Absolute path of the CSV output file. Output: - CSV of app–fraud distances and PII paths + CSV of app-fraud distances and PII paths */ MapAccum, VERTEX> @@cc_map; MaxAccum @dis; diff --git a/financial_crime/application_fraud/queries/get_application_cc_features.gsql b/financial_crime/application_fraud/queries/get_application_cc_features.gsql index 63eaf365..9f64f58d 100644 --- a/financial_crime/application_fraud/queries/get_application_cc_features.gsql +++ b/financial_crime/application_fraud/queries/get_application_cc_features.gsql @@ -1,7 +1,7 @@ CREATE OR REPLACE QUERY get_application_cc_features(VERTEX application, INT connections=25000) { /* Description: - For an input Application, computes connected-component–level PII + For an input Application, computes connected-component-level PII connectivity features for use in near real-time fraud analysis. Parameters: diff --git a/financial_crime/application_fraud/queries/match_application_entities.gsql b/financial_crime/application_fraud/queries/match_application_entities.gsql index 3c23012c..b24191ff 100644 --- a/financial_crime/application_fraud/queries/match_application_entities.gsql +++ b/financial_crime/application_fraud/queries/match_application_entities.gsql @@ -23,23 +23,6 @@ CREATE OR REPLACE DISTRIBUTED QUERY match_application_entities( similarity and inserts Same_Application edges for pairs whose score exceeds a threshold. - Parameters: - Application_*_weight: - Weights for each PII type - num_of_source_batches: - Number of source-side batches for Applications. - num_of_target_batches: - Number of target-side batches for candidate matches. - threshold: - Minimum similarity score to create Same_Application. - pii_low_connections_limit: - Max total degree for “low-degree” PII candidates. - pii_high_connections_limit: - Max total degree for PII in high-degree phase. - compute_entities_after_date: - Only Applications created after this timestamp are - considered as “new” for similarity computation. - Output: Summary of matched pairs and score statistics plus total execution time. */ diff --git a/financial_crime/application_fraud/queries/output_application_cc_to_file.gsql b/financial_crime/application_fraud/queries/output_application_cc_to_file.gsql index 7fe1aef7..dc2dc0f9 100644 --- a/financial_crime/application_fraud/queries/output_application_cc_to_file.gsql +++ b/financial_crime/application_fraud/queries/output_application_cc_to_file.gsql @@ -4,11 +4,6 @@ CREATE OR REPLACE DISTRIBUTED QUERY output_application_cc_to_file(STRING output_ Exports a CSV mapping each Application vertex to its Connected_Component via Application_In_Ring edges. - Parameters: - output_file_path: - Path to the output CSV file for Application → - Connected_Component mappings. - Output: CSV file listing Application id and Connected_Component id for all mapped Applications, plus a status message. diff --git a/financial_crime/entity_resolution_kyc/queries/batch_party_cc_features.gsql b/financial_crime/entity_resolution_kyc/queries/batch_party_cc_features.gsql index c0bcc7d2..5c679747 100644 --- a/financial_crime/entity_resolution_kyc/queries/batch_party_cc_features.gsql +++ b/financial_crime/entity_resolution_kyc/queries/batch_party_cc_features.gsql @@ -9,9 +9,9 @@ CREATE OR REPLACE DISTRIBUTED QUERY batch_party_cc_features(INT connections=2500 and other related entities (Accounts, Cards, etc.). Key Use Cases: - • Feature Engineering: Create robust, graph-based variables to train a fraud prediction model. - • Ring Detection: Identify 'Parties' that are linked via shared PII, often indicative of fraud rings. - • Risk Profiling: Assess the risk of a Party based on the size and fraudulence of its community. + . Feature Engineering: Create robust, graph-based variables to train a fraud prediction model. + . Ring Detection: Identify 'Parties' that are linked via shared PII, often indicative of fraud rings. + . Risk Profiling: Assess the risk of a Party based on the size and fraudulence of its community. Parameters: connections: Upper limit for the out-degree of PII/Entity nodes (e.g., limit a shared IP address diff --git a/financial_crime/entity_resolution_kyc/queries/batch_party_distance_and_path.gsql b/financial_crime/entity_resolution_kyc/queries/batch_party_distance_and_path.gsql index 4d71c1b1..f61d1194 100644 --- a/financial_crime/entity_resolution_kyc/queries/batch_party_distance_and_path.gsql +++ b/financial_crime/entity_resolution_kyc/queries/batch_party_distance_and_path.gsql @@ -11,9 +11,9 @@ CREATE DISTRIBUTED QUERY batch_party_distance_and_path(INT depth=5, STRING outpu 3. Generate a feature set that shows the degree of separation between a Party and known fraud. Key Use Cases: - • Feature Engineering: Create "Distance-to-Fraud" and "Path-Type" features for ML models. - • Risk Prioritization: Identify non-fraudulent parties (is_fraud=0) that are only 1-2 hops away from known fraud. - • Investigation: Quickly trace the PII/Entity linkage for high-risk individuals. + . Feature Engineering: Create "Distance-to-Fraud" and "Path-Type" features for ML models. + . Risk Prioritization: Identify non-fraudulent parties (is_fraud=0) that are only 1-2 hops away from known fraud. + . Investigation: Quickly trace the PII/Entity linkage for high-risk individuals. Parameters: depth: Maximum traversal depth (number of hops) for the search. Limits search time and focuses on close connections. diff --git a/financial_crime/entity_resolution_kyc/queries/distance_and_path_to_fraud_party.gsql b/financial_crime/entity_resolution_kyc/queries/distance_and_path_to_fraud_party.gsql index 15172301..9f3e48b1 100644 --- a/financial_crime/entity_resolution_kyc/queries/distance_and_path_to_fraud_party.gsql +++ b/financial_crime/entity_resolution_kyc/queries/distance_and_path_to_fraud_party.gsql @@ -10,9 +10,9 @@ CREATE OR REPLACE QUERY distance_and_path_to_fraud_party(VERTEX input, IN `Fraud Party` within a specified hop `depth`. Key Use Cases: - • Transaction Monitoring: Identify if a customer is closely linked to fraud before approving a transaction. - • New Application Scoring: Immediately flag new applicants connected to high-risk communities. - • Investigation: Quickly map the shortest connection path between a suspect and known fraud entities. + . Transaction Monitoring: Identify if a customer is closely linked to fraud before approving a transaction. + . New Application Scoring: Immediately flag new applicants connected to high-risk communities. + . Investigation: Quickly map the shortest connection path between a suspect and known fraud entities. Parameters: input: The specific Party vertex to start the search from (the target of the risk assessment). diff --git a/financial_crime/entity_resolution_kyc/queries/find_shared_piis_of_two_parties.gsql b/financial_crime/entity_resolution_kyc/queries/find_shared_piis_of_two_parties.gsql index 5af73b39..ece20ce2 100644 --- a/financial_crime/entity_resolution_kyc/queries/find_shared_piis_of_two_parties.gsql +++ b/financial_crime/entity_resolution_kyc/queries/find_shared_piis_of_two_parties.gsql @@ -11,9 +11,9 @@ CREATE OR REPLACE QUERY find_shared_piis_of_two_parties(VERTEX party_1, V step in verifying potential fraud rings or suspicious collusion. Key Output Features: - • PII Type (e.g., 'Full_Name'): What type of entity is shared. - • PII Value (Vertex ID): The ID of the shared entity itself. - • Degree (Out-degree): How many *other* parties are also connected to this shared PII. + . PII Type (e.g., 'Full_Name'): What type of entity is shared. + . PII Value (Vertex ID): The ID of the shared entity itself. + . Degree (Out-degree): How many *other* parties are also connected to this shared PII. A high degree indicates a high-risk 'hub' entity. Parameters: diff --git a/financial_crime/entity_resolution_kyc/queries/get_party_cc_features.gsql b/financial_crime/entity_resolution_kyc/queries/get_party_cc_features.gsql index eac4fb10..6e260b6d 100644 --- a/financial_crime/entity_resolution_kyc/queries/get_party_cc_features.gsql +++ b/financial_crime/entity_resolution_kyc/queries/get_party_cc_features.gsql @@ -10,9 +10,9 @@ CREATE OR REPLACE QUERY get_party_cc_features(VERTEX party, INT connectio for calculating a risk score for a single entity (e.g., a new loan application or login attempt). Key Features Calculated (for the CC): - • Size and Composition: Total Party nodes, distinct PII nodes (Name, IP, Device, etc.). - • Linkage Strength: Number of Parties connected via shared PII. - • Specificity: Number of Parties connected *only* by a single type of shared PII (e.g., only by DOB). + . Size and Composition: Total Party nodes, distinct PII nodes (Name, IP, Device, etc.). + . Linkage Strength: Number of Parties connected via shared PII. + . Specificity: Number of Parties connected *only* by a single type of shared PII (e.g., only by DOB). Parameters: party: The specific Party vertex ID to analyze. diff --git a/financial_crime/entity_resolution_kyc/queries/get_party_fraud_status.gsql b/financial_crime/entity_resolution_kyc/queries/get_party_fraud_status.gsql index ed5ee8a1..5408d647 100644 --- a/financial_crime/entity_resolution_kyc/queries/get_party_fraud_status.gsql +++ b/financial_crime/entity_resolution_kyc/queries/get_party_fraud_status.gsql @@ -11,9 +11,9 @@ CREATE DISTRIBUTED QUERY get_party_fraud_status(SET> parties) { It performs no graph traversal, making it extremely fast. Key Use Cases: - • Model Evaluation: Retrieve ground truth labels for a set of Parties for model performance testing. - • System Integration: Fetch current fraud flags for multiple entities efficiently. - • Audit and Reporting: Generate a quick report on the current status of specific accounts. + . Model Evaluation: Retrieve ground truth labels for a set of Parties for model performance testing. + . System Integration: Fetch current fraud flags for multiple entities efficiently. + . Audit and Reporting: Generate a quick report on the current status of specific accounts. Parameters: parties: A set of Party vertex IDs whose fraud status needs to be retrieved. diff --git a/financial_crime/entity_resolution_kyc/queries/match_party_entities.gsql b/financial_crime/entity_resolution_kyc/queries/match_party_entities.gsql index 599f6c35..942aa444 100644 --- a/financial_crime/entity_resolution_kyc/queries/match_party_entities.gsql +++ b/financial_crime/entity_resolution_kyc/queries/match_party_entities.gsql @@ -29,9 +29,9 @@ CREATE OR REPLACE DISTRIBUTED QUERY match_party_entities( `threshold`, it creates a `Same_As` edge between them. Key Concepts: - • **Weighted Similarity:** Each shared PII type contributes a different weight to the final score. - • **Nested Batching:** The graph is divided into `source` and `target` batches to manage memory and parallelize the all-pairs comparison efficiently. - • **Super-Node Filtering:** PII nodes (e.g., a common IP address shared by millions of users) are filtered out using `pii_low_connections_limit` and `pii_high_connections_limit` to prevent false positives and poor performance. + . **Weighted Similarity:** Each shared PII type contributes a different weight to the final score. + . **Nested Batching:** The graph is divided into `source` and `target` batches to manage memory and parallelize the all-pairs comparison efficiently. + . **Super-Node Filtering:** PII nodes (e.g., a common IP address shared by millions of users) are filtered out using `pii_low_connections_limit` and `pii_high_connections_limit` to prevent false positives and poor performance. Parameters: Customer_[Entity]_weight: Weights for each PII type defining match strength. diff --git a/financial_crime/entity_resolution_kyc/queries/output_party_cc_to_file.gsql b/financial_crime/entity_resolution_kyc/queries/output_party_cc_to_file.gsql index 3d31fb90..01919618 100644 --- a/financial_crime/entity_resolution_kyc/queries/output_party_cc_to_file.gsql +++ b/financial_crime/entity_resolution_kyc/queries/output_party_cc_to_file.gsql @@ -7,13 +7,13 @@ CREATE OR REPLACE QUERY output_party_cc_to_file(STRING output_file_path = "/home The primary goal of this query is to **generate a plain text or CSV file** that maps every `Party` vertex in the graph to the `Connected_Component` community vertex it currently belongs to. This output is critical for: - • **Model Training:** Providing ground truth labels (community IDs) for machine learning models. - • **Reporting/Auditing:** Creating a comprehensive record of the current entity resolution state. - • **External System Integration:** Loading the community assignments into a data warehouse or external database. + . **Model Training:** Providing ground truth labels (community IDs) for machine learning models. + . **Reporting/Auditing:** Creating a comprehensive record of the current entity resolution state. + . **External System Integration:** Loading the community assignments into a data warehouse or external database. Key Concepts: - • FILE Object: TigerGraph's utility to write data directly to a file on the server. - • Simple Traversal: A single hop is used to link the Party to its assigned community. + . FILE Object: TigerGraph's utility to write data directly to a file on the server. + . Simple Traversal: A single hop is used to link the Party to its assigned community. Parameters: output_file_path: The full path where the output file (e.g., CSV) should be created on the TigerGraph server. diff --git a/financial_crime/entity_resolution_kyc/queries/top_connected_component.gsql b/financial_crime/entity_resolution_kyc/queries/top_connected_component.gsql index 1e8ffe99..820e41bc 100644 --- a/financial_crime/entity_resolution_kyc/queries/top_connected_component.gsql +++ b/financial_crime/entity_resolution_kyc/queries/top_connected_component.gsql @@ -11,10 +11,10 @@ CREATE OR REPLACE DISTRIBUTED QUERY Top_Connected_Component(/* Parameters here * results are then sorted to show the largest/most active communities first. Key Metrics Calculated per Connected_Component: - • Parties: Total number of Party vertices belonging to the CC. - • Address: Total number of unique Address PII vertices linked to the CC. - • Phone: Total number of unique Phone PII vertices linked to the CC. - • Risk: The sum of the `is_fraud` attribute from all associated Party vertices (cumulative risk score). + . Parties: Total number of Party vertices belonging to the CC. + . Address: Total number of unique Address PII vertices linked to the CC. + . Phone: Total number of unique Phone PII vertices linked to the CC. + . Risk: The sum of the `is_fraud` attribute from all associated Party vertices (cumulative risk score). */ SumAccum @parties,@address,@phone,@risk; diff --git a/financial_crime/transaction_fraud/model/model_training.ipynb b/financial_crime/transaction_fraud/model/model_training.ipynb index 2f48e85e..1f094801 100644 --- a/financial_crime/transaction_fraud/model/model_training.ipynb +++ b/financial_crime/transaction_fraud/model/model_training.ipynb @@ -22,7 +22,7 @@ "\n", "Our journey begins with an exploration of a sample graph included in the Solution Kit, embodying transactions between credit cards and merchants, alongside cardholders ('Party') and their attributes such as addresses. For a visual guide and more details on the graph schema, please refer to the **Solution Kit Documentation**.\n", "\n", - "Utilizing an XGBoost model, we will demonstrate the stark contrast in performance between models trained with and without our graph features. Here’s a glimpse of the empirical evidence highlighting the effectiveness of incorporating graph features:\n", + "Utilizing an XGBoost model, we will demonstrate the stark contrast in performance between models trained with and without our graph features. Here's a glimpse of the empirical evidence highlighting the effectiveness of incorporating graph features:\n", "\n", "\n", "| Model | Precision | Recall | \n", diff --git a/financial_crime/transaction_fraud/model/training/model_training.md b/financial_crime/transaction_fraud/model/training/model_training.md index 8d3c2b90..0e9e103f 100644 --- a/financial_crime/transaction_fraud/model/training/model_training.md +++ b/financial_crime/transaction_fraud/model/training/model_training.md @@ -7,7 +7,7 @@ Prerequisites for this tutorial are working knowledge of TigerGraph database and Our journey begins with an exploration of a sample graph included in the Solution Kit, embodying transactions between credit cards and merchants, alongside cardholders ('Party') and their attributes such as addresses. For a visual guide and more details on the graph schema, please refer to the **Solution Kit Documentation**. -Utilizing an XGBoost model, we will demonstrate the stark contrast in performance between models trained with and without our graph features. Here’s a glimpse of the empirical evidence highlighting the effectiveness of incorporating graph features: +Utilizing an XGBoost model, we will demonstrate the stark contrast in performance between models trained with and without our graph features. Here's a glimpse of the empirical evidence highlighting the effectiveness of incorporating graph features: | Model | Precision | Recall | From 1603eb6d1658e0124fa3eb944c4f3c6c66168595 Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Tue, 9 Dec 2025 17:06:48 +0530 Subject: [PATCH 31/55] corrected customer 360 query documentation --- .../queries/Application_Engagement_Individuals.gsql | 4 +--- .../customer_360/queries/Customers_No_Engagement.gsql | 2 +- .../customer_360/queries/Email_Engagement_Accounts.gsql | 8 +++----- .../customer_360/queries/Email_Engagement_By_Product.gsql | 4 ++-- .../customer_360/queries/Individual_WebSearch.gsql | 3 +-- .../queries/Individuals_K_Most_Engagement.gsql | 6 ++---- .../customer_360/queries/Individuals_Product_Browse.gsql | 3 +-- 7 files changed, 11 insertions(+), 19 deletions(-) diff --git a/connected_customer/customer_360/queries/Application_Engagement_Individuals.gsql b/connected_customer/customer_360/queries/Application_Engagement_Individuals.gsql index 735550a2..d649d477 100644 --- a/connected_customer/customer_360/queries/Application_Engagement_Individuals.gsql +++ b/connected_customer/customer_360/queries/Application_Engagement_Individuals.gsql @@ -1,9 +1,7 @@ CREATE DISTRIBUTED QUERY Application_Engagement_Individuals(/* Parameters here */) FOR GRAPH Customer_360_Financial { /* Description: - Returns Individuals who have engaged with a product application, - via the path: SessionID -(action)-> ApplicationEngagement - -(created_by)-> Individual. + Returns individuals who have engaged with a product application via at least one session Parameters: (None) diff --git a/connected_customer/customer_360/queries/Customers_No_Engagement.gsql b/connected_customer/customer_360/queries/Customers_No_Engagement.gsql index f0698a81..99680ca0 100644 --- a/connected_customer/customer_360/queries/Customers_No_Engagement.gsql +++ b/connected_customer/customer_360/queries/Customers_No_Engagement.gsql @@ -10,7 +10,7 @@ CREATE DISTRIBUTED QUERY Customers_No_Engagement(/* Parameters here */) FOR GRAP Output: not_engaged: - Individual vertices where outdegree("created_session") == 0. + Individual vertices with no recorded engagement */ start = {Individual.*}; diff --git a/connected_customer/customer_360/queries/Email_Engagement_Accounts.gsql b/connected_customer/customer_360/queries/Email_Engagement_Accounts.gsql index 6f45f833..7094c1fd 100644 --- a/connected_customer/customer_360/queries/Email_Engagement_Accounts.gsql +++ b/connected_customer/customer_360/queries/Email_Engagement_Accounts.gsql @@ -1,8 +1,8 @@ CREATE DISTRIBUTED QUERY Email_Engagement_Accounts(/* Parameters here */) FOR GRAPH Customer_360_Financial { /* Description: - Returns Accounts that have had email engagement, by traversing from - SessionID through EmailEngagement to Individual and then to Account. + Returns accounts whose representing individuals initiated sessions that + include an EmailEngagement action. Parameters: (None) @@ -10,9 +10,7 @@ CREATE DISTRIBUTED QUERY Email_Engagement_Accounts(/* Parameters here */) FOR GR Output: accounts: - Account vertices reachable via - SessionID -(action)-> EmailEngagement -(created_by)-> Individual - -(represents_account)-> Account. + Account vertices reachable with linkage to EmailEngagement through a session */ start = {SessionID.*}; diff --git a/connected_customer/customer_360/queries/Email_Engagement_By_Product.gsql b/connected_customer/customer_360/queries/Email_Engagement_By_Product.gsql index 3b093196..f130ff8a 100644 --- a/connected_customer/customer_360/queries/Email_Engagement_By_Product.gsql +++ b/connected_customer/customer_360/queries/Email_Engagement_By_Product.gsql @@ -10,11 +10,11 @@ CREATE DISTRIBUTED QUERY Email_Engagement_By_Product(/* Parameters here */) FOR linked to engaged accounts (CreditCard / Loan). Output: - @@product_total: - Map of product_type -> total number of such products. @@account_prod_total: Map of product_type -> number of such products held by email-engaged accounts. + @@product_total: + Map of product_type -> total number of such products. @@engagement_rate: Map of product_type -> engagement rate (email-engaged products / total products), rounded to 3 decimals. diff --git a/connected_customer/customer_360/queries/Individual_WebSearch.gsql b/connected_customer/customer_360/queries/Individual_WebSearch.gsql index 25334aab..246ab8ec 100644 --- a/connected_customer/customer_360/queries/Individual_WebSearch.gsql +++ b/connected_customer/customer_360/queries/Individual_WebSearch.gsql @@ -1,8 +1,7 @@ CREATE DISTRIBUTED QUERY Individual_WebSearch(/* Parameters here */) FOR GRAPH Customer_360_Financial { /* Description: - Returns Individuals who have performed a web search activity, - via WebSearch events linked from SessionID. + Returns Individuals who have performed a web search activity. Parameters: (None) diff --git a/connected_customer/customer_360/queries/Individuals_K_Most_Engagement.gsql b/connected_customer/customer_360/queries/Individuals_K_Most_Engagement.gsql index cfb9d3f3..50fe9e16 100644 --- a/connected_customer/customer_360/queries/Individuals_K_Most_Engagement.gsql +++ b/connected_customer/customer_360/queries/Individuals_K_Most_Engagement.gsql @@ -11,10 +11,8 @@ CREATE DISTRIBUTED QUERY Individuals_K_Most_Engagement(INT k = 10) FOR GRAPH Cus Output: individuals: - Top-k Individual vertices ordered by n.@engagements, where - n.@engagements is the sum of outdegree("action") over all - sessions created by that Individual. - */ + Top-k Individual vertices with the highest total engagement +*/ MaxAccum @engagements; sessions = {SessionID.*}; diff --git a/connected_customer/customer_360/queries/Individuals_Product_Browse.gsql b/connected_customer/customer_360/queries/Individuals_Product_Browse.gsql index a9576871..f697ceea 100644 --- a/connected_customer/customer_360/queries/Individuals_Product_Browse.gsql +++ b/connected_customer/customer_360/queries/Individuals_Product_Browse.gsql @@ -1,8 +1,7 @@ CREATE DISTRIBUTED QUERY Individuals_Product_Browse(/* Parameters here */) FOR GRAPH Customer_360_Financial { /* Description: - Returns Individuals who have browsed products, via ProductBrowse - events linked from SessionID. + Returns Individuals who have browsed products, via ProductBrowse. Parameters: (None) From e076991840210f8294a2b7004d5fd7111deba969 Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Wed, 10 Dec 2025 14:09:02 +0530 Subject: [PATCH 32/55] made the customer 360 kit query descriptions less verbose --- .../queries/Application_Engagement_Individuals.gsql | 5 +++-- .../customer_360/queries/Application_Starts.gsql | 3 +-- .../queries/Application_Submissions.gsql | 12 +++--------- .../queries/Customers_No_Engagement.gsql | 4 +--- .../customer_360/queries/Customers_With_Product.gsql | 2 +- .../queries/Customers_With_Sessions.gsql | 5 +---- .../queries/Email_Engagement_Accounts.gsql | 4 +--- .../customer_360/queries/Individual_WebSearch.gsql | 5 ++--- .../queries/Individuals_K_Most_Engagement.gsql | 5 ++--- .../queries/Individuals_No_Application.gsql | 6 ------ .../queries/Individuals_Product_Browse.gsql | 4 ---- .../customer_360/queries/delete_all.gsql | 8 -------- 12 files changed, 15 insertions(+), 48 deletions(-) diff --git a/connected_customer/customer_360/queries/Application_Engagement_Individuals.gsql b/connected_customer/customer_360/queries/Application_Engagement_Individuals.gsql index d649d477..b9620071 100644 --- a/connected_customer/customer_360/queries/Application_Engagement_Individuals.gsql +++ b/connected_customer/customer_360/queries/Application_Engagement_Individuals.gsql @@ -1,11 +1,12 @@ CREATE DISTRIBUTED QUERY Application_Engagement_Individuals(/* Parameters here */) FOR GRAPH Customer_360_Financial { /* Description: - Returns individuals who have engaged with a product application via at least one session + Returns individuals who have engaged with a product application via + at least one session Parameters: (None) - Uses all SessionID vertices; no date, product, or channel filters. + Uses all SessionID vertices; Output: Set of Individual vertices with at least one application engagement. diff --git a/connected_customer/customer_360/queries/Application_Starts.gsql b/connected_customer/customer_360/queries/Application_Starts.gsql index d16a4a0b..22c20076 100644 --- a/connected_customer/customer_360/queries/Application_Starts.gsql +++ b/connected_customer/customer_360/queries/Application_Starts.gsql @@ -7,8 +7,7 @@ CREATE DISTRIBUTED QUERY Application_Starts(/* Parameters here */) FOR GRAPH Cus Parameters: (None) - Uses all SessionID vertices; relies on action_detail values - "start_application" and "submit_application". + Uses all SessionID vertices; Output: unfinished_individuals: Individuals with started but unfinished diff --git a/connected_customer/customer_360/queries/Application_Submissions.gsql b/connected_customer/customer_360/queries/Application_Submissions.gsql index 1a9984e8..5cb311fa 100644 --- a/connected_customer/customer_360/queries/Application_Submissions.gsql +++ b/connected_customer/customer_360/queries/Application_Submissions.gsql @@ -1,19 +1,13 @@ CREATE DISTRIBUTED QUERY Application_Submissions(/* Parameters here */) FOR GRAPH Customer_360_Financial { /* Description: - Returns Individuals who both started and submitted an application, - based on sessions that have: - - "start_application" and - - "submit_application" actions. + Returns Individuals who started and submitted an application, Parameters: (None) - Uses all SessionID vertices and ApplicationEngagement edges - filtered by action_detail. - + Uses all SessionID vertices Output: - - @@finished: Set of SessionID vertices with completed applications. - - finished_individuals: Individuals linked to those sessions. + The individuals who submitted applications, and those session ids */ start = {SessionID.*}; SetAccum @@app_starts, @@app_submits, @@finished; diff --git a/connected_customer/customer_360/queries/Customers_No_Engagement.gsql b/connected_customer/customer_360/queries/Customers_No_Engagement.gsql index 99680ca0..278c5a94 100644 --- a/connected_customer/customer_360/queries/Customers_No_Engagement.gsql +++ b/connected_customer/customer_360/queries/Customers_No_Engagement.gsql @@ -1,15 +1,13 @@ CREATE DISTRIBUTED QUERY Customers_No_Engagement(/* Parameters here */) FOR GRAPH Customer_360_Financial { /* Description: - Returns Individuals who have never had any recorded engagement, - defined as having no outgoing "created_session" edges. + Returns Individuals who have never had any recorded engagement. Parameters: (None) Uses all Individual vertices in the graph. Output: - not_engaged: Individual vertices with no recorded engagement */ start = {Individual.*}; diff --git a/connected_customer/customer_360/queries/Customers_With_Product.gsql b/connected_customer/customer_360/queries/Customers_With_Product.gsql index 3f6daf24..7201baff 100644 --- a/connected_customer/customer_360/queries/Customers_With_Product.gsql +++ b/connected_customer/customer_360/queries/Customers_With_Product.gsql @@ -1,7 +1,7 @@ CREATE DISTRIBUTED QUERY Accounts_With_Product(STRING product_type) FOR GRAPH Customer_360_Financial { /* Description: - Returns Accounts that hold a specified product type, matching either + Returns Accounts that are associated with either a credit card or loan products linked via has_product edges. Parameters: diff --git a/connected_customer/customer_360/queries/Customers_With_Sessions.gsql b/connected_customer/customer_360/queries/Customers_With_Sessions.gsql index 8eeccce8..465f413d 100644 --- a/connected_customer/customer_360/queries/Customers_With_Sessions.gsql +++ b/connected_customer/customer_360/queries/Customers_With_Sessions.gsql @@ -1,17 +1,14 @@ CREATE DISTRIBUTED QUERY Customers_With_Sessions(/* Parameters here */) FOR GRAPH Customer_360_Financial { /* Description: - Returns Individuals who have created at least one session, - i.e., those with an outgoing "created_session" edge to SessionID. + Returns Individuals who have created at least one session. Parameters: (None) Uses all Individual vertices in the graph. Output: - engaged: Individual vertices that are connected to at least one SessionID - via created_session. */ start = {Individual.*}; engaged = SELECT s FROM start:s - (created_session:e) - SessionID:t; diff --git a/connected_customer/customer_360/queries/Email_Engagement_Accounts.gsql b/connected_customer/customer_360/queries/Email_Engagement_Accounts.gsql index 7094c1fd..1958ef38 100644 --- a/connected_customer/customer_360/queries/Email_Engagement_Accounts.gsql +++ b/connected_customer/customer_360/queries/Email_Engagement_Accounts.gsql @@ -1,15 +1,13 @@ CREATE DISTRIBUTED QUERY Email_Engagement_Accounts(/* Parameters here */) FOR GRAPH Customer_360_Financial { /* Description: - Returns accounts whose representing individuals initiated sessions that - include an EmailEngagement action. + Returns accounts whose representing individuals have an EmailEngagement. Parameters: (None) Uses all SessionID vertices in the graph. Output: - accounts: Account vertices reachable with linkage to EmailEngagement through a session */ start = {SessionID.*}; diff --git a/connected_customer/customer_360/queries/Individual_WebSearch.gsql b/connected_customer/customer_360/queries/Individual_WebSearch.gsql index 246ab8ec..90fefccd 100644 --- a/connected_customer/customer_360/queries/Individual_WebSearch.gsql +++ b/connected_customer/customer_360/queries/Individual_WebSearch.gsql @@ -9,9 +9,8 @@ CREATE DISTRIBUTED QUERY Individual_WebSearch(/* Parameters here */) FOR GRAPH C Output: individuals: - Individual vertices reachable through - SessionID -(action)-> WebSearch -(created_by)-> Individual. - */ + Individuals (vertices) that have used web_sessions. + */ start = {SessionID.*}; web_sessions = SELECT s FROM start:s - (action:e) - WebSearch:t; diff --git a/connected_customer/customer_360/queries/Individuals_K_Most_Engagement.gsql b/connected_customer/customer_360/queries/Individuals_K_Most_Engagement.gsql index 50fe9e16..98426718 100644 --- a/connected_customer/customer_360/queries/Individuals_K_Most_Engagement.gsql +++ b/connected_customer/customer_360/queries/Individuals_K_Most_Engagement.gsql @@ -1,9 +1,8 @@ CREATE DISTRIBUTED QUERY Individuals_K_Most_Engagement(INT k = 10) FOR GRAPH Customer_360_Financial { /* Description: - Returns the top-k Individuals with the highest total engagement, - where engagement is the number of action events across their - associated SessionID vertices. + Returns the top-k Individuals with the highest total engagement across + events Parameters: k (INT, default = 10): diff --git a/connected_customer/customer_360/queries/Individuals_No_Application.gsql b/connected_customer/customer_360/queries/Individuals_No_Application.gsql index a563d1bd..58995565 100644 --- a/connected_customer/customer_360/queries/Individuals_No_Application.gsql +++ b/connected_customer/customer_360/queries/Individuals_No_Application.gsql @@ -6,12 +6,6 @@ CREATE DISTRIBUTED QUERY Individuals_No_Application(/* Parameters here */) FOR G Parameters: (None) Uses all SessionID vertices in the graph. - - Output: - individuals: - Individual vertices reachable from sessions in 'interest', - where 'interest' are SessionID vertices with at least one - action to a non-ApplicationEngagement vertex. */ sessions = {SessionID.*}; diff --git a/connected_customer/customer_360/queries/Individuals_Product_Browse.gsql b/connected_customer/customer_360/queries/Individuals_Product_Browse.gsql index f697ceea..886b2a81 100644 --- a/connected_customer/customer_360/queries/Individuals_Product_Browse.gsql +++ b/connected_customer/customer_360/queries/Individuals_Product_Browse.gsql @@ -7,10 +7,6 @@ CREATE DISTRIBUTED QUERY Individuals_Product_Browse(/* Parameters here */) FOR G (None) Uses all SessionID vertices in the graph. - Output: - individuals: - Individual vertices reachable through - SessionID -(action)-> ProductBrowse -(created_by)-> Individual. */ start = {SessionID.*}; diff --git a/connected_customer/customer_360/queries/delete_all.gsql b/connected_customer/customer_360/queries/delete_all.gsql index 09a444c5..ad2158d1 100644 --- a/connected_customer/customer_360/queries/delete_all.gsql +++ b/connected_customer/customer_360/queries/delete_all.gsql @@ -3,14 +3,6 @@ CREATE DISTRIBUTED QUERY delete_all(/* Parameters here */) FOR GRAPH Customer_36 Description: Deletes all vertices and edges from the Customer_360_Financial graph, effectively clearing the graph. - - Parameters: - (None) - - Output: - (None) - The graph is emptied; all vertices and edges are removed. - Use with extreme caution in non-production or reset scenarios only. */ all = {ANY}; From a9bd87d2d24d66eb930b7e2989e64507d065b785 Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Wed, 10 Dec 2025 16:05:54 +0530 Subject: [PATCH 33/55] made entity resolution less verbose --- .../queries/delete_all_cc_connections.gsql | 13 +------ .../queries/delete_unused_cc_nodes.gsql | 10 ------ .../find_shared_piis_of_two_entities.gsql | 14 +++----- .../queries/match_entities.gsql | 35 +++++++------------ .../queries/output_entity_cc_to_file.gsql | 15 -------- .../queries/unify_entities.gsql | 23 +++++------- 6 files changed, 26 insertions(+), 84 deletions(-) diff --git a/connected_customer/entity_resolution/queries/delete_all_cc_connections.gsql b/connected_customer/entity_resolution/queries/delete_all_cc_connections.gsql index c471e6b8..6969d7cc 100644 --- a/connected_customer/entity_resolution/queries/delete_all_cc_connections.gsql +++ b/connected_customer/entity_resolution/queries/delete_all_cc_connections.gsql @@ -7,24 +7,13 @@ CREATE OR REPLACE DISTRIBUTED QUERY delete_all_cc_connections(INT num_of_batches Purpose: Remove all `Entity_In_Ring` edges linking `Entity` vertices to - `Connected_Component` vertices. This is typically run before a new - community-detection or CC assignment step so that fresh connections can - be recomputed from scratch. + `Connected_Component` vertices. Do this for a fresh start. Key Concept: . Batch Deletion: Uses `num_of_batches` and `batch_id` to partition the `Entity` vertex space so that very large graphs can be processed across multiple jobs without overloading resources. - - Parameters: - num_of_batches - Total number of batches to divide the `Entity` set into. - batch_id - Zero-based index (0 -> num_of_batches - 1) of the batch - to process in this execution. - - Output: - Deletes the matching `Entity_In_Ring` edges for the selected batch and - prints a summary string with the number of edges deleted. */ SumAccum @@count; diff --git a/connected_customer/entity_resolution/queries/delete_unused_cc_nodes.gsql b/connected_customer/entity_resolution/queries/delete_unused_cc_nodes.gsql index 99dffa3b..979172c9 100644 --- a/connected_customer/entity_resolution/queries/delete_unused_cc_nodes.gsql +++ b/connected_customer/entity_resolution/queries/delete_unused_cc_nodes.gsql @@ -14,16 +14,6 @@ CREATE OR REPLACE DISTRIBUTED QUERY delete_unused_cc_nodes(INT num_of_batches = . Orphaned CC Nodes: A `Connected_Component` vertex with `outdegree() == 0` is considered unused and can be safely deleted. - - Parameters: - num_of_batches - Total number of batches used to partition all - `Connected_Component` vertices for deletion. - batch_id - Zero-based index (0 -> num_of_batches - 1) of the batch - processed in this execution. - - Output: - Deletes all unused `Connected_Component` vertices in the selected batch and - prints a summary with the number of vertices removed. */ SumAccum @@count; diff --git a/connected_customer/entity_resolution/queries/find_shared_piis_of_two_entities.gsql b/connected_customer/entity_resolution/queries/find_shared_piis_of_two_entities.gsql index 75ac79d8..db2db44c 100644 --- a/connected_customer/entity_resolution/queries/find_shared_piis_of_two_entities.gsql +++ b/connected_customer/entity_resolution/queries/find_shared_piis_of_two_entities.gsql @@ -7,22 +7,16 @@ CREATE OR REPLACE QUERY find_shared_piis_of_two_entities(VERTEX entity_1 Purpose: Given two `Entity` vertices, identify all **PII vertices** that are directly - shared between them. This helps explain why two entities are connected or - suspected to be related (e.g., fraud review, ER explainability, data quality). + shared between them. Key Output Fields: . pii_type - The type/label of the shared PII vertex. - . pii_value - The shared PII vertex instance (its ID/value). - . degree - Outdegree of the PII vertex, i.e., how many entities share it. - High-degree PII may indicate a common hub (e.g., shared email domain). - - Parameters: - entity_1 - First `Entity` vertex to compare. - entity_2 - Second `Entity` vertex to compare. + . pii_value - The shared PII vertex instance + . degree - How many vertices share the PII Output: A list of tuples (pii_type, pii_value, degree) for each PII vertex that is - connected to both `entity_1` and `entity_2`. + connected to both entities */ TYPEDEF TUPLE pii_info; ListAccum @@degrees_of_shared_piis; diff --git a/connected_customer/entity_resolution/queries/match_entities.gsql b/connected_customer/entity_resolution/queries/match_entities.gsql index e4c9ca10..df44a7dc 100644 --- a/connected_customer/entity_resolution/queries/match_entities.gsql +++ b/connected_customer/entity_resolution/queries/match_entities.gsql @@ -19,34 +19,24 @@ CREATE OR REPLACE DISTRIBUTED QUERY match_entities( DATETIME compute_entities_after_date = to_datetime("1970-01-01 00:00:00") // All entities that were created in TG after this date, will be computed to find similarity edges related to these new entities. The default value is the earliest time possible (great for the first time running this query) ) FOR GRAPH Entity_Resolution { -/ /* Query Name: match_entities Batch-Based Weighted & Fuzzy Entity Resolution (All-Pairs Similarity) Purpose: - Offline batch job that finds and links similar `Entity` vertices based on shared PII. - Uses both exact matches and fuzzy string similarity (on hashed email, name, and phone) - to compute a weighted similarity score. When the score exceeds `threshold`, a `Same_As` - edge is created between the matching entities. + Batch job that finds and links similar `Entity` vertices based on shared PII + using exact and fuzzy matching to compute a weighted similarity score. + When the score exceeds `threshold`, a `Same_As` edge is created between + the matching entities. + This operation may be resource intensive. Key Concepts: - . Weighted Similarity: - Each PII type contributes a configurable weight to the final match score. - . Fuzzy Matching: - Hashed email, name, and phone are compared with Jaro-Winkler-based scores - derived from the original values. - . Nested Batching: - Entities are divided into `source` and `target` batches to scale all-pairs - comparison and control memory usage. - . Super-Node Filtering: - Highly connected PII vertices are skipped using `pii_low_connections_limit` - and `pii_high_connections_limit` to prevent false positives and hot spots. - . Incremental Runs: - `compute_entities_after_date` allows focusing on recently created entities - for incremental/recurring execution. - - Parameters (high-level): + . Each PII type contributes a configurable weight to the final match score. + . PIIs like name, email and phone number are hashed and compared with + derived Jaro-Winkler scores. + . Highly connected PII vertices are skipped to prevent false positives and hot spots. + + Parameters: customer_has_*_weight Controls how strongly each PII attribute influences the similarity score. num_of_source_batches, num_of_target_batches @@ -58,8 +48,7 @@ CREATE OR REPLACE DISTRIBUTED QUERY match_entities( and considered for similarity edge creation. Output: - Inserts `Same_As` edges between matched `Entity` pairs and prints aggregate - statistics (number of pairs matched and score distribution). + Inserts `Same_As` edges between matched `Entity` pairs and prints these stats */ TYPEDEF TUPLE, str STRING, created_at DATETIME> entity_fuzzy_vertex_info; ListAccum @entity_email_address_list; diff --git a/connected_customer/entity_resolution/queries/output_entity_cc_to_file.gsql b/connected_customer/entity_resolution/queries/output_entity_cc_to_file.gsql index c6c0a314..2ab66b96 100644 --- a/connected_customer/entity_resolution/queries/output_entity_cc_to_file.gsql +++ b/connected_customer/entity_resolution/queries/output_entity_cc_to_file.gsql @@ -11,21 +11,6 @@ CREATE OR REPLACE QUERY output_entity_cc_to_file(STRING output_file_path = "/hom `Entity_In_Ring` edges. Useful for reporting, audits, and downstream ML or warehousing workflows. - Key Concepts: - . FILE output: - Uses TigerGraph's `FILE` object to write directly to a server-side file. - . One-hop traversal: - Traverses from each `Entity` to its `Connected_Component` in a single hop. - - Parameters: - output_file_path - Absolute path where the CSV file will be created on - the TigerGraph server. - - Output: - Creates a CSV file with header: - "Entity,Connected_Component" - and one row per `Entity`-`Connected_Component` pair, then prints a - confirmation message. */ FILE f (output_file_path); diff --git a/connected_customer/entity_resolution/queries/unify_entities.gsql b/connected_customer/entity_resolution/queries/unify_entities.gsql index d2939766..ff1ab9e9 100644 --- a/connected_customer/entity_resolution/queries/unify_entities.gsql +++ b/connected_customer/entity_resolution/queries/unify_entities.gsql @@ -3,29 +3,24 @@ USE GRAPH Entity_Resolution CREATE OR REPLACE DISTRIBUTED QUERY unify_entities() FOR GRAPH Entity_Resolution { /* Query Name: unify_entities - Finalize Entity Resolution: Connected Component Assignment for Entities + Finalize Entity Resolution: Connected Component Assignment for Similar Entities Purpose: - Use existing `Same_As` similarity edges (e.g., created by `match_entities`) - to group all connected `Entity` vertices into communities, then materialize - each community as a `Connected_Component` vertex linked by `Entity_In_Ring` - edges. + Using the existing `Same_As` similarity edges created by `match_entities`, we + group all connected `Entity` vertices into communities, then each community + is a `Connected_Component` vertex linked by `Entity_In_Ring` edges. - Core Logic (Label Propagation with MinAccum): - 1. Initialization: - Each `Entity` vertex is labeled with its own internal ID as a tentative - component ID. - 2. Propagation: - The smallest ID is iteratively propagated across `Same_As` edges until + Core Logic + 1. Each `Entity` vertex is labeled with its own internal ID + 2. The smallest ID is iteratively propagated across `Same_As` edges until all vertices in a connected group share the same ID. - 3. Creation: - For each final ID, a `Connected_Component` vertex is created and every + 3. For each final ID, a `Connected_Component` vertex is created and every member `Entity` is connected to it via `Entity_In_Ring`. Output: . One `Connected_Component` vertex per connected group of `Entity` vertices. . One `Entity_In_Ring` edge from each `Entity` to its community. - . Printed execution time (in seconds) and a completion timestamp. + . Printed execution time */ MinAccum @cc_id; // Each vertex's tentative component id From 1b554aa962d630eec605615d496106ed0f4cfc71 Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Wed, 10 Dec 2025 20:47:14 +0530 Subject: [PATCH 34/55] made product recommendation query descriptions less verbose --- .../queries/combine_features.gsql | 18 ++++----------- .../queries/k_means.gsql | 23 +++++-------------- .../queries/recommend_products.gsql | 16 ++++++------- 3 files changed, 18 insertions(+), 39 deletions(-) diff --git a/connected_customer/product_recommendations/queries/combine_features.gsql b/connected_customer/product_recommendations/queries/combine_features.gsql index 63877e3b..7f3f71b4 100644 --- a/connected_customer/product_recommendations/queries/combine_features.gsql +++ b/connected_customer/product_recommendations/queries/combine_features.gsql @@ -6,26 +6,19 @@ CREATE QUERY combine_features( INT hub_threshold, INT split_threshold) SYNTAX V2 { - /* Query Name: combine_features Feature Co-Occurrence Grouping Purpose: Identify frequently co-occurring feature pairs around high-degree - feature vertices and collapse them into `Combined_Feature` vertices. This - reduces hub noise and creates more informative composite features for - downstream models or recommenders. + feature vertices and collapse them into `Combined_Feature` vertices. High-Level Flow: - . Hub detection: - Select `hub_v_type` vertices whose degree over `e_type` exceeds - `hub_threshold` and mark them as hubs. - . Co-occurrence counting: - For each hub-feature pair connected through `target_v_type` vertices + . Find hub vertices. + . For each hub-feature pair connected through `target_v_type` vertices (e.g., Customers), count how many targets they share. - . Pair selection: - Keep hub-feature pairs whose shared target count lies between 2 and + . Keep hub-feature pairs whose shared target count lies between 2 and `split_threshold`, treating them as meaningful but not overly common co-occurrences. . Combined feature creation: @@ -37,8 +30,7 @@ CREATE QUERY combine_features( Configuration: `hub_v_type`, `feature_v_type`, `target_v_type`, and `e_type` define the - schema context (which vertices/edges are treated as hubs, features, and - targets), while `hub_threshold` and `split_threshold` control how aggressively + schema context, while `hub_threshold` and `split_threshold` control how aggressively features are combined. Output: diff --git a/connected_customer/product_recommendations/queries/k_means.gsql b/connected_customer/product_recommendations/queries/k_means.gsql index 7a6c51c9..ace28ed9 100644 --- a/connected_customer/product_recommendations/queries/k_means.gsql +++ b/connected_customer/product_recommendations/queries/k_means.gsql @@ -11,7 +11,6 @@ CREATE QUERY k_means( BOOL use_custom_timestamp=FALSE, DATETIME custom_timestamp=to_datetime("1970-01-01")) SYNTAX V1 { - /* Query Name: k_means K-Means Clustering over Vertex Attribute Vectors @@ -24,26 +23,16 @@ CREATE QUERY k_means( elbow point. High-Level Flow: - . Build feature vectors: - Parse `attr_set` to select a subset of numeric keys from `attr_map`, - normalize each feature across all vertices, and construct per-vertex + . Select a subset of numeric keys from `attr_map` and construct per-vertex vectors. - . K sweep & initialization: - For each candidate K in [min_cluster_count, max_cluster_count] (step + . For each candidate K in [min_cluster_count, max_cluster_count] (step `cluster_inc`), run up to `random_iter_count` random initializations of - centroids using a seeded PRNG (`random_seed`). - . K-Means iterations: - Assign each vertex to its closest centroid, recompute centroids from - assigned members, and repeat until convergence (change in SSE below - `conv_threshold` or `conv_iter_limit` reached). - . Model selection & merge: - Track SSE across K values and iterations, approximate the elbow via + centroids + . Assign each vertex to its closest centroid, recompute centroids from + assigned members, and repeat until convergence + . Track SSE across K values and iterations, approximate the elbow via second differences in SSE, and optionally merge nearly identical centroids. - . Materialization: - For the chosen K and iteration, insert one `Cluster` vertex per - centroid and an `In_Cluster` edge from each vertex to its cluster, - stamped with either `now()` or `custom_timestamp`. Configuration: Parameters control the vertex type (`v_type`), feature subset (`attr_set`), diff --git a/connected_customer/product_recommendations/queries/recommend_products.gsql b/connected_customer/product_recommendations/queries/recommend_products.gsql index f10903e4..f6a9e6d1 100644 --- a/connected_customer/product_recommendations/queries/recommend_products.gsql +++ b/connected_customer/product_recommendations/queries/recommend_products.gsql @@ -17,32 +17,30 @@ CREATE DISTRIBUTED QUERY recommend_products( Hybrid Customer-Item Collaborative Filtering Recommender Purpose: - For each source `Customer`, generate a ranked list of `Product_Variant` + For each `Customer` vertex, generate a ranked list of `Product_Variant` recommendations using a combination of: - . customer-customer similarity (shared features/interactions) + . customer-customer similarity. . item-item co-occurrence (co-purchasing behavior). High-Level Flow: - . Select source customers (explicit input set or batched over all customers). + . Select source customers . Build customer-customer similarity from feature overlaps, weighted by interaction strength, edge importance, and feature popularity. . Derive item popularity per customer from similar customers and co-occurring items. - . Optionally reweight by category importance and vertex-degree scaling. + . (Optionally reweight by category importance and vertex-degree scaling.) . Keep up to `recommendation_count` best items per customer. Configuration: - Key parameters control which customers are processed (src_customer_input, + Parameters that control which customers are processed (src_customer_input, batch_index/num_batches), filtering and list length (ignore_threshold, recommendation_count), and how schema/weights are applied (data_types, edge_importance_factors, vertex_degree_scales, customer_popularity_scale, item_popularity_scale). Output: - For each source `Customer`, returns a list of up to `recommendation_count` - `Product_Variant` recommendations with normalized scores, ordered by - descending score. - */ + For each `Customer` vertex, returns a list of `Product_Variant` recommendations + */ TYPEDEF TUPLE Item_Tuple; HeapAccum (recommendation_count, score DESC, item DESC) @recommended_items; MapAccum> @sum_intersection_size, @@sum_set_size; From c7b54ec6af059908e49859c3cefbd8ca7d995b22 Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Tue, 16 Dec 2025 15:04:35 +0530 Subject: [PATCH 35/55] minor comment changes --- .../customer_360/queries/Individuals_No_Application.gsql | 4 ++-- .../queries/batch_application_cc_features.gsql | 3 --- financial_crime/mule_account_detection/README.md | 3 +++ 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/connected_customer/customer_360/queries/Individuals_No_Application.gsql b/connected_customer/customer_360/queries/Individuals_No_Application.gsql index 58995565..04629361 100644 --- a/connected_customer/customer_360/queries/Individuals_No_Application.gsql +++ b/connected_customer/customer_360/queries/Individuals_No_Application.gsql @@ -1,7 +1,7 @@ CREATE DISTRIBUTED QUERY Individuals_No_Application(/* Parameters here */) FOR GRAPH Customer_360_Financial { /* Description: - Find individuals who looked at a product but never started an application + Find individuals who looked at a product but dont ONLY have an application Parameters: (None) @@ -19,4 +19,4 @@ CREATE DISTRIBUTED QUERY Individuals_No_Application(/* Parameters here */) FOR G PRINT individuals; } -UPDATE DESCRIPTION OF QUERY Individuals_No_Application "Return individuals who have never started an application but have looked at products" +UPDATE DESCRIPTION OF QUERY Individuals_No_Application "Return individuals who have not ONLY made an application, but have products" diff --git a/financial_crime/application_fraud/queries/batch_application_cc_features.gsql b/financial_crime/application_fraud/queries/batch_application_cc_features.gsql index 7419b919..c9f20ad2 100644 --- a/financial_crime/application_fraud/queries/batch_application_cc_features.gsql +++ b/financial_crime/application_fraud/queries/batch_application_cc_features.gsql @@ -10,9 +10,6 @@ CREATE OR REPLACE DISTRIBUTED QUERY batch_application_cc_features(INT connection Maximum outdegree for a PII vertex when counting shared links; output_file_path: CSV Output file path - - Output: - CSV file of application connected components and PII stats */ MapAccum @@connect_component, @@distinct_name, @@distinct_dob, @@distinct_email, @@distinct_phone, @@distinct_address, @@distinct_ip, @@distinct_id, @@distinct_device, @@distinct_party, @@distinct_account, @@distinct_card, @@connected_via_name, @@connected_via_dob, @@connected_via_email, @@connected_via_phone, @@connected_via_address, @@connected_via_ip, @@connected_via_id, @@connected_via_device, @@connected_via_party, @@connected_via_account, @@connected_via_card, @@only_connected_via_name, @@only_connected_via_dob, @@only_connected_via_email, @@only_connected_via_phone, @@only_connected_via_address, @@only_connected_via_ip, @@only_connected_via_id, @@only_connected_via_device, @@only_connected_via_party, @@only_connected_via_account, @@only_connected_via_card; OrAccum @connected_via_name, @connected_via_dob, @connected_via_email, @connected_via_phone, @connected_via_address, @connected_via_ip, @connected_via_id, @connected_via_device, @connected_via_party, @connected_via_account, @connected_via_card; diff --git a/financial_crime/mule_account_detection/README.md b/financial_crime/mule_account_detection/README.md index b7eb64ec..ecb9b991 100644 --- a/financial_crime/mule_account_detection/README.md +++ b/financial_crime/mule_account_detection/README.md @@ -122,6 +122,9 @@ This repository includes multiple components: ## Prerequisites +On TG Savanna, you can install these with the click of a button, and the +following prerequisites are for an on-prem setup. + You will need: - **TigerGraph 3.x or 4.x** From 77fc36165015a86c64c37997f1d7338f52d5238a Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Thu, 8 Jan 2026 10:57:15 +0530 Subject: [PATCH 36/55] Fixed schema application fraud --- .../schema/create_schema.gsql | 171 +----------------- .../application_fraud/schema/local_schema.txt | 171 ++++++++++++++++++ 2 files changed, 172 insertions(+), 170 deletions(-) create mode 100644 financial_crime/application_fraud/schema/local_schema.txt diff --git a/financial_crime/application_fraud/schema/create_schema.gsql b/financial_crime/application_fraud/schema/create_schema.gsql index 11cb39c7..6df55ace 100644 --- a/financial_crime/application_fraud/schema/create_schema.gsql +++ b/financial_crime/application_fraud/schema/create_schema.gsql @@ -1,171 +1,2 @@ -USE GLOBAL - -// (PII) -CREATE VERTEX Phone ( - PRIMARY_ID phone_number STRING -) WITH primary_id_as_attribute="true" - -CREATE VERTEX Device ( - PRIMARY_ID id STRING, - is_blocked BOOL -) WITH primary_id_as_attribute="true" - -CREATE VERTEX Full_Name ( - PRIMARY_ID name STRING -) WITH primary_id_as_attribute="true" - -CREATE VERTEX Card ( - PRIMARY_ID card_number INT, - is_fraud INT, - pagerank FLOAT, - c_id INT, - c_size INT, - occupation STRING -) WITH primary_id_as_attribute="true" - -CREATE VERTEX DOB ( - PRIMARY_ID dob STRING -) WITH primary_id_as_attribute="true" - -// Products (cards, loans, etc.) -CREATE VERTEX Product ( - PRIMARY_ID id STRING, - name STRING, - description STRING, - type_of STRING -) WITH primary_id_as_attribute="true" - -// Connected component / fraud ring ID -CREATE VERTEX Connected_Component ( - PRIMARY_ID id INT -) WITH primary_id_as_attribute="true" - -// Account entity -CREATE VERTEX Account ( - PRIMARY_ID id STRING, - create_Time DATETIME, - is_fraud INT, - account_type STRING, - account_level STRING, - com_size INT, - pagerank FLOAT, - shortest_path_length INT, - ip_collision INT, - fraud_ip INT, - device_collision INT, - fraud_device INT, - trans_in_mule_ratio FLOAT, - trans_out_mule_ratio FLOAT, - mule_cnt INT, - com_id INT, -) WITH primary_id_as_attribute="true" - -CREATE VERTEX State ( - PRIMARY_ID id STRING -) WITH primary_id_as_attribute="true" - -CREATE VERTEX IP ( - PRIMARY_ID id STRING, - is_blocked BOOL -) WITH primary_id_as_attribute="true" - -CREATE VERTEX Address ( - PRIMARY_ID address STRING -) WITH primary_id_as_attribute="true" - -CREATE VERTEX Email ( - PRIMARY_ID email STRING -) WITH primary_id_as_attribute="true" - -CREATE VERTEX Country ( - PRIMARY_ID country STRING -) WITH primary_id_as_attribute="true" - -CREATE VERTEX City ( - PRIMARY_ID id STRING, - city STRING, - population INT -) WITH primary_id_as_attribute="true" - -CREATE VERTEX County ( - PRIMARY_ID id STRING -) WITH primary_id_as_attribute="true" - -CREATE VERTEX Zipcode ( - PRIMARY_ID id STRING -) WITH primary_id_as_attribute="true" - -// Party (person / organization) -CREATE VERTEX Party ( - PRIMARY_ID id STRING, - is_fraud INT, - gender STRING, - dob DATETIME, - party_type STRING, - name STRING, - created_at DATETIME -) WITH primary_id_as_attribute="true" - -CREATE VERTEX ID ( - PRIMARY_ID id STRING, - id_type STRING -) WITH primary_id_as_attribute="true" - -// Credit application -CREATE VERTEX Application ( - PRIMARY_ID id STRING, - created_at DATETIME, - status STRING, - line_of_credit FLOAT, - annual_percentage_rate FLOAT, - is_fraud BOOL -) WITH primary_id_as_attribute="true" - - -CREATE UNDIRECTED EDGE Has_Address (FROM Party, TO Address) -CREATE UNDIRECTED EDGE Has_ID (FROM Party, TO ID) -CREATE UNDIRECTED EDGE Has_IP (FROM Party, TO IP) -CREATE UNDIRECTED EDGE Has_Device (FROM Party, TO Device) -CREATE UNDIRECTED EDGE Has_Phone (FROM Party, TO Phone) -CREATE UNDIRECTED EDGE Has_Email (FROM Party, TO Email) -CREATE UNDIRECTED EDGE Has_DOB (FROM Party, TO DOB) -CREATE UNDIRECTED EDGE Has_Full_Name (FROM Party, TO Full_Name) - -CREATE UNDIRECTED EDGE Application_Has_Full_Name (FROM Application, TO Full_Name) -CREATE UNDIRECTED EDGE Application_Has_Phone (FROM Application, TO Phone) -CREATE UNDIRECTED EDGE Application_Has_Email (FROM Application, TO Email) -CREATE UNDIRECTED EDGE Application_Has_DOB (FROM Application, TO DOB) -CREATE UNDIRECTED EDGE Application_Has_ID (FROM Application, TO ID) -CREATE UNDIRECTED EDGE Application_Has_Device (FROM Application, TO Device) -CREATE UNDIRECTED EDGE Application_Has_IP (FROM Application, TO IP) -CREATE UNDIRECTED EDGE Application_Has_Address (FROM Application, TO Address) -CREATE UNDIRECTED EDGE Application_Has_Product (FROM Application, TO Product) -CREATE UNDIRECTED EDGE Application_Has_Account (FROM Application, TO Account) -CREATE UNDIRECTED EDGE Application_Has_Party (FROM Party, TO Application) -CREATE UNDIRECTED EDGE Application_Has_Card (FROM Application, TO Card) - - -CREATE UNDIRECTED EDGE Assigned_To (FROM Address|Zipcode, TO Zipcode|City) -CREATE UNDIRECTED EDGE Assigned_To_County (FROM Zipcode, TO County) -CREATE UNDIRECTED EDGE Located_In (FROM Address|City, TO City|State) -CREATE UNDIRECTED EDGE Located_In_State (FROM County, TO State) -CREATE UNDIRECTED EDGE Located_In_Country (FROM State, TO Country) - - -CREATE UNDIRECTED EDGE Application_In_Ring (FROM Application, TO Connected_Component) -CREATE UNDIRECTED EDGE Entity_In_Ring (FROM Connected_Component, TO Party) -CREATE UNDIRECTED EDGE Same_As (FROM Party, TO Party, score DOUBLE) -CREATE UNDIRECTED EDGE Same_Application (FROM Application, TO Application, score DOUBLE) - - -CREATE GRAPH Application_Fraud(Phone, Email, Address, IP, Device, City, -Country,State, Full_Name, Zipcode, County, ID, Party, Connected_Component, DOB, -Application, Product, Account, Card, Has_Address, Has_ID, Has_IP, Has_Device, -Has_Phone, Has_Email, Located_In, Assigned_To_County, Located_In_State, -Located_In_Country, Same_As, Entity_In_Ring, Has_DOB, Has_Full_Name, -Application_Has_Full_Name, Application_Has_Phone, Application_Has_Email, -Application_Has_DOB, Application_Has_ID, Application_Has_Device, -Application_Has_IP, Same_Application, Application_Has_Address, -Application_In_Ring, Application_Has_Product, Application_Has_Account, -Application_Has_Party, Assigned_To, Application_Has_Card) +CREATE GRAPH Application_Fraud(Phone, Email, Address, IP, Device, City, Country, State, Full_Name, Zipcode, County, ID, Party, Connected_Component, DOB, Application, Product, Account, Card, Has_Address, Has_ID, Has_IP, Has_Device, Has_Phone, Has_Email, Located_In, Assigned_To_County, Located_In_State, Located_In_Country, Same_As, Entity_In_Ring, Has_DOB, Has_Full_Name, Application_Has_Full_Name, Application_Has_Phone, Application_Has_Email, Application_Has_DOB, Application_Has_ID, Application_Has_Device, Application_Has_IP, Same_Application, Application_Has_Address, Application_In_Ring, Application_Has_Product, Application_Has_Account, Application_Has_Party, Assigned_To, Application_Has_Card) set exit_on_error = "false" diff --git a/financial_crime/application_fraud/schema/local_schema.txt b/financial_crime/application_fraud/schema/local_schema.txt new file mode 100644 index 00000000..11cb39c7 --- /dev/null +++ b/financial_crime/application_fraud/schema/local_schema.txt @@ -0,0 +1,171 @@ +USE GLOBAL + +// (PII) +CREATE VERTEX Phone ( + PRIMARY_ID phone_number STRING +) WITH primary_id_as_attribute="true" + +CREATE VERTEX Device ( + PRIMARY_ID id STRING, + is_blocked BOOL +) WITH primary_id_as_attribute="true" + +CREATE VERTEX Full_Name ( + PRIMARY_ID name STRING +) WITH primary_id_as_attribute="true" + +CREATE VERTEX Card ( + PRIMARY_ID card_number INT, + is_fraud INT, + pagerank FLOAT, + c_id INT, + c_size INT, + occupation STRING +) WITH primary_id_as_attribute="true" + +CREATE VERTEX DOB ( + PRIMARY_ID dob STRING +) WITH primary_id_as_attribute="true" + +// Products (cards, loans, etc.) +CREATE VERTEX Product ( + PRIMARY_ID id STRING, + name STRING, + description STRING, + type_of STRING +) WITH primary_id_as_attribute="true" + +// Connected component / fraud ring ID +CREATE VERTEX Connected_Component ( + PRIMARY_ID id INT +) WITH primary_id_as_attribute="true" + +// Account entity +CREATE VERTEX Account ( + PRIMARY_ID id STRING, + create_Time DATETIME, + is_fraud INT, + account_type STRING, + account_level STRING, + com_size INT, + pagerank FLOAT, + shortest_path_length INT, + ip_collision INT, + fraud_ip INT, + device_collision INT, + fraud_device INT, + trans_in_mule_ratio FLOAT, + trans_out_mule_ratio FLOAT, + mule_cnt INT, + com_id INT, +) WITH primary_id_as_attribute="true" + +CREATE VERTEX State ( + PRIMARY_ID id STRING +) WITH primary_id_as_attribute="true" + +CREATE VERTEX IP ( + PRIMARY_ID id STRING, + is_blocked BOOL +) WITH primary_id_as_attribute="true" + +CREATE VERTEX Address ( + PRIMARY_ID address STRING +) WITH primary_id_as_attribute="true" + +CREATE VERTEX Email ( + PRIMARY_ID email STRING +) WITH primary_id_as_attribute="true" + +CREATE VERTEX Country ( + PRIMARY_ID country STRING +) WITH primary_id_as_attribute="true" + +CREATE VERTEX City ( + PRIMARY_ID id STRING, + city STRING, + population INT +) WITH primary_id_as_attribute="true" + +CREATE VERTEX County ( + PRIMARY_ID id STRING +) WITH primary_id_as_attribute="true" + +CREATE VERTEX Zipcode ( + PRIMARY_ID id STRING +) WITH primary_id_as_attribute="true" + +// Party (person / organization) +CREATE VERTEX Party ( + PRIMARY_ID id STRING, + is_fraud INT, + gender STRING, + dob DATETIME, + party_type STRING, + name STRING, + created_at DATETIME +) WITH primary_id_as_attribute="true" + +CREATE VERTEX ID ( + PRIMARY_ID id STRING, + id_type STRING +) WITH primary_id_as_attribute="true" + +// Credit application +CREATE VERTEX Application ( + PRIMARY_ID id STRING, + created_at DATETIME, + status STRING, + line_of_credit FLOAT, + annual_percentage_rate FLOAT, + is_fraud BOOL +) WITH primary_id_as_attribute="true" + + +CREATE UNDIRECTED EDGE Has_Address (FROM Party, TO Address) +CREATE UNDIRECTED EDGE Has_ID (FROM Party, TO ID) +CREATE UNDIRECTED EDGE Has_IP (FROM Party, TO IP) +CREATE UNDIRECTED EDGE Has_Device (FROM Party, TO Device) +CREATE UNDIRECTED EDGE Has_Phone (FROM Party, TO Phone) +CREATE UNDIRECTED EDGE Has_Email (FROM Party, TO Email) +CREATE UNDIRECTED EDGE Has_DOB (FROM Party, TO DOB) +CREATE UNDIRECTED EDGE Has_Full_Name (FROM Party, TO Full_Name) + +CREATE UNDIRECTED EDGE Application_Has_Full_Name (FROM Application, TO Full_Name) +CREATE UNDIRECTED EDGE Application_Has_Phone (FROM Application, TO Phone) +CREATE UNDIRECTED EDGE Application_Has_Email (FROM Application, TO Email) +CREATE UNDIRECTED EDGE Application_Has_DOB (FROM Application, TO DOB) +CREATE UNDIRECTED EDGE Application_Has_ID (FROM Application, TO ID) +CREATE UNDIRECTED EDGE Application_Has_Device (FROM Application, TO Device) +CREATE UNDIRECTED EDGE Application_Has_IP (FROM Application, TO IP) +CREATE UNDIRECTED EDGE Application_Has_Address (FROM Application, TO Address) +CREATE UNDIRECTED EDGE Application_Has_Product (FROM Application, TO Product) +CREATE UNDIRECTED EDGE Application_Has_Account (FROM Application, TO Account) +CREATE UNDIRECTED EDGE Application_Has_Party (FROM Party, TO Application) +CREATE UNDIRECTED EDGE Application_Has_Card (FROM Application, TO Card) + + +CREATE UNDIRECTED EDGE Assigned_To (FROM Address|Zipcode, TO Zipcode|City) +CREATE UNDIRECTED EDGE Assigned_To_County (FROM Zipcode, TO County) +CREATE UNDIRECTED EDGE Located_In (FROM Address|City, TO City|State) +CREATE UNDIRECTED EDGE Located_In_State (FROM County, TO State) +CREATE UNDIRECTED EDGE Located_In_Country (FROM State, TO Country) + + +CREATE UNDIRECTED EDGE Application_In_Ring (FROM Application, TO Connected_Component) +CREATE UNDIRECTED EDGE Entity_In_Ring (FROM Connected_Component, TO Party) +CREATE UNDIRECTED EDGE Same_As (FROM Party, TO Party, score DOUBLE) +CREATE UNDIRECTED EDGE Same_Application (FROM Application, TO Application, score DOUBLE) + + +CREATE GRAPH Application_Fraud(Phone, Email, Address, IP, Device, City, +Country,State, Full_Name, Zipcode, County, ID, Party, Connected_Component, DOB, +Application, Product, Account, Card, Has_Address, Has_ID, Has_IP, Has_Device, +Has_Phone, Has_Email, Located_In, Assigned_To_County, Located_In_State, +Located_In_Country, Same_As, Entity_In_Ring, Has_DOB, Has_Full_Name, +Application_Has_Full_Name, Application_Has_Phone, Application_Has_Email, +Application_Has_DOB, Application_Has_ID, Application_Has_Device, +Application_Has_IP, Same_Application, Application_Has_Address, +Application_In_Ring, Application_Has_Product, Application_Has_Account, +Application_Has_Party, Assigned_To, Application_Has_Card) +set exit_on_error = "false" From 964301cd5f3f7b8752dfe1c2f1764b8b0a194409 Mon Sep 17 00:00:00 2001 From: Abraham Chandy <149845012+abrahamchandy95@users.noreply.github.com> Date: Mon, 2 Feb 2026 15:51:33 +0530 Subject: [PATCH 37/55] Update connected_customer/product_recommendations/queries/k_means.gsql Co-authored-by: jim-limprasert-tigergraph <152193148+jim-limprasert-tigergraph@users.noreply.github.com> --- connected_customer/product_recommendations/queries/k_means.gsql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connected_customer/product_recommendations/queries/k_means.gsql b/connected_customer/product_recommendations/queries/k_means.gsql index ace28ed9..6eb2be6b 100644 --- a/connected_customer/product_recommendations/queries/k_means.gsql +++ b/connected_customer/product_recommendations/queries/k_means.gsql @@ -261,7 +261,7 @@ CREATE QUERY k_means( UPDATE DESCRIPTION OF QUERY k_means "This query clusters vertices according to some subset of their numerical attributes using the KMeans algorithm, which is a vector quantization clustering algorithm. The algorithm iteratively attempts to cluster the vertices using an incremental number of centroids and then selects the centroid assignment which results in the best sum of squared errors, such that the clusters are not under or overfit to the dataset. The algorithm inserts the centroids as Cluster vertices and links all vertices to their respective centroids via the In_Cluster edge type." UPDATE DESCRIPTION OF QUERY_PARAM k_means.v_type "This is the target vertex type to cluster. It defaults to Customer." -UPDATE DESCRIPTION OF QUERY_PARAM k_means.attr_set "This is a JSON-formatted string representing a list of tuples containing an index and the key of a numerical value contained in the MAP attribute, 'attr_map', on the target vertex type. Example: '[0, \"Age\"]'" +UPDATE DESCRIPTION OF QUERY_PARAM k_means.attr_set "This is a SET of JSON-formatted string where each string represent a list of tuples containing an index and the key of a numerical value contained in the MAP attribute, 'attr_map', on the target vertex type. Example of ONE of the strings in attr_set: `[0, \"age\"]` (change `\"` to be `"` if quotes don't need to be escaped) UPDATE DESCRIPTION OF QUERY_PARAM k_means.min_cluster_count "This is the minimum number of centroids to try out when clustering." UPDATE DESCRIPTION OF QUERY_PARAM k_means.max_cluster_count "This is the maximum number of centroids to try out when clustering. Must be greater than or equal to min_cluster_count." UPDATE DESCRIPTION OF QUERY_PARAM k_means.cluster_inc "This is the value by which the centroid count is incremented when moving onto the next centroid count. Defaults to 1." From 10320e376df846d4a5fba82d719b099a51939566 Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Mon, 2 Feb 2026 17:02:52 +0530 Subject: [PATCH 38/55] fixed broken links in customer 360 readme --- connected_customer/customer_360/README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/connected_customer/customer_360/README.md b/connected_customer/customer_360/README.md index 7bce69d3..a654e42f 100644 --- a/connected_customer/customer_360/README.md +++ b/connected_customer/customer_360/README.md @@ -20,10 +20,9 @@ With this kit you can: - [Overview](#overview) - [Features](#features) - [Prerequisites](#prerequisites) -- [Quickstart](#quickstart) -- [Working-with-the-graph](#working-with-the-graph) +- [Working-with-the-graph] - [Query-Explanations](#query-explanations) - - [Running-example-queries](#running-example-queries) + - [Run-example-queries](#run-example-queries) - [Using-your-own-data](#using-your-own-data) - [Resetting-the-environment](#resetting-the-environment) From 373c6192306705442c57b945e04fcf5094283b9b Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Mon, 2 Feb 2026 17:08:31 +0530 Subject: [PATCH 39/55] Accounts_with_Product file name updated to match query name --- .../{Customers_With_Product.gsql => Accounts_With_Product.gsql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename connected_customer/customer_360/queries/{Customers_With_Product.gsql => Accounts_With_Product.gsql} (100%) diff --git a/connected_customer/customer_360/queries/Customers_With_Product.gsql b/connected_customer/customer_360/queries/Accounts_With_Product.gsql similarity index 100% rename from connected_customer/customer_360/queries/Customers_With_Product.gsql rename to connected_customer/customer_360/queries/Accounts_With_Product.gsql From 44109bae480d1903827c906072b111b283f19bcf Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Mon, 2 Feb 2026 17:43:28 +0530 Subject: [PATCH 40/55] fixed broken links in all readme files --- .../network_infrastructure/README.md | 12 ++++---- .../supply_chain_management/README.md | 11 ++++--- connected_customer/customer_360/README.md | 19 ++++++++---- .../entity_resolution/README.md | 12 +++----- .../product_recommendations/README.md | 9 +++--- financial_crime/application_fraud/README.md | 15 +++++----- .../mule_account_detection/README.md | 26 ++++++++-------- financial_crime/transaction_fraud/README.md | 30 +++++++++++-------- 8 files changed, 70 insertions(+), 64 deletions(-) diff --git a/agile_operations/network_infrastructure/README.md b/agile_operations/network_infrastructure/README.md index c470e58c..50724425 100644 --- a/agile_operations/network_infrastructure/README.md +++ b/agile_operations/network_infrastructure/README.md @@ -27,15 +27,15 @@ data, and GSQL queries to help you: ## Contents - [Overview](#overview) -- [Features](#features) -- [Repository layout](#repository-layout) +- [Components](#components) - [Prerequisites](#prerequisites) -- [Quickstart](#quickstart) -- [Working with the graph](#working-with-the-graph) - - [Query explanations](#query-explanations) - - [Insights application](#insights-application) +- [Setup Instructions](#setup-instructions) +- [Query Explanations](#query-explanations) +- [Mock Data](#mock-data) +- [Insights Application](#insights-application) --- + ## Overview Modern enterprises operate complex on-premise and hybrid data centers with diff --git a/agile_operations/supply_chain_management/README.md b/agile_operations/supply_chain_management/README.md index 581beb45..e079a0cd 100644 --- a/agile_operations/supply_chain_management/README.md +++ b/agile_operations/supply_chain_management/README.md @@ -27,13 +27,12 @@ across the supply chain. ## Contents - [Overview](#overview) -- [Features](#features) -- [Repository layout](#repository-layout) +- [Components](#components) - [Prerequisites](#prerequisites) -- [Quickstart](#quickstart) -- [Working with the graph](#working-with-the-graph) - - [Query explanations](#query-explanations) - - [Scalability](#Scalability) +- [Setup Instructions](#setup-instructions) +- [Query Execution Order and Explanations](#query-execution-order-and-explanations) +- [Scalability](#scalability) +- [Mock Data](#mock-data) --- diff --git a/connected_customer/customer_360/README.md b/connected_customer/customer_360/README.md index a654e42f..80503285 100644 --- a/connected_customer/customer_360/README.md +++ b/connected_customer/customer_360/README.md @@ -20,11 +20,11 @@ With this kit you can: - [Overview](#overview) - [Features](#features) - [Prerequisites](#prerequisites) -- [Working-with-the-graph] - - [Query-Explanations](#query-explanations) - - [Run-example-queries](#run-example-queries) -- [Using-your-own-data](#using-your-own-data) -- [Resetting-the-environment](#resetting-the-environment) +- [Setup Instructions](#setup-instructions) +- [Query Explanations](#query-explanations) +- [Run Example Queries](#run-example-queries) +- [Using Your Own Data](#using-your-own-data) +- [Resetting the Environment](#resetting-the-environment) --- @@ -133,13 +133,22 @@ Then, run the automated script using: This script will: * Create the Customer_360_Financial graph and schema by defining all the vertices, edges and their attributes with the command: + ```bash + gsql schema/1_create_schema.gsql + ``` * Loads all the data from TigerGraph's public s3 bucket using the command: + ```bash + gsql loading_job/load_data.gsql + ``` * Installs all the queries using another shell script located in: +```bash queries/install_queries.sh + +``` This script runs through all the queries in the repository and installs them one by one. diff --git a/connected_customer/entity_resolution/README.md b/connected_customer/entity_resolution/README.md index 9e1c5543..c362d3bd 100644 --- a/connected_customer/entity_resolution/README.md +++ b/connected_customer/entity_resolution/README.md @@ -29,16 +29,12 @@ With this kit you can: ## Contents - [Overview](#overview) -- [Features](#features) +- [Components](#components) - [Prerequisites](#prerequisites) -- [Quickstart](#quickstart) -- [Working-with-the-graph](#working-with-the-graph) - - [Entity-Resolution-workflow](#entity-resolution-workflow) - - [Additional-queries](#additional-queries) +- [Setup Instructions](#setup-instructions) +- [Query Execution Order and Explanations](#query-execution-order-and-explanations) - [Scalability](#scalability) -- [Further-reading](#further-reading) - ---- +- [Mock Data](#mock-data)--- ## Overview diff --git a/connected_customer/product_recommendations/README.md b/connected_customer/product_recommendations/README.md index 83cf3441..312eca3f 100644 --- a/connected_customer/product_recommendations/README.md +++ b/connected_customer/product_recommendations/README.md @@ -22,11 +22,10 @@ With this kit you can: - [Features](#features) - [Prerequisites](#prerequisites) - [Setup Instructions](#setup-instructions) -- [Working-with-the-graph](#working-with-the-graph) - - [Query-Explanations](#query-explanations) - - [Running-example-queries](#running-example-queries) -- [Using-your-own-data](#using-your-own-data) -- [Resetting-the-environment](#resetting-the-environment) +- [Query Explanations](#query-explanations) +- [Run Example Queries](#run-example-queries) +- [Using Your Own Data](#using-your-own-data) +- [Resetting the Environment](#resetting-the-environment) --- diff --git a/financial_crime/application_fraud/README.md b/financial_crime/application_fraud/README.md index 515e2db5..f9da1989 100644 --- a/financial_crime/application_fraud/README.md +++ b/financial_crime/application_fraud/README.md @@ -20,14 +20,13 @@ With this kit you can: - [Overview](#overview) - [Features](#features) - [Prerequisites](#prerequisites) -- [Setup Instructions](#setupInstructions) -- [Installation Notes](#InstallationNotes) -- [Working-with-the-graph](#working-with-the-graph) - - [Query-Explanations](#query-explanations) - - [Running-example-queries](#running-example-queries) -- [Using-your-own-data](#using-your-own-data) -- [Resetting-the-environment](#resetting-the-environment) -- [ML-model-and-insights-application](#ml-model-and-insights-application) +- [Setup Instructions](#setup-instructions) +- [Installation Note for Queries](#installation-note-for-queries) +- [Query Execution Order and Explanations](#query-execution-order-and-explanations) +- [Run an Example Query](#run-an-example-query) +- [Using Your Own Data](#using-your-own-data) +- [Resetting the Environment](#resetting-the-environment) +- [ML Model and Insights Application](#ml-model-and-insights-application) --- diff --git a/financial_crime/mule_account_detection/README.md b/financial_crime/mule_account_detection/README.md index ecb9b991..8612afb5 100644 --- a/financial_crime/mule_account_detection/README.md +++ b/financial_crime/mule_account_detection/README.md @@ -5,19 +5,19 @@ ## Contents -- [Overview](#overview) -- [ML Features](#ml-features) -- [Queries Included](#queries-included) -- [Components](#components) -- [Prerequisites](#prerequisites) -- [Quickstart Guide](#quickstart-guide) -- [Query Execution Order and Explanations](#query-execution-order-and-explanations) -- [Load Sample Data](#load-sample-data) -- [Graph Schema](#graph-schema) -- [Sample Dataset](#sample-dataset) -- [Using Your Own Data](#using-your-own-data) -- [Cleanup (Optional)](#cleanup-optional) -- [Insights Applications](#insights-applications) +- [Overview](#overview) +- [ML Features](#ml-features) +- [Queries Included](#queries-included) +- [Components](#components) +- [Prerequisites](#prerequisites) +- [Quickstart Guide](#quickstart-guide) +- [Query Execution Order and Explanations](#query-execution-order-and-explanations) +- [Load Sample Data](#load-sample-data) +- [Graph Schema](#graph-schema) +- [Sample Dataset](#sample-dataset) +- [Using Your Own Data](#using-your-own-data) +- [Cleanup](#cleanup-optional) +- [Insights Applications](#insights-applications) --- diff --git a/financial_crime/transaction_fraud/README.md b/financial_crime/transaction_fraud/README.md index ad793b5d..4edc9bf8 100644 --- a/financial_crime/transaction_fraud/README.md +++ b/financial_crime/transaction_fraud/README.md @@ -4,19 +4,23 @@ A real-time, graph-powered solution for detecting credit card fraud rings, busting coordinated attacks, and preventing financial losses using TigerGraph. ## Contents -- [Overview](#Overview) -- [ML Features](#ML-Features) -- [Queries Included](#Queries-Included) -- [Components](#Components) -- [Prerequisites](#Prerequisites) -- [Quickstart Guide](#Quickstart-Guide) -- [Query Execution Order and Explanations](#Query-Execution-Order-and-Explanations) -- [Load Sample Data](#Load-Sample-Data) -- [Graph Schema](#Graph-Schema) -- [Sample Dataset](#Sample-Dataset) -- [Using Your Own Data](#Using-Your-Own-Data) -- [Cleanup (Optional)](#Cleanup-(Optional)) -- [Insights Applications](#Insights-Applications) + +- [Overview](#overview) +- [Components](#components) +- [Prerequisites](#prerequisites) +- [Quickstart Guide](#quickstart-guide) +- [Query Execution Order and Explanations](#query-execution-order-and-explanations) +- [ML Features](#ml-features) +- [Graph Schema](#graph-schema) +- [Sample Dataset](#sample-dataset) +- [Using Your Own Data](#using-your-own-data) +- [Cleanup (Optional)](#cleanup-optional) +- [Insights Applications](#insights-applications) +- [ML Model and Insights Application](#ml-model-and-insights-application) + +--- + + ## Overview Credit card transaction fraud detection identifies and prevents unauthorized From 00ef381e4489c527da8d9eae1d3646783016b114 Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Mon, 2 Feb 2026 19:12:21 +0530 Subject: [PATCH 41/55] fixed broken fencing across all kits --- connected_customer/customer_360/README.md | 2 +- .../entity_resolution/README.md | 22 +- .../product_recommendations/README.md | 1 + financial_crime/application_fraud/README.md | 243 +++++++++++++++++- .../mule_account_detection/README.md | 3 +- financial_crime/transaction_fraud/README.md | 33 +-- 6 files changed, 273 insertions(+), 31 deletions(-) diff --git a/connected_customer/customer_360/README.md b/connected_customer/customer_360/README.md index 80503285..36e3c97d 100644 --- a/connected_customer/customer_360/README.md +++ b/connected_customer/customer_360/README.md @@ -344,7 +344,7 @@ To use it: ```bash gsql -g Customer_360_Financial queries/delete_all.gsql - + ``` 2. **Run the delete query** diff --git a/connected_customer/entity_resolution/README.md b/connected_customer/entity_resolution/README.md index c362d3bd..ce22a5f6 100644 --- a/connected_customer/entity_resolution/README.md +++ b/connected_customer/entity_resolution/README.md @@ -34,7 +34,9 @@ With this kit you can: - [Setup Instructions](#setup-instructions) - [Query Execution Order and Explanations](#query-execution-order-and-explanations) - [Scalability](#scalability) -- [Mock Data](#mock-data)--- +- [Mock Data](#mock-data) + +--- ## Overview @@ -159,8 +161,12 @@ the value of `batch_id` will initially be `0` and we'll increment it each run until `49`. Instead of running this query 50 times manually from the UI, we can run this query 50 times as a REST endpoint from Linux shell: +```bash -`for i in {0..49};do curl -X GET "http://:9000/query/ER_2023/delete_all_cc_connections?num_of_batches=50&batch_id=${i}";done;` +for i in {0..49};do + curl -X GET "http://:9000/query/ER_2023/delete_all_cc_connections?num_of_batches=50&batch_id=${i}"; +done; +``` **Note**: By calling this query multiple times and deleting data in hard batches it allows us to delete data in parallel, and make the deltion process @@ -282,7 +288,11 @@ until `49`. Instead of running this query 50 times manually from the UI, we can run this query 50 times as a REST endpoint from Linux shell: -`for i in {0..49};do curl -X GET "http://:9000/query/ER_2023/delete_unused_cc_nodes?num_of_batches=50&batch_id=${i}";done;` +```bash +for i in {0..49}; + do curl -X GET "http://:9000/query/ER_2023/delete_unused_cc_nodes?num_of_batches=50&batch_id=${i}"; +done; +``` **Note**: By calling this query multiple times and deleting data in hard batches it allows us to delete data in parallel, and make the deltion process run @@ -314,6 +324,12 @@ The steps to configure the system to save all edges on disk instead of Ram are a - Run the command: `gadmin config apply -y` - Restart cluster: `gadmin restart -y` +```bash +gadmin config set GPE.EdgeDataMemoryLimit 0 +gadmin config apply -y +gadmin restart -y +``` + **Note**: Additional scalability-related information is noted in the query details above. Please reference the following link for more information on MinHash: diff --git a/connected_customer/product_recommendations/README.md b/connected_customer/product_recommendations/README.md index 312eca3f..8376ec6a 100644 --- a/connected_customer/product_recommendations/README.md +++ b/connected_customer/product_recommendations/README.md @@ -303,3 +303,4 @@ DROP QUERY ALL -- clear schema and data DROP GRAPH Product_Recommendation +``` diff --git a/financial_crime/application_fraud/README.md b/financial_crime/application_fraud/README.md index f9da1989..d213a052 100644 --- a/financial_crime/application_fraud/README.md +++ b/financial_crime/application_fraud/README.md @@ -213,25 +213,250 @@ Application, Application.is_fraud, Application Connected_Component, Fraud Applic As new Applications get submitted these can be added to the graph and associated to a Connected_Component community in near real time using `incremental_application_match` query accepting a JSON payload containing all Application and PII data including Name, DOB, Email, Phone, Address, IP, ID, Device, Party, Account, and Card vertices. It's typical to have consistent weight and threshold settings between this and the former `match_application_entities` query for consistent matching behavior. -Raw Request Example +**Raw Request Example** - http://18.217.164.69:14240/restpp/query/Application_Fraud/incremental_application_match +```http +GET http://18.217.164.69:14240/restpp/query/Application_Fraud/incremental_application_match - input={"application": "6f9e1ca5-e60c-48fd-a1c1-03d13348f888", "created_at": "2024-04-17 11:15:26.076265", "status": "740e989a-3614-427b-b3b0-21fc96ce2fc4", "line_of_credit": "9884.82", "annual_percentage_rate": "36.06", "fraud": true, "product": "414882cd-3fcb-42ef-8ee6-0423ce526124", "name": "3a58b7ce-33cb-46c2-ab65-527e1334927b", "dob": "a1d4472b-0f3c-4465-82a0-e2f889138989", "email": "18baaf68-12de-4291-8fae-8df7f2bf6026", "phone_numbers": [{"type": "mobile", "id": "5ed411af-e38c-4e97-b657-25017695148a"}, {"type": "landline", "id": "60669163-67e7-4e0e-a46b-2c25bdd0ee6b"}], "addresses": [{"type": "mailing", "line_1": "61d2bd45-57f1-4b3c-98a9-265cbcefdead", "line_2": "ed7020ae-35ef-4aae-a13d-3513222fcc50", "city": "fd1e10fb-91e4-4ecf-9ec1-afb2280dfbb5", "state": "694d083b-7006-4561-a2c1-913d80831ba7", "zipcode": "5b8eafe4-01c0-4b1e-8024-595ac6969a7c", "county": "fb4b12a7-bc0e-47cc-852f-ff7da7e61d66", "country": "US"}, {"type": "physical", "line_1": "8f1a04ed-ef4a-4324-8321-be3fe85f17da", "line_2": "e9a58e29-68cd-4e35-a43b-f28914c56e5a", "city": "13cf8f10-814b-4db6-930d-c0a162ef24f1", "state": "d8d7e179-5bb7-4cde-8fb5-97f0e9e55bde", "zipcode": "ff1456dd-b2f8-4c55-9dcc-e2b13b42d05b", "county": "6ba0eec9-4e72-45f1-91aa-1fd246d7400c", "country": "US"}], "ip_address": "83f7dc2f-928c-48a7-b157-0c50572071aa", "ids": [{"type": "Passport", "id": "36f26ab2-35ba-4361-b719-964ae23283b4"}, {"type": "Driver's License Number", "id": "8cefe5da-9f83-4013-a1e8-2e59108726c7"}], "device_id": "7a1f0167-1004-4d6c-8411-e38e414a6129", "party": "00cb01dc-e3e3-492d-b1fe-7c225ba3dde1", "accounts": [{"type": "savings", "id": "fb08e35b-b428-4840-8800-c0f841e7650e"}, {"type": "checking", "id": "3f41189c-b55b-4ec5-b073-182f9f87ceee"}], "cards": [{"type": "credit_card", "id": 241556}, {"type": "debit_card", "id": 278916}]} +``` -Response +```json +{ + "application": "6f9e1ca5-e60c-48fd-a1c1-03d13348f888", + "created_at": "2024-04-17 11:15:26.076265", + "status": "740e989a-3614-427b-b3b0-21fc96ce2fc4", + "line_of_credit": "9884.82", + "annual_percentage_rate": "36.06", + "fraud": true, + "product": "414882cd-3fcb-42ef-8ee6-0423ce526124", + "name": "3a58b7ce-33cb-46c2-ab65-527e1334927b", + "dob": "a1d4472b-0f3c-4465-82a0-e2f889138989", + "email": "18baaf68-12de-4291-8fae-8df7f2bf6026", + "phone_numbers": [ + { + "type": "mobile", + "id": "5ed411af-e38c-4e97-b657-25017695148a" + }, + { + "type": "landline", + "id": "60669163-67e7-4e0e-a46b-2c25bdd0ee6b" + } + ], + "addresses": [ + { + "type": "mailing", + "line_1": "61d2bd45-57f1-4b3c-98a9-265cbcefdead", + "line_2": "ed7020ae-35ef-4aae-a13d-3513222fcc50", + "city": "fd1e10fb-91e4-4ecf-9ec1-afb2280dfbb5", + "state": "694d083b-7006-4561-a2c1-913d80831ba7", + "zipcode": "5b8eafe4-01c0-4b1e-8024-595ac6969a7c", + "county": "fb4b12a7-bc0e-47cc-852f-ff7da7e61d66", + "country": "US" + }, + { + "type": "physical", + "line_1": "8f1a04ed-ef4a-4324-8321-be3fe85f17da", + "line_2": "e9a58e29-68cd-4e35-a43b-f28914c56e5a", + "city": "13cf8f10-814b-4db6-930d-c0a162ef24f1", + "state": "d8d7e179-5bb7-4cde-8fb5-97f0e9e55bde", + "zipcode": "ff1456dd-b2f8-4c55-9dcc-e2b13b42d05b", + "county": "6ba0eec9-4e72-45f1-91aa-1fd246d7400c", + "country": "US" + } + ], + "ip_address": "83f7dc2f-928c-48a7-b157-0c50572071aa", + "ids": [ + { + "type": "Passport", + "id": "36f26ab2-35ba-4361-b719-964ae23283b4" + }, + { + "type": "Driver's License Number", + "id": "8cefe5da-9f83-4013-a1e8-2e59108726c7" + } + ], + "device_id": "7a1f0167-1004-4d6c-8411-e38e414a6129", + "party": "00cb01dc-e3e3-492d-b1fe-7c225ba3dde1", + "accounts": [ + { + "type": "savings", + "id": "fb08e35b-b428-4840-8800-c0f841e7650e" + }, + { + "type": "checking", + "id": "3f41189c-b55b-4ec5-b073-182f9f87ceee" + } + ], + "cards": [ + { + "type": "credit_card", + "id": 241556 + }, + { + "type": "debit_card", + "id": 278916 + } + ] +} +``` +```Response {'Application': '6f9e1ca5-e60c-48fd-a1c1-03d13348f888', 'entity_resolution': True} -If the entity_resolution response is True the incoming Application was successfully able to be matched with the historical Application dataset using the matching logic. The `distance_and_path_to_fraud_account` and `get_application_cc_features` queries can be called to retrieve the graph features to augment the downstream ML decision model in near real time. +``` - http://18.217.164.69:14240/restpp/query/Application_Fraud/distance_and_path_to_fraud_application +If the entity_resolution response is True the incoming Application was successfully able to be matched with the historical Application dataset using the matching logic. +The `distance_and_path_to_fraud_account` and `get_application_cc_features` queries can be called to retrieve the graph features to augment the downstream ML decision model in near real time. +**Check Distance to Fraud** - {"version":{"edition":"enterprise","api":"v2","schema":59},"error":false,"message":"","results":[{"result":[{"v_id":"31aeadde-d12a-486d-b2b4-a217daa9404d","v_type":"Application","attributes":{"input_application_id":"fa881c0a-adff-42d5-adf5-19de0cbe3547","input_application_id_cc":"675282944","degree_of_connection":2,"is_fraud":true,"application_id":"31aeadde-d12a-486d-b2b4-a217daa9404d","application_id_cc":"675282944","path_of_connection":["Device"]}}]}]} +```http +GET http://18.217.164.69:14240/restpp/query/Application_Fraud/distance_and_path_to_fraud_application +``` - http://18.217.164.69:14240/restpp/query/Application_Fraud/get_application_cc_features +**Response** + +```json +{ + "version": { + "edition": "enterprise", + "api": "v2", + "schema": 59 + }, + "error": false, + "message": "", + "results": [ + { + "result": [ + { + "v_id": "31aeadde-d12a-486d-b2b4-a217daa9404d", + "v_type": "Application", + "attributes": { + "input_application_id": "fa881c0a-adff-42d5-adf5-19de0cbe3547", + "input_application_id_cc": "675282944", + "degree_of_connection": 2, + "is_fraud": true, + "application_id": "31aeadde-d12a-486d-b2b4-a217daa9404d", + "application_id_cc": "675282944", + "path_of_connection": [ + "Device" + ] + } + } + ] + } + ] +} +``` - {"version":{"edition":"enterprise","api":"v2","schema":59},"error":false,"message":"","results":[{"result":[{"v_id":"31aeadde-d12a-486d-b2b4-a217daa9404d","v_type":"Application","attributes":{"connectedComponentID":643825664,"total_cc_nodes":2,"fraud_applications_in_cc":2,"name_distinct_in_cc":2,"name_number_nodes":0,"name_number_nodes_only_connected":0,"dob_distinct_in_cc":2,"dob_number_nodes":0,"dob_number_nodes_only_connected":0,"email_distinct_in_cc":2,"email_number_nodes":0,"email_number_nodes_only_connected":0,"phone_distinct_in_cc":4,"phone_number_nodes":0,"phone_number_nodes_only_connected":0,"address_distinct_in_cc":4,"address_number_nodes":0,"address_number_nodes_only_connected":0,"ip_distinct_in_cc":2,"ip_number_nodes":0,"ip_number_nodes_only_connected":0,"id_distinct_in_cc":4,"id_number_nodes":0,"id_number_nodes_only_connected":0,"device_distinct_in_cc":1,"device_number_nodes":0,"device_number_nodes_only_connected":0,"party_distinct_in_cc":0,"party_number_nodes":0,"party_number_nodes_only_connected":0,"account_distinct_in_cc":4,"account_number_nodes":0,"account_number_nodes_only_connected":0,"card_distinct_in_cc":4,"card_number_nodes":0,"card_number_nodes_only_connected":0}},{"v_id":"fa881c0a-adff-42d5-adf5-19de0cbe3547","v_type":"Application","attributes":{"connectedComponentID":643825664,"total_cc_nodes":2,"fraud_applications_in_cc":2,"name_distinct_in_cc":2,"name_number_nodes":0,"name_number_nodes_only_connected":0,"dob_distinct_in_cc":2,"dob_number_nodes":0,"dob_number_nodes_only_connected":0,"email_distinct_in_cc":2,"email_number_nodes":0,"email_number_nodes_only_connected":0,"phone_distinct_in_cc":4,"phone_number_nodes":0,"phone_number_nodes_only_connected":0,"address_distinct_in_cc":4,"address_number_nodes":0,"address_number_nodes_only_connected":0,"ip_distinct_in_cc":2,"ip_number_nodes":0,"ip_number_nodes_only_connected":0,"id_distinct_in_cc":4,"id_number_nodes":0,"id_number_nodes_only_connected":0,"device_distinct_in_cc":1,"device_number_nodes":0,"device_number_nodes_only_connected":0,"party_distinct_in_cc":0,"party_number_nodes":0,"party_number_nodes_only_connected":0,"account_distinct_in_cc":4,"account_number_nodes":0,"account_number_nodes_only_connected":0,"card_distinct_in_cc":4,"card_number_nodes":0,"card_number_nodes_only_connected":0}}]}]} +**Get Application Connected Component Features** + +```http +GET http://18.217.164.69:14240/restpp/query/Application_Fraud/get_application_cc_features +``` + +**Response** + +```json +{ + "version": { + "edition": "enterprise", + "api": "v2", + "schema": 59 + }, + "error": false, + "message": "", + "results": [ + { + "result": [ + { + "v_id": "31aeadde-d12a-486d-b2b4-a217daa9404d", + "v_type": "Application", + "attributes": { + "connectedComponentID": 643825664, + "total_cc_nodes": 2, + "fraud_applications_in_cc": 2, + "name_distinct_in_cc": 2, + "name_number_nodes": 0, + "name_number_nodes_only_connected": 0, + "dob_distinct_in_cc": 2, + "dob_number_nodes": 0, + "dob_number_nodes_only_connected": 0, + "email_distinct_in_cc": 2, + "email_number_nodes": 0, + "email_number_nodes_only_connected": 0, + "phone_distinct_in_cc": 4, + "phone_number_nodes": 0, + "phone_number_nodes_only_connected": 0, + "address_distinct_in_cc": 4, + "address_number_nodes": 0, + "address_number_nodes_only_connected": 0, + "ip_distinct_in_cc": 2, + "ip_number_nodes": 0, + "ip_number_nodes_only_connected": 0, + "id_distinct_in_cc": 4, + "id_number_nodes": 0, + "id_number_nodes_only_connected": 0, + "device_distinct_in_cc": 1, + "device_number_nodes": 0, + "device_number_nodes_only_connected": 0, + "party_distinct_in_cc": 0, + "party_number_nodes": 0, + "party_number_nodes_only_connected": 0, + "account_distinct_in_cc": 4, + "account_number_nodes": 0, + "account_number_nodes_only_connected": 0, + "card_distinct_in_cc": 4, + "card_number_nodes": 0, + "card_number_nodes_only_connected": 0 + } + }, + { + "v_id": "fa881c0a-adff-42d5-adf5-19de0cbe3547", + "v_type": "Application", + "attributes": { + "connectedComponentID": 643825664, + "total_cc_nodes": 2, + "fraud_applications_in_cc": 2, + "name_distinct_in_cc": 2, + "name_number_nodes": 0, + "name_number_nodes_only_connected": 0, + "dob_distinct_in_cc": 2, + "dob_number_nodes": 0, + "dob_number_nodes_only_connected": 0, + "email_distinct_in_cc": 2, + "email_number_nodes": 0, + "email_number_nodes_only_connected": 0, + "phone_distinct_in_cc": 4, + "phone_number_nodes": 0, + "phone_number_nodes_only_connected": 0, + "address_distinct_in_cc": 4, + "address_number_nodes": 0, + "address_number_nodes_only_connected": 0, + "ip_distinct_in_cc": 2, + "ip_number_nodes": 0, + "ip_number_nodes_only_connected": 0, + "id_distinct_in_cc": 4, + "id_number_nodes": 0, + "id_number_nodes_only_connected": 0, + "device_distinct_in_cc": 1, + "device_number_nodes": 0, + "device_number_nodes_only_connected": 0, + "party_distinct_in_cc": 0, + "party_number_nodes": 0, + "party_number_nodes_only_connected": 0, + "account_distinct_in_cc": 4, + "account_number_nodes": 0, + "account_number_nodes_only_connected": 0, + "card_distinct_in_cc": 4, + "card_number_nodes": 0, + "card_number_nodes_only_connected": 0 + } + } + ] + } + ] +} +``` If entity_resolution is False the incoming Application was not able to be matched with the historical Application dataset and the same respective Application should be sent to `incremental_unify_entity` query so it's eligible for matching against future Applications in near real time. diff --git a/financial_crime/mule_account_detection/README.md b/financial_crime/mule_account_detection/README.md index 8612afb5..dffe6983 100644 --- a/financial_crime/mule_account_detection/README.md +++ b/financial_crime/mule_account_detection/README.md @@ -147,10 +147,9 @@ mule-account detection solution: ```bash gsql /home/tigergraph/solution_kits/financial_crime/library/schema/general_global_financial_crime_super_schema.gsql -gsql /home/tigergraph/solution_kits/financial_crime/mule_account_detection/schema/create_schema_bottomup.gsql### **3. Install queries** +gsql /home/tigergraph/solution_kits/financial_crime/mule_account_detection/schema/create_schema_bottomup.gsql ``` - ### 2. Load data Load data into the schema by running the local loading job script: diff --git a/financial_crime/transaction_fraud/README.md b/financial_crime/transaction_fraud/README.md index 4edc9bf8..d1a31d5b 100644 --- a/financial_crime/transaction_fraud/README.md +++ b/financial_crime/transaction_fraud/README.md @@ -37,11 +37,11 @@ financial losses and protecting consumers from fraudulent activity. ## Components - Schema - Graph schema definition - Queries - Complete GSQL query library -- Query Installation Script - 3_install_queries.sh (handles dependencies automatically) +- Query Installation Script - `3_install_queries.sh` (handles dependencies automatically) - Mock Data - Realistic synthetic dataset -- Loading Job - loading_job/load_data.gsql +- Loading Job - `loading_job/load_data.gsql` - Insights Applications - Pre-built JSON dashboards -- Machine Learning Model - Training code & docs in ./model +- Machine Learning Model - Training code & docs in `./model` - Unit Tests - Validation suite ## Prerequisites @@ -140,20 +140,21 @@ These queries can be executed at any time and have no dependencies on the previo - `merchant_category_transaction_stats`: Delivers transaction statistics categorized by merchant types. ## ML Features -Graph-derived features automatically attached to each Payment_Transaction vertex for the included machine learning model: -Feature | Query | Attribute on Payment_Transaction | Type --------------------------------------------|--------------------------------------------|----------------------------------------|------- -Community Size (Merchant & Card) | tg_wcc_*_weight_based | mer_com_size, cd_com_size | INT -PageRank (Merchant & Card) | tg_pagerank_wt_* | mer_pagerank, cd_pagerank | FLOAT -Shortest Path to Known Fraud | all_shortest_path_length | shortest_path_length | INT -Max Amount in Recent Interval | card_merchant_max_amount_within_interval | max_txn_amt_interval | FLOAT -Max Tx Count in Interval | card_merchant_max_txn_count_in_interval | max_txn_cnt_interval | INT -Repeated Card Count | number_of_repeated_card | cnt_repeated_card | INT -Community Tx Stats (count,total,avg,max,min)| community_transaction_* | com_mer_* / com_cd_* | INT/FLOAT -Merchant Category Stats (count,total,avg,max,min) | merchant_category_* | mer_cat_* | INT/FLOAT -In/Out Degree | degrees | indegree, outdegree | INT -Demographics & Context | direct attributes | gender, age, city_pop, occupation, unix_time, mer_cat | STRING/INT +Graph-derived features automatically attached to each `Payment_Transaction` vertex for the included machine learning model: + +| Feature | Query | Attribute on Payment_Transaction | Type | +| :--- | :--- | :--- | :--- | +| **Community Size** (Merchant & Card) | `tg_wcc_*_weight_based` | `mer_com_size`, `cd_com_size` | INT | +| **PageRank** (Merchant & Card) | `tg_pagerank_wt_*` | `mer_pagerank`, `cd_pagerank` | FLOAT | +| **Shortest Path to Known Fraud** | `all_shortest_path_length` | `shortest_path_length` | INT | +| **Max Amount in Recent Interval** | `card_merchant_max_amount_within_interval` | `max_txn_amt_interval` | FLOAT | +| **Max Tx Count in Interval** | `card_merchant_max_txn_count_in_interval` | `max_txn_cnt_interval` | INT | +| **Repeated Card Count** | `number_of_repeated_card` | `cnt_repeated_card` | INT | +| **Community Tx Stats** | `community_transaction_*` | `com_mer_*` / `com_cd_*` | INT/FLOAT | +| **Merchant Category Stats** | `merchant_category_*` | `mer_cat_*` | INT/FLOAT | +| **In/Out Degree** | `degrees` | `indegree`, `outdegree` | INT | +| **Demographics & Context** | direct attributes | `gender`, `age`, `city_pop`, `occupation`, `unix_time`, `mer_cat` | STRING/INT | ## Graph Schema Main vertices: Cardholder, Credit_Card, Payment_Transaction, Merchant, Merchant_Category, Location Full schema available in ./schema/ From 37d3d46ad752df0c6d65b9c8a5387838f515bb7e Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Mon, 2 Feb 2026 19:48:36 +0530 Subject: [PATCH 42/55] removed incorrect product recommendation schema --- .../images/Product_Recommendation_Schema.png | Bin 202271 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 connected_customer/product_recommendations/meta/images/Product_Recommendation_Schema.png diff --git a/connected_customer/product_recommendations/meta/images/Product_Recommendation_Schema.png b/connected_customer/product_recommendations/meta/images/Product_Recommendation_Schema.png deleted file mode 100644 index 135742b24e91dfabdfddc34700423b254f21eac4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 202271 zcmeFY=T}t0(=KX2G6DjUvt%VoPLe@D1SCirV3dp`$zdc9SwLinBUvO2IWr?!vINOF z4LJ=kz%aw*ea~6zoZtK5uKNevFMI9XtGl{acXjQmr=E&=`$mI|n1T4-y?bO@nyLo( z?&0&@yZ7Lb5dW@azU2Jty@xbgTB=I#{Id74)9Dr_-?xZZe8s#f>c33gQ+hI)*+FRc zCvo6;veE3UeA#Y=@$hbRKxs)pCejLJGHE7OR^rKDE6S;2h*$rVP3is%rq24Qfq>hK zdZ4gx6CRWMlM^i6)5pH8K1a>S?7-oD%*c|gB>(^=Qzm4Op!h!zH01OD_4QxvGx+1K z2M0?3*=@}#71sztAs+q5U}bQi?%1@w;7{(z{}gg4tp=n@73KTCXHq8g808ya_@9DV zQXl`H)%^F{{1+YK|D48!JpEwspNp_H|El$$x&d}o`k&hO{~~As`CL#i%0+^ao144y^0G<3 zS6NL>?PqD}IdrE^;R!K29OKTqE+QsIWoK`{k29hd&CvN@t6X+FTy`s*U5K9PaXBr+ z@|sKNQ7C@5&0U2|8bdkFc*b(zIDx$ zet7K7O&iEdHVL4!=1c+rH0j>t5MYi&=I&id*XpiAmkHJ(WQ~dMgLFT^1D2zUf@=#8 zmIzk28nWmMXNG#RP>DG-C7yjtEASMr{&Njej0|Z{yvbM!3LyIUij2fJg)wR2Je4&r zZLVhN^67rk_op<`H_g2sv1Frwq{#Jedu~U2YhhP(N$Hk8ht1A)4n-#bix4dLZ91ad zWIKAp?73MWj%H-F)Bjx+qEmwQ!Y~BmS5YCM*ENpdntnYF)vlbN*$P+{A@1`mIzXWh zzRDu&5B(!u&6n(lq*+I+E`mi{_vg@k7NdIZE7|-flUf>6dq2#C%xw{sT3ToKHuTJa0`jY2!hT zM55zwE+UcD1MTI~Sf==Iqj+~O zFjIx5*^T5?R9-eUZu~=uqfu8ok#Dm{h;}ag{cLPRC;{;`Eh%r8-fksDW<&~$zho}P zx9dFH)`38?NlM2*Yo>Eqra|t-;X0%ZlONOQZsrYHnV1=-C?0n1hq3zBfcSOD-e8vF z#JH`#6@CZHLWtrTU?~H2^?A?V`5MnDm{<{Qu>R>oc3#%U2z!2; z(yH>xX~g|nHfMA~Pd53re}Bc4_aZzz+{VE{n3S9x8PU1ccG*pvo|2;T^H=5L7cVN{ zonn<=ZU0dVydv1w?*Gvq^5H|b3F!OxZxQc4(eT1b5U)kK@`@WI>KYoNdVAGDpX8N3 ze6n5Yb=m!nLR6fvS}Qi6G&ZO6TT0;Kv{=8t+vo*p6e*5Moy_ehId}ch;jNi2)?P>+ zAb-`nrQ_c|96Ck3EOgwGu}U9!@EOtooxO75trpo-!tiGaD7OTm(&fDT^6f=+&fZ+# zlrI`Fzdp$9{|2jddcos4{=Jl4HhtAmb~&5Zf*KZ@HEmPOPsN`(sN!v2l+a}XVace+ zWt~ooJyx)<5c1nVC;hPTVg?orR7S=Y*Ss}0XP9DMwpbp~9SOYsxOVxJA#`e)%V=se zp+RqNPsd*diNH!LcMYq0=IhYx|+^cZ_a`^{(55F zv7;{{zA%0DD0Jt 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�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≪a^&qw_O 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 F{{ntXZruO? From 632dae5791301f0a41f38838b61b98dd645c7cab Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Mon, 2 Feb 2026 19:51:42 +0530 Subject: [PATCH 43/55] added correct schema for product recommendation solution kit --- .../images/Product_Recommendation_Schema.png | Bin 0 -> 375350 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 connected_customer/product_recommendations/meta/images/Product_Recommendation_Schema.png diff --git a/connected_customer/product_recommendations/meta/images/Product_Recommendation_Schema.png b/connected_customer/product_recommendations/meta/images/Product_Recommendation_Schema.png new file mode 100644 index 0000000000000000000000000000000000000000..914f73ba00df52c11cd9d50a1fe51f17793a1e7c GIT binary patch literal 375350 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 Date: Tue, 3 Feb 2026 17:49:29 +0530 Subject: [PATCH 44/55] customer no engagement query corrected in customer 360 solution kit --- .../queries/Customers_No_Engagement.gsql | 49 ++++++++++++++++--- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/connected_customer/customer_360/queries/Customers_No_Engagement.gsql b/connected_customer/customer_360/queries/Customers_No_Engagement.gsql index 278c5a94..1b06138c 100644 --- a/connected_customer/customer_360/queries/Customers_No_Engagement.gsql +++ b/connected_customer/customer_360/queries/Customers_No_Engagement.gsql @@ -1,4 +1,5 @@ -CREATE DISTRIBUTED QUERY Customers_No_Engagement(/* Parameters here */) FOR GRAPH Customer_360_Financial { +CREATE OR REPLACE DISTRIBUTED QUERY Customers_No_Engagement(/* Parameters here */) + FOR GRAPH Customer_360_Financial SYNTAX V1 { /* Description: Returns Individuals who have never had any recorded engagement. @@ -6,16 +7,48 @@ CREATE DISTRIBUTED QUERY Customers_No_Engagement(/* Parameters here */) FOR GRAP Parameters: (None) Uses all Individual vertices in the graph. - + Checks two association paths: + 1) Individual ->(created_session)-> SessionID -(action)- ApplicationEngagement + 2) Individual -(has_contact_point)- Contact_Point -(has_contact_point)- SessionID -(action)- ApplicationEngagement Output: Individual vertices with no recorded engagement */ - start = {Individual.*}; - - not_engaged = SELECT s FROM start:s - WHERE s.outdegree("created_session") == 0; - - PRINT not_engaged; + OrAccum @app_engagement; + start = {Individual.*}; + + // Path 1: individual -> created_session -> action -> ApplicationEngagement + sessions = SELECT s + FROM start:i -(created_session:e1)-> SessionID:s; + + sessions_by_app = SELECT s + FROM sessions:s -(action:e2)- ApplicationEngagement:a; + + engaged_p1 = SELECT i + FROM sessions_by_app:s -(created_by:e3)-> Individual:i + ACCUM i.@app_engagement = true; + + // Path 2: individual -> has_contact_point -> Contact_Point -> has_contact_point -> session_id -> action -> ApplicationEngagement + + contact_pts = SELECT cp + FROM start:i -(has_contact_point:e4)- Contact_Point:cp; + + sessions_cp = SELECT s + FROM contact_pts:cp -(has_contact_point:e5)- SessionID:s; + + sessions_cp_by_app = SELECT s + FROM sessions_cp:s -(action:e6)- ApplicationEngagement:a; + + cp_from_app_sessions = SELECT cp + FROM sessions_cp_by_app:s -(has_contact_point:e7)- Contact_Point:cp; + + engaged_p2 = SELECT i + FROM cp_from_app_sessions:cp -(has_contact_point:e8)- Individual:i + ACCUM i.@app_engagement = true; + + not_engaged = SELECT i + FROM start:i + WHERE i.@app_engagement == false; + PRINT not_engaged; } UPDATE DESCRIPTION OF QUERY Customers_No_Engagement "Returns individuals who have never had any engagement" From bc8d4d1a941884f02c474398235e5507cdf9f530 Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Tue, 3 Feb 2026 17:56:10 +0530 Subject: [PATCH 45/55] ER and KYC added contents to conform --- .../entity_resolution_kyc/README.md | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/financial_crime/entity_resolution_kyc/README.md b/financial_crime/entity_resolution_kyc/README.md index a0768478..3e2c96c3 100644 --- a/financial_crime/entity_resolution_kyc/README.md +++ b/financial_crime/entity_resolution_kyc/README.md @@ -1,4 +1,23 @@ -## Narratives: Enhanced KYC and Fraud Linkage +# Entitiy Resolution (ER) and Know Your Customer (KYC) +This solution kit provides a comprehensive graph-based approach to Entity Resolution (ER) +and Know Your Customer (KYC) compliance. It enables the creation of a Single Customer +View (SCV) by linking scattered records through weighted attribute matching, +detecting fraud rings in real-time, and generating powerful graph features for +downstream machine learning models. +--- + +## Contents + +- [Overview](#overview) +- [Components](#components) +- [ML Features](#ml-features-er-derived-features-for-risk-scoring) +- [Instructions](#instructions) + - [Setup and Installation](#setup-and-installation) + - [Query Execution Order and Explanations](#query-execution-order-and-explanations) + +--- + +## Overview Accurate **Entity Resolution (ER)** is the foundation of effective Know Your Customer (KYC) compliance and financial crime detection. Traditional systems often fail to link scattered customer records, leading to gaps in risk profiles. This TigerGraph solution addresses this by: From 29bf59bb089fb34feab106b16142574c640b3199 Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Wed, 4 Feb 2026 12:02:23 +0530 Subject: [PATCH 46/55] reverted the customers no engagement query --- .../queries/Customers_No_Engagement.gsql | 59 +++---------------- 1 file changed, 8 insertions(+), 51 deletions(-) diff --git a/connected_customer/customer_360/queries/Customers_No_Engagement.gsql b/connected_customer/customer_360/queries/Customers_No_Engagement.gsql index 1b06138c..0dff9398 100644 --- a/connected_customer/customer_360/queries/Customers_No_Engagement.gsql +++ b/connected_customer/customer_360/queries/Customers_No_Engagement.gsql @@ -1,54 +1,11 @@ -CREATE OR REPLACE DISTRIBUTED QUERY Customers_No_Engagement(/* Parameters here */) - FOR GRAPH Customer_360_Financial SYNTAX V1 { - /* - Description: - Returns Individuals who have never had any recorded engagement. - - Parameters: - (None) - Uses all Individual vertices in the graph. - Checks two association paths: - 1) Individual ->(created_session)-> SessionID -(action)- ApplicationEngagement - 2) Individual -(has_contact_point)- Contact_Point -(has_contact_point)- SessionID -(action)- ApplicationEngagement - Output: - Individual vertices with no recorded engagement - */ - OrAccum @app_engagement; - start = {Individual.*}; - - // Path 1: individual -> created_session -> action -> ApplicationEngagement - sessions = SELECT s - FROM start:i -(created_session:e1)-> SessionID:s; - - sessions_by_app = SELECT s - FROM sessions:s -(action:e2)- ApplicationEngagement:a; - - engaged_p1 = SELECT i - FROM sessions_by_app:s -(created_by:e3)-> Individual:i - ACCUM i.@app_engagement = true; - - // Path 2: individual -> has_contact_point -> Contact_Point -> has_contact_point -> session_id -> action -> ApplicationEngagement - - contact_pts = SELECT cp - FROM start:i -(has_contact_point:e4)- Contact_Point:cp; - - sessions_cp = SELECT s - FROM contact_pts:cp -(has_contact_point:e5)- SessionID:s; - - sessions_cp_by_app = SELECT s - FROM sessions_cp:s -(action:e6)- ApplicationEngagement:a; - - cp_from_app_sessions = SELECT cp - FROM sessions_cp_by_app:s -(has_contact_point:e7)- Contact_Point:cp; - - engaged_p2 = SELECT i - FROM cp_from_app_sessions:cp -(has_contact_point:e8)- Individual:i - ACCUM i.@app_engagement = true; - - not_engaged = SELECT i - FROM start:i - WHERE i.@app_engagement == false; - PRINT not_engaged; +CREATE DISTRIBUTED QUERY Customers_No_Engagement(/* Parameters here */) FOR GRAPH Customer_360_Financial { + + start = {Individual.*}; + + not_engaged = SELECT s FROM start:s + WHERE s.outdegree("created_session") == 0; + + PRINT not_engaged; } UPDATE DESCRIPTION OF QUERY Customers_No_Engagement "Returns individuals who have never had any engagement" From 172939da81a15c7ea1334d372bfa6fc9dc19c313 Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Wed, 4 Feb 2026 13:17:33 +0530 Subject: [PATCH 47/55] corrected customer 360 qyery for Individuals with no application --- .../queries/Individuals_No_Application.gsql | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/connected_customer/customer_360/queries/Individuals_No_Application.gsql b/connected_customer/customer_360/queries/Individuals_No_Application.gsql index 04629361..ff49fa8a 100644 --- a/connected_customer/customer_360/queries/Individuals_No_Application.gsql +++ b/connected_customer/customer_360/queries/Individuals_No_Application.gsql @@ -1,22 +1,22 @@ -CREATE DISTRIBUTED QUERY Individuals_No_Application(/* Parameters here */) FOR GRAPH Customer_360_Financial { - /* - Description: - Find individuals who looked at a product but dont ONLY have an application +CREATE OR REPLACE DISTRIBUTED QUERY Individuals_No_Application() +FOR GRAPH Customer_360_Financial SYNTAX V1 { + // Find individuals who never started an application - Parameters: - (None) - Uses all SessionID vertices in the graph. - */ - sessions = {SessionID.*}; - - # Sessions with ProductBrowse or Websearch - - interest = SELECT s FROM sessions:s - (action:e) - :t - WHERE t.type != "ApplicationEngagement"; - - individuals = SELECT t FROM interest:s - (created_by:e) - Individual:t; - - PRINT individuals; + all_people = {Individual.*}; + + // Any session that has ApplicationEngagement + app_sessions = SELECT s + FROM SessionID:s -(action:e1)- ApplicationEngagement:a; + + // Individuals who have at least one such session + applied_people = SELECT i + FROM app_sessions:s -(created_by:e2)-> Individual:i; + + // People with no application sessions + result = all_people MINUS applied_people; + + PRINT result; } -UPDATE DESCRIPTION OF QUERY Individuals_No_Application "Return individuals who have not ONLY made an application, but have products" + +UPDATE DESCRIPTION OF QUERY Individuals_No_Application "Return individuals who have not made an application" From 2a6b16b95ef299099a9bc1547b47610390424cb7 Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Wed, 4 Feb 2026 15:41:58 +0530 Subject: [PATCH 48/55] fixed bug in customer 360 kit Application_Submissions --- .../customer_360/queries/Application_Submissions.gsql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connected_customer/customer_360/queries/Application_Submissions.gsql b/connected_customer/customer_360/queries/Application_Submissions.gsql index 5cb311fa..d2fce709 100644 --- a/connected_customer/customer_360/queries/Application_Submissions.gsql +++ b/connected_customer/customer_360/queries/Application_Submissions.gsql @@ -17,7 +17,7 @@ CREATE DISTRIBUTED QUERY Application_Submissions(/* Parameters here */) FOR GRAP ACCUM @@app_starts += s; app_submits = SELECT s FROM start:s - (action:e) - ApplicationEngagement:t - WHERE e.action_detail == "submit_application" + WHERE e.action_detail == "submitted_application" ACCUM @@app_submits += s; @@finished = @@app_starts INTERSECT @@app_submits; From 1110eadbe025ea3af795d07e752f1f4966536c66 Mon Sep 17 00:00:00 2001 From: Jim Limprasert Date: Mon, 16 Feb 2026 22:50:51 +0700 Subject: [PATCH 49/55] documentation: change k_means.attr_set description so that gsql execution doesn't fail. --- connected_customer/product_recommendations/queries/k_means.gsql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connected_customer/product_recommendations/queries/k_means.gsql b/connected_customer/product_recommendations/queries/k_means.gsql index 6eb2be6b..6cd5aaaa 100644 --- a/connected_customer/product_recommendations/queries/k_means.gsql +++ b/connected_customer/product_recommendations/queries/k_means.gsql @@ -261,7 +261,7 @@ CREATE QUERY k_means( UPDATE DESCRIPTION OF QUERY k_means "This query clusters vertices according to some subset of their numerical attributes using the KMeans algorithm, which is a vector quantization clustering algorithm. The algorithm iteratively attempts to cluster the vertices using an incremental number of centroids and then selects the centroid assignment which results in the best sum of squared errors, such that the clusters are not under or overfit to the dataset. The algorithm inserts the centroids as Cluster vertices and links all vertices to their respective centroids via the In_Cluster edge type." UPDATE DESCRIPTION OF QUERY_PARAM k_means.v_type "This is the target vertex type to cluster. It defaults to Customer." -UPDATE DESCRIPTION OF QUERY_PARAM k_means.attr_set "This is a SET of JSON-formatted string where each string represent a list of tuples containing an index and the key of a numerical value contained in the MAP attribute, 'attr_map', on the target vertex type. Example of ONE of the strings in attr_set: `[0, \"age\"]` (change `\"` to be `"` if quotes don't need to be escaped) +UPDATE DESCRIPTION OF QUERY_PARAM k_means.attr_set "This is a SET of JSON-formatted string where each string represent a list of tuples containing an index and the key of a numerical value contained in the MAP attribute, 'attr_map', on the target vertex type. Example of ONE of the strings in attr_set: `[0, \"age\"]`" UPDATE DESCRIPTION OF QUERY_PARAM k_means.min_cluster_count "This is the minimum number of centroids to try out when clustering." UPDATE DESCRIPTION OF QUERY_PARAM k_means.max_cluster_count "This is the maximum number of centroids to try out when clustering. Must be greater than or equal to min_cluster_count." UPDATE DESCRIPTION OF QUERY_PARAM k_means.cluster_inc "This is the value by which the centroid count is incremented when moving onto the next centroid count. Defaults to 1." From 14a8bc87cfa8e58f455350da7d42e6190ab2e88c Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Wed, 25 Feb 2026 10:43:25 +0530 Subject: [PATCH 50/55] All insights applications match glob Insights*.json format --- ...sights.json => Insights_Supply_Chain.json} | 0 ...nsights.json => Insights_Customer360.json} | 0 ...s.json => Insights_Entity_Resolution.json} | 0 .../mule_account_detection/README.md | 84 ++++++++++++------- .../queries/account_account_with_weights.gsql | 11 +-- 5 files changed, 60 insertions(+), 35 deletions(-) rename agile_operations/supply_chain_management/meta/{Supply_Chain_Insights.json => Insights_Supply_Chain.json} (100%) rename connected_customer/customer_360/meta/{insights.json => Insights_Customer360.json} (100%) rename connected_customer/entity_resolution/meta/{insights.json => Insights_Entity_Resolution.json} (100%) diff --git a/agile_operations/supply_chain_management/meta/Supply_Chain_Insights.json b/agile_operations/supply_chain_management/meta/Insights_Supply_Chain.json similarity index 100% rename from agile_operations/supply_chain_management/meta/Supply_Chain_Insights.json rename to agile_operations/supply_chain_management/meta/Insights_Supply_Chain.json diff --git a/connected_customer/customer_360/meta/insights.json b/connected_customer/customer_360/meta/Insights_Customer360.json similarity index 100% rename from connected_customer/customer_360/meta/insights.json rename to connected_customer/customer_360/meta/Insights_Customer360.json diff --git a/connected_customer/entity_resolution/meta/insights.json b/connected_customer/entity_resolution/meta/Insights_Entity_Resolution.json similarity index 100% rename from connected_customer/entity_resolution/meta/insights.json rename to connected_customer/entity_resolution/meta/Insights_Entity_Resolution.json diff --git a/financial_crime/mule_account_detection/README.md b/financial_crime/mule_account_detection/README.md index dffe6983..548dd816 100644 --- a/financial_crime/mule_account_detection/README.md +++ b/financial_crime/mule_account_detection/README.md @@ -52,39 +52,60 @@ the movement of illicit funds. ### Here is a list of graph features we provide in the solution kit: -1. **Community Detection**: - We use the weighted Weakly Connected Components (WCC) algorithm to detect communities within the graph. This includes updating the size of each community and assigning community IDs, which help enhance the machine learning models for identifying mule accounts. - -2. **PageRank Score**: - The PageRank score for each account vertex in the account network is calculated. This score represents the importance or influence of each account, aiding the machine learning models in detecting mule accounts. - -3. **Shortest Path Length**: - We calculate the shortest path length from each account to identified mule accounts. This metric helps determine the proximity of each account to known mule accounts, providing valuable features for the models. - -4. **IP Sharing**: - The number of mule accounts that share the same IP address for each account is calculated. This feature helps identify suspicious accounts connected through the same IP address. - -5. **Multi-Hop IP Analysis**: - We calculate the number of IP addresses linked to any mule accounts within a specified number of hops. This feature identifies potentially fraudulent IP connections, enhancing the machine learning models. - -6. **Device Sharing**: - The number of mule accounts that share the same device for each account is calculated. This aids in identifying suspicious accounts connected through the same device. - -7. **Multi-Hop Device Analysis**: - We calculate the number of devices linked to any mule accounts within a specified number of hops, helping to identify potentially fraudulent device connections. - -8. **Transfer Ratio**: - The ratio of the total amount of money transferred to and from mule accounts relative to the total transfer amount for each account is calculated. This includes the ratio of incoming and outgoing transfers, identifying suspicious transfer patterns. - -9. **Multi-Hop Mule Account Analysis**: - We calculate the total number of mule accounts within a specified number of hops from each account. This helps assess the proximity and density of mule accounts in the network. +1. **Community Detection**: + We use the weighted Weakly Connected Components (WCC) algorithm to detect + communities within the graph. This includes updating the size of each community + and assigning community IDs, which help enhance the machine learning models + for identifying mule accounts. + +2. **PageRank Score**: + The PageRank score for each account vertex in the account network is calculated. + This score represents the importance or influence of each account, aiding the + machine learning models in detecting mule accounts. + +3. **Shortest Path Length**: + We calculate the shortest path length from each account to identified mule + accounts. This metric helps determine the proximity of each account to known + mule accounts, providing valuable features for the models. + +4. **IP Sharing**: + The number of mule accounts that share the same IP address for each account + is calculated. This feature helps identify suspicious accounts connected + through the same IP address. + +5. **Multi-Hop IP Analysis**: + We calculate the number of IP addresses linked to any mule accounts within a + specified number of hops. This feature identifies potentially fraudulent IP + connections, enhancing the machine learning models. + +6. **Device Sharing**: + The number of mule accounts that share the same device for each account is + calculated. This aids in identifying suspicious accounts connected through + the same device. + +7. **Multi-Hop Device Analysis**: + We calculate the number of devices linked to any mule accounts within a + specified number of hops, helping to identify potentially fraudulent device + connections. + +8. **Transfer Ratio**: + The ratio of the total amount of money transferred to and from mule accounts + relative to the total transfer amount for each account is calculated. This + includes the ratio of incoming and outgoing transfers, identifying suspicious + transfer patterns. + +9. **Multi-Hop Mule Account Analysis**: + We calculate the total number of mule accounts within a specified number of + hops from each account. This helps assess the proximity and density of mule + accounts in the network. ## Queries Included Below is a list of queries included in this solution. ### **1. Identify Potential Mule Accounts** -Scores accounts based on suspicious behavior patterns such as high-velocity flows, layering, quick pass-through transactions, etc. +Scores accounts based on suspicious behavior patterns such as high-velocity flows, +layering, quick pass-through transactions, etc. ### **2. Multi-hop Transaction Tracing** Finds the complete chain of fund movement starting from a given account up to N hops. @@ -99,7 +120,8 @@ Uses graph connectivity to identify tightly linked groups behaving in coordinate Compares current account transaction behavior against its own historical patterns. ### **6. Account Profile Explorer** -Retrieves everything related to a specific account: customer, devices, IPs, merchants, inflow/outflow, partner accounts, and more. +Retrieves everything related to a specific account: customer, devices, IPs, +merchants, inflow/outflow, partner accounts, and more. ### **7. Counterparty Risk Aggregation** Measures the risk level of counterparties transacting with a given account. @@ -169,13 +191,15 @@ Load data into the schema by running the local loading job script: ### Step 1: Insert Edges for Merchant and Card Networks -To initiate the `wcc` and `pagerank` algorithms on the Merchant and Card network, it's essential to first execute the following two queries: +To initiate the `wcc` and `pagerank` algorithms on the Merchant and Card network, +it's essential to first execute the following two queries: - `account_account_with_weights` ### Step 2: Form Communities -The following query runs weighted wcc algorithm using the weights on Account_Account edge. +The following query runs weighted wcc algorithm using the weights on Account_Account +edge. - `tg_wcc_account_with_weights` diff --git a/financial_crime/mule_account_detection/queries/account_account_with_weights.gsql b/financial_crime/mule_account_detection/queries/account_account_with_weights.gsql index 6b97f395..d33dcb43 100644 --- a/financial_crime/mule_account_detection/queries/account_account_with_weights.gsql +++ b/financial_crime/mule_account_detection/queries/account_account_with_weights.gsql @@ -4,14 +4,15 @@ CREATE DISTRIBUTED QUERY account_account_with_weights( INT min_edge_weight=1) FOR GRAPH Mule_Account_Detection { /* - This query creates a Account_Account edges for every pair of accounts that share the same transfer_transaction. - It then assigns weights to these connections based on the frequency of transactions involving the common merchant - pair across all cards. Weighted Personalized PageRank algorithm will be applied to this account_account network. + This query creates Account_Account edges for every pair of accounts that + share the same transfer_transaction. It then assigns weights to these + connections based on the frequency of transactions involving the common merchant + pair across all cards. Weighted Personalized PageRank algorithm will be + applied to this account_account network. */ SetAccum> @accounts; MapAccum, INT> @edge_weight; - //clear previous Account_Account edges tmp = SELECT s FROM Account:s -(Account_Account:e)- Account:t @@ -31,4 +32,4 @@ CREATE DISTRIBUTED QUERY account_account_with_weights( END END; PRINT "Account_Account Edges Inserted " AS Status; - } \ No newline at end of file + } From 13d4ad06464af44db7a719e16e1215eb679aac91 Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Thu, 26 Feb 2026 15:00:37 +0530 Subject: [PATCH 51/55] Fixed insights bugs in supply shain kit --- .../meta/Insights_Supply_Chain.json | 8 +- .../queries/explore_BOM_insights.gsql | 37 ++++++++++ .../queries/explore_BOM_line_Insights.gsql | 73 +++++++++++++++++++ 3 files changed, 114 insertions(+), 4 deletions(-) create mode 100644 agile_operations/supply_chain_management/queries/explore_BOM_insights.gsql create mode 100644 agile_operations/supply_chain_management/queries/explore_BOM_line_Insights.gsql diff --git a/agile_operations/supply_chain_management/meta/Insights_Supply_Chain.json b/agile_operations/supply_chain_management/meta/Insights_Supply_Chain.json index 955d55df..73a13827 100644 --- a/agile_operations/supply_chain_management/meta/Insights_Supply_Chain.json +++ b/agile_operations/supply_chain_management/meta/Insights_Supply_Chain.json @@ -441,7 +441,7 @@ } ] }, - "graphName": "AntiFraud", + "graphName": "Supply_Chain_Management", "hideWidgetName": false, "id": "jiFArchKghkbBhGC4d548q", "patternLimit": 5, @@ -853,7 +853,7 @@ } ] }, - "graphName": "AntiFraud", + "graphName": "Supply_Chain_Management", "hideWidgetName": false, "id": "7GwzFLnQ5dDDVtUtVx7mPv", "patternLimit": 5, @@ -1041,7 +1041,7 @@ "id": "input_35dZipma5w14zcPa9srhP7", "name": "vertType", "settings": { - "graphName": "AntiFraud", + "graphName": "Supply_Chain_Management", "open": false, "options": [ { @@ -1111,7 +1111,7 @@ } ] }, - "graphName": "AntiFraud", + "graphName": "Supply_Chain_Management", "hideWidgetName": false, "id": "wGYxHtNz9dmfUBHvh8vi8a", "patternLimit": 5, diff --git a/agile_operations/supply_chain_management/queries/explore_BOM_insights.gsql b/agile_operations/supply_chain_management/queries/explore_BOM_insights.gsql new file mode 100644 index 00000000..f3943bd2 --- /dev/null +++ b/agile_operations/supply_chain_management/queries/explore_BOM_insights.gsql @@ -0,0 +1,37 @@ +CREATE OR REPLACE DISTRIBUTED QUERY explore_BOM_insights(STRING vertType,STRING id, INT depth, BOOL upstream) FOR GRAPH Supply_Chain_Management { + + SetAccum @@edges; + SetAccum @@nodes; + OrAccum @visited; + OrAccum @isSrc; + vertex vertss; + vertss = to_vertex(id,vertType); + // get src + verts = {vertss}; + verts = select s from verts:s ACCUM s.@isSrc += TRUE,@@nodes += s; + + // while loop + WHILE verts.size() > 0 LIMIT depth DO + // traverse up or down + IF upstream == TRUE THEN + verts = + SELECT t FROM verts:s -((Supplies|reverse_Has_Component_Material|reverse_Produced_By):e)- (Material|BOM):t + WHERE t.@visited == FALSE + ACCUM @@edges += e,@@nodes += t + POST-ACCUM + s.@visited = TRUE; + ELSE + verts = + SELECT t FROM verts:s -((reverse_Supplies|Has_Component_Material|Produced_By):e)- (Material|BOM|Supplier):t + WHERE t.@visited == FALSE + ACCUM @@edges += e,@@nodes += t + POST-ACCUM + s.@visited = TRUE; + END; + END; + + // print edges + PRINT @@edges; + nodes = {@@nodes}; + PRINT nodes; +} diff --git a/agile_operations/supply_chain_management/queries/explore_BOM_line_Insights.gsql b/agile_operations/supply_chain_management/queries/explore_BOM_line_Insights.gsql new file mode 100644 index 00000000..506f7f91 --- /dev/null +++ b/agile_operations/supply_chain_management/queries/explore_BOM_line_Insights.gsql @@ -0,0 +1,73 @@ +CREATE OR REPLACE DISTRIBUTED QUERY explore_BOM_line_Insights(STRING vertType,STRING id, INT depth, BOOL upstream, BOOL use_date_range, DATETIME start_date, DATETIME end_date) FOR GRAPH Supply_Chain_Management { + /* + Description: + End-to-end transactional lineage traversal (order-to-cash & procure-to-pay) + across actual production instances (SFC), sales orders, purchase orders, + shipments, and customers/suppliers. + + This is the "digital thread" query — it connects the physical flow + (what was actually built and shipped) with the commercial flow + (who ordered it and who supplied the components). + + Parameters: + input_vert (VERTEX): + Starting vertex — typically SFC_Material (actual batch), Sales_Order, + Purchase_Order, Customer, or Supplier + depth (INT): + Maximum traversal depth + upstream (BOOL): + FALSE → Follow the flow downstream + TRUE → Follow the flow upstream + use_date_range (BOOL): + If TRUE, only traverse through dated vertices (Sales_Order, Purchase_Order, + SFC_Assembly) that fall within the specified window + start_date / end_date (DATETIME): + Optional time fence for filtering transactional vertices + + */ + SetAccum @@nodes; + OrAccum @isSrc; + vertex vertss; + vertss = to_vertex(id,vertType); + // get src + verts = {vertss}; + verts = select s from verts:s ACCUM s.@isSrc += TRUE,@@nodes += s; + + SetAccum @@edges; + OrAccum @visited; + + + // while loop + WHILE verts.size() > 0 LIMIT depth DO + // traverse up or down + IF upstream == TRUE THEN + verts = + SELECT t FROM verts:s -((Has_Purchase_Order|Has_Line_Number|reverse_Used_For| + reverse_Has_Component_SFC|reverse_To_Be_Produced_By|reverse_For_Material| + reverse_Has_Sales_Order_Item|reverse_Has_Sales_Order):e)- + (Purchase_Order|Line_Number|SFC_Material|SFC_Assembly|Sales_Order_Item|Sales_Order|Customer):t + WHERE t.@visited == FALSE + // relevant vertices in date range (SFC_Assembly,Sales_Order,Purchase_Order) + ACCUM @@edges += e,@@nodes += t + POST-ACCUM + s.@visited = TRUE; + ELSE + verts = + SELECT t FROM verts:s -((reverse_Has_Purchase_Order|reverse_Has_Line_Number|Used_For| + Has_Component_SFC|To_Be_Produced_By|For_Material| + Has_Sales_Order_Item|Has_Sales_Order):e)- + (Supplier|Purchase_Order|Line_Number|SFC_Material|SFC_Assembly|Sales_Order_Item|Sales_Order):t + WHERE t.@visited == FALSE + // relevant vertices in date range (SFC_Assembly,Sales_Order,Purchase_Order) + ACCUM @@edges += e,@@nodes += t + POST-ACCUM + s.@visited = TRUE; + END;//Based_On,reverse_Based_On,Happens_At,reverse_Happens_At,Produced_By,reverse_Produced_By,Has_Component_Material,reverse_Has_Component_Material,SFC_To_Material,reverse_SFC_To_Material,Supplies,reverse_Supplies,Has,reverse_Has + END; + + // print edges + PRINT @@edges; + nodes = {@@nodes}; + PRINT nodes; + PRINT "explore_BOMLine works!"; +} From fa05d2e43517f88d86f6004b621aaac089fe40bc Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Fri, 27 Feb 2026 11:10:59 +0530 Subject: [PATCH 52/55] Entity resoliton KYC insights graph name fixed for consistency --- .../meta/Insights_Application_Entity_Resolution_kyc.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/financial_crime/entity_resolution_kyc/meta/Insights_Application_Entity_Resolution_kyc.json b/financial_crime/entity_resolution_kyc/meta/Insights_Application_Entity_Resolution_kyc.json index c1740344..f2167dce 100644 --- a/financial_crime/entity_resolution_kyc/meta/Insights_Application_Entity_Resolution_kyc.json +++ b/financial_crime/entity_resolution_kyc/meta/Insights_Application_Entity_Resolution_kyc.json @@ -1,5 +1,5 @@ { - "defaultGraph": "AntiFraud", + "defaultGraph": "Entity_Resolution_KYC", "iconURL": "/insights/static/media/atom.14f5dd297b1a450cae3413a44f69a75b.svg", "id": "arDCiwHPNbgkqU51x1tBNY", "pageConfigSeparated": true, @@ -334,7 +334,7 @@ } ] }, - "graphName": "AntiFraud", + "graphName": "Entity_Resolution_KYC", "hideWidgetName": false, "id": "pWVjbPx54GYQyYYTHe8Qpj", "patternLimit": 5, From 45d51434737f8b9913d86c0260d20d14f5c6b6e0 Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Fri, 27 Feb 2026 11:30:29 +0530 Subject: [PATCH 53/55] Enchanced comments in supply chain insights queries --- .../queries/explore_BOM_insights.gsql | 25 ++++++++++++++++++- .../queries/explore_BOM_line_Insights.gsql | 9 ++++--- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/agile_operations/supply_chain_management/queries/explore_BOM_insights.gsql b/agile_operations/supply_chain_management/queries/explore_BOM_insights.gsql index f3943bd2..bc2d874a 100644 --- a/agile_operations/supply_chain_management/queries/explore_BOM_insights.gsql +++ b/agile_operations/supply_chain_management/queries/explore_BOM_insights.gsql @@ -1,5 +1,28 @@ CREATE OR REPLACE DISTRIBUTED QUERY explore_BOM_insights(STRING vertType,STRING id, INT depth, BOOL upstream) FOR GRAPH Supply_Chain_Management { - + /* + Query Name: explore_BOM_insights + Multi-level BOM exploration (downstream) or where-used analysis (upstream) + starting from a vertex identified by (vertType, id). + + Key Use Cases: + . Explode a material/BOM into its components and related suppliers (downstream) + . Reverse trace: find BOMs/material relationships and supply paths (upstream) + . Useful for impact analysis, sourcing visibility, and BOM troubleshooting. + + Parameters: + vertType (STRING): + Vertex type name of the starting vertex (e.g., Material, BOM, Supplier) + id (STRING): + Primary/external ID of the starting vertex (converted via to_vertex(id, vertType)) + depth (INT): + Maximum traversal depth + upstream (BOOL): + TRUE -> Where-used / reverse trace + FALSE -> BOM explosion / forward trace + + Output: + Prints the traversed edge set (@@edges) and vertex set (@@nodes). + */ SetAccum @@edges; SetAccum @@nodes; OrAccum @visited; diff --git a/agile_operations/supply_chain_management/queries/explore_BOM_line_Insights.gsql b/agile_operations/supply_chain_management/queries/explore_BOM_line_Insights.gsql index 506f7f91..5e3b98b8 100644 --- a/agile_operations/supply_chain_management/queries/explore_BOM_line_Insights.gsql +++ b/agile_operations/supply_chain_management/queries/explore_BOM_line_Insights.gsql @@ -10,9 +10,12 @@ CREATE OR REPLACE DISTRIBUTED QUERY explore_BOM_line_Insights(STRING vertType,ST (who ordered it and who supplied the components). Parameters: - input_vert (VERTEX): - Starting vertex — typically SFC_Material (actual batch), Sales_Order, - Purchase_Order, Customer, or Supplier + vertType (STRING): + Vertex type name of the starting vertex — typically SFC_Material (actual batch), + Sales_Order, Purchase_Order, Customer, or Supplier + id (STRING): + Primary/external ID of the starting vertex (paired with vertType and converted + to a vertex via to_vertex(id, vertType)) depth (INT): Maximum traversal depth upstream (BOOL): From e7ee33d0d5a900502802ade0e7b1a086480d5c1e Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Wed, 4 Mar 2026 13:54:16 +0530 Subject: [PATCH 54/55] changes to Insights application in mule account solution kit removed call to shortest path algorithm --- .../meta/Insights_mule_analytics.json | 164 ++++++++---------- 1 file changed, 73 insertions(+), 91 deletions(-) diff --git a/financial_crime/mule_account_detection/meta/Insights_mule_analytics.json b/financial_crime/mule_account_detection/meta/Insights_mule_analytics.json index 94a7dfc5..eada1dd7 100644 --- a/financial_crime/mule_account_detection/meta/Insights_mule_analytics.json +++ b/financial_crime/mule_account_detection/meta/Insights_mule_analytics.json @@ -1,12 +1,12 @@ { "defaultGraph": "Mule_Account_Detection", "iconURL": "/studio/assets/gvis/icons/builtin/64/285-asset.png", - "id": "fHvaU5uuocyNTvLK1hvnhZ", + "id": "nm7u6Gfy5RyCjFn5a4XXvX", "owner": "tigergraph", "pageConfigSeparated": true, "title": "Mule Account Detection", "userRoleForApp": "owner", - "version": "1718182992759391308", + "version": "1772468490523977939", "pages": [ { "globalParameters": { @@ -56,7 +56,7 @@ ] }, "title": "Device Conflicts", - "version": "1718182992772794626", + "version": "1772468490536218944", "weight": 65, "chartMap": { "bRkhvjVhY1TWQcogdN5qWX": { @@ -156,7 +156,7 @@ "staticData": "{}", "title": "Device Sharing Table", "type": "table", - "version": "1718182992806038828" + "version": "1772468490570044181" }, "bsGHYaUbye71mmcqUw73hu": { "chartSettings": { @@ -193,7 +193,7 @@ "staticData": "{}", "title": "Device Sharing Graph", "type": "internal-graph", - "version": "1718182992813412007" + "version": "1772468490577999522" }, "eQZKGCXHQALSLsewAjs1ea": { "chartSettings": { @@ -220,7 +220,7 @@ "staticData": "{}", "title": "Input Account", "type": "Inputs", - "version": "1718182992819392426" + "version": "1772468490584087918" } } }, @@ -298,7 +298,7 @@ ] }, "title": "Account_Network_Pagerank", - "version": "1718182992778659189", + "version": "1772468490542439646", "weight": 20, "chartMap": { "4QYhdJVbfoeUnGjAzhi5sG": { @@ -359,7 +359,7 @@ "staticData": "{}", "title": "Top Pagerank Score Account", "type": "table", - "version": "1718182992825778629" + "version": "1772468490589993580" }, "d3UvsqzXpUWp8BroNYLgqM": { "chartSettings": { @@ -389,7 +389,7 @@ "staticData": "{}", "title": "Input", "type": "Inputs", - "version": "1718182992833516058" + "version": "1772468490595807693" }, "nLdSm9KfPcJrmCcdi8Y1gW": { "chartSettings": {}, @@ -403,7 +403,7 @@ "staticData": "{}", "title": "Account Network Pagerank", "type": "internal-graph", - "version": "1718182992840084316" + "version": "1772468490603726226" } } }, @@ -452,7 +452,7 @@ ] }, "title": "WCC Community", - "version": "1718182992784492285", + "version": "1772468490551008026", "weight": 35, "chartMap": { "jkio2L3Um2ufCJ7gkybBWw": { @@ -529,7 +529,7 @@ "staticData": "{}", "title": "WCC Table", "type": "table", - "version": "1718182992846644161" + "version": "1772468490609449424" }, "rVesAEnUrQfHxr86YV2rfy": { "chartSettings": {}, @@ -543,7 +543,7 @@ "staticData": "{}", "title": "Communities", "type": "internal-graph", - "version": "1718182992853827816" + "version": "1772468490615273082" }, "xsBfPK3EJ6n4J9gZMoxdhs": { "chartSettings": { @@ -573,11 +573,60 @@ "staticData": "{}", "title": "Input", "type": "Inputs", - "version": "1718182992859656801" + "version": "1772468490624413433" } } }, { + "globalParameters": { + "account": { + "id": "input_4bVYZSdcBZcMkXxvqLiAfC", + "name": "account", + "type": "VERTEX", + "value": { + "vertexID": "0448743965", + "vertexType": "Account" + } + } + }, + "iconURL": "/studio/assets/gvis/icons/builtin/64/052-cells.png", + "id": "sXcEF1LsFkipMZXidn8WoX", + "isDetail": true, + "isNew": false, + "layouts": { + "md": [ + { + "h": 45, + "i": "eGUYb6o4Lf5YaTokas5xi8", + "moved": false, + "static": false, + "w": 6, + "x": 0, + "y": 0 + }, + { + "h": 35, + "i": "m4XfGCnug9rZ6zMXUX8eth", + "moved": false, + "static": false, + "w": 6, + "x": 6, + "y": 10 + }, + { + "h": 10, + "i": "umcMFXKpivgT6ftHb3sf2R", + "moved": false, + "static": false, + "w": 6, + "x": 6, + "y": 0 + } + ] + }, + "title": "Shortest Path", + "version": "1772468490556807422", + "weight": 45, "chartMap": { "eGUYb6o4Lf5YaTokas5xi8": { "chartSettings": { @@ -618,28 +667,19 @@ }, { "data": "account", - "id": "87a48d71-cdd3-4317-91e9-512550c26af6", + "id": "ac6681b5-e84c-4694-8960-c1da25d2f256", "paramGlobalInput": "account", "paramName": "ver", "paramType": "VERTEX", "paramTypeReadonly": true, "type": "PARAM", "vertexType": "Account" - }, - { - "data": "number_paths", - "id": "f4ee8d07-3306-4f4c-8e03-ce502c62e0e4", - "paramGlobalInput": "number_paths", - "paramName": "print_number", - "paramType": "INT", - "paramTypeReadonly": true, - "type": "PARAM" } ], "staticData": "{}", "title": "shortest path", "type": "internal-graph", - "version": "1718226734254095335" + "version": "1772600035200321987" }, "m4XfGCnug9rZ6zMXUX8eth": { "chartSettings": { @@ -791,28 +831,19 @@ }, { "data": "account", - "id": "a8d8e336-d857-41b0-ab62-f87252703050", + "id": "f1427c4b-90db-4498-a62d-b37bb466db93", "paramGlobalInput": "account", "paramName": "ver", "paramType": "VERTEX", "paramTypeReadonly": true, "type": "PARAM", "vertexType": "Account" - }, - { - "data": "number_paths", - "id": "18491db9-f7a0-4546-a30f-45dbcc86e868", - "paramGlobalInput": "number_paths", - "paramName": "print_number", - "paramType": "INT", - "paramTypeReadonly": true, - "type": "PARAM" } ], "staticData": "{}", "title": "Shortest Path Table", "type": "table", - "version": "1718227114441036065" + "version": "1772600048009176957" }, "umcMFXKpivgT6ftHb3sf2R": { "chartSettings": { @@ -838,58 +869,9 @@ "staticData": "{}", "title": "Input", "type": "Inputs", - "version": "1718227489486288049" - } - }, - "globalParameters": { - "account": { - "id": "input_4bVYZSdcBZcMkXxvqLiAfC", - "name": "account", - "type": "VERTEX", - "value": { - "vertexID": "0448743965", - "vertexType": "Account" - } + "version": "1772468490644947116" } - }, - "iconURL": "/studio/assets/gvis/icons/builtin/64/052-cells.png", - "id": "sXcEF1LsFkipMZXidn8WoX", - "isDetail": true, - "isNew": false, - "layouts": { - "md": [ - { - "h": 45, - "i": "eGUYb6o4Lf5YaTokas5xi8", - "moved": false, - "static": false, - "w": 6, - "x": 0, - "y": 0 - }, - { - "h": 35, - "i": "m4XfGCnug9rZ6zMXUX8eth", - "moved": false, - "static": false, - "w": 6, - "x": 6, - "y": 10 - }, - { - "h": 10, - "i": "umcMFXKpivgT6ftHb3sf2R", - "moved": false, - "static": false, - "w": 6, - "x": 6, - "y": 0 - } - ] - }, - "title": "Shortest Path", - "version": "1718227489476782113", - "weight": 45 + } }, { "globalParameters": { @@ -939,7 +921,7 @@ ] }, "title": "IP Conflicts", - "version": "1718182992797652966", + "version": "1772468490562837106", "weight": 55, "chartMap": { "1dQRNR2aD7aFypgUHjQHRn": { @@ -967,7 +949,7 @@ "staticData": "{}", "title": "Input Account", "type": "Inputs", - "version": "1718182992883991368" + "version": "1772468490652793186" }, "2JscpDG7RsknwWoxrytfGT": { "chartSettings": { @@ -1004,7 +986,7 @@ "staticData": "{}", "title": "IP Sharing Graph", "type": "internal-graph", - "version": "1718182992890948957" + "version": "1772468490658972004" }, "gFu8x13AQTuCwQdnTTn18k": { "chartSettings": { @@ -1103,7 +1085,7 @@ "staticData": "{}", "title": "IP Sharing Table", "type": "table", - "version": "1718182992897871800" + "version": "1772468490665221019" } } } From 949e62933c4996e17415058f1df58b72e1c86185 Mon Sep 17 00:00:00 2001 From: abrahamchandy95 Date: Wed, 4 Mar 2026 14:08:12 +0530 Subject: [PATCH 55/55] KYC solution in financial crime readme formatting corrected --- financial_crime/entity_resolution_kyc/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/financial_crime/entity_resolution_kyc/README.md b/financial_crime/entity_resolution_kyc/README.md index 3e2c96c3..99c4601f 100644 --- a/financial_crime/entity_resolution_kyc/README.md +++ b/financial_crime/entity_resolution_kyc/README.md @@ -4,6 +4,7 @@ and Know Your Customer (KYC) compliance. It enables the creation of a Single Cus View (SCV) by linking scattered records through weighted attribute matching, detecting fraud rings in real-time, and generating powerful graph features for downstream machine learning models. + --- ## Contents