Observação: todos os exemplos podem ser encontrados aqui.
Hoje estamos lançando um Developer Preview (DP) muito especial de um novo recurso na próxima versão do Couchbase Server chamado Subdocumentojuntamente com o nosso Couchbase Server .NET SDK 2.2.6 normal! A API de subdocumento é um novo recurso do Couchbase Server que está disponível no Developer Preview do Couchbase Server 4.5.
Se você se lembra, no Couchbase todas as mutações de documentos são atômicas e envolvem o documento inteiro. Se você quiser alterar apenas um único campo e depois fazer uma atualização, todo o documento no servidor Couchbase será copiado pela nova revisão. O problema é que, se o documento for grande ou a rede for lenta (ou ambos), muitos recursos serão desperdiçados com o envio de dados que não foram modificados. Uma solução melhor e de melhor desempenho seria enviar apenas a parte do documento ou o valor que sofreu mutação. Essencialmente, é isso que você obtém com a API de subdocumento; quando você atualiza um elemento ou exclui um elemento de um documento, somente o caminho do fragmento a ser alterado é enviado pelo cabo e somente essa parte do documento é modificada.
Há várias operações diferentes que são suportadas pela API, desde mutações em elementos individuais aninhados ou subpara modificações em matrizes e dicionários. As operações de contador também são suportadas, assim como as operações de recuperação de fragmentos JSON incorporados.
A API é exposta por meio de uma interface fluente que permite anexar várias operações e executá-las atomicamente no documento. Há dois "construtores" diferentes: um construtor para operações de mutação e um construtor para leituras ou "pesquisas" e para verificar se um elemento existe em um determinado caminho.
IMPORTANTE: A API de subdocumentos é uma visualização para desenvolvedores!!!
Observe que esta é uma versão inicial da API de subdocumentos e não passou pelas verificações e balanços usuais pelos quais as APIs normais passam antes de uma versão. Além disso, com base no feedback dos usuários, as interfaces públicas podem ser alteradas em versões subsequentes, portanto, não é recomendável usar a API de subdocumentos na produção... ainda. No entanto, o restante da versão 2.2.6 foi testado e está pronto para uso em produção.
Pré-requisito: Couchbase Server 4.5.0 Developer Preview
Para seguir os exemplos abaixo, você precisará fazer o download e instalar Visualização para desenvolvedores do Couchbase Server 4.5. Vá, faça isso agora!
Visão geral do subdocumento API DP
Os exemplos a seguir usarão um documento com um ID de "puppy" e terão a seguinte aparência:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
{ "tipo": "cachorro", "raça": "Pitbull/Chihuahua", "name" (nome): "Filhote de cachorro", "brinquedos": [ "squeaker", "bola", "sapato" ], "proprietário": { "tipo": "servo", "name" (nome): "Don Knotts", "idade": 63 }, "atributos": { "pulgas": verdadeiro, "cor": "branco", "eyeColor" (cor dos olhos): "marrom", "idade": 5, "sujo": verdadeiro, "sexo": "feminino" } } |
Todos os exemplos estão disponíveis em Github para que você possa clonar o projeto e brincar com a API.
MutateInBuilder e LookupInBuilder
Conforme mencionado anteriormente, a API de subdocumento oferece dois novos tipos que utilizam um padrão de construtor por meio de uma interface fluente para encadear várias operações em um documento. Ambos os objetos são criados chamando Mutação
ou Pesquisa
em um CouchbaseBucket
e passando a chave ou o ID do documento com o qual você está trabalhando:
1 2 3 4 5 6 7 8 9 10 11 12 |
//Inicialize o auxiliar de cluster com as configurações padrão, ou seja, localhost Ajudante de cluster.Inicializar(); var balde = Ajudante de cluster.GetBucket("default"); //criar um construtor de mutação para o documento "thekey" var sofrer mutação = balde.Mutação("thekey"); //criar um construtor de pesquisa para o documento "thekey2" var pesquisa = balde.Pesquisa("thekey2"); Ajudante de cluster.Fechar(); |
Depois de ter o objeto construtor, você pode encadear várias operações para executar no documento, por exemplo:
1 2 3 4 5 6 |
var construtor = balde.Pesquisa(id). Obter("tipo"). Obter("name" (nome)). Obter("proprietário"). Existe("notfound"); |
Em seguida, você pode enviar todas as operações para o servidor em um único lote:
1 2 |
var fragmento = construtor.Executar(); |
E, em seguida, verifique o resultado de uma operação para o caminho tipo
:
1 2 3 4 5 6 |
se (fragmento.Status da operação("tipo") == Status da resposta.Sucesso) { string formato = "Path='{0}' Value='{1}'"; Console.WriteLine(formato, "tipo", fragmento.Conteúdo("tipo")); } |
O IDocumentFragment
Nome | Descrição |
---|---|
Conteúdo(...) | Obtém o conteúdo de um determinado caminho ou índice. |
Existe(...) | Retorna true se houver um resultado para um determinado caminho ou índice. |
Count() | O número de operações atuais mantidas pelo construtor. |
OpStatus(...) | O Status da resposta de uma operação em um determinado índice ou caminho. |
Status | O Status da resposta para toda a operação múltipla. |
Sucesso | Verdadeiro se toda a operação múltipla for bem-sucedida. |
Além dessas propriedades ou métodos, há todas as outras propriedades herdadas de Resultado da operação
que é a resposta padrão de uma operação de K/V: Upsert, Remove, Replace, etc.
Tratamento de erros
Ao enviar várias mutações, se uma delas falhar, toda a solicitação de várias operações falhará, permitindo uma semântica transacional do tipo tudo ou nada ao realizar mutações em um único documento. Ao enviar várias pesquisas, algumas operações podem ser bem-sucedidas e outras podem falhar, com o servidor tentando retornar tantos itens quantos forem solicitados. Se as operações falharem, a propriedade Status conterá uma resposta de erro de nível superior, como SubDocMultiPathFailure,
que é uma indicação para se aprofundar nos resultados da operação para obter o erro específico. Você pode fazer isso por iteração, chamando o método OpStatus e passando o índice ou o caminho:
1 2 3 4 5 6 7 8 9 10 11 12 |
var construtor = balde.Pesquisa(id). Obter("tipo"). Obter("somepaththatdoesntexist" (algum caminho que não existe)). Obter("proprietário"); var fragmento = construtor.Executar(); Console.WriteLine("Erro genérico: {0}{1}Erro específico: {2}", fragmento.Status, Meio ambiente.Nova linha, fragmento.Status da operação(1)); Console.WriteLine("Erro genérico: {0}{1}Erro específico: {2}", fragmento.Status, Meio ambiente.Nova linha, fragmento.Status da operação("somepaththatdoesntexist" (algum caminho que não existe))); |
Nesse caso, como o caminho não existia no documento, o erro específico retornado foi SubDocPathNotFound
. Há muitas combinações diferentes de erros, dependendo do tipo de construtor e da condição do erro - esta é uma breve introdução e deve ser adequada para quem está começando a usar a API.
Exemplos de LookupInBuilder
O LookUpInBuilder suporta duas operações: buscar um valor por caminho e verificar a existência de um valor em um determinado caminho.
Obter:
Vamos procurar o proprietário
fragmento por caminho
:
1 2 3 4 5 6 |
var construtor = balde.Pesquisa(id). Obter("proprietário"). Executar(); var proprietário = construtor.Conteúdo("proprietário"); |
O resultado é:
1 2 3 4 5 6 |
{ "tipo": "servo", "name" (nome): "Don Knotts", "idade": 63 } |
Existir:
Vamos verificar se o proprietário
caminho existe:
1 2 3 4 5 6 |
var construtor = balde.Pesquisa(id). Existe("proprietário"). Executar(); var encontrado = construtor.Conteúdo("proprietário"); |
A saída é verdadeiro
, o caminho proprietário
existe de fato no documento.
MutateInBuilder
O MutateInBuilder oferece vários métodos de suporte a mutações em valores escalares, dicionários e matrizes, além de suporte a operações de contador atômico.
Inserir:
Insert adiciona um valor a um dicionário, permitindo opcionalmente que o elemento que o contém (o próprio dicionário) seja adicionado:
1 2 3 4 |
var construtor = balde.Mutação(id). Inserir("attributes.hairLength", "curto"). Executar(); |
O dicionário de atributos do documento agora terá a seguinte aparência:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
... "atributos": { "pulgas": verdadeiro, "cor": "branco", "eyeColor" (cor dos olhos): "marrom", "idade": 5, "sujo": verdadeiro, "sexo": "feminino", "hairLength" (comprimento do cabelo): "curto" } ... |
Agora, se o elemento pai não existir, o createParents
pode ser usado para criar o elemento pai. Isso é verdadeiro por padrão, portanto você não precisa fazer nada - passe false se quiser falhar se o elemento pai não existir:
1 2 3 4 |
var construtor = balde.Mutação(id). Inserir("anewattribute.withakey", "somevalue" (algum valor)). Executar(); |
Isso criará o novo atributo chamado um novo atributo
no documento e adicione uma única chave chamada withakey
com um valor de algum valor
.
1 2 3 4 5 6 7 |
... "anewattribute": { "withakey": "somevalue" (algum valor) } ... |
Agora, se passarmos falso
para createParents
e o atributo pai não existisse, a mutação múltipla falharia com um status de resposta de nível superior de SubDocMultiPathFailure
e o erro específico seria SubDocPathNotFound
.
Upsert
Upsert adiciona ou substitui uma entrada existente no dicionário. O uso é exatamente o mesmo que Inserir
com exceção do nome do método que é Upsert
.
Remover
Remover
removerá um elemento em um determinado caminho. Como exemplo, removeremos o nome do proprietário do documento acima:
1 2 3 4 |
var fragmento = balde.Mutação(id). Remover("owner.name"). Executar(); |
E o documento depois chamado Remover
:
1 2 3 4 5 6 7 8 |
... "proprietário": { "tipo": "servo", "idade": 63 }, ... |
Substituir
Replace trocará o valor do elemento em um determinado caminho, falhando se o caminho não existir:
1 2 3 4 |
var fragmento = balde.Mutação(id). Substituir("proprietário", novo { Amante de gatos=verdadeiro, CatName="celia"}). Executar(); |
O documento agora terá um valor diferente para "proprietário":
1 2 3 4 5 6 7 8 |
... "proprietário": { "catLover": verdadeiro, "catName": "celia" }, ... |
PushBack
Adiciona um valor à parte de trás de uma matriz, adicionando opcionalmente o elemento pai (o próprio elemento da matriz) se ele não existir.
1 2 3 4 |
var fragmento = balde.Mutação(id). PushBack(caminho, valor, falso). Executar(); |
O brinquedos
no documento agora tem o valor "slipper" no último ordinal:
1 2 3 4 5 6 7 8 9 10 |
... "brinquedos": [ "squeaker", "bola", "sapato", "chinelo" ], ... |
PushFront
Adiciona um valor à frente de uma matriz, adicionando opcionalmente o elemento pai (a própria matriz) se ele não existir:
1 2 3 4 |
var fragmento = balde.Mutação(id). PushFront(caminho, valor, falso). Executar(); |
O brinquedos
agora tem o valor "slipper" em seu primeiro ordinal:
1 2 3 4 5 6 7 8 9 10 |
... "brinquedos": [ "chinelo", "squeaker", "bola", "sapato" ], ... |
ArrayInsert
Insere um valor em uma matriz em um determinado índice:
1 2 3 4 |
var fragmento = balde.Mutação(id). ArrayInsert("toys[2]", "chinelo"). Executar(); |
O brinquedos
A matriz agora tem o valor "slipper" em seu terceiro ordinal (índice 2):
1 2 3 4 5 6 7 8 |
"brinquedos": [ "squeaker", "bola", "chinelo", "sapato" ], |
AddUnique
Insere um valor em uma matriz, falhando se ele existir (o valor deve ser único dentro da matriz):
1 2 3 4 |
var fragmento = balde.Mutação(id). AddUnique("brinquedos", "sapato"). Executar(); |
Como o valor "shoe" já existe no documento original brinquedos
isso falhará com o seguinte status:
1 2 |
SubDocPathExists |
Observe que esse método só permite a inserção de primitivos JSON: cadeias de caracteres, números e valores especiais para true, false ou null. O motivo é que não há como comparar a exclusividade sem descer em cada objeto JSON e comparar os elementos item por item.
Balcão
Adiciona o delta especificado a um valor existente, criando o elemento se ele não existir e definindo como padrão o valor e o delta como 0. Se o delta for negativo, o valor do elemento será diminuído pelo delta fornecido.
1 2 3 4 |
var fragmento = balde.Mutação(id). Balcão("curtidas", 1). Executar(); |
Como o elemento não existe, ele será criado e, em seguida, definido como um (1). O documento agora terá a seguinte aparência:
1 2 3 4 5 |
... ], "curtidas": 1 } |
Se passarmos um valor negativo (-1), o contador para gostos
será decrementado de volta para zero (0):
1 2 3 4 |
var fragmento = balde.Mutação(id). Balcão("curtidas", -1). Executar(); |
E o documento JSON agora terá a seguinte aparência:
1 2 3 4 5 |
... ], "curtidas": 0 } |
Notas de versão da v2.2.6
Bug
- [NCBC-981] - Quando o FQDN é definido para a instância do Couchbase, o SSL falha
- [NCBC-1074] - A solicitação de exibição bloqueia indefinidamente se for esperada de forma síncrona
- [NCBC-1083] - PoolConfiguration ainda usa as configurações padrão quando substituídas
- [NCBC-1084] - ConfigurationSection ignora UseSsl
- [NCBC-1086] - GetAndLock não retorna o status de Bloqueado, mas expira quando o documento está bloqueado
Melhoria
- [NCBC-1070] - Fazer com que o QueryRequest não dependa do JSON.NET
- [NCBC-1082] - Adicionar suporte para sortCount em QueryResult.Metrics
- [NCBC-1085] - Aguardar retorno de chamada para que o thread em execução não seja bloqueado
- [NCBC-1090] - Correção de "Não é possível aguardar no corpo de uma cláusula catch" no SSL IO
Novo recurso
- [NCBC-998] - Incluir suporte para a API de subdocumentos - Parte 1: DP de vários comandos