Entidades, propriedades e chaves

Observação: os desenvolvedores que criam novos aplicativos são fortemente incentivados a usar a Biblioteca de cliente do NDB, que oferece diversos benefícios em comparação com essa biblioteca de cliente, como armazenamento em cache automático de entidades por meio da Memcache API. Se você estiver usando a antiga biblioteca de cliente de DB, leia o Guia de migração de DB para NDB

Os objetos de dados no Cloud Datastore são conhecidos como entidades. Uma entidade tem uma ou mais propriedades nomeadas, que podem ter um ou mais valores. Entidades do mesmo tipo não precisam ter as mesmas propriedades. Além disso, os valores de uma entidade para uma determinada propriedade não precisam ser todos do mesmo tipo de dados. Se necessário, um aplicativo pode estabelecer e aplicar essas restrições no próprio modelo de dados.

O Cloud Datastore é compatível com uma grande variedade de tipos de dados para valores de propriedade. Eles incluem, entre outros:

  • números inteiros
  • números de ponto flutuante
  • strings
  • datas
  • dados binários

Para uma lista completa de tipos, consulte Propriedades e tipos de valor.

Cada entidade no Cloud Datastore tem uma chave que a identifica de maneira exclusiva. A chave consiste nos seguintes componentes:

  • O namespace da entidade permite a multilocação.
  • O tipo da entidade a categoriza para a finalidade de consultas do Cloud Datastore.
  • Um identificador da entidade individual, que pode ser:
    • uma string de nome da chave
    • um código numérico inteiro
  • Um caminho ancestral opcional localiza a entidade na hierarquia do Cloud Datastore.

Um aplicativo pode buscar uma entidade individual no Cloud Datastore ao usar a chave da entidade ou pode recuperar uma ou mais entidades ao emitir uma consulta com base nas chaves ou nos valores de propriedade das entidades.

O SDK do App Engine para Python inclui uma biblioteca de modelagem de dados para representar entidades do Cloud Datastore como instâncias de classes do Python e para armazenar e recuperar essas entidades no Datastore.

O Cloud Datastore em si não aplica nenhuma restrição sobre a estrutura das entidades, como por exemplo, se uma determinada propriedade tem um valor de um tipo específico. Essa tarefa é do aplicativo e da biblioteca de modelagem de dados.

Tipos e identificadores

Cada entidade do Cloud Datastore é de um determinado tipo, que categoriza a entidade para fins de consultas. Por exemplo, um aplicativo de recursos humanos representa cada funcionário de uma empresa com uma entidade do tipo Employee. Na Python Datastore API, o tipo de uma entidade é determinado pela classe de modelo, que você define no aplicativo como uma subclasse da classe de biblioteca de modelagem de dados db.Model. O nome da classe do modelo se torna o tipo de entidades pertencentes a ela. Todos os nomes de tipo que começam com dois sublinhados (__) são reservados e não podem ser usados.

Veja no exemplo a seguir como criar uma entidade do tipo Employee, preencher os valores de propriedade e a salvar no Datastore:

import datetime
from google.appengine.ext import db

class Employee(db.Model):
  first_name = db.StringProperty()
  last_name = db.StringProperty()
  hire_date = db.DateProperty()
  attended_hr_training = db.BooleanProperty()

employee = Employee(first_name='Antonio',
                    last_name='Salieri')

employee.hire_date = datetime.datetime.now().date()
employee.attended_hr_training = True

employee.put()

A classe Employee declara quatro propriedades para o modelo de dados: first_name, last_name, hire_date e attended_hr_training. A superclasse Model garante que os atributos de objetos Employee estejam em conformidade com esse modelo. Por exemplo, uma tentativa de atribuir um valor de string ao atributo hire_date resultaria em um erro de tempo de execução, porque o modelo de dados de hire_date foi declarado como db.DateProperty.

Além de um tipo, cada entidade tem um identificador, que é atribuído quando ela é criada. Como ele é parte da chave da entidade, o identificador é associado permanentemente à entidade e não pode ser alterado. Pode ser atribuído de duas formas:

  • O aplicativo pode especificar sua própria string de nome da chave para a entidade.
  • O Cloud Datastore pode atribuir automaticamente um código numérico inteiro à entidade.

Para atribuir uma entidade a um nome de chave, forneça o argumento nomeado key_name ao construtor da classe de modelo ao criar a entidade:

# Create an entity with the key Employee:'asalieri'.
employee = Employee(key_name='asalieri')

Para que o Cloud Datastore atribua um código numérico automaticamente, omita o argumento key_name:

# Create an entity with a key such as Employee:8261.
employee = Employee()

Como atribuir identificadores

O Cloud Datastore pode ser configurado para gerar códigos automaticamente usando duas políticas de identificação automática diferentes:

  • A política default gera uma sequência aleatória de códigos não utilizados que são distribuídos uniformemente de forma aproximada. Cada código pode ter até 16 dígitos decimais.
  • A política legacy cria uma sequência de códigos inteiros menores não consecutivos.

Se você quiser exibir os códigos de entidade para o usuário e/ou depender da ordem deles, a melhor coisa a fazer é usar a alocação manual.

O Cloud Datastore gera uma sequência aleatória de códigos não utilizados que são distribuídos de maneira quase uniforme. Cada código pode ter até 16 dígitos decimais.

Os valores de código atribuídos pelo sistema são garantidamente exclusivos para o grupo de entidades. Se você copiar uma entidade de um grupo de entidades ou namespace para outro e quiser preservar a parte do código da chave, primeiro atribua o código para impedir que o Cloud Datastore o selecione em uma atribuição futura.

Caminhos ancestrais

As entidades do Cloud Datastore formam um espaço hierarquicamente estruturado, semelhante à estrutura de diretórios de um sistema de arquivos. Ao criar uma entidade, é possível designar outra entidade como pai e a nova é a entidade filho (observe que, diferentemente do que ocorre em um sistema de arquivos, a entidade pai não existe de verdade). Uma entidade sem pai é uma entidade raiz. A associação entre uma entidade e a entidade pai é permanente e não pode ser alterada depois que a entidade é criada. O Cloud Datastore nunca atribuirá o mesmo código numérico a duas entidades com o mesmo pai ou a duas entidades raiz (sem pai).

O pai de uma entidade, o pai do pai, e assim por diante são ancestrais dela. O filho, o filho do filho, e assim por diante são descendentes dela. Uma entidade raiz e todos os descendentes pertencem ao mesmo grupo de entidades. A sequência de entidades começando com uma entidade raiz e procedendo de pai para filho, levando a uma determinada entidade, constitui o caminho ancestral dessa entidade. A chave completa que identifica a entidade consiste em uma sequência de pares de identificadores de tipo especificando seu caminho ancestral e terminando com os da própria entidade:

[Person:GreatGrandpa, Person:Grandpa, Person:Dad, Person:Me]

Para uma entidade raiz, o caminho ancestral está vazio, e a chave consiste unicamente no próprio tipo e identificador da entidade:

[Person:GreatGrandpa]

Esse conceito é ilustrado pelo seguinte diagrama:

Grupo de entidades

Para criar o pai de uma entidade, use o argumento parent para o construtor da classe de modelo ao criar a entidade filho. O valor desse argumento pode ser a própria entidade pai ou a chave. Você pode receber a chave chamando o método key() da entidade pai. O exemplo a seguir cria uma entidade do tipo Address e mostra duas maneiras de designar uma entidade Employee como o pai dela:

# Create Employee entity
employee = Employee()
employee.put()

# Set Employee as Address entity's parent directly...
address = Address(parent=employee)

# ...or using its key
e_key = employee.key()
address = Address(parent=e_key)

# Save Address entity to datastore
address.put()

Transações e grupos de entidades

Toda tentativa de criar, atualizar ou excluir uma entidade ocorre no contexto de uma transação. Uma única transação inclui qualquer número dessas operações. Para manter a consistência dos dados, a transação garante a aplicação de todas as suas operações no Cloud Datastore como uma unidade ou, em caso de falha de alguma operação, que nenhuma delas seja aplicada. Além disso, todas as leituras com consistência forte (consultas de ancestral ou operações de get), executadas na mesma transação, seguem um instantâneo consistente dos dados.

Como mencionado acima, um grupo de entidades é um conjunto de entidades conectadas por meio de um ancestral a um elemento raiz comum. A organização dos dados em grupos de entidades pode limitar quais transações podem ser realizadas:

  • Todos os dados acessados por uma transação devem estar contidos em, no máximo, 25 grupos de entidades.
  • Se você quiser usar consultas em uma transação, os dados precisam estar organizados em grupos de entidades para que você possa especificar filtros de ancestral que corresponderão aos dados corretos.
  • Há um limite de capacidade de gravação de aproximadamente uma transação por segundo em um único grupo de entidades. Essa limitação existe porque o Cloud Datastore executa a replicação síncrona sem mestre de cada grupo de entidades em uma ampla área geográfica para fornecer alta confiabilidade e tolerância a falhas.

Em muitos aplicativos, é aceitável usar a consistência eventual (ou seja, uma que não seja de ancestral abrangendo vários grupos de entidades, que às vezes pode retornar dados um pouco desatualizados) ao ter uma visualização ampla dos dados não relacionados. Além disso, usar a consistência forte (uma consulta de ancestral ou uma operação get para uma única entidade) ao visualizar ou editar um único conjunto de dados altamente relacionados. Em tais aplicativos, geralmente é uma boa abordagem usar um grupo de entidades separado para cada conjunto de dados altamente relacionados. Para mais informações, consulte Como estruturar uma consistência forte.

Propriedades e tipos de valores

Os valores de dados associados a uma entidade consistem em uma ou mais propriedades. Cada propriedade tem um nome e um ou mais valores. Uma propriedade pode ter valores de mais de um tipo, e duas entidades podem ter valores de tipos diferentes para a mesma propriedade. As propriedades podem ser indexadas ou não (consultas que ordenam ou filtram em uma propriedade P ignoram entidades em que P não seja indexada). Uma entidade pode ter no máximo 20.000 propriedades indexadas.

Estes são os tipos de valor compatíveis:

Tipo de valor Tipo(s) do Python Ordem de classificação Observações
Inteiro int
long
Numérica Número inteiro de 64 bits, assinado
Número de ponto flutuante float Numérica Precisão dupla de 64 bits,
IEEE 754
Booleano bool False<True
String de texto (curta) str
unicode
Unicode
(str tratado como ASCII)
Até 1.500 bytes
String de texto (longa) db.Text Nenhum Até 1 megabyte

Não indexado
String de bytes (curta) db.ByteString Ordem de bytes Até 1.500 bytes
String de bytes (longa) db.Blob Nenhum Até 1 megabyte

Não indexado
Data e hora datetime.date
datetime.time
datetime.datetime
Cronológica
Ponto geográfico db.GeoPt Por latitude
e depois por longitude
Endereço postal db.PostalAddress Unicode
Número de telefone db.PhoneNumber Unicode
Endereço de e-mail db.Email Unicode
Usuário das Contas do Google users.User Endereço de e-mail
em ordem Unicode
Identificador de mensagens instantâneas db.IM Unicode
Link db.Link Unicode
Categoria db.Category Unicode
Classificação db.Rating Numérico
Chave do Cloud Datastore db.Key Por elementos do caminho
(tipo, identificador,
tipo, identificador...)
Chave do Blobstore blobstore.BlobKey Ordem de bytes
Nulo NoneType Nenhum

Importante: é altamente recomendável não armazenar um UserProperty, porque ele inclui o endereço de e-mail e o código exclusivo do usuário. Se um usuário alterar o endereço de e-mail dele e você comparar o User anterior armazenado com o novo valor User, eles não coincidirão.

Para strings de texto e dados binários não codificados (strings de bytes), o Cloud Datastore aceita dois tipos de valor:

  • Strings curtas (até 1500 bytes) são indexadas e podem ser usadas em condições de filtro de consulta e ordens de classificação.
  • Strings longas (até 1 megabyte) não são indexadas e não podem ser usadas em filtros de consulta e ordens de classificação.
Observação: o tipo longo de string de bytes é denominado Blob na Cloud Datastore API. Esse tipo não está relacionado a blobs conforme usado na Blobstore API.

Quando uma consulta envolve uma propriedade com valores de tipos mistos, o Cloud Datastore usa uma ordem determinística baseada nas representações internas.

  1. valores nulos
  2. números de ponto fixo
    • números inteiros
    • datas e horas
    • classificações
  3. valores booleanos
  4. sequências de bytes
    • string de bytes
    • string Unicode
    • chaves do Blobstore
  5. números de ponto flutuante
  6. pontos geográficos
  7. usuários das Contas do Google
  8. chaves do Cloud Datastore

Como strings de texto e de bytes longas não são indexadas, elas não têm uma ordem definida.

Como trabalhar com entidades

Os aplicativos podem usar a Cloud Datastore API para criar, recuperar, atualizar e excluir entidades. Se o aplicativo souber a chave completa de uma entidade (ou puder derivá-la da própria chave mãe, do tipo e do identificador), ele poderá usá-la para operar diretamente na entidade. Um aplicativo também pode conseguir a chave de uma entidade por meio de uma consulta do Cloud Datastore. Consulte a página Consultas do Datastore para mais informações.

Como criar uma entidade

No Python, você cria uma nova entidade criando uma instância de uma classe de modelo, preenchendo as propriedades dela se necessário e chamando o método put() para salvá-la no Datastore. Você pode especificar o nome da chave da entidade passando um argumento key_name para o construtor:

employee = Employee(key_name='asalieri',
                    first_name='Antonio',
                    last_name='Salieri')

employee.hire_date = datetime.datetime.now().date()
employee.attended_hr_training = True

employee.put()

Se o nome de chave não for fornecido, o Cloud Datastore gerará automaticamente um código numérico para a chave da entidade:

employee = Employee(first_name='Antonio',
                    last_name='Salieri')

employee.hire_date = datetime.datetime.now().date()
employee.attended_hr_training = True

employee.put()

Como recuperar uma entidade

Para recuperar uma entidade identificada por uma determinada chave, passe o objeto Key como um argumento para a função db.get(). Você pode gerar o objeto Key usando o método de classe Key.from_path(). O caminho completo é uma sequência de entidades no caminho ancestral, com cada entidade representada pelo tipo (uma sequência) dela seguido do identificador (nome da chave ou código numérico):

address_k = db.Key.from_path('Employee', 'asalieri', 'Address', 1)
address = db.get(address_k)

db.get() retorna uma instância da classe de modelo apropriada. Verifique se você importou a classe de modelo para a entidade que está sendo recuperada.

Como atualizar uma entidade

Para atualizar uma entidade existente, modifique os atributos do objeto e chame o método put() dele. Os dados do objeto substituem a entidade existente. O objeto inteiro é enviado para o Cloud Datastore com cada chamada a put().

Para excluir uma propriedade, exclua o atributo do objeto do Python:

del address.postal_code

e salve o objeto.

Como excluir uma entidade

Dada a chave de uma entidade, você pode excluir a entidade com a função db.delete()

address_k = db.Key.from_path('Employee', 'asalieri', 'Address', 1)
db.delete(address_k)

ou chamando o método delete() da própria entidade:

employee_k = db.Key.from_path('Employee', 'asalieri')
employee = db.get(employee_k)

# ...

employee.delete()

Operações em lote

As funções db.put(), db.get() e db.delete() (e as contrapartes assíncronas delas db.put_async(), db.get_async() e db.delete_async()) podem aceitar um argumento de lista para atuar em várias entidades em uma única chamada do Cloud Datastore:

# A batch put.
db.put([e1, e2, e3])

# A batch get.
entities = db.get([k1, k2, k3])

# A batch delete.
db.delete([k1, k2, k3])

As operações em lote não alteram os custos. Você será cobrado por todas as chaves em uma operação em lote, independentemente de cada chave existir ou não. O tamanho das entidades envolvidas em uma operação não afeta o custo.

Como excluir entidades em massa por meio do console do GCP

No console do GCP, você pode excluir todas as entidades de um determinado tipo ou todas as entidades de todos os tipos no namespace padrão:

  1. Acesse a página Administrador do Cloud Datastore.
  2. Se você ainda não ativou a funcionalidade "Administrador" do Cloud Datastore, clique em Ativar Datastore Admin.
  3. Selecione os tipos de entidade que quer excluir.
  4. Clique em Excluir entidades. Observe que a exclusão em massa ocorre no aplicativo e, portanto, é descontada da cota.

Como usar uma lista vazia

Para a interface NDB, o Cloud Datastore escreveu historicamente uma lista vazia como uma propriedade omitida para propriedades estáticas e dinâmicas. Para manter a compatibilidade com versões anteriores, esse comportamento continua sendo o padrão. Para substituir isso globalmente ou segundo uma base ListProperty, defina o argumento write_empty_list como true na classe Property. A lista vazia é gravada no Cloud Datastore e pode ser lida como uma lista vazia.

Para a interface DB, as gravações de lista vazia não eram historicamente permitidas se a propriedade fosse dinâmica. Se tentasse isso, você receberia um erro. Isso significa que não há comportamento padrão que precise ser preservado para compatibilidade com versões anteriores das propriedades dinâmicas DB. Dessa maneira, basta escrever e ler a lista vazia no modelo dinâmico sem nenhuma alteração.

No entanto, para propriedades estáticas DB, a lista vazia foi gravada como uma propriedade omitida, e esse comportamento continua por padrão para compatibilidade com versões anteriores. Se você quiser ativar listas vazias para propriedades estáticas DB, use o argumento write_empty_list como true na classe Property. A lista vazia é gravada no Cloud Datastore.

Noções básicas sobre os custos de gravação

Quando o aplicativo executa uma operação put no Cloud Datastore, esse serviço precisa executar uma série de gravações para armazenar a entidade. O aplicativo é cobrado por cada uma dessas gravações. É possível ver quantas gravações serão necessárias para armazenar uma entidade verificando o visualizador de dados no console de desenvolvimento do SDK. Nesta seção, explicamos como esses custos de gravação são calculados.

Toda entidade exige o mínimo de duas gravações para armazenamento: uma para a própria entidade e outra para o índice EntitiesByKind incorporado, que é usado pelo planejador da consulta para atender a uma variedade de consultas. Além disso, o Cloud Datastore mantém dois outros índices incorporados: EntitiesByProperty e EntitiesByPropertyDesc. Eles fornecem verificações eficientes de entidades por valores de propriedade única em ordem crescente e decrescente, respectivamente. Cada um dos valores da propriedade indexada de uma entidade precisa ser gravado em cada um desses índices.

Por exemplo, pense em uma entidade com propriedades A, B e C:

Key: 'Foo:1' (kind = 'Foo', id = 1, no parent)
A: 1, 2
B: null
C: 'this', 'that', 'theOther'

Supondo que não existam índices compostos (veja abaixo) para entidades desse tipo, essa entidade requer 14 gravações para armazenamento:

  • um para a própria entidade
  • um para o índice EntitiesByKind
  • quatro para a propriedade A (dois para cada um dos dois valores)
  • dois para a propriedade B (um valor nulo ainda precisa ser gravado)
  • seis para a propriedade C (dois para cada um dos três valores)

Os índices compostos (os referentes a várias propriedades) exigem gravações adicionais para manter. Suponha que você defina o seguinte índice composto:

Kind: 'Foo'
A ▲, B ▼

em que os triângulos indicam a ordem de classificação das propriedades especificadas: crescente para a propriedade A e decrescente para a propriedade B. O armazenamento da entidade definida acima agora leva uma gravação adicional ao índice composto para cada combinação de valores A e B:

(1, null) (2, null)

Isso adiciona duas gravações para o índice composto, para um total de 1 + 1 + 4 + 2 + 6 + 2 = 16. Agora adicione a propriedade C ao índice:

Kind: 'Foo'
A ▲, B ▼, C ▼

O armazenamento da mesma entidade agora requer uma gravação para o índice composto de cada combinação possível de valores A, B e C:

(1, null, 'this') (1, null, 'that') (1, null, 'theOther')

(2, null, 'this') (2, null, 'that') (2, null, 'theOther')

Isso resulta no número total de gravações 1 + 1 + 4 + 2 + 6 + 6 = 20.

Se uma entidade do Cloud Datastore tiver muitas propriedades de diversos valores, ou se uma dessas propriedades for consultada muitas vezes, o número de gravações necessárias para manter o índice poderá explodir de maneira combinatória. Esses índices em explosão podem ser muito caros de manter. Por exemplo, pense em um índice composto que inclui ancestrais:

Kind: 'Foo'
A ▲, B ▼, C ▼
Ancestor: True

O armazenamento de uma entidade simples com este índice presente utiliza o mesmo número de gravações de antes. No entanto, se a entidade tiver ancestrais, será necessário uma gravação para cada combinação possível de valores de propriedade e ancestrais, além daquela para a própria entidade. Assim, uma entidade definida como

Key: 'GreatGrandpa:1/Grandpa:1/Dad:1/Foo:1' (kind = 'Foo', id = 1, parent = 'GreatGrandpa:1/Grandpa:1/Dad:1')
A: 1, 2
B: null
C: 'this', 'that', 'theOther'

exigiria uma gravação para o índice composto de cada uma das seguintes combinações de propriedades e ancestrais:

(1, null, 'this', 'GreatGrandpa') (1, null, 'this', 'Grandpa') (1, null, 'this', 'Dad') (1, null, 'this', 'Foo')

(1, null, 'that', 'GreatGrandpa') (1, null, 'that', 'Grandpa') (1, null, 'that', 'Dad') (1, null, 'that', 'Foo')

(1, null, 'theOther', 'GreatGrandpa') (1, null, 'theOther', 'Grandpa') (1, null, 'theOther', 'Dad') (1, null, 'theOther', 'Foo')

(2, null, 'this', 'GreatGrandpa') (2, null, 'this', 'Grandpa') (2, null, 'this', 'Dad') (2, null, 'this', 'Foo')

(2, null, 'that', 'GreatGrandpa') (2, null, 'that', 'Grandpa') (2, null, 'that', 'Dad') (2, null, 'that', 'Foo')

(2, null, 'theOther', 'GreatGrandpa') (2, null, 'theOther', 'Grandpa') (2, null, 'theOther', 'Dad') (2, null, 'theOther', 'Foo')

O armazenamento dessa entidade no Cloud Datastore agora requer 1 + 1 + 4 + 2 + 6 + 24 = 38 gravações.

Esta página foi útil? Conte sua opinião sobre:

Enviar comentários sobre…

Ambiente padrão do App Engine para Python