SQL and PPL API

SQL and PPL API

SQL and PPL API를 사용해 SQL 플러그인에 질의를 보내요. SQL 질의를 보내려면 _sql 엔드포인트를, PPL 질의를 보내려면 _ppl 엔드포인트를 사용해요. 두 경우 모두 _explain 엔드포인트를 사용해 질의를 OpenSearch 도메인 특화 언어(DSL)로 변환하거나 오류를 진단할 수 있어요.

출처: 문서

본문

Query API

SQL/PPL 질의를 SQL 플러그인에 보내요. 응답 형식을 질의 파라미터로 전달할 수 있어요.

질의 파라미터 (Query parameters)

Parameter Data type Description
format String 응답 형식이에요. _sql 엔드포인트는 jdbc, csv, raw, json 형식을 지원하고, _ppl 엔드포인트는 jdbc, csv, raw 형식을 지원해요. 기본값은 jdbc예요.
sanitize Boolean 결과에서 특수 문자를 이스케이프할지 여부를 지정해요. 자세한 내용은 Response formats를 참고하세요. 기본값은 true예요.

요청 본문 필드 (Request body fields)

Field Data type Description
query String 실행할 질의예요. 필수예요.
filter JSON object 결과에 대한 필터예요. 선택 사항이에요.
fetch_size integer 한 응답에서 반환할 결과 수예요. 결과 페이지네이션에 사용돼요. 기본값은 1,000이에요. 선택 사항이에요. fetch_size는 SQL에서 지원되며 jdbc 응답 형식을 사용해야 해요.

예시 요청

POST /_plugins/_sql
{
  "query" : "SELECT * FROM accounts"
}

예시 응답

응답에는 스키마와 결과가 포함돼요.

{
  "schema": [
    {
      "name": "account_number",
      "type": "long"
    },
    {
      "name": "firstname",
      "type": "text"
    },
    {
      "name": "address",
      "type": "text"
    },
    {
      "name": "balance",
      "type": "long"
    },
    {
      "name": "gender",
      "type": "text"
    },
    {
      "name": "city",
      "type": "text"
    },
    {
      "name": "employer",
      "type": "text"
    },
    {
      "name": "state",
      "type": "text"
    },
    {
      "name": "age",
      "type": "long"
    },
    {
      "name": "email",
      "type": "text"
    },
    {
      "name": "lastname",
      "type": "text"
    }
  ],
  "datarows": [
    [
      1,
      "Amber",
      "880 Holmes Lane",
      39225,
      "M",
      "Brogan",
      "Pyrami",
      "IL",
      32,
      "[email protected]",
      "Duke"
    ],
    [
      6,
      "Hattie",
      "671 Bristol Street",
      5686,
      "M",
      "Dante",
      "Netagy",
      "TN",
      36,
      "[email protected]",
      "Bond"
    ],
    [
      13,
      "Nanette",
      "789 Madison Street",
      32838,
      "F",
      "Nogal",
      "Quility",
      "VA",
      28,
      "[email protected]",
      "Bates"
    ],
    [
      18,
      "Dale",
      "467 Hutchinson Court",
      4180,
      "M",
      "Orick",
      null,
      "MD",
      33,
      "[email protected]",
      "Adams"
    ]
  ],
  "total": 4,
  "size": 4,
  "status": 200
}

응답 본문 필드 (Response body fields)

Field Data type Description
schema Array 모든 필드의 필드 이름과 타입을 지정해요.
data_rows Two-dimensional array 결과 배열이에요. 각 결과는 하나의 일치하는 행(문서)을 나타내요.
total Integer 인덱스의 총 행(문서) 수예요.
size Integer 한 응답에서 반환할 결과 수예요.
status String 질의를 실행한 후 OpenSearch가 반환하는 HTTP 응답 상태예요.

Explain API

SQL 플러그인의 explain 기능은 질의가 OpenSearch에 대해 어떻게 실행되는지 보여줘서 디버깅과 개발에 유용해요. _plugins/_sql/_explain 또는 _plugins/_ppl/_explain 엔드포인트에 POST 요청을 보내면 OpenSearch 도메인 특화 언어(DSL)가 JSON 형식으로 반환돼요.

OpenSearch 3.0.0부터 plugins.calcite.enabled를 true로 설정하면 explain 응답이 질의 실행 계획에 대한 향상된 정보를 제공해요. 이 API는 네 가지 출력 형식을 지원해요.

  • standard: 논리 및 물리 계획을 표시해요(지정하지 않으면 기본값)
  • simple: 속성 없이 논리 계획을 표시해요
  • cost: 비용과 함께 논리 및 물리 계획을 표시해요
  • extended: 생성된 코드와 함께 논리 및 물리 계획을 표시해요

예시 (Examples)

다음 예시들은 다양한 explain 질의를 보여줘요.

기본 SQL 질의

다음 요청은 기본 SQL explain 질의를 보여줘요.

POST _plugins/_sql/_explain
{
  "query": "SELECT firstname, lastname FROM accounts WHERE age > 20"
}

응답은 질의 실행 계획을 보여줘요.

{
  "root": {
    "name": "ProjectOperator",
    "description": {
      "fields": "[firstname, lastname]"
    },
    "children": [
      {
        "name": "OpenSearchIndexScan",
        "description": {
          "request": "\"\"\"OpenSearchQueryRequest(indexName=accounts, sourceBuilder={\"from\":0,\"size\":200,\"timeout\":\"1m\",\"query\":{\"range\":{\"age\":{\"from\":20,\"to\":null,\"include_lower\":false,\"include_upper\":true,\"boost\":1.0}}},\"_source\":{\"includes\":[\"firstname\",\"lastname\"],\"excludes\":[]},\"sort\":[{\"_doc\":{\"order\":\"asc\"}}]}, searchDone=false)\"\"\""
        },
        "children": []
      }
    ]
  }
}

Calcite 엔진을 사용한 고급 질의

다음 요청은 Calcite 엔진을 사용하는 더 복잡한 질의를 보여줘요.

POST _plugins/_ppl/_explain
{
  "query" : "source=state_country | where country = 'USA' OR country = 'England' | stats count() by country"
}

응답은 표준 형식으로 논리 및 물리 계획을 모두 보여줘요.

{
  "calcite": {
    "logical": "\"\"\"LogicalProject(count()=[$1], country=[$0])
  LogicalAggregate(group=[{1}], count()=[COUNT()])
    LogicalFilter(condition=[SEARCH($1, Sarg['England', 'USA':CHAR(7)]:CHAR(7))])
      CalciteLogicalIndexScan(table=[[OpenSearch, state_country]])
\"\"\",
    "physical": "\"\"\"EnumerableCalc(expr#0..1=[{inputs}], count()=[$t1], country=[$t0])
  CalciteEnumerableIndexScan(table=[[OpenSearch, state_country]], PushDownContext=[[FILTER->SEARCH($1, Sarg['England', 'USA':CHAR(7)]:CHAR(7)), AGGREGATION->rel#53:LogicalAggregate.NONE.[](input=RelSubset#43,group={1},count()=COUNT())], OpenSearchRequestBuilder(sourceBuilder={\"from\":0,\"size\":0,\"timeout\":\"1m\",\"query\":{\"terms\":{\"country\":[\"England\",\"USA\"],\"boost\":1.0}},\"sort\":[{\"_doc\":{\"order\":\"asc\"}}],\"aggregations\":{\"composite_buckets\":{\"composite\":{\"size\":1000,\"sources\":[{\"country\":{\"terms\":{\"field\":\"country\",\"missing_bucket\":true,\"missing_order\":\"first\",\"order\":\"asc\"}}}]},\"aggregations\":{\"count()\":{\"value_count\":{\"field\":\"_index\"}}}}}}, requestedTotalSize=10000, pageSize=null, startFrom=0)])
\"\"\"
  }
}

질의 계획을 단순하게 보려면 simple 형식을 사용할 수 있어요.

POST _plugins/_ppl/_explain?format=simple
{
  "query" : "source=state_country | where country = 'USA' OR country = 'England' | stats count() by country"
}

응답은 간결한 논리 계획을 보여줘요.

{
  "calcite": {
    "logical": "\"\"\"LogicalProject
  LogicalAggregate
    LogicalFilter
      CalciteLogicalIndexScan
\"\"\"
  }
}

후처리가 필요한 질의의 경우 explain 응답은 OpenSearch DSL과 함께 질의 계획을 포함해요. 후처리가 필요 없는 질의의 경우 완전한 DSL만 볼 수 있어요.

결과 페이지네이션 (Paginating results)

페이지네이션된 응답을 받으려면 fetch_size 파라미터를 사용하세요. fetch_size의 값은 0보다 커야 해요. 기본값은 1,000이에요. 값이 0이면 페이지네이션되지 않은 응답으로 폴백해요.

fetch_size 파라미터는 jdbc 응답 형식에서만 지원돼요.

예시

다음 요청은 SQL 질의를 포함하고 한 번에 5개의 결과를 반환하도록 지정해요.

POST _plugins/_sql/
{
  "fetch_size" : 5,
  "query" : "SELECT firstname, lastname FROM accounts WHERE age > 20 ORDER BY state ASC"
}

응답에는 fetch_size가 없는 질의가 담는 모든 필드와 함께, 이후 결과 페이지를 가져오는 데 사용되는 cursor 필드가 포함돼요.

{
  "schema": [
    {
      "name": "firstname",
      "type": "text"
    },
    {
      "name": "lastname",
      "type": "text"
    }
  ],
  "cursor": "d:eyJhIj...NTF9",
  "total": 956,
  "datarows": [
    [
      "Cherry",
      "Carey"
    ],
    [
      "Lindsey",
      "Hawkins"
    ],
    [
      "Sargent",
      "Powers"
    ],
    [
      "Campos",
      "Olsen"
    ],
    [
      "Savannah",
      "Kirby"
    ]
  ],
  "size": 5,
  "status": 200
}

이후 페이지를 가져오려면 이전 응답의 cursor를 사용하세요.

POST /_plugins/_sql
{
   "cursor": "d:eyJhIj...NTF9"
}

다음 응답에는 결과의 datarows와 새 cursor만 포함돼요.

{
  "cursor": "d:eyJhIj...2345",
  "datarows": [
    [
      "Abbey",
      "Karen"
    ],
    [
      "Chen",
      "Ken"
    ],
    [
      "Ani",
      "Jade"
    ],
    [
      "Peng",
      "Hu"
    ],
    [
      "John",
      "Doe"
    ]
  ]
}

중첩 필드가 평탄화되는 경우 datarows에 fetch_size보다 많은 레코드가 있을 수 있어요.

마지막 결과 페이지에는 datarows만 있고 cursor가 없어요. cursor 컨텍스트는 마지막 페이지에서 자동으로 정리돼요.

커서 컨텍스트를 명시적으로 지우려면 _plugins/_sql/close 엔드포인트 연산을 사용하세요.

POST /_plugins/_sql/close
{
   "cursor": "d:eyJhIj...NTF9"
}

응답은 OpenSearch의 확인(acknowledgment)이에요.

{"succeeded":true}

결과 필터링 (Filtering results)

filter 파라미터를 사용해 OpenSearch DSL에 조건을 직접 추가할 수 있어요.

다음 SQL 질의는 모든 고객의 이름과 계좌 잔액을 반환해요. 이후 결과가 잔액이 $10,000 미만인 고객만 포함하도록 필터링돼요.

POST /_plugins/_sql/
{
  "query" : "SELECT firstname, lastname, balance FROM accounts",
  "filter" : {
    "range" : {
      "balance" : {
        "lt" : 10000
      }
    }
  }
}

응답에는 일치하는 결과가 포함돼요.

{
  "schema": [
    {
      "name": "firstname",
      "type": "text"
    },
    {
      "name": "lastname",
      "type": "text"
    },
    {
      "name": "balance",
      "type": "long"
    }
  ],
  "total": 2,
  "datarows": [
    [
      "Hattie",
      "Bond",
      5686
    ],
    [
      "Dale",
      "Adams",
      4180
    ]
  ],
  "size": 2,
  "status": 200
}

Explain API를 사용해 이 질의가 OpenSearch에 대해 어떻게 실행되는지 확인할 수 있어요.

POST /_plugins/_sql/_explain
{
  "query" : "SELECT firstname, lastname, balance FROM accounts",
  "filter" : {
    "range" : {
      "balance" : {
        "lt" : 10000
      }
    }
  }
}

응답에는 앞의 질의에 해당하는 OpenSearch DSL의 Boolean 질의가 포함돼요.

{
  "from": 0,
  "size": 200,
  "query": {
    "bool": {
      "filter": [{
        "bool": {
          "filter": [{
            "range": {
              "balance": {
                "from": null,
                "to": 10000,
                "include_lower": true,
                "include_upper": false,
                "boost": 1.0
              }
            }
          }],
          "adjust_pure_negative": true,
          "boost": 1.0
        }
      }],
      "adjust_pure_negative": true,
      "boost": 1.0
    }
  },
  "_source": {
    "includes": [
      "firstname",
      "lastname",
      "balance"
    ],
    "excludes": []
  }
}

파라미터 사용하기 (Using parameters)

parameters 필드를 사용해 준비된(prepared) SQL 질의에 파라미터 값을 전달할 수 있어요.

다음 explain 연산은 age 파라미터가 있는 SQL 질의를 사용해요.

POST /_plugins/_sql/_explain
{
  "query": "SELECT * FROM accounts WHERE age = ?",
  "parameters": [{
    "type": "integer",
    "value": 30
  }]
}

응답에는 앞의 SQL 질의에 해당하는 OpenSearch DSL의 Boolean 질의가 포함돼요.

{
  "from": 0,
  "size": 200,
  "query": {
    "bool": {
      "filter": [{
        "bool": {
          "must": [{
            "term": {
              "age": {
                "value": 30,
                "boost": 1.0
              }
            }
          }],
          "adjust_pure_negative": true,
          "boost": 1.0
        }
      }],
      "adjust_pure_negative": true,
      "boost": 1.0
    }
  }
}
  • Data source APIs

더 알아보기 (Learn more)