Observação: os desenvolvedores que criam novos aplicativos são bastante incentivados a usar a Biblioteca de cliente NDB. Ela oferece diversos benefícios em comparação a esta biblioteca de cliente, como o armazenamento em cache automático de entidades por meio da API Memcache. Se você estiver usando a antiga biblioteca de cliente de DB, leia o Guia de migração de DB para NDB.
O Cloud Datastore fornece acesso programático a alguns dos metadados dele para oferecer suporte à metaprogramação, implementar funções administrativas de back-end, simplificar o cache consistente e propósitos similares. Você pode usá-lo, por exemplo, a fim de criar um visualizador personalizado do Cloud Datastore para o aplicativo. Nos metadados disponíveis, há informações sobre grupos de entidades, namespaces, tipos de entidade e propriedades utilizadas pelo aplicativo, além de representações de cada propriedade.
O Painel do Cloud Datastore no console do GCP também fornece alguns metadados sobre o aplicativo, mas os dados exibidos nele diferem em alguns aspectos importantes dos retornados por essas funções.
- Atualizações. A leitura de metadados pela API fornece dados atuais, enquanto os dados do painel são atualizados somente uma vez por dia.
- Conteúdo. Alguns metadados no painel não estão disponíveis através das APIs e vice-versa.
- Velocidade. Consultas e resultados de metadados são faturados da mesma maneira que consultas e resultados do Cloud Datastore. As consultas de metadados que buscam informações sobre namespaces, tipos e propriedades geralmente demoram a serem executadas. Como regra geral, uma consulta de metadados que retorne N entidades precisa levar mais ou menos o mesmo tempo que N consultas normais que retornem uma única entidade. Além disso, as consultas de representação de propriedade, que têm somente propriedades sem chave, são mais lentas do que as consultas somente de propriedades com chave. Os resultados de metadados do grupo de entidades são um pouco mais rápidos do que uma entidade normal.
Funções auxiliares
As seguintes funções recebem informações de metadados:
get_entity_group_version()
recebe um número de versão para um grupo de entidades. Ela é útil para descobrir se alguma entidade no grupo mudou desde a última vez que você recebeu o número da versão.get_namespaces()
retorna uma lista contendo os nomes de todos os namespaces de um aplicativo ou aqueles em um intervalo especificado.get_kinds()
retorna uma lista contendo os nomes de todos os tipos de entidade de um aplicativo ou aqueles em um intervalo especificado.get_properties_of_kind()
retorna uma lista contendo os nomes de todas as propriedades indexadas de um aplicativo ou de um intervalo especificado associadas a um determinado tipo de entidade. Propriedades não indexadas não estão incluídas.get_representations_of_kind()
retorna um dicionário contendo as representações de todas as propriedades indexadas de um aplicativo ou de um intervalo especificado associadas a um determinado tipo de entidade. O dicionário mapeia o nome de cada propriedade a uma lista das representações dessa propriedade. Propriedades não indexadas não estão incluídas.
Metadados do grupo de entidades
No Cloud Datastore é possível ter acesso à "versão" de um grupo de entidades, um número estritamente positivo que tem garantia de aumentar a cada alteração no grupo de entidades.
O seguinte exemplo mostra como receber a versão de um grupo de entidades:
from google.appengine.ext import db
from google.appengine.ext.db import metadata
class Simple(db.Model):
x = db.IntegerProperty()
entity1 = Simple(x=11)
entity1.put()
# Print entity1's entity group version
print 'version', metadata.get_entity_group_version(entity1)
# Write to a different entity group
entity2 = Simple(x=22)
entity2.put()
# Will print the same version, as entity1's entity group has not changed
print 'version', metadata.get_entity_group_version(entity1)
# Change entity1's entity group by adding a new child entity
entity3 = Simple(x=33, parent=entity1.key())
entity3.put()
# Will print a higher version, as entity1's entity group has changed
print metadata.get_entity_group_version(entity1)
Comportamento legado
No comportamento legado da versão do grupo de entidades, a versão aumenta apenas em alterações no grupo de entidades. O comportamento legado dos metadados do grupo de entidades poderia ser usado, por exemplo, para manter um cache consistente de uma consulta de ancestral complexa em um grupo de entidades.
Neste exemplo, armazenamos em cache os resultados da consulta (uma contagem de resultados correspondentes) e usamos o comportamento legado das versões do grupo de entidades para utilizar o valor em cache se ele for atual:
from google.appengine.api import memcache
from google.appengine.ext import db
from google.appengine.ext.db import metadata
def count_entity_group(entity_group_key):
"""Count the entities in the specified entity group."""
# Check if we have a cached version of the current entity group count
cached = memcache.get(str(entity_group_key))
if cached:
(version, count) = cached
# Is the cached value for the current version?
if version == metadata.get_entity_group_version(entity_group_key):
return count
def tx():
# Need to actually count entities. Using a transaction to get a consistent
# count and entity group version.
count = db.Query(keys_only=True).ancestor(entity_group_key).count(limit=5000)
# Cache the count and the entity group version
version = metadata.get_entity_group_version(entity_group_key)
memcache.set(str(entity_group_key), (version, count))
return count
return db.run_in_transaction(tx)
get_entity_group_version()
pode retornar None
para um grupo de entidades que jamais foi gravado.
As versões do grupo de entidades são recebidas chamando-se get()
em uma pseudoentidade especial que contém uma propriedade __version__
. Consulte a documentação de referência em EntityGroup para detalhes.
Consultas de metadados
Se as funções auxiliares descritas na seção anterior não atenderem às necessidades, você poderá emitir solicitações de metadados mais elaboradas ou flexíveis com uma consulta de metadados explícita. Em Python, as classes de modelo para essas consultas são definidas no pacote google.appengine.ext.db.metadata
. Esses modelos fornecem tipos de entidade especiais reservados para consultas de metadados:
Classe de modelo | Tipo de entidade |
---|---|
Namespace |
__namespace__ |
Kind |
__kind__ |
Property |
__property__ |
Esses modelos e tipos não entrarão em conflito com outros de nomes iguais que talvez já existam no aplicativo. Ao consultar esses tipos especiais, você pode recuperar entidades que contenham os metadados desejados.
As entidades retornadas por consultas de metadados são geradas dinamicamente, com base no estado atual do Cloud Datastore. Embora seja possível criar instâncias locais Namespace
, Kind
ou classes de modelo Property
, qualquer tentativa de armazená-las no Cloud Datastore falhará com uma exceção BadRequestError
.
É possível emitir consultas de metadados usando um objeto de consulta pertencente a uma de duas classes:
- Um objeto
Query
retornado pelo método de classeNamespace.all()
,Kind.all()
ouProperty.all()
, herdado do método de superclasseModel.all()
- Um objeto
GqlQuery
para consultas no estilo GQL
Veja no exemplo a seguir como retornar os nomes de todos os tipos de entidade em um aplicativo:
from google.appengine.ext import db
from google.appengine.ext.db.metadata import Kind
for k in Kind.all():
print "kind: '%s'" % k.kind_name
Consultas de namespace
Se o aplicativo opta pela API Namespaces, é possível utilizar uma consulta de namespace para encontrar todos os namespaces usados nas entidades do aplicativo. Isso permite que você execute atividades, como funções administrativas, em vários namespaces.
As consultas de namespace retornam entidades do tipo especial __namespace__
, cujo nome da chave é o nome de um namespace. Uma exceção é o namespace padrão designado pela string vazia ""
. Como a string vazia não é um nome de chave válido, o namespace usa o código numérico 1
como chave. Consultas desse tipo são compatíveis somente com filtragem de intervalos na pseudopropriedade especial __key__
, que tem a chave da entidade como valor. Os resultados podem ser classificados pelo valor crescente __key__
(mas não decrescente). Como as entidades __namespace__
não têm propriedades, tanto as consultas somente com chaves como as somente sem chaves retornam as mesmas informações.
As entidades de namespace são instâncias da classe de modelo google.appengine.ext.db.metadata.Namespace
. A propriedade da string namespace_name
computada da chave da entidade retorna o nome do namespace correspondente.
Caso a chave tenha um código numérico 1
, a propriedade retornará a string vazia. Para facilitar a consulta, o modelo Namespace
fornece os seguintes métodos de classe:
Namespace.key_for_namespace()
cria uma chave__namespace__
com base em um nome de namespace.Namespace.key_to_namespace()
retorna o nome do namespace correspondente a uma chave__namespace__
especificada.
Por exemplo, a implementação da função auxiliar get_namespaces()
retorna uma lista contendo os nomes de todos os namespaces de um aplicativo ou que estão entre dois nomes especificados (start
e end
):
from google.appengine.ext import db
from google.appengine.ext.db.metadata import Namespace
def get_namespaces(start=None, end=None):
# Start with unrestricted namespace query
q = Namespace.all()
# Limit to specified range, if any
if start is not None:
q.filter('__key__ >=', Namespace.key_for_namespace(start))
if end is not None:
q.filter('__key__ <', Namespace.key_for_namespace(end))
# Return list of query results
return [ns.namespace_name for ns in q]
Consultas de tipo
As consultas de tipo retornam entidades do tipo __kind__
, e nome da chave é o nome de um tipo de entidade. As consultas desse tipo estão implicitamente restritas ao namespace atual e são compatíveis somente com filtragem de intervalos acima da pseudopropriedade __key__
. Os resultados podem ser classificados pelo valor crescente (mas não decrescente) __key__
. Como as entidades __kind__
não têm propriedades, tanto as consultas somente com chaves como as somente sem chaves retornam as mesmas informações.
As entidades de tipo são instâncias da classe de modelo google.appengine.ext.db.metadata.Kind
.
A propriedade de string kind_name
, calculada com base na chave da entidade, retorna o nome do tipo de entidade correspondente. Para facilitar a consulta, o modelo Kind
fornece os seguintes métodos de classe:
Kind.key_for_kind()
cria uma chave__kind__
com base em um nome de tipo.Kind.key_to_kind()
retorna o nome do tipo correspondente a uma chave__kind__
especificada.
Por exemplo, esta é a implementação da função auxiliar get_kinds()
, que retorna uma lista contendo os nomes de todos os tipos de entidade de um aplicativo ou que estão entre dois nomes especificados (start
e end
):
from google.appengine.ext import db
from google.appengine.ext.db.metadata import Kind
def get_kinds(start=None, end=None):
# Start with unrestricted kind query
q = Kind.all()
# Limit to specified range, if any
if start is not None and start != '':
q.filter('__key__ >=', Kind.key_for_kind(start))
if end is not None:
if end == '':
return [] # Empty string is not a valid kind name, so can't filter
q.filter('__key__ <', Kind.key_for_kind(end))
# Return list of query results
return [k.kind_name for k in q]
O exemplo a seguir imprime todos os tipos com nomes que começam com uma letra minúscula:
from google.appengine.ext import db
from google.appengine.ext.db.metadata import Kind
# Start with unrestricted kind query
q = Kind.all()
# Limit to lowercase initial letters
q.filter('__key__ >=', Kind.key_for_kind('a'))
endChar = chr(ord('z') + 1) # Character after 'z'
q.filter('__key__ <', Kind.key_for_kind(endChar))
# Print query results
for k in q:
print k.kind_name
Consultas de propriedade
As consultas de propriedade retornam entidades do tipo __property__
indicando as propriedades associadas a um tipo de entidade, independentemente de estarem ou não definidas no modelo do tipo. A entidade que representa a propriedade P do tipo K é criada da seguinte forma:
- A chave da entidade tem o tipo
__property__
e o nome de chave P. - A chave da entidade pai tem o tipo
__kind__
e o nome K.
As entidades de propriedade são instâncias da classe de modelo google.appengine.ext.db.metadata.Property
. As propriedades de string kind_name
e property_name
, calculadas com base na chave da entidade, retornam o nome do tipo de entidade correspondente. O modelo Property
fornece quatro métodos de classe para simplificar a criação e o exame de chaves __property__
:
Property.key_for_kind()
cria uma chave pai__kind__
para as chaves__property__
de um tipo de entidade especificado.Property.key_for_property()
cria uma chave__property__
para um tipo e uma propriedade especificados.Property.key_to_kind()
retorna o nome do tipo associado a uma chave__property__
.Property.key_to_property()
retorna o nome da propriedade associado a uma chave__property__
ouNone
se a chave especificar apenas um tipo.
O exemplo a seguir ilustra esses métodos:
from google.appengine.ext import db
from google.appengine.ext.db.metadata import Property
class Employee(db.Model):
name = db.StringProperty()
ssn = db.IntegerProperty()
employee_key = Property.key_for_kind("Employee")
employee_name_key = Property.key_for_property("Employee", "Name")
Property.key_to_kind(employee_key) # Returns "Employee"
Property.key_to_property(employee_name_key) # Returns "Name"
O comportamento de uma consulta de propriedade depende do fato de ela ser uma consulta somente com chave ou somente sem chave (representação de propriedade), conforme detalhado nas subseções a seguir.
Consultas de propriedade: somente com chaves
As consultas de propriedades somente com chaves retornam uma chave para cada propriedade indexada de um tipo de entidade especificado. As propriedades não indexadas não estão incluídas. Veja no exemplo a seguir como imprimir os nomes de todos os tipos de entidade de um aplicativo e as propriedades associadas a cada um deles:
from google.appengine.ext import db
from google.appengine.ext.db.metadata import Property
# Create unrestricted keys-only property query
q = Property.all(keys_only=True)
# Print query results
for p in q:
print "%s: %s" % (Property.key_to_kind(p), Property.key_to_property(p))
As consultas desse tipo estão implicitamente restritas ao namespace atual e são compatíveis somente com filtragem de intervalos na pseudopropriedade __key__
, em que as chaves indicam as entidades __kind__
ou __property__
. Os resultados podem ser classificados pelo valor crescente __key__
(mas não decrescente). A filtragem é aplicada a pares de propriedade-tipo, classificados primeiro por tipo e depois por propriedade. Por exemplo, suponha que você tenha uma entidade com estas propriedades:
- tipo
Account
, com as propriedadesbalance
company
- tipo
Employee
, com as propriedadesname
ssn
- tipo
Invoice
, com as propriedadesdate
amount
- tipo
Manager
, com as propriedadesname
title
- tipo
Product
, com as propriedadesdescription
price
A consulta para retornar os dados da propriedade tem esta aparência:
from google.appengine.ext import db
from google.appengine.ext.db.metadata import Property
# Start with unrestricted keys-only property query
q = Property.all(keys_only=True)
# Limit range
q.filter('__key__ >=', Property.key_for_property("Employee", "salary"))
q.filter('__key__ <=', Property.key_for_property("Manager", "salary"))
# Print query results
for p in q:
print "%s: %s" % (Property.key_to_kind(p), Property.key_to_property(p))
A consulta acima apresenta o seguinte resultado:
Employee: ssn
Invoice: date
Invoice: amount
Manager: name
Nos resultados, não há a propriedade name
do tipo Employee
e a propriedade title
do tipo Manager
nem outras propriedades dos tipos Account
e Product
, porque elas estão fora do intervalo especificado para a consulta.
As consultas de propriedade também são compatíveis com a filtragem de ancestrais em uma chave __kind__
ou __property__
para limitar os resultados a um único tipo ou propriedade. Você pode usar isso, por exemplo, para ver as propriedades associadas a um determinado tipo de entidade, como no exemplo a seguir:
get_properties_of_kind()
from google.appengine.ext import db
from google.appengine.ext.db.metadata import Property
def get_properties_of_kind(kind, start=None, end=None):
# Start with unrestricted keys-only property query
q = Property.all(keys_only=True)
# Limit to specified kind
q.ancestor(Property.key_for_kind(kind))
# Limit to specified range, if any
if start is not None and start != '':
q.filter('__key__ >=', Property.key_for_property(kind, start))
if end is not None:
if end == '':
return [] # Empty string is not a valid property name, so can't filter
q.filter('__key__ <', Property.key_for_property(kind, end))
# Return list of query results
return [Property.key_to_property(p) for p in q]
Consultas de propriedade: somente sem chaves (representação de propriedade)
As consultas de propriedade somente sem chaves, conhecidas como consultas de representação de propriedade, retornam mais informações sobre as representações usadas por cada par de propriedade-tipo. As propriedades não indexadas não estão incluídas. A entidade retornada para a propriedade P do tipo K tem a mesma chave de uma consulta somente com chave correspondente com uma propriedade property_representation
extra que retorna as representações da propriedade. O valor dessa propriedade é uma instância da classe StringListProperty
que contém uma string para cada representação da propriedade P encontrada em entidades do tipo K.
Representações e classes de propriedades não são iguais. Várias classes de propriedades podem ser mapeadas para a mesma representação. Por exemplo, StringProperty
e PhoneNumberProperty
usam a representação STRING
.
Veja na tabela a seguir o mapeamento das classes de propriedades para as respectivas representações:
Classe de propriedade | Representação |
---|---|
IntegerProperty |
INT64 |
FloatProperty |
DOUBLE |
BooleanProperty |
BOOLEAN |
StringProperty |
STRING |
ByteStringProperty |
STRING |
DateProperty |
INT64 |
TimeProperty |
INT64 |
DateTimeProperty |
INT64 |
GeoPtProperty |
POINT |
PostalAddressProperty |
STRING |
PhoneNumberProperty |
STRING |
EmailProperty |
STRING |
UserProperty |
USER |
IMProperty |
STRING |
LinkProperty |
STRING |
CategoryProperty |
STRING |
RatingProperty |
INT64 |
ReferenceProperty SelfReferenceProperty |
REFERENCE |
blobstore.BlobReferenceProperty |
STRING |
ListProperty |
Listar a representação do elemento |
StringListProperty |
Listar a representação do elemento |
Por exemplo, esta é a implementação da função auxiliar get_representations_of_kind()
, que retorna um dicionário contendo as representações de todas as propriedades indexadas de um aplicativo ou que estão entre dois nomes especificados (start
e end
) associadas a um determinado tipo de entidade. O dicionário mapeia o nome de cada propriedade e cria uma lista das representações dessa propriedade.
from google.appengine.ext import db
from google.appengine.ext.db.metadata import Property
def get_representations_of_kind(kind, start=None, end=None):
# Start with unrestricted non-keys-only property query
q = Property.all()
# Limit to specified kind
q.ancestor(Property.key_for_kind(kind))
# Limit to specified range, if any
if start is not None and start != '':
q.filter('__key__ >=', Property.key_for_property(kind, start))
if end is not None:
if end == '':
return [] # Empty string is not a valid property name, so can't filter
q.filter('__key__ <', Property.key_for_property(kind, end))
# Initialize result dictionary
result = {}
# Add query results to dictionary
for p in q:
result[p.property_name] = p.property_representation
# Return dictionary
return result