Metadados

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 classe Namespace.all(), Kind.all() ou Property.all(), herdado do método de superclasse Model.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:

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:

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__:

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 propriedades
    • balance
    • company
  • tipo Employee, com as propriedades
    • name
    • ssn
  • tipo Invoice, com as propriedades
    • date
    • amount
  • tipo Manager, com as propriedades
    • name
    • title
  • tipo Product, com as propriedades
    • description
    • 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:

Uma implementação da função auxiliar 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
Esta página foi útil? Conte sua opinião sobre:

Enviar comentários sobre…

Ambiente padrão do App Engine para Python 2