프로젝션 쿼리

대부분의 Datastore 쿼리는 전체 항목을 결과로 반환하지만 실제로 애플리케이션이 관심을 갖는 항목의 속성은 몇 가지에 불과한 경우가 많습니다. 프로젝션 쿼리를 사용하면 실제로 필요한 항목의 특정 속성만 Datastore에 쿼리할 수 있으므로 전체 항목을 검색할 때보다 지연 시간과 비용이 낮아집니다.

프로젝션 쿼리는 SQL 쿼리 형식과 유사합니다.

SELECT name, email, phone FROM CUSTOMER

아래에 설명된 제한사항에 따라 표준 항목 쿼리에 사용할 수 있는 모든 필터링 및 정렬 기능을 사용할 수 있습니다. 쿼리는 지정된 속성(위 예시에서는 name, email, phone) 값만 채워진 요약된 결과를 반환하며 다른 모든 속성에는 데이터가 없습니다.

Python 2에서 프로젝션 쿼리 사용

다음 모델을 살펴보세요.

class Article(ndb.Model):
    title = ndb.StringProperty()
    author = ndb.StringProperty()
    tags = ndb.StringProperty(repeated=True)

다음과 같은 방식으로 프로젝션을 지정합니다.

def print_author_tags():
    query = Article.query()
    articles = query.fetch(20, projection=[Article.author, Article.tags])
    for article in articles:
        print(article.author)
        print(article.tags)
        # article.title will raise a ndb.UnprojectedPropertyError

결과를 반복하는 것과 같이 이러한 쿼리 결과를 표준 항목 쿼리와 동일하게 처리합니다.

for article in articles:
        print(article.author)
        print(article.tags)
        # article.title will raise a ndb.UnprojectedPropertyError

구조화된 속성에서 색인이 생성된 하위 속성을 프로젝션할 수 있습니다. 다음과 같은 프로젝션을 사용하여 구조화된 연락처의 address 속성 중 city 속성만 가져올 수 있습니다.

class Address(ndb.Model):
    type = ndb.StringProperty()  # E.g., 'home', 'work'
    street = ndb.StringProperty()
    city = ndb.StringProperty()
...
class Contact(ndb.Model):
    name = ndb.StringProperty()
    addresses = ndb.StructuredProperty(Address, repeated=True)
...
Contact.query().fetch(projection=["name", "addresses.city"])
Contact.query().fetch(projection=[Contact.name, Contact.addresses.city])

그룹화(실험용)

프로젝션 쿼리는 distinct 메서드를 사용하여 완전히 고유한 결과만 결과 집합으로 반환되도록 할 수 있습니다. 그러면 프로젝션되는 속성 값이 동일한 항목의 첫 번째 결과만 반환됩니다.

Article.query(projection=[Article.author], group_by=[Article.author])
Article.query(projection=[Article.author], distinct=True)

두 쿼리는 동등하며 각 작성자의 이름을 한 번만 생성합니다.

프로젝션의 제한사항

프로젝션 쿼리에는 다음과 같은 제한사항이 적용됩니다.

  • 색인이 생성된 속성만 프로젝션할 수 있습니다.

    명시적이든 암묵적이든 관계없이 색인이 생성되지 않은 속성에 대해서는 프로젝션이 지원되지 않습니다. 긴 텍스트 문자열(Text ) 및 긴 바이트 문자열(Blob)은 색인이 생성되지 않습니다.

  • 동일한 속성을 여러 번 프로젝션할 수 없습니다.

  • 균등(=) 필터 또는 멤버십(IN) 필터에서 참조된 속성을 프로젝션할 수 없습니다.

    예를 들면 다음과 같습니다.

    SELECT A FROM kind WHERE B = 1
    

    위의 쿼리에는 프로젝션된 속성이 일치 필터에 사용되지 않아 유효합니다.

    SELECT A FROM kind WHERE A > 1
    

    위의 쿼리에는 일치 필터가 사용되지 않았지만

    SELECT A FROM kind WHERE A = 1
    

    위의 쿼리는 프로젝션된 속성이 일치 필터에 사용되었으므로 유효하지 않습니다.

  • 프로젝션 쿼리에서 반환된 결과를 Datastore에 다시 저장할 수 없습니다.

    쿼리는 일부만 채워진 결과를 반환하므로 이 결과를 다시 Datastore에 쓸 수 없습니다.

프로젝션 및 다중 값 속성

다중 값을 가진 속성을 프로젝션하면 해당 속성의 값이 일부만 채워집니다. 대신 쿼리와 일치하는 프로젝션된 값의 고유한 조합마다 별도의 항목이 반환됩니다. 예를 들어 종류가 Foo이고 두 개의 다중 값 속성 AB가 있는 항목이 있다고 가정해 보겠습니다.

entity = Foo(A=[1, 1, 2, 3], B=['x', 'y', 'x'])

아래의 프로젝션 쿼리를 실행합니다.

SELECT A, B FROM Foo WHERE A < 3

그러면 다음의 값 조합이 있는 4개 항목을 반환합니다.

A = 1, B = 'x'
A = 1, B = 'y'
A = 2, B = 'x'
A = 2, B = 'y'

항목에 값이 없는 다중 값 속성이 있는 경우, 항목이 색인에 포함되지 않고 해당 항목에 대한 결과가 해당 속성을 포함하는 프로젝션 쿼리에서 반환되지 않습니다.

프로젝션 색인

프로젝션 쿼리를 사용하려면 프로젝션에 지정된 모든 속성이 Datastore 색인에 포함되어야 합니다. App Engine 개발 서버는 애플리케이션과 함께 업로드되는 색인 구성 파일 index.yaml에서 필요한 색인을 자동으로 생성합니다.

필요한 색인 수를 최소화하는 한 가지 방법은 모든 속성이 항상 필요하지 않더라도 동일한 속성을 일관적으로 프로젝션하는 것입니다. 예를 들어 다음 쿼리에는 별도의 색인 두 개가 필요합니다.

SELECT A, B FROM Kind
SELECT A, B, C FROM Kind

그러나 항상 A, B, C 속성을 프로젝션하면 C가 필요하지 않는 경우에도 한 개의 색인만 필요합니다.

기존 쿼리를 프로젝션으로 변환하려면 프로젝션의 속성이 쿼리의 다른 부분에 이미 포함되어 있지 않은 경우 새 색인을 빌드해야 할 수도 있습니다. 예를 들어, 다음과 같은 기존 쿼리가 있다고 가정해 보겠습니다.

SELECT * FROM Kind WHERE A > 1 ORDER BY A, B

이 쿼리에는 다음과 같은 색인이 필요합니다.

Index(Kind, A, B)

이를 다음 프로젝션 쿼리 중 하나로 변환합니다.

SELECT C FROM Kind WHERE A > 1 ORDER BY A, B
SELECT A, B, C FROM Kind WHERE A > 1 ORDER BY A, B

그러면 새 속성(C)이 도입되어 새로운 색인 Index(Kind, A, B, C)를 생성해야 합니다. 아래의 프로젝션 쿼리를 실행합니다.

SELECT A, B FROM Kind WHERE A > 1 ORDER BY A, B

이 쿼리의 경우 프로젝션된 속성 AB가 이미 기존 쿼리에 포함되어 있기 때문에 필수 색인을 변경하지 않습니다.