向 Clean Room 添加自定义模板

提供商和使用者都可以向 Clean Room 添加自定义模板。自定义模板的运行方式与 Snowflake 提供的模板相同。

备注

Clean Room 模板是有效的 JinjaSQL 模板。在尝试创建自己的 Clean Room 模板之前,您应该 阅读自定义模板的 Clean Room 参考指南

提供商编写的自定义模板

提供商可以在未经使用者批准的情况下向 Clean Room 添加自定义模板。使用者可以在未经批准的情况下运行提供商编写的模板。下面几节介绍提供商如何使用 API 添加自定义模板,以及使用者如何运行该模板。

如果提供商想要设计一个使用者可以在 Clean Room UI 中运行的模板,他们必须为该模板 创建用户输入表单

添加提供商编写的模板

提供商通过调用 provider.add_custom_sql_template 并将模板 JinjaSQL 作为字符串传入,一次添加一个自定义模板。自定义模板会出现在 Clean Room 的模板列表中,其行为与 Snowflake 提供的模板相同。Clean Room 可以包含自定义模板和 Snowflake 提供的模板的任意组合。

您也可以上传 自定义 Python UDFs,以供模板调用。

小技巧

在您添加自定义模板供使用者使用时,应该提供文档,描述模板的用途以及模板使用的必需和可选实参。

以下示例展示了提供商如何向 Clean Room 添加自定义模板:

CALL samooha_by_snowflake_local_db.provider.add_custom_sql_template(
    $cleanroom_name,
    $basic_template_name,
    $$
    SELECT
      COUNT(*) AS total_count
    FROM IDENTIFIER({{ my_table[0] }}) AS c
      INNER JOIN IDENTIFIER({{ source_table[0] }}) AS p
      ON IDENTIFIER({{ consumer_id | join_policy }}) = IDENTIFIER({{ provider_id | join_policy }})
    {% if where_clause %}
    WHERE {{ where_clause | sqlsafe }}
    {% endif %};
  $$
);
Copy

在大多数模板中,包括前面的示例中,列名必须使用表名完全限定,以免列名冲突。这是因为要将表名前缀连接到前缀中的列名并获得有效的标识符(IDENTIFIER(p.{{col_name | sqlsafe }}) 就是一个错误)并不容易。因此,您可能需要调用方提供完全限定的表名,而不仅仅是列名。表名应使用批准的 pc 别名。

运行提供商编写的模板

使用 Clean Room API 时,使用者调用 consumer.run_analysis 来运行模板,提供商为 提供商运行的分析 调用 provider.submit_analysis_request

如果您希望模板可以在 Clean Room UI 中运行,则提供商必须为该模板 创建用户输入表单。只有提供商编写的模板才能在 Clean Room UI 中运行。

除非提供商 对模板进行了模糊处理,否则 Clean Room 协作者可以通过调用 consumer.view_template_definition 查看 Clean Room 中任何模板的 JinjaSQL。只能对提供商编写的模板进行模糊处理。

您可以调用 consumer.get_arguments_from_template,解析和列出模板中使用的变量。但对于大型或复杂的模板,此过程可能不会列出所有模板变量,因此请确保为模板用户提供有用的文档。

以下示例显示了使用者如何运行前面显示的提供商的自定义模板:

 CALL samooha_by_snowflake_local_db.consumer.run_analysis(
  $cleanroom_name,
  'basic_template',
  ['SAMOOHA_SAMPLE_DATABASE.DEMO.CUSTOMERS'],  -- Populates the my_table array.
  ['SAMOOHA_SAMPLE_DATABASE.DEMO.CUSTOMERS'],  -- Populates the source_table array.
  OBJECT_CONSTRUCT(
  'consumer_id', 'c.hashed_email',  -- hashed_email column from the consumer's table.
  'provider_id', 'p.hashed_email',  -- hashed_email column from the provider's table.
  'where_clause','c.status = $$MEMBER$$ AND c.age_band > 30' -- $$...$$ is used to stringify the column value.
  )
);
Copy

提供商模板示例代码

以下是完整的代码示例,展示了提供商如何添加自定义模板,以及使用者如何运行该模板。您需要两个安装了 Clean Room API 的独立账户来运行代码;一个账户充当提供商,另一个账户充当使用者。

使用者编写的自定义模板

如果提供商批准,使用者可以向 Clean Room 添加自定义模板。将使用者编写的模板添加到 Clean Room 后,可以像运行提供商编写的模板一样运行它。以下是使用者添加自定义模板的方式:

  1. 提供商以标准方式创建、共享和发布 Clean Room。

  2. 使用者以标准方式安装和配置 Clean Room。

  3. 使用者调用 consumer.create_template_request 并传入自定义模板字符串。

  4. 提供商调用 provider.list_pending_template_requests 查看待处理的请求。

  5. 提供商可以批准 (provider.approve_template_request) 或拒绝 (provider.reject_template_request) 使用者运行自己的模板的请求。(这些方法也有批量版本,用于批准或拒绝多个请求。)如果提供商批准,模板将立即添加到 Clean Room。

    • 如果提供商批准该模板,则提供商应为该模板及其数据添加任何必要的联接和列策略。如果提供商在 Clean Room 中没有联接或列策略,则默认设置为提供商的所有列均可联接,并且可以投影。

  6. 使用者通过调用 ``consumer.list_template_requests``(用于显示状态,包括拒绝状态和解释)或 ``consumer.view_added_templates``(用于查看模板是否已添加到 Clean Room)来检查其请求的状态。

  7. 使用者可以选择为模板添加列策略。

  8. 使用者通过标准方式调用 consumer.run_analysis 来运行模板。

备注

如果 使用者授予权限,则提供商可以运行使用者添加的模板。

使用者模板示例

以下是完整的代码示例,展示了使用者如何提交和运行自定义模板。将以下工作簿文件上传到您的 Snowflake 账户。您需要两个安装了 Clean Room API 的独立账户来运行代码;一个账户充当提供商,另一个充当使用者。

为自定义模板定义用户输入表单

为了使自定义模板可以在 Clean Room UI 中运行,提供商必须为该模板定义输入表单。即使模板没有可供使用者设置的实参,此要求也适用。使用者无法为模板定义用户输入表单。

重要

如果您使用 provider.restrict_table_options_to_consumersprovider.restrict_template_options_to_consumers 将表格或模板限制为仅供特定用户使用,则这些限制在 Clean Room UI 中无法按预期发挥作用。存在这些限制的情况下,您不应启用模板以供在 Clean Room UI 中使用。

配置表单允许 Clean Room UI 中的用户将值传递给自定义模板,类似于您在使用 API 时将值传递给模板的方式。

以下示例显示了使用三个变量(max_agefavorite_colorsource_table)的自定义模板:

CALL samooha_by_snowflake_local_db.provider.add_custom_sql_template(
  $cleanroom_name,
  'color_picker_template',
  $$
  SELECT p.hashed_email
    FROM source_table[0] AS p
    WHERE
      p.age <= {{ max_age }} AND
      UPPER(p.favorite_color) = UPPER({{ favorite_color }});
  $$);
Copy

以下示例显示了在代码中运行先前的自定义模板时,如何传递模板变量:

CALL samooha_by_snowflake_local_db.consumer.run_analysis(
  $cleanroom_name,
  'color_picker_template',
  [],                                   -- Consumer tables, assigned to my_table array.
  ['MYDB.MYSCH.COLOR_PREFERENCES'],     -- Provider tables, assigned to source_table array.
  object_construct(
    'max_age', 30,                      -- Assign max_age.
    'favorite_color', 'blue'            -- Assign favorite_color.
  )
);
Copy

要在 Clean Room UI 中运行此模板,必须定义一个表单,让使用者可以在其中分配这些模板变量。以下示例说明如何定义一个简单的表单,使用者可以在其中为 max_agefavorite_colorsource_table 分配值:

CALL samooha_by_snowflake_local_db.provider.add_ui_form_customizations(
    $cleanroom_name,
    'color_picker_template',
    {                                     -- Top-level template settings.
      'display_name': 'Color matcher',
      'description': 'See which users like the same color as you',
      'methodology': 'Choose a color and a max age',
      'render_table_dropdowns': {
        'render_consumer_table_dropdown': false,
        'render_provider_table_dropdown': true    -- Show a dropdown of provider tables.
      }                                           -- Chosen value is assigned to source_table.
    },
    { -- Form entry elements, one per template argument.
      'max_age': {
        'type': 'integer',
        'display_name': 'Maximum age',
        'description': 'Matching user must be less than or equal to this value.',
        'required': TRUE
      },
      'favorite_color': {
        'type': 'dropdown',
        'display_name': 'Favorite color',
        'description': 'Choose the favorite color to match.',
        'choices': ['Red', 'Blue', 'Green', 'Yellow'],
        'required': TRUE
      }
    },
    {} -- Output config not used in this example.
);

-- You must always call this procedure to propagate UI changes.
CALL samooha_by_snowflake_local_db.provider.create_or_update_cleanroom_listing(
  $cleanroom_name);
Copy

当使用者在 Configure Analysis & Query 步骤中运行模板时,先前定义的表单会出现在 Clean Room UI 中。该表单包括一个带 Collaborator table 标签的 source_table 表选择器、一个用于带 Maximum age 标签的 max_age 整数选择器元素,以及一个包含带 Favorite color 标签的 favorite_color 颜色名称的下拉菜单,如下图所示:

使用表、年龄和颜色选择器呈现的用户输入表单。

您还可以定义预填充来自提供商或使用者的联接策略、列策略、表等的列的下拉菜单。有关表单元素类型的更多信息,请参阅 provider.add_ui_form_customizations

填充 source_tablemy_table

标准 source_tablemy_table 模板变量可按如下方式填充:

  • 启用默认表选择器下拉菜单: 这些下拉菜单为单选菜单。您可以使用 render_provider_table_dropdownrender_consumer_table_dropdown 设置显示或隐藏它们。下拉菜单将完全限定表名分别传递给 source_tablemy_table 模板变量。

限定列名

大多数模板要求所有列名必须为完全限定形式,以避免列名出现歧义。

模板必须将所有表的别名设为 pc,具体取决于它们是提供商表还是使用者表。模板应使用所有列的 pc 别名引用它们。了解有关别名的更多信息。

如果创建下拉列选择器,则必须在下拉菜单的 choices 数组中明确提供 pc 表别名,或者必须在模板中添加别名。

以下示例显示如何在下拉菜单中提供表别名:

  'provider_join_col': {
    'display_name': 'Provider Join Column',
    'choices': ['p.HASHED_EMAIL', 'p.HASHED_SSN'],
    'type': 'dropdown',
    'description': 'Select the provider column to join users on.',
    'infoMessage': 'We recommend using HASHED_EMAIL.',
    'size': 'M',
    'group': 'Enable Provider Features'
}
Copy

但这种方法存在一定的局限性,因为您必须事先知道所有列名。

作为替代方案,您可以通过提供 references 属性来动态填充列下拉菜单。但这样的选择器返回的是裸列名,例如 hashed_email,而非完全限定列名,例如 p.hashed_email。如果返回的是裸列名,则必须在模板中明确地将列范围限定为表。例如,以下代码创建了一个下拉菜单,用户可以在其中从提供商的联接策略中选择一列:

'p_join_col': {
  'type': 'dropdown',
  'references': ['PROVIDER_JOIN_POLICY']
}
Copy

要在模板中使用列名,模板必须将表别名硬编码到列名前面,如以下示例所示:

SELECT p.{{ p_join_col | sqlsafe }} FROM table_col AS p;
Copy

关于开发可在 Clean Room UI 中运行的模板的建议

以下步骤显示了开发可在 Clean Room UI 中运行的模板的推荐工作流程:

1. 开发模板

首先,仅使用提供商和使用者账户中的 Clean Room API 来开发模板及其调用的任何 脚本。与使用 UI 相比,在 API 中测试模板要快得多,而且不易出错。

在 API 中的提供商和使用者端对您的模板进行 :emph:`全面`测试,以确保模板完全符合您的要求。API 中的测试速度极快,更改会立即传播到使用者账户。

测试模板并确保其可以完全按照您的要求运行后,即可推进到输入表单的设计。

2.开发输入表单

在模板和任何上传的脚本按预期运行时,则开始处理输入表单。在此阶段,您在提供商账户中使用 API,但在使用者账户中使用 UI。

使用 API 进行更改时,UI 中的一些值会立即刷新,有些值会在用户点击 Refresh 时刷新,有些值只会每 10 分钟刷新一次。因此,在处理输入表单时,在提供商端使用 API 创建和更新表单,但使用 Clean Room UI,而非 API 在使用者账户中安装和配置 Clean Room。这样可以确保您在 Clean Room UI 中使用新数据。

此外,每次更改 API 中的输入表单时,都要创建一个新的 Clean Room 以确保使用最新的 Clean Room 数据。在名称中使用递增的数字;例如,“My clean room 1”、“My clean room 2”等。然后,使用 UI 在客户端中安装 Clean Room。最后,删除旧的 Clean Room,因为账户可以容纳的 Clean Room 数量是有限的。

输入表单必须附加到模板,否则 Clean Room 和表单将无法在 Clean Room UI 中运行。开发表单时,可以考虑使用一个能够镜像回表单中选定的所有值的模板,这样就能验证向该模板发送了哪些值。

例如,假设您的生产模板看起来像下面这样:

SELECT {{ col1 | sqlsafe | column_policy }}, {{ col2 | sqlsafe | column_policy }}
  FROM IDENTIFIER({{ source_table[0] }}) AS p
  JOIN IDENTIFIER({{ my_table[0] }}) AS c
Copy

您可以创建以下模板,镜像回该生产模板的所有值:

SELECT
  {{ col1 | default('Undefined')}},
  {{ col2 | default('Undefined') }},
  {{ source_table[0] | default('Undefined') }},
  {{ my_table[0] | default('Undefined') }},
  {{ provider_join_col | default('Undefined') }},
  {{ consumer_join_col | default('Undefined') }}
;
Copy

然后设计一个设置这六个变量值的表单,并将该表单附加到镜像模板,而非生产模板。

开发输入表单的一般提示

以下列表提供了详细提示,可帮助您开发有效的输入表单:

  • 如果您在 UI 中安装、配置或运行 Clean Room 时遇到通用的“Installation failed”或“Something went wrong”消息,此类消息可能表示 UI 表单或关联模板存在错误,而在添加表单或模板时未发现该错误。

  • 当一个字段依赖于另一个字段时(例如,基于一个表下拉菜单中所选值的列下拉菜单),应该将父字段放在首位,可能的话可以置于子字段的正上方,这样用户就可以先填充父字段,然后再填充子字段。对于依赖字段,在为父字段选择值之前,子项下拉菜单为空。

  • 如果您未指定 ordergroup 值,则项目将按其定义的顺序呈现。

  • 包括信息性的 infoMessagedescription 文本,并显示用户可能输入的示例值。

  • 为变量数据类型选择精确的元素类型。例如,对于整数,请选择 integer 而非自由形式的文本框。您的模板可以使用 Jinja 筛选器来转换值;例如:SELECT {{ max_age | int }};

  • 如果您没有为自定义模板定义最低配置表单,则该模板无法在 Clean Room UI 中运行。

  • 如果您没有为模板中的变量定义表单元素,则系统会在用户表单中为该变量呈现一个纯文本框。这可能并不符合您的需求,因为文本框会带有模板变量名称标签,没有描述或建议。

  • 除非存在与元素同名的匹配模板变量,否则不会呈现 add_ui_form_customizations 中指定的表单元素。

  • 在 API 中进行的模板更改会快速可靠地传播到 UI,因此您无需为模板更改创建新的 Clean Room。但在您访问 UI 暂存区 API :emph: 之前,您应该开发并测试自己的模板。

  • 您不能使用给定表中的列值自动填充下拉菜单。您可以在下拉菜单中对值进行硬编码,但不能在运行时显示表中的值。

3.将输入表单连接到生产模板

当表单的外观完全符合您的要求,并且所有模板变量都可供用户访问后,您就可以在对 provider.add_ui_form_customizations 的调用中,将您的工作模板分配给输入表单。

语言: 中文