메타데이터

참고: 새로운 애플리케이션을 빌드하는 개발자는 이 클라이언트 라이브러리보다 Memcache API를 통한 자동 항목 캐싱과 같은 여러 이점이 있는 NDB 클라이언트 라이브러리를 사용할 것을 적극 권장합니다. 현재 기존 DB 클라이언트 라이브러리를 사용하는 중이라면 DB에서 NDB로의 이전 가이드를 참조하세요.

Cloud Datastore는 일부 메타데이터에 프로그래매틱 액세스 기능을 제공하여 메타프로그래밍을 통해 백엔드 관리 기능을 구현하고 일관된 캐싱을 단순화합니다. 또한 유사한 목적, 예를 들면 애플리케이션을 대상으로 커스텀 Cloud Datastore 뷰어를 빌드하는 데에도 사용할 수 있습니다. 사용 가능한 메타데이터에는 항목 그룹, 네임스페이스, 항목 종류, 애플리케이션에서 사용하는 속성에 대한 정보와 각 속성의 속성 표현이 포함됩니다.

GCP Console의 Cloud Datastore 대시보드도 애플리케이션에 대한 메타데이터를 일부 제공하지만 여기에 표시되는 데이터는 이러한 기능에서 반환하는 결과와는 중요한 점에서 차이를 보입니다.

  • 최신 정보. API를 사용한 메타데이터 읽기는 최신 데이터를 가져오며 이때 대시보드의 데이터는 매일 한 번씩만 업데이트됩니다.
  • 콘텐츠. 대시보드 메타데이터 중 일부는 API를 통해 제공되지 않으며 반대의 경우도 마찬가지입니다.
  • 속도. 메타데이터 가져오기 및 쿼리는 Cloud Datastore 가져오기 및 쿼리와 동일한 방식으로 청구됩니다. 네임스페이스, 종류, 속성에 정보를 가져오는 메타데이터 쿼리는 일반적으로 느린 속도로 실행됩니다. 보통의 경우 N개의 항목을 반환하는 메타데이터 쿼리는 각각 단일 항목을 반환하는 N개의 일반 쿼리와 비슷한 시간이 걸리게 됩니다. 그뿐만 아니라 속성 표현 쿼리(키 전용이 아닌 속성 쿼리)는 키 전용 속성 쿼리보다 느립니다. 항목 그룹 메타데이터의 메타데이터 가져오기는 일반 항목 가져오기보다 다소 빠르게 실행됩니다.

도우미 함수

다음 함수는 메타데이터 정보를 가져옵니다.

  • get_entity_group_version()은 항목 그룹의 버전 번호를 가져옵니다. 이 함수는 마지막으로 버전 번호를 가져온 후에 그룹의 항목이 변경되었는지 확인하는 데 유용합니다.
  • get_namespaces()는 애플리케이션의 모든 네임스페이스의 이름 또는 지정한 범위의 네임스페이스의 이름이 포함된 목록을 반환합니다.
  • get_kinds()는 애플리케이션의 모든 항목 종류의 이름 또는 지정한 범위의 항목 종류의 이름이 포함된 목록을 반환합니다.
  • get_properties_of_kind()는 지정한 항목 종류와 연관된 애플리케이션의 모든 색인이 생성된 속성의 이름 또는 지정한 범위의 색인이 생성된 속성의 이름이 포함된 목록을 반환합니다. 색인이 생성되지 않은 속성은 포함되지 않습니다.
  • get_representations_of_kind()는 지정한 항목 종류와 연관된 애플리케이션의 모든 색인이 생성된 속성의 표현 또는 지정한 범위의 색인이 생성된 속성의 표현이 포함된 사전을 반환합니다. 이 사전은 각 속성의 이름을 해당 속성의 표현으로 이루어진 목록에 매핑합니다. 색인이 생성되지 않은 속성은 포함되지 않습니다.

항목 그룹 메타데이터

Cloud Datastore는 항목 그룹의 '버전'에 대한 액세스를 제공합니다. 버전은 항목 그룹이 변경될 때마다 반드시 증가하는 값이며 전적으로 양수입니다.

다음 예시는 항목 그룹의 버전을 가져오는 방법을 보여줍니다.

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)

이전 동작

이전 항목 그룹 버전 동작에서는 항목 그룹 버전이 항목 그룹 변경시에만 증가합니다. 예를 들어 이전 항목 그룹 메타데이터 동작은 항목 그룹에서 복잡한 상위 쿼리의 일관된 캐시를 유지하는 데 사용될 수 있습니다.

이 예시에서는 쿼리 결과(일치하는 결과 수)를 캐시하고 항목 그룹 버전의 이전 동작을 사용하여 캐시된 값이 최신 버전의 값일 때만 이 값을 사용합니다.

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()은 한 번도 기록되지 않은 항목 그룹에 대하여 None을 반환할 수 있습니다.

항목 그룹 버전은 __version__ 속성이 포함된 특별한 유사 항목에서 get()을 호출하여 가져올 수 있습니다. 자세한 내용은 EntityGroup에 대한 참조 문서를 확인하세요.

메타데이터 쿼리

이전 섹션에서 설명한 도우미 함수가 요구사항을 충족하지 않으면 명시적인 메타데이터 쿼리를 사용하여 보다 정교하거나 유연한 메타데이터 요청을 실행할 수 있습니다. Python에서는 이러한 쿼리의 모델 클래스가 google.appengine.ext.db.metadata 패키지에 정의되어 있습니다. 이러한 모델은 메타데이터 쿼리용으로 예약된 특수한 항목 종류를 제공합니다.

모델 클래스 항목 종류
Namespace __namespace__
Kind __kind__
Property __property__

이러한 모델과 종류는 이미 애플리케이션에 존재할 가능성이 있는 이름이 같은 다른 모델 및 종류와 충돌하지 않습니다. 이러한 특수한 종류를 쿼리하면 원하는 메타데이터가 포함된 항목을 검색할 수 있습니다.

메타데이터 쿼리에서 반환하는 항목은 Cloud Datastore의 현재 상태에 기반하여 동적으로 생성됩니다. Namespace, Kind, Property 모델 클래스의 로컬 인스턴스를 생성할 수 있지만, 이러한 인스턴스를 Cloud Datastore에 저장하려고 시도하면 BadRequestError 예외가 발생하고 실패합니다.

두 클래스 중 하나에 속한 쿼리 객체를 사용하여 메타데이터 쿼리를 실행할 수 있습니다.

  • 슈퍼클래스 메서드 Model.all()에서 상속된 클래스 메서드 Namespace.all(), Kind.all(), Property.all()에 의해 반환된 Query 객체
  • GQL 스타일 쿼리를 위한 GqlQuery 객체

다음 예는 애플리케이션의 모든 항목 종류의 이름을 반환합니다.

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

네임스페이스 쿼리

애플리케이션에서 네임스페이스 API를 사용하는 경우 네임스페이스 쿼리를 사용하여 애플리케이션의 항목에서 사용되는 모든 네임스페이스를 찾을 수 있습니다. 이 방법으로 여러 네임스페이스에 대한 관리 기능과 같은 작업을 수행할 수 있습니다.

네임스페이스 쿼리는 키 이름이 네임스페이스 이름인 특별한 __namespace__ 종류의 항목을 반환합니다. 단, 빈 문자열 ""로 지정된 기본 네임스페이스는 예외입니다. 빈 문자열이 유효한 키 이름이 아니기 때문에 이 네임스페이스에 숫자 ID 1의 키가 대신 지정됩니다. 이 유형의 쿼리는 값이 항목 키인 특수한 유사 속성 __key__의 범위에서만 필터링을 지원합니다. 결과는 __key__ 값의 오름차순으로 정렬할 수 있습니다(내림차순 불가). __namespace__ 항목에 속성이 없기 때문에 키 전용 및 키 전용이 아닌 쿼리가 모두 동일한 정보를 반환합니다.

네임스페이스 항목은 모델 클래스 google.appengine.ext.db.metadata.Namespace의 인스턴스입니다. 항목의 키에서 계산된 문자열 속성 namespace_name은 해당 네임스페이스의 이름을 반환합니다. 키에 숫자 ID 1이 있으면 이 속성은 빈 문자열을 반환합니다. 쿼리를 용이하게 하기 위해 Namespace 모델은 다음과 같은 클래스 메서드를 제공합니다.

예를 들어 다음은 도우미 함수 get_namespaces()의 구현입니다. 이 함수는 애플리케이션의 모든 네임스페이스의 이름(또는 두 개의 지정한 이름 startend 사이의 범위에 있는 이름)을 포함하는 목록을 반환합니다.

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]

종류 쿼리

종류 쿼리는 키 이름이 항목 종류의 이름인 __kind__ 종류의 항목을 반환합니다. 이 유형의 쿼리는 현재 네임스페이스에만 암시적으로 제한되어 있으며 __key__ 유사 속성을 범위로 하는 필터링만 지원합니다. 결과는 __key__ 값의 오름차순으로 정렬할 수 있습니다(내림차순 불가). __kind__ 항목에는 속성이 없기 때문에 키 전용 및 그 외의 쿼리 모두 동일한 정보를 반환합니다.

종류 항목은 모델 클래스 google.appengine.ext.db.metadata.Kind의 인스턴스입니다. 항목의 키에서 계산된 문자열 속성 kind_name은 해당하는 항목 종류의 이름을 반환합니다. 쿼리를 용이하게 하기 위해 Kind 모델은 다음과 같은 클래스 메서드를 제공합니다.

예를 들어 다음은 도우미 함수 get_kinds()의 구현입니다. 이 함수는 애플리케이션의 모든 항목 종류의 이름(또는 두 개의 지정한 이름 startend 사이의 범위에 있는 이름)을 포함하는 목록을 반환합니다.

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]

다음 예시는 이름이 소문자로 시작하는 모든 종류를 표시합니다.

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

속성 쿼리

속성 쿼리는 항목 종류와 연관된 속성을 나타내는 __property__ 종류의 항목을 반환합니다(이 속성이 현재 이 종류의 모델에 정의되어 있는지 여부와 관계없이). K 종류의 P 속성을 나타내는 항목은 다음과 같이 빌드됩니다.

  • 항목 키에는 __property__ 종류와 키 이름 P가 있습니다.
  • 상위 항목의 키에는 __kind__ 종류와 키 이름 K가 있습니다.

속성 항목은 모델 클래스 google.appengine.ext.db.metadata.Property의 인스턴스입니다. 항목의 키에서 계산된 문자열 속성 kind_nameproperty_name은 해당 종류 및 속성의 이름을 반환합니다. Property 모델은 __property__ 키의 빌드와 검사를 간소화하는 네 개의 클래스 메서드를 제공합니다.

다음 예시는 이러한 메서드를 보여줍니다.

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"

속성 쿼리의 동작은 아래 하위 섹션의 설명과 같이 키 전용 쿼리인지 또는 키 전용이 아닌(속성 표현) 쿼리인지에 따라 달라집니다.

속성 쿼리: 키 전용

키 전용 속성 쿼리는 지정된 항목 종류의 색인이 생성된 각 속성의 키를 반환합니다. (색인이 생성되지 않은 속성은 포함되지 않습니다.) 다음 예에서는 애플리케이션의 모든 항목 종류 및 각 종류와 연관된 속성의 이름을 보여줍니다.

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))

이 유형의 쿼리는 현재 네임스페이스로 암시적으로 제한되며, 유사 속성 __key__ 범위의 필터링만 지원합니다. 여기서 키는 __kind__ 또는 __property__ 항목을 의미합니다. 결과는 __key__ 값의 오름차순으로 정렬할 수 있습니다(내림차순 불가). 필터링은 종류-속성 쌍에 적용되며, 처음에는 종류에 따라 그리고 두 번째는 속성에 따라 정렬됩니다. 예를 들어 다음과 같은 속성을 가진 항목이 있다고 가정합니다.

  • Account 종류의 속성
    • balance
    • company
  • Employee 종류의 속성
    • name
    • ssn
  • Invoice 종류의 속성
    • date
    • amount
  • Manager 종류의 속성
    • name
    • title
  • Product 종류의 속성
    • description
    • price

속성 데이터를 반환하는 쿼리는 다음과 같습니다.

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))

위의 쿼리는 다음을 반환합니다.

Employee: ssn
Invoice: date
Invoice: amount
Manager: name

결과에 Employee 종류의 name 속성과 Manager 종류의 title 속성, 그리고 AccountProduct 종류의 속성이 포함되지 않습니다. 이러한 속성이 쿼리에 지정된 범위 밖에 있기 때문입니다.

속성 쿼리는 또한 __kind__ 또는 __property__ 키에서의 상위 필터링을 지원하여 쿼리 결과를 단일 종류 또는 속성으로 제한합니다. 다음 예와 같이 이 점을 사용하여, 예를 들면 지정된 항목 종류와 연관된 속성을 가져올 수 있습니다.

(도우미 함수 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]

속성 쿼리: 키 전용이 아닌 쿼리(속성 표현)

키 전용이 아닌 속성 쿼리(속성 표현 쿼리라고 함)는 각 종류-속성 쌍에 사용된 표현에 대한 추가 정보를 반환합니다. (색인이 생성되지 않은 속성은 포함되지 않습니다.) K 종류의 속성 P에 반환된 항목에는 속성의 표현을 반환하는 추가 property_representation 속성과 함께 해당 키 전용 쿼리와 동일한 키가 포함됩니다. 이 속성의 값은 K 종류의 항목에서 발견되는 P 속성의 각 표현에 대한 하나의 문자열을 포함하는 StringListProperty 클래스의 인스턴스입니다.

표현은 속성 클래스와 동일하지 않으며, 여러 속성 클래스가 동일 표현에 매핑될 수 있습니다. 예를 들어 StringPropertyPhoneNumberProperty는 모두 STRING 표현을 사용합니다.

다음 표는 속성 클래스와 해당 표현을 보여줍니다.

속성 클래스 표현
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 목록 요소의 표현
StringListProperty 목록 요소의 표현

예를 들어 다음은 도우미 함수 get_representations_of_kind()의 구현입니다. 이 함수는 지정한 항목 종류와 관련된 애플리케이션의 모든 색인이 생성된 속성의 표현(또는 두 개의 지정한 이름 startend 사이의 범위에 있는 표현)을 포함하는 사전을 반환합니다. 이 사전은 각 속성의 이름을 해당 속성의 표현으로 이루어진 목록에 매핑합니다.

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
이 페이지가 도움이 되었나요? 평가를 부탁드립니다.

다음에 대한 의견 보내기...

Python 2용 App Engine 표준 환경