Utilizar o extrator definido pelo utilizador

U-SQL UDO: extrator definido pelo utilizador

O U-SQL permite-lhe importar dados externos com uma instrução EXTRACT. Uma instrução EXTRACT pode utilizar extratores UDO incorporados:

  • Extractors.Text(): fornece extração de ficheiros de texto delimitados de diferentes codificações.

  • Extractors.Csv(): fornece extração de ficheiros de valores separados por vírgulas (CSV) de diferentes codificações.

  • Extractors.Tsv(): fornece extração de ficheiros TSV (tab-separated value) de diferentes codificações.

Pode ser útil desenvolver um extrator personalizado. Isto pode ser útil durante a importação de dados se quisermos realizar qualquer uma das seguintes tarefas:

  • Modifique os dados de entrada ao dividir colunas e modificar valores individuais. A funcionalidade PROCESSADOR é melhor para combinar colunas.
  • Analise dados não estruturados, como páginas Web e e-mails, ou dados semiestruturados, como XML/JSON.
  • Analisar dados na codificação não suportada.

Como definir e utilizar o extrator definido pelo utilizador

Para definir um extrator definido pelo utilizador, ou UDE, precisamos de criar uma IExtractor interface. Todos os parâmetros de entrada para o extrator, como delimitadores de colunas/linhas e codificação, têm de ser definidos no construtor da classe. A IExtractor interface também deve conter uma definição para a substituição da IEnumerable<IRow> seguinte forma:

[SqlUserDefinedExtractor]
public class SampleExtractor : IExtractor
{
	 public SampleExtractor(string row_delimiter, char col_delimiter)
	 { … }

	 public override IEnumerable<IRow> Extract(IUnstructuredReader input, IUpdatableRow output)
	 { … }
}

O atributo SqlUserDefinedExtractor indica que o tipo deve ser registado como um extrator definido pelo utilizador. Esta classe não pode ser herdada.

SqlUserDefinedExtractor é um atributo opcional para a definição de UDE. Utilizado para definir a propriedade AtomicFileProcessing para o objeto UDE.

  • bool AtomicFileProcessing

  • true = Indica que este extrator necessita de ficheiros de entrada atómica (JSON, XML, ...)

  • false = Indica que este extrator pode lidar com ficheiros divididos/distribuídos (CSV, SEQ, ...)

Os principais objetos de programação UDE são entrada e saída. O objeto de entrada é utilizado para enumerar dados de entrada como IUnstructuredReader. O objeto de saída é utilizado para definir dados de saída como resultado da atividade do extrator.

Os dados de entrada são acedidos através de System.IO.Stream e System.IO.StreamReader.

Para enumeração de colunas de entrada, dividimos primeiro o fluxo de entrada com um delimitador de linhas.

foreach (Stream current in input.Split(my_row_delimiter))
{
…
}

Em seguida, divida ainda mais a linha de entrada em partes de coluna.

foreach (Stream current in input.Split(my_row_delimiter))
{
…
	string[] parts = line.Split(my_column_delimiter);
	foreach (string part in parts)
	{ … }
}

Para definir dados de saída, utilizamos o output.Set método .

É importante compreender que o extrator personalizado só produz colunas e valores definidos com o resultado. Definir chamada de método.

output.Set<string>(count, part);

O resultado real do extrator é acionado ao chamar yield return output.AsReadOnly();.

Segue-se o exemplo do extrator:

[SqlUserDefinedExtractor(AtomicFileProcessing = true)]
public class FullDescriptionExtractor : IExtractor
{
	 private Encoding _encoding;
	 private byte[] _row_delim;
	 private char _col_delim;

	public FullDescriptionExtractor(Encoding encoding, string row_delim = "\r\n", char col_delim = '\t')
	{
	     this._encoding = ((encoding == null) ? Encoding.UTF8 : encoding);
	     this._row_delim = this._encoding.GetBytes(row_delim);
	     this._col_delim = col_delim;

	}

	public override IEnumerable<IRow> Extract(IUnstructuredReader input, IUpdatableRow output)
	{
	     string line;
	     //Read the input line by line
	     foreach (Stream current in input.Split(_encoding.GetBytes("\r\n")))
	     {
		using (System.IO.StreamReader streamReader = new StreamReader(current, this._encoding))
		 {
		     line = streamReader.ReadToEnd().Trim();
		     //Split the input by the column delimiter
		     string[] parts = line.Split(this._col_delim);
		     int count = 0; // start with first column
		     foreach (string part in parts)
		     {
	if (count == 0)
			 {  // for column “guid”, re-generated guid
			     Guid new_guid = Guid.NewGuid();
			     output.Set<Guid>(count, new_guid);
			 }
			 else if (count == 2)
			 {
			     // for column “user”, convert to UPPER case
			     output.Set<string>(count, part.ToUpper());

			 }
			 else
			 {
			     // keep the rest of the columns as-is
			     output.Set<string>(count, part);
			 }
			 count += 1;
		     }

		 }
		 yield return output.AsReadOnly();
	     }
	     yield break;
	 }
}

Neste cenário de caso de utilização, o extrator regenera o GUID para a coluna "guid" e converte os valores da coluna "utilizador" em maiúsculas. Os extratores personalizados podem produzir resultados mais complicados ao analisar dados de entrada e manipulá-lo.

Segue-se o script U-SQL base que utiliza um extrator personalizado:

DECLARE @input_file string = @"\usql-programmability\input_file.tsv";
DECLARE @output_file string = @"\usql-programmability\output_file.tsv";

@rs0 =
	EXTRACT
        guid Guid,
        dt String,
        user String,
        des String
	FROM @input_file
        USING new USQL_Programmability.FullDescriptionExtractor(Encoding.UTF8);

OUTPUT @rs0 TO @output_file USING Outputters.Text();

Passos seguintes