HOWTO · MongoDB

쿼리로 시작하는 MongoDB

이 MongoDB 기사에서 사용자는 $regex를 사용하여 쿼리로 시작하는 방법을 배웁니다. 쿼리의 패턴 일치 문자열에 대한 정규식 기능을 제공합니다.

이 페이지의 내용

MongoDB에서 흔히 하는 작업 중 하나는 문자열 필드가 선택한 접두사로 시작하는 모든 문서를 찾는 것입니다. 예를 들어 name이 Ja로 시작하는 모든 사용자를 찾는 경우입니다. MongoDB에는 전용 startsWith 연산자가 없으므로 표준적인 해결 방법은 문자열 시작 부분에 고정된 정규식을 사용하는 것이며, 이는 $regex 연산자로 작성하거나 쿼리 필터에 정규식 리터럴로 작성할 수 있습니다.

이 페이지의 쿼리 스니펫은 시스템 셸이 아니라 MongoDB Shell 또는 동등한 드라이버 세션에서 실행되는 mongosh 구문을 사용합니다. 실제 결과를 보려면 자신의 배포 환경에서 실행해 보세요. 아래에 있는 실행 가능한 스크립트는 동일한 일치 로직을 순수 JavaScript로 재현합니다.

접두사 일치에 앵커드 $regex 사용하기

“~로 시작하는” 일치는 ^ 앵커가 패턴을 문자열의 첫 문자에 고정하는 패턴 일치입니다. /^Ja/ 패턴은 처음 두 문자가 Ja인 값만 일치시킵니다. McJane처럼 뒤쪽에 Ja를 포함하기만 하는 값은 일치하지 않습니다. ^가 없으면 /Ja/ 패턴이 전체 문자열을 검색하여 어디에 있든 Ja와 일치하므로 접두사 쿼리가 “포함” 쿼리로 바뀝니다.

starts-with 쿼리의 $regex 연산자 형태는 db.collection.find({ name: { $regex: "^Ja" } })이며, 각 부분의 의미는 다음과 같습니다.

  • db.collection은 검색할 컬렉션입니다.
  • name은 검사할 문자열 필드입니다.
  • $regex는 MongoDB에 값을 정규식으로 평가하도록 지시합니다.
  • "^Ja"는 패턴입니다. ^가 문자열 시작 부분에 고정하고 Ja는 리터럴 접두사입니다.

mongosh와 정규식 리터럴을 지원하는 드라이버에서는 같은 필터를 db.users.find({ name: /^Ja/ })처럼 더 간결하게 작성할 수 있습니다.

패턴은 일반 문자열이 아니라 정규식이므로 ., *, +, (, ), [, ], $와 같은 정규식 메타문자를 포함하는 접두사는 백슬래시로 이스케이프해야 합니다. 예를 들어 리터럴 접두사 v1.에는 "^v1\\." 패턴이 필요합니다. 이스케이프하지 않으면 .가 임의의 문자와 일치하여 쿼리가 더 이상 리터럴 접두사 일치로 동작하지 않습니다.

접두사 일치 쿼리 실행하기

users 컬렉션에 name 값이 John Doe, Jane Smith, Jack Johnson, Jill Jackson, James Brown인 문서가 있다고 가정해 보겠습니다. 예를 들어 db.users.insertMany([{ name: "John Doe" }, { name: "Jane Smith" }, { name: "Jack Johnson" }, { name: "Jill Jackson" }, { name: "James Brown" }])로 삽입한 경우입니다.

db.users.find({ name: { $regex: "^Ja" } }) 쿼리는 name이 Ja로 시작하는 문서인 Jane Smith, Jack Johnson, James Brown을 반환합니다. John Doe는 Jo로 시작하고 Jill Jackson은 Ji로 시작하므로 앵커가 둘을 모두 제외합니다.

mongosh는 /^Ja/와 같은 JavaScript 스타일의 정규식 리터럴을 받아들이지만 서버는 $regex를 JavaScript가 아닌 PCRE 호환 엔진으로 평가합니다. 이 앵커드 패턴에서는 두 의미가 일치하므로 다음 스크립트는 동일한 일치 로직을 순수 JavaScript로 재현하며 어떤 문서가 선택되는지 확인할 수 있습니다.

이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.

const names = ["John Doe", "Jane Smith", "Jack Johnson", "Jill Jackson", "James Brown"];
const prefix = /^Ja/;
console.log(names.filter((name) => prefix.test(name)).join("\n"));
Jane Smith
Jack Johnson
James Brown

대소문자 구분 또는 대소문자 비구분 일치 선택하기

MongoDB의 정규식 일치는 기본적으로 대소문자를 구분하므로 ^Ja는 james brown과 같은 값과 일치하지 않습니다. 대소문자에 관계없이 접두사를 일치시키려면 db.users.find({ name: /^ja/i })처럼 i 옵션을 추가합니다. 동등한 $regex 형태는 $options를 통해 플래그를 전달합니다: db.users.find({ name: { $regex: "^ja", $options: "i" } }).

i를 사용하면 ^ja 패턴은 여전히 Jane Smith, Jack Johnson, James Brown과 일치하며 jane smith나 JACK JOHNSON처럼 저장된 철자와도 일치합니다. 접두사의 대소문자가 의미 있을 때는 대소문자 구분 형태를 유지하세요. 다음 섹션에서 설명하듯이 인덱스와 함께 더 잘 동작하기 때문입니다.

인덱스 사용과 성능 이해하기

앵커드 접두사 패턴은 MongoDB가 일반 인덱스로 효율적으로 평가할 수 있는 유일한 정규식 형태입니다. db.users.find({ name: /^Ja/ })와 같은 대소문자 구분 쿼리에서 MongoDB는 모든 문서에 대해 패턴을 검사하는 대신 접두사를 name 인덱스에 대한 경계가 있는 범위, 대략 Ja와 Jb 사이의 값으로 변환할 수 있습니다. ^ 앵커가 바로 이 인덱스 사용을 가능하게 하는 요소이며 최적화를 방해하지 않습니다.

두 가지 유의 사항이 있습니다.

  • i 옵션은 이 최적화를 무력화합니다. $regex는 콜레이션을 인식하지 않으므로 어떤 컬렉션 콜레이션에서도 대소문자 비구분 패턴은 인덱스를 효과적으로 사용할 수 없습니다. name에 인덱스가 있어도 db.users.find({ name: /^ja/i })는 모든 후보 값을 검사하는 방식으로 돌아갑니다. 대소문자 비구분 접두사 조회에 인덱스 지원이 필요한 경우 일반적인 해결 방법은 필드의 정규화된 소문자 복사본을 저장하고 앵커드 대소문자 구분 패턴으로 쿼리하는 것입니다. 대소문자 비구분 콜레이션도 도움이 될 수 있지만 $regex에는 적용되지 않고 정규식이 아닌 동등 또는 범위 조건자에만 해당합니다.
  • /Ja/와 같이 앵커가 없는 패턴은 접두사 범위를 전혀 사용할 수 없으며 MongoDB는 모든 후보 값에 대해 표현식을 검사해야 합니다.

접두사로 자주 쿼리하는 컬렉션의 경우 db.users.createIndex({ name: 1 })로 필드에 인덱스를 만들고 데이터가 허용하는 한 패턴을 앵커드 상태이면서 대소문자 구분으로 유지하세요. 그런 다음 db.users.find(...).explain("executionStats")로 선택된 플랜을 확인합니다. 최종 선택된 플랜은 전체 COLLSCAN이 아니라 해당 필드에 대한 IXSCAN을 보여야 합니다. 매우 작은 컬렉션에서는 옵티마이저가 비용이 더 저렴하기 때문에 여전히 스캔을 선택할 수 있으며 이는 문제없습니다. 앵커드 접두사 형태는 중요할 때 MongoDB가 인덱스를 사용할 수 있는 선택지를 제공합니다.