여러 속성에서 범위 및 불일치 필터를 사용하여 쿼리 최적화

이 페이지에서는 효율적인 쿼리 경험을 만들기 위해 여러 필드에 범위 및 불일치 필터가 있는 쿼리에 사용해야 하는 색인 생성 전략의 예시를 제공합니다.

쿼리를 최적화하기 전에 여러 속성의 범위 및 불일치 필터 개념에 대해 알아보세요.

Query Explain으로 쿼리 최적화

사용된 쿼리와 색인이 최적인지 확인하려면 Query Explain을 사용하여 쿼리를 만들고 실행 요약을 검토하면 됩니다.

Java

...
// Build the query
Query<Entity> query =
    Query.newEntityQueryBuilder()
        .setKind("employees")
        .setFilter(
            CompositeFilter.and(
                PropertyFilter.gt("salary", 100000), PropertyFilter.gt("experience", 0)))
        .setOrderBy(OrderBy("experience"), OrderBy("salary"))
        .build();

// Set the explain options to get back *only* the plan summary
ExplainResults<Entity> explainResults = datastore.run(query, ExplainOptions.newBuilder().build());

// Get the explain metrics
Optional<ExplainMetrics> explainMetrics = results.getExplainMetrics();
if (!explainMetrics.isPresent()) {
  throw new Exception("No explain metrics returned");
}

// Get the plan summary
PlanSummary planSummary = explainMetrics.get().getPlanSummary();
List<Map<String, Object>> indexesUsed = planSummary.getIndexesUsed();
System.out.println("----- Indexes Used -----");
indexesUsed.forEach(map -> map.forEach((s, o) -> System.out.println(s + ": " + o)));

// Get the execution stats
if (!explainMetrics.getExecutionStats().isPresent()) {
  throw new Exception("No execution stats returned");
}

ExecutionStats queryStats = explainMetrics.getExecutionStats().get();
Map<String, Object> debugStats = queryStats.getDebugStats();
System.out.println("----- Debug Stats -----");
debugStats.forEach((s, o) -> System.out.println(s + ": " + o));

다음 예시에서는 올바른 색인 순서 지정을 통해 Datastore 모드의 Firestore가 스캔하는 항목 수를 저장하는 방법을 보여줍니다.

간단한 쿼리

직원 컬렉션의 이전 예시에서 (salary, experience) 색인을 사용해 실행되는 간단한 쿼리는 다음과 같습니다.

GQL

SELECT *
FROM /employees
WHERE salary > 100000 AND experience > 0
ORDER BY experience, salary;

Java

Query<Entity> query =
   Query.newEntityQueryBuilder()
    .setKind("employees")
    .setFilter(
        CompositeFilter.and(
            PropertyFilter.gt("salary", 100000), PropertyFilter.gt("experience", 0)))
    .setOrderBy(OrderBy("experience"), OrderBy("salary"))
    .build();

이 쿼리는 95,000개의 색인 항목을 스캔하여 5개 항목만을 반환합니다. 많은 수의 색인 항목을 읽었지만 쿼리 조건자를 충족하지 않아 필터링되었습니다.

// Output query planning info
{
        "indexesUsed": [
            {
                "query_scope": "Collection Group",
                "properties": "(experience ASC, salary ASC, __name__ ASC)"
            }
        ]
    },
    // Output Query Execution Stats
    {
        "resultsReturned": "5",
        "executionDuration": "2.5s",
        "readOperations": "100",
        "debugStats": {
            "index_entries_scanned": "95000",
            "documents_scanned": "5",
            "billing_details": {
                "documents_billable": "5",
                "index_entries_billable": "95000",
                "small_ops": "0",
                "min_query_cost": "0"
            }
        }
    }

앞의 예시에 따라 salary 제약조건이 experience 제약조건보다 선택성이 높다고 추론할 수 있습니다.

GQL

SELECT *
FROM /employees
WHERE salary > 100000 AND experience > 0
ORDER BY salary, experience;

Java

Query<Entity> query =
   Query.newEntityQueryBuilder()
    .setKind("employees")
    .setFilter(
        CompositeFilter.and(
            PropertyFilter.gt("salary", 100000), PropertyFilter.gt("experience", 0)))
    .setOrderBy(OrderBy("salary"), OrderBy("experience"))
    .build();

orderBy() 절을 명시적으로 사용하여 이전 순서로 조건자를 추가하면 Datastore 모드의 Firestore에서 (salary, experience) 색인을 사용해 쿼리를 실행합니다. 따라서 첫 번째 범위 필터를 선택하는 것이 이전 쿼리보다 더 좋기 때문에 쿼리가 더 빨리 실행되고 비용 효율적입니다.

    // Output query planning info
{
    "indexesUsed": [
        {
            "query_scope": "Collection Group",
            "properties": "(salary ASC, experience ASC, __name__ ASC)"
        }
    ],
    // Output Query Execution Stats
        "resultsReturned": "5",
        "executionDuration": "0.2s",
        "readOperations": "6",
        "debugStats": {
            "index_entries_scanned": "1000",
            "documents_scanned": "5",
            "billing_details": {
                "documents_billable": "5",
                "index_entries_billable": "1000",
                "small_ops": "0",
                "min_query_cost": "0"
            }
        }
    }

다음 단계