Uso de tradutores de solicitação e resposta com dados para um serviço remoto¶
Com tradutores de solicitação e resposta, você pode alterar o formato dos dados enviados e recebidos de serviços remotos utilizados por funções externas.
Neste tópico:
Objetivo¶
Quando o Snowflake envia dados para um serviço remoto, o Snowflake formata os dados de acordo com estas regras. Da mesma forma, quando o Snowflake recebe dados de um serviço remoto, o Snowflake espera que os dados estejam formatados de acordo com as mesmas regras.
Muitos serviços remotos esperam tratar dados em um formato diferente. Com tradutores de solicitação e resposta, você tem a conveniência de:
Converter dados do formato do Snowflake para o formato de entrada nativo do serviço remoto (tradutor de solicitação).
Converter dados do formato de saída nativo do serviço remoto para o formato do Snowflake (tradutor de resposta).
Implementação do SQL¶
Para traduzir dados entre o formato do Snowflake e o formato de entrada nativo do serviço remoto, você usa JavaScript UDFs (funções definidas pelo usuário). Você quase sempre escreve um par de UDFs: uma para traduzir a solicitação e outra para traduzir a resposta.
O Snowflake chama essas funções como parte de cada chamada de função externa. Por exemplo, para uma solicitação a um serviço remoto, o Snowflake chama a função do tradutor de solicitação, passa os dados no formato Snowflake, depois pega os dados retornados e os envia para o serviço remoto. Quando o serviço remoto retorna os dados, o Snowflake chama a função do tradutor de resposta para converter os dados de volta para o formato que o Snowflake entende.
Da perspectiva do usuário, chamar uma função externa quando um tradutor está convertendo é o mesmo que chamar uma função externa sem um tradutor. Depois de especificar os tradutores como parte da instrução CREATE EXTERNAL FUNCTION, eles são chamados automaticamente.
Uma função externa pode ter no máximo um tradutor de solicitação e um tradutor de resposta de cada vez.
As UDFs do tradutor de solicitação e resposta podem ser UDFs seguras.
Atribuição de uma função de tradutor a uma função externa¶
Para especificar qual função definida pelo usuário usar como tradutor, inclua cláusulas REQUEST_TRANSLATOR
e RESPONSE_TRANSLATOR
na instrução CREATE EXTERNAL FUNCTION
. Cada uma leva o nome da função do tradutor para usar durante a execução.
Por exemplo:
CREATE EXTERNAL FUNCTION f(...) RETURNS OBJECT ... REQUEST_TRANSLATOR = my_request_translator_udf RESPONSE_TRANSLATOR = my_response_translator_udf ... AS <url_of_proxy_and_resource>;
A sintaxe para especificar tradutores como parte de uma instrução CREATE EXTERNAL FUNCTION
é mostrada abaixo:
CREATE EXTERNAL FUNCTION f(...) RETURNS OBJECT ... [ REQUEST_TRANSLATOR = <request_translator_udf_name> ] [ RESPONSE_TRANSLATOR = <response_translator_udf_name> ] ...onde:
request_translator_udf_name
O nome da função do tradutor de solicitação.
response_translator_udf_name
O nome da função do tradutor de resposta.
Cada um dos parâmetros REQUEST_TRANSLATOR
e RESPONSE_TRANSLATOR
toma um parâmetro do tipo OBJECT.
Você também pode especificar um tradutor de solicitação ou resposta em um comando ALTER FUNCTION. Você pode:
Adicionar um tradutor se a função externa ainda não tiver um.
Substituir um tradutor existente.
Remover um tradutor.
Use a palavra-chave SET
para adicionar um novo tradutor ou para substituir um tradutor existente.
Para adicionar ou substituir um tradutor:
ALTER FUNCTION ... SET [REQUEST_TRANSLATOR | RESPONSE_TRANSLATOR] = <udf_name>;onde
udf_name
O nome de uma JavaScript UDF previamente criada.
Para remover um tradutor:
ALTER FUNCTION ... UNSET [REQUEST_TRANSLATOR | RESPONSE_TRANSLATOR];
Requisitos para o SQL¶
O nome da função do tradutor na instrução
CREATE EXTERNAL FUNCTION
ouALTER FUNCTION
deve ser uma das seguintes opções:Um nome qualificado (por exemplo, MyDatabase.MySchema.MyJavaScriptUDF).
Definido no mesmo banco de dados e esquema que a função externa que o utiliza.
Quando o tradutor é especificado em uma instrução
CREATE EXTERNAL FUNCTION
ouALTER FUNCTION
, a UDF do tradutor já deve existir. Você não pode especificar o nome primeiro e criar a UDF depois, mesmo se você não chamar a função externa antes de criar a UDF.Uma UDF usada como tradutor não deve ser descartada sem antes removê-la de todas as funções externas que a utilizam. (No momento em que a função externa é chamada, o Snowflake apresenta falha com um erro se o tradutor não existir).
Se a UDF do tradutor for modificada (via
ALTER FUNCTION
), ela deve manter os mesmos requisitos de interface. Se ela não mantiver os requisitos de interface, uma exceção é apresentada antes de executar a função externa.
Implementação do JavaScript¶
No momento da execução, o SQL passa um OBJECT para a UDF do tradutor. O código JavaScript recebe isso como um objeto JavaScript.
Implementação de um tradutor de solicitação¶
Propriedades de entrada do tradutor de solicitação¶
Uma UDF de tradutor recebe um objeto JavaScript chamado event
. O objeto contém as seguintes propriedades:
body
: o formato do campodata
é o mesmo que o do lote do conjunto de linhas do Snowflake existente (ou seja, uma matriz de linhas).Por exemplo,
{ "body": { "data": [ [0,"cat"], [1,"dog"] ] } }
Os dados existentes estão aninhados sob o corpo externo.
serviceUrl
: a URL definida da função externa a chamar.contextHeaders
: um objeto que contém todos os cabeçalhos relacionados ao contexto, em que os nomes são os nomes dos campos. Por exemplo, o objeto pode conter o nome de campo “SF_CONTEXT_CURRENT_DATABASE”, e o valor correspondente será uma cadeia de caracteres contendo o nome atual do banco de dados.
Propriedades de saída do tradutor de solicitação¶
O tradutor de solicitação retorna um objeto com campos utilizados para se comunicar com o gateway de API do serviço externo. Esse objeto tem três campos opcionais:
body
: define o corpo real a ser passado para o serviço. Se isso não for definido, não há corpo. O valorbody
deve ser uma cadeia de caracteres ou um objeto JSON no formato esperado pelo serviço remoto. Se o valor for uma cadeia de caracteres, essa cadeia pode conter estrutura interna (por exemplo, ser compatível com JSON). Se o valor for um objeto JSON, esse objeto é convertido em uma cadeia de caracteres para que possa ser incluído como parte da cadeia de comando HTTP POST.urlSuffix
: define o sufixo da URL de serviço, que é adicionado ao final do valorserviceUrl
. Esse sufixo também pode conter parâmetros de consulta. Os nomes e valores dos parâmetros devem ser codificados de URL. Por exemplo, se você quiser definir um parâmetro chamadoa
para o valormy param
, você precisa fazer a codificação de URL do caractere de espaço, então o parâmetro seria?a=my%20param
.translatorData
: passados do tradutor de solicitação para o tradutor de resposta. Esse campo pode passar informações de contexto, como o corpo de entrada, a URL de serviço ou o sufixo, ou cabeçalhos de contexto.
Todos os três campos são opcionais. Entretanto, como questão prática, a maioria dos tradutores de solicitação retornam pelo menos os dados do corpo.
Implementação de um tradutor de resposta¶
Propriedades de entrada do tradutor de resposta¶
O parâmetro de entrada para a função do tradutor de resposta é um objeto. O exemplo abaixo usa EVENT
, que contém duas propriedades:
body
: a resposta a ser decodificada a partir da resposta do serviço externo.translatorData
: se este campo for retornado pelo tradutor de solicitação, o Snowflake o passa para o tradutor de resposta.
Propriedades de saída do tradutor de resposta¶
A resposta do tradutor de resposta é retornada como um objeto sob o elemento body
; o formato é o formato da função externa existente (matriz de linhas). Por exemplo:
{ "body": { "data": [ [0, "Life"], [1, "the universe"], [2, "and everything"] ] } }
Requisitos para a função de tradutor¶
Cada UDF de tradutor deve atender aos seguintes requisitos:
Deve ser uma JavaScript UDF.
Deve tomar exatamente um parâmetro do tipo OBJECT, que representa um lote de linhas.
Deve retornar um valor do tipo OBJECT, que também representa um lote de linhas.
Deve ser uma UDF escalar (retornando uma linha para cada linha (OBJECT) passada).
Nota
Embora o tradutor seja escalar, o OBJECT passado para o tradutor pode ter (e normalmente tem) várias linhas embutidas dentro do JSON no OBJECT.
O número e a ordem das linhas (dentro do OBJECT) retornadas pela UDF do tradutor de resposta devem ser os mesmos que o número e a ordem das linhas passadas para a UDF do tradutor de solicitação (dentro do OBJECT).
Exemplo de tradutor de solicitação e tradutor de resposta¶
O exemplo a seguir mostra um tradutor de solicitação e um tradutor de resposta sendo usados para converter dados no formato exigido por um serviço externo que faz análise de sentimento, Amazon Comprehend BatchDetectSentiment. O tradutor de solicitação cria a solicitação HTTP para corresponder ao formato que o serviço de back-end espera.
Para utilizar tradutores, você precisará de um gateway de API. Este exemplo usa um gateway de API que já está configurado para se comunicar com o serviço de análise de sentimento. Para obter mais informações sobre como fazer a integração com um serviço da Amazon Web Services (AWS) como back-end, consulte Configuração de uma solicitação de integração de API usando o console do API Gateway na documentação da AWS.
É importante ter a integração de API funcionando corretamente antes de adicionar tradutores.
Configuração¶
Configure um banco de dados para armazenar dados de demonstração.
Escolha uma função que tenha permissão para criar funções externas:
USE ROLE ACCOUNTADMIN;
Especifique qual warehouse, banco de dados e esquema usar:
USE WAREHOUSE w;
USE DATABASE a;
USE SCHEMA b;
Crie uma tabela para armazenar suas frases de teste:
CREATE TABLE demo(vc varchar);
INSERT INTO demo VALUES('Today is a good day'),('I am feeling mopey');
Solicitação do corpo antes da tradução¶
Esta função externa não tem um tradutor de solicitação ou de resposta:
CREATE OR REPLACE EXTERNAL FUNCTION ComprehendSentiment(thought varchar)
RETURNS VARIANT
API_INTEGRATION = aws_comprehend_gateway
AS 'https://<MY_GATEWAY>.execute-api.us-east-1.amazonaws.com/test/comprehend_proxy';
Você pode chamar a função externa com seus dados de teste a partir da tabela de demonstração:
SELECT ComprehendSentiment(vc), vc FROM demo;
O corpo de solicitação gerado usa o formato de dados da função externa do Snowflake:
{"body":{"data:" [[0, "Today is a good day"],[1,"I am feeling mopey"]]}}
Entretanto, o serviço de análise de sentimento externo espera um formato diferente que especifique o idioma e uma matriz de cadeias de caracteres:
{"body": { Language: "en", TextList: [ "Today is a good day", "I am feeling mopey"]}}
A próxima seção descreve como você pode adicionar um tradutor de solicitação para mudar o corpo de solicitação para o formato exigido.
Converter o corpo de solicitação¶
Ao usar um tradutor de solicitação, você pode converter a entrada padrão descrita acima (no formato de dados do Snowflake) para o formato que o serviço externo exige.
O seguinte SQL cria uma função de tradutor awscomprehendrequest_translator
.
CREATE OR REPLACE FUNCTION AWSComprehendrequest_translator(EVENT OBJECT)
RETURNS OBJECT
LANGUAGE JAVASCRIPT AS
'
var textlist = []
for(i = 0; i < EVENT.body.data.length; i++) {
let row = EVENT.body.data[i];
// row[0] is the row number and row[1] is the input text.
textlist.push(row[1]); //put text into the textlist
}
// create the request for the service. Also pass the input request as part of the output.
return { "body": { "LanguageCode": "en", "TextList" : textlist }, "translatorData": EVENT.body }
';
Na função do tradutor de solicitação, o código:
Executa loops por cada uma das linhas de entrada. Para cada linha, adiciona a cadeia de caracteres, que está em
row[1]
, à matriztextlist
. O valor emrow[0]
é o número da linha e pode ser ignorado.Retorna um corpo JSON que possui o código do idioma e a lista de texto que corresponde aos requisitos do serviço externo.
Retorna os dados pelo campo
translatorData
. Isso é usado pelo tradutor de resposta. Neste exemplo, você está enviando os dados de entrada originais. Você usará o comprimento dos dados de entrada no tradutor de resposta para saber quantas solicitações de entrada foram feitas.
Você pode testar o tradutor de solicitação chamando-o diretamente.
SELECT AWSComprehendrequest_translator(parse_json('{"body":{"data": [[0, "I am so happy we got a sunny day for my birthday."], [1, "$$$$$."], [2, "Today is my last day in the old house."]]}}'));
O tradutor de solicitação coloca o corpo na forma esperada pelo serviço externo.
{"body":{
"LanguageCode": "en",
"TextList": [
"I am so happy we got a sunny day for my birthday.",
"$$$$$.",
"Today is my last day in the old house."
]
},
"translatorData": {
"data": [[0, "I am so happy we got a sunny day for my birthday."],
[1, "$$$$$."],
[2, "Today is my last day in the old house."]]
}
}
Corpo de resposta antes de adicionar um tradutor de resposta¶
Um corpo de resposta do serviço externo é semelhante a isto.
{"body":{
"ErrorList": [ { "ErrorCode": 57, "ErrorMessage": "Language unknown", "Index": 1} ],
"ResultList":[ { "Index": 0, "Sentiment": "POSITIVE",
"SentimentScore": { "Mixed": 25, "Negative": 5, "Neutral": 1, "Positive": 90 }},
{ "Index": 2, "Sentiment": "NEGATIVE",
"SentimentScore": { "Mixed": 25, "Negative": 75, "Neutral": 30, "Positive": 20 }}
]
}
}
Conversão do corpo de resposta¶
O tradutor de resposta processa os resultados que você recebe de volta do serviço externo. Os resultados contêm uma combinação de erros na ErrorList
e resulta na ResultList
.
O código do tradutor de resposta combina esses resultados para fazer um conjunto completo que corresponde à ordem das linhas que foram passadas para o serviço externo. O tradutor de resposta retorna os resultados no formato do Snowflake.
O seguinte SQL cria uma função de tradutor awscomprehendresponse_translator
.
CREATE OR REPLACE FUNCTION AWSComprehendresponse_translator(EVENT OBJECT)
RETURNS OBJECT
LANGUAGE JAVASCRIPT AS
'
// Combine the scored results and the errors into a single list.
var responses = new Array(EVENT.translatorData.data.length);
// output format: array of {
// "Sentiment": (POSITIVE, NEUTRAL, MIXED, NEGATIVE, or ERROR),
// "SentimentScore": <score>, "ErrorMessage": ErrorMessage }.
// If error, set ErrorMessage; otherwise, set SentimentScore.
// Insert good results into proper position.
for(i = 0; i < EVENT.body.ResultList.length; i++) {
let row = EVENT.body.ResultList[i];
let result = [row.Index, {"Sentiment": row.Sentiment, "SentimentScore": row.SentimentScore}]
responses[row.Index] = result
}
// Insert errors.
for(i = 0; i < EVENT.body.ErrorList.length; i++) {
let row = EVENT.body.ErrorList[i];
let result = [row.Index, {"Sentiment": "Error", "ErrorMessage": row.ErrorMessage}]
responses[row.Index] = result
}
return { "body": { "data" : responses } };
';
Na função do tradutor de resposta, o código:
Inicializa uma matriz chamada
responses
com o tamanho da entrada a partir do comprimento da matriztranslatorData
. Você envioutranslatorData
do tradutor de solicitação para o tradutor de resposta para passar a lista original das cadeias de caracteres de teste.Executa loops por cada um dos resultados sem erro e os coloca na lista de resultados.
Executa loops pelos resultados de erros e os coloca na lista de resultados. A lista de resultados tem uma posição de índice que informa cada registro. A ordem dos resultados produzidos deve corresponder à ordem de entrada. A lista de resultados também contém as informações sobre sentimentos.
Após todas as respostas terem sido coletadas, elas são retornadas em um corpo JSON no formato esperado pelo Snowflake.
O seguinte teste direto retornará um corpo JSON com o formato correto.
SELECT AWSComprehendresponse_translator(
parse_json('{
"translatorData": {
"data": [[0, "I am so happy we got a sunny day for my birthday."],
[1, "$$$$$."],
[2, "Today is my last day in the old house."]]
}
"body": {
"ErrorList": [ { "ErrorCode": 57, "ErrorMessage": "Language unknown", "Index": 1 } ],
"ResultList": [
{ "Index": 0, "Sentiment": "POSITIVE",
"SentimentScore": { "Mixed": 25, "Negative": 5, "Neutral": 1, "Positive": 90 }
},
{ "Index": 2, "Sentiment": "NEGATIVE",
"SentimentScore": { "Mixed": 25, "Negative": 75, "Neutral": 30, "Positive": 20 }
}
]
},
}'
)
);
Atribuição de tradutores à função externa¶
À função externa, adicione as funções de tradutor de solicitação e resposta, atribuindo os nomes das funções como valores aos parâmetros request_translator
e response_translator
. Dessa forma, eles serão chamados automaticamente quando a função externa for executada.
CREATE OR REPLACE EXTERNAL FUNCTION ComprehendSentiment(thought varchar)
RETURNS VARIANT
API_INTEGRATION = aws_comprehend_gateway
request_translator = db_name.schema_name.AWSComprehendrequest_translator
response_translator = db_name.schema_name.AWSComprehendresponse_translator
AS 'https://<MY_GATEWAY>.execute-api.us-east-1.amazonaws.com/test/comprehend_proxy';
Você pode descrever a função para obter informações sobre ela.
DESCRIBE FUNCTION ComprehendSentiment(VARCHAR);
Chamada da função externa¶
Teste a função externa chamando-a com uma única frase.
SELECT ComprehendSentiment('Today is a good day');
Você vê os resultados da análise de sentimento.
{"Sentiment": "POSITIVE",
"SentimentScore":{"Mixed":0.002436627633869648,
"Negative":0.0014803812373429537,
"Neutral":0.015923455357551575,
"Positive": 0.9801595211029053}}
Teste a função externa chamando-a com múltiplas frases. Use a mesma tabela demo
que você criou anteriormente.
SELECT ComprehendSentiment(vc), vc FROM demo;
Os resultados da análise de sentimento são exibidos.
Quando a função externa foi chamada, o tradutor de solicitação converteu automaticamente os dados no formato exigido pelo serviço externo. Então, o tradutor de resposta converteu automaticamente a resposta do serviço externo no formato exigido pelo Snowflake.
Dicas para testar tradutores de solicitação e resposta¶
Os valores dos casos de teste são geralmente valores de OBJECT (coleções de pares chave-valor). Estes devem ser formatados para atender aos requisitos destas regras.
Você pode começar a testar seu tradutor de solicitação ou tradutor de resposta passando um exemplo de entrada convertido em uma cadeia de caracteres. Por exemplo:
select my_request_translator_function(parse_json('{"body": {"data": [ [0,"cat",867], [1,"dog",5309] ] } }'));
(A entrada para
PARSE_JSON()
deve ser uma cadeia de caracteres formatada com JSON).Teste com valores
NULL
se apropriado.Inclua pelo menos um valor SQL
NULL
em seus casos de teste.Inclua pelo menos um valor JSON NULL em seus casos de teste.
A tradução de uma solicitação e a tradução de uma resposta são processos muitas vezes inversos. Conceitualmente:
my_response_translator_udf(my_request_translator_udf(x)) = x
Você pode usar essa característica para ajudar a testar seu tradutor de solicitação e tradutor de resposta se os formatos dos dados corresponderem. Crie uma tabela com bons valores de teste, depois execute um comando semelhante a:
SELECT test_case_column FROM test_table WHERE my_response_translator_udf(my_request_translator_udf(x)) != x;
A consulta não deve retornar nenhuma linha.
Note que traduzir uma solicitação e traduzir uma resposta nem sempre são processos exatamente inversos. Para um exemplo de situações em que eles podem não ser inversos, consulte a discussão de funções inversas na seção “Notas de uso” da documentação da função TO_JSON().