정규 표현식 문법

정규 표현식 문법 (Regular expression syntax)

정규 표현식(regex)은 특수 기호와 연산자를 사용해 검색 패턴을 정의하는 방법이에요. 이러한 패턴을 사용하면 문자열에서 문자 시퀀스를 일치시킬 수 있습니다.

OpenSearch에서는 다음 쿼리 유형에서 정규 표현식을 사용할 수 있어요.

  • regexp
  • query_string

OpenSearch는 고유한 문법과 제한 사항을 가진 Apache Lucene regex 엔진을 사용해요. Perl 호환 정규 표현식(PCRE)을 사용하지 않으므로, 익숙한 일부 regex 기능이 다르게 동작하거나 지원되지 않을 수 있습니다.

출처: 문서

본문

regexp 쿼리와 query_string 쿼리 선택하기

regexp 쿼리와 query_string 쿼리 모두 정규 표현식을 지원하지만, 동작 방식이 다르고 활용 사례도 달라요.

  • 패턴 매칭: regexp 쿼리는 정규식 패턴이 필드 값 전체와 일치해야 해요. query_string 쿼리는 정규식 패턴이 필드의 어떤 부분이든 일치할 수 있어요.
  • flags 지원: regexp 쿼리는 flags가 선택적 regex 연산자를 지원해요. query_string 쿼리는 flags를 지원하지 않아요.
  • 쿼리 유형: regexp 쿼리는 용어 레벨 쿼리(점수 계산 없음)이고, query_string 쿼리는 전체 텍스트 쿼리(점수 계산 및 파싱됨)예요.
  • 사용 사례: regexp 쿼리는 keyword 또는 정확한 필드에 대한 엄격한 패턴 매칭에 가장 적합하고, query_string 쿼리는 regex 패턴을 지원하는 유연한 쿼리 문자열을 사용해 분석된 필드 안에서 검색하는 데 가장 적합해요.
  • 복잡한 쿼리 구성: regexp 쿼리는 regex 패턴으로 제한되고, query_string 쿼리는 AND, OR, 와일드카드, 필드, boost 및 기타 기능을 지원해요(Query string query 참고).

예약 문자 (Reserved characters)

Lucene의 regex 엔진은 모든 유니코드 문자를 지원해요. 하지만 다음 문자는 특수 연산자로 처리됩니다.

. ? + * | { } [ ] ( ) " \

활성화된 flags에 따라 선택적 연산자를 지정하는 다음 문자들도 예약될 수 있어요.

@ & ~ 

이 문자들을 문자 그대로 매치하려면 백슬래시(\)로 이스케이프하거나 전체 문자열을 큰따옴표로 감싸면 돼요.

  • \&: 리터럴 &를 매치해요.
  • \\: 리터럴 백슬래시(\)를 매치해요.
  • "hello@world": 전체 문자열 hello@world를 매치해요.

표준 regex 연산자 (Standard regex operators)

Lucene은 핵심 regex 연산자 세트를 지원해요.

  • . – 임의의 단일 문자를 매치해요. 예: f.n은 f 다음에 임의의 문자와 n이 오는 문자열(fan, fin 등)을 매치해요.
  • ? – 앞 문자가 0개 또는 1개인 것을 매치해요. 예: colou?r은 color와 colour를 매치해요.
  • + – 앞 문자가 하나 이상인 것을 매치해요. 예: go+는 g 다음에 하나 이상의 o가 오는 문자열(go, goo, gooo 등)을 매치해요.
  • * – 앞 문자가 0개 이상인 것을 매치해요. 예: lo*se는 l 다음에 0개 이상의 o와 se가 오는 문자열(lse, lose, loose, loooose 등)을 매치해요.
  • {min,max} – 특정 반복 범위를 매치해요. max를 생략하면 매치되는 문자의 수에 상한이 없어요. 예: x{3}은 정확히 3개의 x(xxx)를 매치하고, x{2,4}는 2~4개의 x(xx, xxx, xxxx)를 매치하며, x{3,}는 3개 이상의 x(xxx, xxxx, xxxxx 등)를 매치해요.
  • | – 논리 OR 역할을 해요. 예: apple|orange는 apple 또는 orange를 매치해요.
  • ( ) – 문자를 하위 패턴으로 그룹화해요. 예: ab(cd)?는 ab와 abcd를 매치해요.
  • [ ] – 세트나 범위에서 한 문자를 매치해요. 예: [aeiou]는 임의의 모음을 매치해요.
  • - – 대괄호 안에 제공되면 범위를 나타내요(이스케이프되거나 대괄호의 첫 번째 문자인 경우 제외). 예: [a-z]는 임의의 소문자를 매치하고, [-az]는 -, a 또는 z를 매치하며, [a\-z]는 a, - 또는 z를 매치해요.
  • ^ – 대괄호 안에 제공되면 논리 NOT 역할을 하며 문자 범위나 세트의 임의의 문자를 부정해요. 예: [^az]는 a 또는 z를 제외한 임의의 문자를 매치하고, [^a-z]는 소문자를 제외한 임의의 문자를 매치하며, [^-az]는 -, a, z를 제외한 임의의 문자를 매치하고, [^a\-z]는 a, -, z를 제외한 임의의 문자를 매치해요.

선택적 연산자 (Optional operators)

flags 파라미터를 사용해 추가 regex 연산자를 활성화할 수 있어요. 여러 flags는 |로 구분해요.

다음은 사용 가능한 flags예요.

  • ALL (기본값) – 모든 선택적 연산자를 활성화해요.
  • COMPLEMENT – ~를 활성화하며, 이는 바로 다음의 가장 짧은 표현식을 부정해요. 예: d~ef는 dgf, dxf를 매치하지만 def는 매치하지 않아요.
  • INTERSECTION – &를 AND 논리 연산자로 활성화해요. 예: ab.+&.+cd는 시작 부분에 ab가 있고 끝 부분에 cd가 있는 문자열을 매치해요.
  • INTERVAL – 숫자 범위를 매치하는 <min-max> 문법을 활성화해요. 예: id<10-12>는 id10, id11, id12를 매치해요.
  • ANYSTRING – @가 임의의 문자열을 매치하도록 활성화해요. ~와 &와 결합해 제외 조건을 만들 수 있어요. 예: @&.*error.*&.*[0-9]{3}.*는 "error"라는 단어와 세 자리 숫자 시퀀스를 모두 포함하는 문자열을 매치해요.

지원되지 않는 기능 (Unsupported features)

Lucene의 엔진은 다음 자주 사용되는 regex 앵커를 지원하지 않아요.

  • ^ – 줄의 시작
  • $ – 줄의 끝

대신 패턴이 매치를 내기 위해 전체 문자열과 일치해야 해요.

예제 (Example)

정규 표현식을 시험해 보려면 다음 문서를 logs 인덱스에 인덱싱하세요.

PUT /logs/_doc/1
{
  "message": "error404"
}
PUT /logs/_doc/2
{
  "message": "error500"
}
PUT /logs/_doc/3
{
  "message": "error1a"
}

예제: 정규 표현식을 포함하는 기본 쿼리

다음 regexp 쿼리는 message 필드의 전체 값이 "error" 뒤에 하나 이상의 숫자가 오는 패턴과 일치하는 문서를 반환해요. 값이 패턴을 부분 문자열로만 포함하고 있으면 일치하지 않습니다.

GET /logs/_search
{
  "query": {
    "regexp": {
      "message": {
        "value": "error[0-9]+"
      }
    }
  }
}

이 쿼리는 error404와 error500을 매치해요.

{
  "took": 28,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 2,
      "relation": "eq"
    },
    "max_score": 1,
    "hits": [
      {
        "_index": "logs",
        "_id": "1",
        "_score": 1,
        "_source": {
          "message": "error404"
        }
      },
      {
        "_index": "logs",
        "_id": "2",
        "_score": 1,
        "_source": {
          "message": "error500"
        }
      }
    ]
  }
}

예제: 선택적 연산자 사용하기

다음 쿼리는 message 필드가 "error"로 시작하고 그 뒤에 400에서 500 사이의 숫자(포함)가 오는 문자열과 정확히 일치하는 문서를 매치해요. INTERVAL flag는 숫자 범위에 <min-max> 문법을 사용할 수 있게 해줘요.

GET /logs/_search
{
  "query": {
    "regexp": {
      "message": {
        "value": "error",
        "flags": "INTERVAL"
      }
    }
  }
}

이 쿼리는 error404와 error500을 매치해요.

{
  "took": 22,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 2,
      "relation": "eq"
    },
    "max_score": 1,
    "hits": [
      {
        "_index": "logs",
        "_id": "1",
        "_score": 1,
        "_source": {
          "message": "error404"
        }
      },
      {
        "_index": "logs",
        "_id": "2",
        "_score": 1,
        "_source": {
          "message": "error500"
        }
      }
    ]
  }
}

예제: ANYSTRING 사용하기

ANYSTRING flag가 활성화되면 @ 연산자는 전체 문자열을 매치해요. 이는 교집합(&)과 결합할 때 유용한데, 특정 조건에서 전체 문자열을 매치하는 쿼리를 구성할 수 있게 해주기 때문이에요.

다음 쿼리는 "error"라는 단어와 세 자리 숫자 시퀀스를 모두 포함하는 메시지를 매치해요. ANYSTRING을 사용해 전체 필드가 두 패턴의 교집합과 일치해야 한다고 주장해 보죠.

GET /logs/_search
{
  "query": {
    "regexp": {
      "message.keyword": {
        "value": "@&.*error.*&.*[0-9]{3}.*",
        "flags": "ANYSTRING|INTERSECTION"
      }
    }
  }
}

이 쿼리는 error404와 error500을 매치해요.

{
  "took": 20,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 2,
      "relation": "eq"
    },
    "max_score": 1,
    "hits": [
      {
        "_index": "logs",
        "_id": "1",
        "_score": 1,
        "_source": {
          "message": "error404"
        }
      },
      {
        "_index": "logs",
        "_id": "2",
        "_score": 1,
        "_source": {
          "message": "error500"
        }
      }
    ]
  }
}

이 쿼리는 xerror500, error500x, errorxx500도 매치한다는 점에 주의하세요.

더 알아보기 (Learn more)