Skip to content

JAVA-6168: QE prefix/suffix/substring GA + rename Text API to String#2010

Draft
nhachicha wants to merge 26 commits into
mongodb:mainfrom
nhachicha:nh/qe/prefix
Draft

JAVA-6168: QE prefix/suffix/substring GA + rename Text API to String#2010
nhachicha wants to merge 26 commits into
mongodb:mainfrom
nhachicha:nh/qe/prefix

Conversation

@nhachicha

Copy link
Copy Markdown
Collaborator

Summary

Promotes Queryable Encryption string text queries from preview to GA and renames the public explicit-encryption API from "Text" to "String".

Changes

Public API (driver-core, com.mongodb.client.model.vault) — additive / backward compatible:

  • New StringOptions (@Alpha) mirroring TextOptions; TextOptions retained and @Deprecated.
  • EncryptOptions: add stringOptions(...)/getStringOptions(); deprecate textOptions(...)/getTextOptions() (independent backing fields; stringOptions wins, falls back to textOptions).
  • Docs: algorithm "TextPreview""String"; queryType now documents prefix/suffix (GA) + the deprecated prefixPreview/suffixPreview aliases (pre-9.0) + substring/substringPreview. New API is @since 5.9.

libmongocrypt: 1.18.11.20.0 (stable prefix/suffix in 1.19.0, stable substring in 1.20.0). No JNA binding changes — mongocrypt_ctx_setopt_algorithm_text and its BSON schema are unchanged; only doc comments updated.

Tests:

  • Rewrote the QE string explicit-encryption prose test to spec test 27 (cases 1–11): algorithm "String" + stringOptions, GA (prefix/suffix/substring, server 9.0+) vs preview (*Preview, pre-9.0) paths.
  • Un-skipped the QE-Text-substring GA unified spec test (now supported on libmongocrypt 1.20.0).
  • Spec submodule updated (via merge of main).

Compatibility

No breaking changes — all additions or retained deprecations; no major version bump required. Note: passing "TextPreview" as the algorithm value now errors at libmongocrypt (removed in 1.19.x); users must use "String".

Testing

Evergreen patch (server latest/9.0, libmongocrypt 1.20.0): prefix/suffix GA + substring GA prose + unified tests pass. A full-matrix patch showed only pre-existing (Windows CI) and flaky (connection-refused/timeout) failures, none related to these changes.

Jira: https://jira.mongodb.org/browse/JAVA-6168

nhachicha added 18 commits June 29, 2026 11:07
Provides GA support for QE 'prefix'/'suffix' query types and the 'string'
algorithm (formerly 'textPreview'), and restores 'prefixPreview'/'suffixPreview'
for pre-9.0 servers. Required by the Text->String API rename.
Updates testing/resources/specifications 4484038..8990543, bringing the GA
QE-Text unified tests and String Explicit Encryption prose test (DRIVERS-3321,
DRIVERS-3470).
libmongocrypt 1.20.0 adds stable support for substring queries (the GA
'substring' query type), unblocking DRIVERS-3540/JAVA-6244.
…se GA/preview split)

- Un-skip the QE-Text-substring GA unified spec test (now supported via libmongocrypt 1.20.0).
- Prose test 27: split substring cases 5/6 into GA (substring, server 9.0+/libmongocrypt 1.20+)
  and preview (substringPreview, server pre-9.0) paths using the new substring-preview collection;
  update seed strMaxQueryLength to 6; gate cases 10/11 to pre-9.0 per the updated spec.
@nhachicha nhachicha requested a review from a team as a code owner July 4, 2026 16:26
@nhachicha nhachicha requested review from apmasell and Copilot and removed request for apmasell July 4, 2026 16:26
@nhachicha nhachicha changed the title feat(JAVA-6168): QE prefix/suffix/substring GA + rename Text API to String JAVA-6168: QE prefix/suffix/substring GA + rename Text API to String Jul 4, 2026
@nhachicha nhachicha marked this pull request as draft July 4, 2026 16:29

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Promotes Queryable Encryption explicit-encryption string queries from the preview “Text” API to the GA “String” API surface, adds StringOptions, and bumps bundled libmongocrypt to 1.20.0 to enable GA prefix/suffix/substring behavior across server versions.

Changes:

  • Introduces StringOptions and EncryptOptions.stringOptions(...), while deprecating TextOptions / EncryptOptions.textOptions(...) and mapping new options through the existing libmongocrypt “text options” document.
  • Updates functional and unit tests to exercise GA (prefix/suffix/substring) vs pre-9.0 preview (*Preview) queryType behavior.
  • Updates mongodb-crypt packaging to download libmongocrypt 1.20.0 and adjusts GPG verification homedir handling to avoid macOS socket path limits.

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
mongodb-crypt/src/main/com/mongodb/internal/crypt/capi/MongoExplicitEncryptOptions.java Updates internal Javadoc to reflect the algorithm name change to "String".
mongodb-crypt/build.gradle.kts Bumps bundled libmongocrypt to 1.20.0 and moves GPG homedir to system temp to avoid macOS path-length failures.
driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedTestModifications.java Removes skip for the substring unified test now that it’s supported.
driver-sync/src/test/functional/com/mongodb/client/AbstractClientEncryptionTextExplicitEncryptionTest.java Reworks QE prose-style functional tests to cover GA vs preview query types and adds additional cases.
driver-core/src/test/unit/com/mongodb/internal/client/vault/EncryptOptionsHelperTest.java Adds unit tests for mapping StringOptions (and deprecated fallback) into the libmongocrypt options document.
driver-core/src/test/unit/com/mongodb/client/model/vault/StringOptionsTest.java Adds unit tests for the new StringOptions value object.
driver-core/src/test/unit/com/mongodb/client/model/vault/EncryptOptionsTest.java Adds unit tests for EncryptOptions storing stringOptions and deprecated textOptions.
driver-core/src/main/com/mongodb/internal/client/vault/EncryptOptionsHelper.java Implements StringOptions → libmongocrypt “text options” document mapping with deprecated fallback.
driver-core/src/main/com/mongodb/client/model/vault/TextOptions.java Deprecates TextOptions and adjusts defaults for boolean flags.
driver-core/src/main/com/mongodb/client/model/vault/StringOptions.java Adds the new StringOptions API and associated documentation.
driver-core/src/main/com/mongodb/client/model/vault/EncryptOptions.java Adds stringOptions accessors, deprecates textOptions, and updates Javadocs/toString accordingly.
.gitignore Ignores docs/superpowers/.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread driver-core/src/main/com/mongodb/client/model/vault/EncryptOptions.java Outdated
Comment thread driver-core/src/main/com/mongodb/client/model/vault/EncryptOptions.java Outdated
Comment thread driver-core/src/main/com/mongodb/client/model/vault/StringOptions.java Outdated
Comment thread driver-core/src/main/com/mongodb/client/model/vault/StringOptions.java Outdated
skipVerify.set(skipCryptVerify)
expectedFingerprint.set("F2F5BF4ABF517E039AFCADAA81F1404DEBACA586")
gnupgHome.set(layout.buildDirectory.dir("jnaLibs/gnupg"))
// Keep the scratch GPG keyring under the system temp dir, not the module build dir. gpg derives

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fix an issue where gpg invocation used to fail locally when invoked from Gradle/IDE under a deeply nested directories. Worth checking under Windows as well cc @strogiyotec

…ses 10/11, precedence test

- Update EncryptOptions/StringOptions Javadocs: substring is GA (queryType 'substring', server 9.0+),
  'substringPreview' is the deprecated pre-9.0 alias (was still documented as experimental/preview).
- Prose test cases 10/11 (auto-encrypted substring ci-di): run on server 9.0+ (assumeTrue(gaSupported)),
  matching the substring-ci-di 9.0+ setup requirement and the prefix/suffix ci-di cases 8/9. The spec's
  'skip on 9.0.0+' contradicts its own setup; TODO-JAVA-6244 pending upstream clarification.
- Add EncryptOptionsHelper precedence test: stringOptions wins when both stringOptions and textOptions set.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 15 changed files in this pull request and generated 2 comments.

Comment on lines 150 to +154
/**
* The QueryType.
*
* <p>Currently, we support only "equality", "range", "prefixPreview", "suffixPreview" or "substringPreview" queryType.</p>
* <p>It is an error to set queryType when the algorithm is not "Indexed", "Range" or "TextPreview".</p>
* <p>Currently, we support only "equality", "range", "prefix", "suffix", "substring", "prefixPreview",
* "suffixPreview" or "substringPreview" queryType.</p>

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f39e5e2. getQueryType()'s Javadoc now points to queryType(String) for the supported-types list, so the two can't drift apart.

* @since 5.6
* @mongodb.server.release 8.2
* @mongodb.driver.manual /core/queryable-encryption/ queryable encryption
* @deprecated Use {@link StringOptions} instead.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TextOptions is the deprecated pre-rename type kept for backward compatibility, so it intentionally retains its "Text" naming/wording, and its @deprecated tag already points to StringOptions. :driver-core:javadoc builds clean, so leaving as-is.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 15 changed files in this pull request and generated 2 comments.

Comment on lines +65 to +67
private static final ServerVersion REQUIRED_LIB_MONGOCRYPT_VERSION = new ServerVersion(asList(1, 19, 1));
// GA "substring" (server 9.0+) needs libmongocrypt 1.20+; prefix/suffix GA and the preview paths need only 1.19/1.18.
private static final ServerVersion SUBSTRING_GA_LIB_MONGOCRYPT_VERSION = new ServerVersion(asList(1, 20, 0));

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f39e5e2 — reworded to note the class-level 1.19.1 minimum already covers prefix/suffix GA and the pre-9.0 preview query types, while GA substring additionally needs 1.20.

Comment on lines 128 to 135
/**
* The Text Options.
* The String Options.
*
* <p>Only applies when algorithm is "TextPreview".</p>
* <p>Only applies when algorithm is "String". The method name mirrors the libmongocrypt {@code textOptions}
* BSON field, which is unchanged.</p>
*
* @param textOptions the text options
* @param textOptions the string options
* @return this

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f39e5e2 — the @param now reads "the string options, as a BSON document" to distinguish it from the public StringOptions type.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 15 changed files in this pull request and generated 1 comment.

* BSON field, which is unchanged.</p>
*
* @param textOptions the text options
* @param textOptions the string options

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f39e5e2 — the @param now reads "the string options, as a BSON document" to distinguish it from the public StringOptions type.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 15 changed files in this pull request and generated 2 comments.

Comment on lines 24 to 33
/**
* Text options for a Queryable Encryption field that supports text queries.
*
* <p>Note: TextOptions is in Alpha and subject to backwards breaking changes.
*
* @since 5.6
* @mongodb.server.release 8.2
* @mongodb.driver.manual /core/queryable-encryption/ queryable encryption
* @deprecated Use {@link StringOptions} instead.
*/

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TextOptions is the deprecated pre-rename type kept for backward compatibility, so it intentionally retains its "Text" naming/wording, and its @deprecated tag already points to StringOptions. :driver-core:javadoc builds clean, so leaving as-is.

Comment on lines 128 to 136
/**
* The Text Options.
* The String Options.
*
* <p>Only applies when algorithm is "TextPreview".</p>
* <p>Only applies when algorithm is "String". The method name mirrors the libmongocrypt {@code textOptions}
* BSON field, which is unchanged.</p>
*
* @param textOptions the text options
* @param textOptions the string options
* @return this
* @since 5.6

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f39e5e2 — the @param now reads "the string options, as a BSON document" to distinguish it from the public StringOptions type.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 15 changed files in this pull request and generated 2 comments.

* BSON field, which is unchanged.</p>
*
* @param textOptions the text options
* @param textOptions the string options

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f39e5e2 — the @param now reads "the string options, as a BSON document" to distinguish it from the public StringOptions type.

Comment on lines +153 to +158
* <p>Currently, we support only "equality", "range", "prefix", "suffix", "substring", "prefixPreview",
* "suffixPreview" or "substringPreview" queryType.</p>
* <p>The "prefix", "suffix", "substring", "prefixPreview", "suffixPreview" and "substringPreview" query types are
* only valid with the "String" algorithm. "prefixPreview"/"suffixPreview"/"substringPreview" are deprecated
* aliases supported for servers 8.2 to pre-9.0; use "prefix"/"suffix"/"substring" on server 9.0+.</p>
* <p>It is an error to set queryType when the algorithm is not "Indexed", "Range" or "String".</p>

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f39e5e2. getQueryType()'s Javadoc now points to queryType(String) for the supported-types list, so the two can't drift apart.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 15 changed files in this pull request and generated 3 comments.

Comment on lines +131 to 135
* <p>Only applies when algorithm is "String". The method name mirrors the libmongocrypt {@code textOptions}
* BSON field, which is unchanged.</p>
*
* @param textOptions the text options
* @param textOptions the string options
* @return this

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f39e5e2 — the @param now reads "the string options, as a BSON document" to distinguish it from the public StringOptions type.

Comment on lines +153 to +158
* <p>Currently, we support only "equality", "range", "prefix", "suffix", "substring", "prefixPreview",
* "suffixPreview" or "substringPreview" queryType.</p>
* <p>The "prefix", "suffix", "substring", "prefixPreview", "suffixPreview" and "substringPreview" query types are
* only valid with the "String" algorithm. "prefixPreview"/"suffixPreview"/"substringPreview" are deprecated
* aliases supported for servers 8.2 to pre-9.0; use "prefix"/"suffix"/"substring" on server 9.0+.</p>
* <p>It is an error to set queryType when the algorithm is not "Indexed", "Range" or "String".</p>

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f39e5e2. getQueryType()'s Javadoc now points to queryType(String) for the supported-types list, so the two can't drift apart.

Comment on lines 29 to 33
* @since 5.6
* @mongodb.server.release 8.2
* @mongodb.driver.manual /core/queryable-encryption/ queryable encryption
* @deprecated Use {@link StringOptions} instead.
*/

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TextOptions is the deprecated pre-rename type kept for backward compatibility, so it intentionally retains its "Text" naming/wording, and its @deprecated tag already points to StringOptions. :driver-core:javadoc builds clean, so leaving as-is.

…n comment

- EncryptOptions.getQueryType() javadoc referenced only equality/range; point it to
  queryType(String) so it stays consistent with the supported-types list.
- MongoExplicitEncryptOptions.textOptions @param: clarify it takes the string options as a BSON document.
- Prose test: reword the libmongocrypt version comment to reflect the class-level 1.19.1 floor.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 15 changed files in this pull request and generated 2 comments.

Comment on lines 128 to 135
/**
* The Text Options.
* The String Options.
*
* <p>Only applies when algorithm is "TextPreview".</p>
* <p>Only applies when algorithm is "String". The method name mirrors the libmongocrypt {@code textOptions}
* BSON field, which is unchanged.</p>
*
* @param textOptions the text options
* @param textOptions the string options
* @return this

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The @param was already clarified to "the string options, as a BSON document". The parameter name intentionally mirrors the libmongocrypt textOptions BSON field it maps to (an internal binding class), and the description now makes the BSON-document type explicit, so I'll leave the name as-is.

Comment on lines 68 to 71
// Download the libmongocrypt per-platform tarballs (and their signatures) to jnaDownloadsDir.
// To upgrade: change downloadRevision, run `./gradlew clean downloadJnaLibs`, and verify the build.
val downloadRevision = "1.18.1"
val downloadRevision = "1.20.0"
val downloadUrlBase = "https://github.com/mongodb/libmongocrypt/releases/download/$downloadRevision"

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in the follow-up commit — the KDoc example now uses a <version> placeholder instead of a hard-coded version, so it won't go stale on future bumps.

…ion-agnostic

The example hard-coded 1.18.1, now stale after the 1.20.0 bump; use a <version> placeholder.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 15 changed files in this pull request and generated 2 comments.

Comment on lines +286 to +288
val cryptGpgHomeName =
"crypt-gpg-" + layout.buildDirectory.get().asFile.absolutePath.hashCode().toUInt().toString(16)
gnupgHome.set(layout.dir(providers.provider { File(System.getProperty("java.io.tmpdir"), cryptGpgHomeName) }))

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pre-existing code from commit 6e390ac (the gpg-homedir fix), not part of this PR's QE work. layout.buildDirectory is a stable provider and the hash is computed once at configuration time to derive a short, checkout-stable homedir name; realizing it here is low-risk. If configuration-cache hardening is wanted it'd be a separate, self-contained cleanup rather than part of the QE prefix/suffix/substring change.

Comment on lines +279 to +285
// Keep the scratch GPG keyring under the system temp dir, not the module build dir. gpg derives
// its agent socket path from the homedir, and macOS caps AF_UNIX socket paths (sun_path) at 104
// bytes. A deeply-nested checkout pushes "<module>/build/jnaLibs/gnupg/S.gpg-agent" past that
// limit, so on a fresh keyring gpg fails ("can't connect to the gpg-agent: File name too long")
// and exits non-zero even though the import/verify would otherwise succeed. A short,
// checkout-independent homedir avoids this; the hash suffix isolates builds of different
// checkouts. verify() recreates this directory on every run, so a stable name is safe.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — fixed in 97ad4a2. The VerifyLibmongocryptTask KDoc now states the scratch keyring lives under the system temp dir (not build/) and that verify() recreates it each run so the trust state is always reset, matching the gnupgHome assignment.

The VerifyLibmongocryptTask KDoc still said the keyring lives under build/ and that clean resets
trust; the homedir now lives under the system temp dir and verify() recreates it each run. Correct
the comment to match.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 15 changed files in this pull request and generated no new comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants