Este blog anterior aborda os conceitos básicos da execução de instruções N1QL dentro de funções JavaScript.
Passamos agora para...
Iteradores/processamento de valores
Até agora, evitamos propositalmente declarações que retornam dados e que retornam dados da função.
É aqui que começamos a explorar a coleta de dados.
O N1QL() referenciada diretamente ou por meio da mágica do transpilador, retorna um identificador de declaração. O identificador vem na forma de um objeto JavaScript e tem um iterador associado, por exemplo:
1 |
var q = SELECT * DE b1; |
Não vou gastar muito tempo descrevendo os iteradores, portanto, aqui estão os conceitos básicos:
- Você pode obter o próximo valor de um iterador usando a função próximo() método.
- O feito A propriedade é verdadeira se não houver mais documentos a serem obtidos.
- Embora você possa usar a função próximo() e feito em um loop while para obter todos os documentos, uma construção mais legível é o para...de loop sobre o próprio identificador da declaração.
- Nem todos os valores precisam ser recuperados - você pode descartar uma declaração a qualquer momento chamando a função fechar() embora, como veremos mais adiante, você não precise fazer isso.
Juntando tudo:
e a função correspondente:
1 |
CRIAR FUNÇÃO doSelect() LÍNGUA JAVASCRIPT AS "doSelect" AT "udfblog"; |
Neste exemplo, uma matriz chamada res acumula os documentos que obtemos de b1 através do iterador qe, em seguida, é usado para devolvê-los ao chamador.
Algumas observações
Em termos gerais, provavelmente é uma ideia melhor processar os valores de consulta à medida que são recebidos no para em vez de apenas acumulá-los em uma matriz, pois isso tem implicações transitórias no uso da memória.
Por outro lado, o retorno de um valor de matriz de uma função permite que uma instrução selecione a função, por exemplo:
1 |
SELECT * DE doSelect() AS funcDocs; |
Isso é muito parecido com o que você faria com as funções de tabela ou tabelas derivadas de coleção que você encontrou no mundo relacional.
No mundo N1QL, eles são conhecidos como exames de expressão.
É claro que, se tudo o que você deseja que sua função faça é retornar alguns documentos de um Espaço-chave sem nenhuma lógica adicional, provavelmente é melhor escrever uma função inline.
De volta ao iterador!
Quando você terminar de processar os valores, o iterador se fechará automaticamente, portanto, não há nada que você precise fazer.
Se o iterador não for drenado, é uma boa prática fechá-lo, embora isso não seja estritamente necessário, pois ele será fechado automaticamente quando a função retornar, bem como em outras circunstâncias, como veremos mais adiante.
Dentro do para ... de ... é perfeitamente válido ter outras instruções N1QL: alguns aspectos a serem considerados serão descritos em seções posteriores.
Valores de processamento
Vamos tentar usar os valores retornados pelo select, em vez de apenas retorná-los:
1 2 3 4 5 6 7 8 9 |
função doSelect() { var q = SELECT * DE b1 LIMITE 2; var res = []; para (const doc de q) { deixar f1 = doc.f1; res.empurrar(f1); } retorno res; } |
Nada particularmente sofisticado, apenas extrair um campo do documento.
A execução dessa função produz:
1 2 3 4 5 6 |
[ [ nulo, nulo ] ] |
O que acontece? Por que ele não retorna? f1?
Podemos adivinhar a resposta executando:
1 |
SELECT * DE b1 LIMITE 2 |
E vendo, na interface do usuário:
1 2 3 4 5 6 7 8 9 10 11 12 |
[ { "b1": { "f1": 1 } }, { "b1": { "f1": 2 } } ] |
f1 é um campo em b1e não o documento selecionado!
Qualquer um deles faz o que você deseja:
1 2 3 4 5 6 7 8 9 |
função doSelect() { var q = SELECIONAR * DE b1 LIMITE 2; var res = []; para (const doc de q) { deixar f1 = doc.b1.f1; res.empurrar(f1); } retorno res; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
função doSelect() { var q = SELECIONAR bruto b1 DE b1 LIMITE 2; var res = []; para (const doc de q) { deixar f1 = doc.f1; res.empurrar(f1); } retorno res; } função doSelect() { var q = SELECIONAR f1 DE b1 LIMITE 2; var res = []; para (const doc de q) { deixar f1 = doc.f1; res.empurrar(f1); } retorno res; } |
Declarações DML
Considere a seguinte função:
1 2 3 4 |
função doCopy() { var q1 = INSERIR PARA b1 (CHAVE UUID(), VALOR v) SELECIONAR b1 AS v DE b1; var q2 = DELETE DE b1 ONDE COMPRIMENTO(meta().id) = 2; } |
1 |
CRIAR FUNÇÃO doCopy() LÍNGUA JAVASCRIPT AS "doCopy" AT "udfblog" |
Essa função cria novos documentos em b1 copiando-os de documentos existentes e removendo os documentos antigos.
O fato de que q1 e q2 são iteradores, mesmo quando as instruções não retornam valores, não deve ser uma grande surpresa: como veremos mais tarde, as instruções DML podem retornar valores e, pior ainda, o transpilador nem sempre pode determinar se os valores estão sendo retornados ou não, daí o N1QL() sempre retorna um iterador.
A implicação disso é que, uma vez que o N1QL() a instrução N1QL pode muito bem estar sendo executada de forma assíncrona em segundo plano, deixando a função JavaScript livre para executar outro código antes de buscar os dados.
O jsevaluator elimina qualquer ambiguidade comportamental forçando as instruções que não retornam valores a serem executadas imediatamente e em série, o que significa que ele aguardará a conclusão de cada instrução DML antes de iniciar a próxima instrução.
Nenhuma ação no iterador é necessária, nem mesmo fechá-lo.
Isso é consistente com o comportamento que esperamos instintivamente: queremos terminar de inserir uma cópia dos documentos existentes antes de começar a excluir os antigos - ou talvez não estejamos fazendo uma cópia de todos os documentos originais!
Declarações DML que retornam valores
Os comandos DML do N1QL podem ser feitos para retornar valores, como na função abaixo:
1 2 3 4 5 6 7 8 9 |
função doCopy() { var q1 = INSERIR PARA b1 (CHAVE UUID(), VALOR v) SELECIONAR b1 AS v DE b1 RETORNO meta().id, b1; var res = [] para (const doc de q1) { res.empurrar(doc); } var q2 = DELETE DE b1 ONDE COMPRIMENTO(meta().id) = 2; retorno res; } |
Os resultados retornados são processados da mesma forma que para SELECIONAR declarações.
A principal diferença em relação às instruções DML que não retornam resultados é que, quando nenhum resultado é retornado, todas as alterações no disco são feitas assim que a instrução retorna, ao passo que, quando os resultados são retornados, a instrução progride à medida que cada documento individual é coletado do iterador: você precisa processar todos os valores retornados para que todas as alterações sejam feitas no disco.
Mais uma vez, a instrução é executada de forma assíncrona; portanto, ao coletar cada resultado individual, você não deve presumir que essa é a última modificação feita no disco: outras podem já ter sido feitas, com os resultados relevantes apenas na fila esperando para serem coletados.
Assim como as instruções DML que não retornam resultados, as que retornam são executadas em série, o que significa que, se você não processar todos os resultados enfileirados no iterador até o momento em que iniciar a próxima instrução, o jsevaluator concluirá a instrução atual descartando todos os valores não processados.
Conclusão
Cobrimos o processamento de documentos por meio do N1QL e a execução da manipulação de dados.
Em seguida, veremos como lidar com erros.