Skip to content

Commit 204cbb6

Browse files
RUST-1333 Support for Range Indexes (#846)
1 parent ca7bf1d commit 204cbb6

File tree

132 files changed

+61011
-298
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

132 files changed

+61011
-298
lines changed

.evergreen/MSRV-Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.evergreen/config.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1978,6 +1978,7 @@ buildvariants:
19781978
os:
19791979
- ubuntu-20.04
19801980
mongodb-version:
1981+
- "rapid"
19811982
- "6.0"
19821983
topology:
19831984
- "replica-set"

src/client/csfle/client_encryption.rs

Lines changed: 158 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,24 @@
11
//! Support for explicit encryption.
22
3+
use mongocrypt::{
4+
ctx::{Algorithm, Ctx, CtxBuilder, KmsProvider},
5+
Crypt,
6+
};
7+
use serde::{Deserialize, Serialize};
8+
use serde_with::skip_serializing_none;
9+
use typed_builder::TypedBuilder;
10+
311
use crate::{
4-
bson::Binary,
12+
bson::{
13+
doc,
14+
spec::BinarySubtype,
15+
Binary,
16+
Bson,
17+
Document,
18+
RawBinaryRef,
19+
RawBson,
20+
RawDocumentBuf,
21+
},
522
client::options::TlsOptions,
623
coll::options::CollectionOptions,
724
error::{Error, Result},
@@ -12,12 +29,6 @@ use crate::{
1229
Cursor,
1330
Namespace,
1431
};
15-
use bson::{doc, spec::BinarySubtype, RawBinaryRef, RawDocumentBuf};
16-
use mongocrypt::{
17-
ctx::{Algorithm, Ctx, KmsProvider},
18-
Crypt,
19-
};
20-
use serde::{Deserialize, Serialize};
2132

2233
use super::{options::KmsProviders, state_machine::CryptExecutor};
2334

@@ -245,7 +256,7 @@ impl ClientEncryption {
245256
/// # use mongodb::error::Result;
246257
/// # async fn func() -> Result<()> {
247258
/// # let client_encryption: ClientEncryption = todo!();
248-
/// # let key = todo!();
259+
/// # let key = String::new();
249260
/// let encrypted = client_encryption
250261
/// .encrypt(
251262
/// "plaintext",
@@ -271,20 +282,91 @@ impl ClientEncryption {
271282
algorithm,
272283
contention_factor: None,
273284
query_type: None,
285+
range_options: None,
286+
},
287+
}
288+
}
289+
290+
/// NOTE: This method is experimental only. It is not intended for public use.
291+
///
292+
/// Encrypts a match or aggregate expression with the given key.
293+
/// `EncryptExpressionAction::run` returns a [`Document`] containing the encrypted expression.
294+
///
295+
/// The expression will be encrypted using the [`Algorithm::RangePreview`] algorithm and the
296+
/// "rangePreview" query type.
297+
///
298+
/// The returned `EncryptExpressionAction` must be executed via `run`, e.g.
299+
/// ```no_run
300+
/// # use mongocrypt::ctx::Algorithm;
301+
/// # use mongodb::client_encryption::ClientEncryption;
302+
/// # use mongodb::error::Result;
303+
/// # use bson::rawdoc;
304+
/// # async fn func() -> Result<()> {
305+
/// # let client_encryption: ClientEncryption = todo!();
306+
/// # let key = String::new();
307+
/// let expression = rawdoc! {
308+
/// "$and": [
309+
/// { "field": { "$gte": 5 } },
310+
/// { "field": { "$lte": 10 } },
311+
/// ]
312+
/// };
313+
/// let encrypted_expression = client_encryption
314+
/// .encrypt_expression(
315+
/// expression,
316+
/// key,
317+
/// )
318+
/// .contention_factor(10)
319+
/// .run().await?;
320+
/// # }
321+
/// ```
322+
#[must_use]
323+
pub fn encrypt_expression(
324+
&self,
325+
expression: RawDocumentBuf,
326+
key: impl Into<EncryptKey>,
327+
) -> EncryptExpressionAction {
328+
EncryptExpressionAction {
329+
client_enc: self,
330+
value: expression,
331+
opts: EncryptOptions {
332+
key: key.into(),
333+
algorithm: Algorithm::RangePreview,
334+
contention_factor: None,
335+
query_type: Some("rangePreview".into()),
336+
range_options: None,
274337
},
275338
}
276339
}
277340

278-
async fn encrypt_final(&self, value: bson::RawBson, opts: EncryptOptions) -> Result<Binary> {
279-
let ctx = self.encrypt_ctx(value, &opts)?;
341+
async fn encrypt_final(&self, value: RawBson, opts: EncryptOptions) -> Result<Binary> {
342+
let builder = self.get_ctx_builder(&opts)?;
343+
let ctx = builder.build_explicit_encrypt(value)?;
280344
let result = self.exec.run_ctx(ctx, None).await?;
281345
let bin_ref = result
282346
.get_binary("v")
283347
.map_err(|e| Error::internal(format!("invalid encryption result: {}", e)))?;
284348
Ok(bin_ref.to_binary())
285349
}
286350

287-
fn encrypt_ctx(&self, value: bson::RawBson, opts: &EncryptOptions) -> Result<Ctx> {
351+
async fn encrypt_expression_final(
352+
&self,
353+
value: RawDocumentBuf,
354+
opts: EncryptOptions,
355+
) -> Result<Document> {
356+
let builder = self.get_ctx_builder(&opts)?;
357+
let ctx = builder.build_explicit_encrypt_expression(value)?;
358+
let result = self.exec.run_ctx(ctx, None).await?;
359+
let doc_ref = result
360+
.get_document("v")
361+
.map_err(|e| Error::internal(format!("invalid encryption result: {}", e)))?;
362+
let doc = doc_ref
363+
.to_owned()
364+
.to_document()
365+
.map_err(|e| Error::internal(format!("invalid encryption result: {}", e)))?;
366+
Ok(doc)
367+
}
368+
369+
fn get_ctx_builder(&self, opts: &EncryptOptions) -> Result<CtxBuilder> {
288370
let mut builder = self.crypt.ctx_builder();
289371
match &opts.key {
290372
EncryptKey::Id(id) => {
@@ -301,7 +383,11 @@ impl ClientEncryption {
301383
if let Some(qtype) = &opts.query_type {
302384
builder = builder.query_type(qtype)?;
303385
}
304-
Ok(builder.build_explicit_encrypt(value)?)
386+
if let Some(range_options) = &opts.range_options {
387+
let options_doc = bson::to_document(range_options)?;
388+
builder = builder.range_options(options_doc)?;
389+
}
390+
Ok(builder)
305391
}
306392

307393
/// Decrypts an encrypted value (BSON binary of subtype 6).
@@ -444,6 +530,29 @@ pub(crate) struct EncryptOptions {
444530
pub(crate) algorithm: Algorithm,
445531
pub(crate) contention_factor: Option<i64>,
446532
pub(crate) query_type: Option<String>,
533+
pub(crate) range_options: Option<RangeOptions>,
534+
}
535+
536+
/// NOTE: These options are experimental and not intended for public use.
537+
///
538+
/// The index options for a Queryable Encryption field supporting "rangePreview" queries.
539+
/// The options set must match the values set in the encryptedFields of the destination collection.
540+
#[skip_serializing_none]
541+
#[derive(Clone, Default, Debug, Serialize, TypedBuilder)]
542+
#[builder(field_defaults(default, setter(into)))]
543+
#[non_exhaustive]
544+
pub struct RangeOptions {
545+
/// The minimum value. This option must be set if `precision` is set.
546+
pub min: Option<Bson>,
547+
548+
/// The maximum value. This option must be set if `precision` is set.
549+
pub max: Option<Bson>,
550+
551+
/// The sparsity.
552+
pub sparsity: i64,
553+
554+
/// The precision. This value must only be set for Double and Decimal128 fields.
555+
pub precision: Option<i32>,
447556
}
448557

449558
/// A pending `ClientEncryption::encrypt` action.
@@ -466,11 +575,47 @@ impl<'a> EncryptAction<'a> {
466575
}
467576

468577
/// Set the query type.
469-
#[allow(clippy::redundant_clone)]
470578
pub fn query_type(mut self, qtype: impl Into<String>) -> Self {
471579
self.opts.query_type = Some(qtype.into());
472580
self
473581
}
582+
583+
/// NOTE: This method is experimental and not intended for public use.
584+
///
585+
/// Set the range options. This method should only be called when the algorithm is
586+
/// [`Algorithm::RangePreview`].
587+
pub fn range_options(mut self, range_options: impl Into<Option<RangeOptions>>) -> Self {
588+
self.opts.range_options = range_options.into();
589+
self
590+
}
591+
}
592+
593+
/// A pending `ClientEncryption::encrypt_expression` action.
594+
pub struct EncryptExpressionAction<'a> {
595+
client_enc: &'a ClientEncryption,
596+
value: RawDocumentBuf,
597+
opts: EncryptOptions,
598+
}
599+
600+
impl<'a> EncryptExpressionAction<'a> {
601+
/// Execute the encryption of the expression.
602+
pub async fn run(self) -> Result<Document> {
603+
self.client_enc
604+
.encrypt_expression_final(self.value, self.opts)
605+
.await
606+
}
607+
608+
/// Set the contention factor.
609+
pub fn contention_factor(mut self, factor: impl Into<Option<i64>>) -> Self {
610+
self.opts.contention_factor = factor.into();
611+
self
612+
}
613+
614+
/// Set the range options.
615+
pub fn range_options(mut self, range_options: impl Into<Option<RangeOptions>>) -> Self {
616+
self.opts.range_options = range_options.into();
617+
self
618+
}
474619
}
475620

476621
/// An encryption key reference.

src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
//! This crate contains the officially supported MongoDB Rust driver, a
22
//! client side library that can be used to interact with MongoDB deployments
33
//! in Rust applications. It uses the [`bson`] crate for BSON support.
4-
//! The driver contains a fully async API that supports either [`tokio`] (default)
4+
//! The driver contains a fully async API that supports either [`tokio`](https://docs.rs/tokio) (default)
55
//! or [`async-std`](https://docs.rs/async_std), depending on the feature flags set. The driver also has
6-
//! a sync API that may be enabled via the `"sync"` feature flag.
6+
//! a sync API that may be enabled via the `"sync"` or `"tokio-sync"` feature flag.
77
//!
88
//! # Installation
99
//!

0 commit comments

Comments
 (0)