4141from pymongo import _csot
4242from pymongo .cursor import Cursor
4343from pymongo .daemon import _spawn_daemon
44- from pymongo .encryption_options import AutoEncryptionOpts
44+ from pymongo .encryption_options import AutoEncryptionOpts , RangeOpts
4545from pymongo .errors import (
4646 ConfigurationError ,
4747 EncryptionError ,
@@ -416,6 +416,14 @@ class Algorithm(str, enum.Enum):
416416
417417 .. versionadded:: 4.2
418418 """
419+ RANGEPREVIEW = "RangePreview"
420+ """RangePreview.
421+
422+ .. note:: Support for Range queries is in beta.
423+ Backwards-breaking changes may be made before the final release.
424+
425+ .. versionadded:: 4.4
426+ """
419427
420428
421429class QueryType (str , enum .Enum ):
@@ -430,6 +438,9 @@ class QueryType(str, enum.Enum):
430438 EQUALITY = "equality"
431439 """Used to encrypt a value for an equality query."""
432440
441+ RANGEPREVIEW = "rangePreview"
442+ """Used to encrypt a value for a range query."""
443+
433444
434445class ClientEncryption (Generic [_DocumentType ]):
435446 """Explicit client-side field level encryption."""
@@ -627,6 +638,45 @@ def create_data_key(
627638 key_material = key_material ,
628639 )
629640
641+ def _encrypt_helper (
642+ self ,
643+ value ,
644+ algorithm ,
645+ key_id = None ,
646+ key_alt_name = None ,
647+ query_type = None ,
648+ contention_factor = None ,
649+ range_opts = None ,
650+ is_expression = False ,
651+ ):
652+ self ._check_closed ()
653+ if key_id is not None and not (
654+ isinstance (key_id , Binary ) and key_id .subtype == UUID_SUBTYPE
655+ ):
656+ raise TypeError ("key_id must be a bson.binary.Binary with subtype 4" )
657+
658+ doc = encode (
659+ {"v" : value },
660+ codec_options = self ._codec_options ,
661+ )
662+ if range_opts :
663+ range_opts = encode (
664+ range_opts .document ,
665+ codec_options = self ._codec_options ,
666+ )
667+ with _wrap_encryption_errors ():
668+ encrypted_doc = self ._encryption .encrypt (
669+ value = doc ,
670+ algorithm = algorithm ,
671+ key_id = key_id ,
672+ key_alt_name = key_alt_name ,
673+ query_type = query_type ,
674+ contention_factor = contention_factor ,
675+ range_opts = range_opts ,
676+ is_expression = is_expression ,
677+ )
678+ return decode (encrypted_doc )["v" ] # type: ignore[index]
679+
630680 def encrypt (
631681 self ,
632682 value : Any ,
@@ -635,6 +685,7 @@ def encrypt(
635685 key_alt_name : Optional [str ] = None ,
636686 query_type : Optional [str ] = None ,
637687 contention_factor : Optional [int ] = None ,
688+ range_opts : Optional [RangeOpts ] = None ,
638689 ) -> Binary :
639690 """Encrypt a BSON value with a given key and algorithm.
640691
@@ -655,10 +706,10 @@ def encrypt(
655706 when the algorithm is :attr:`Algorithm.INDEXED`. An integer value
656707 *must* be given when the :attr:`Algorithm.INDEXED` algorithm is
657708 used.
709+ - `range_opts`: **(BETA)** An instance of RangeOpts.
658710
659- .. note:: `query_type` and `contention_factor` are part of the
660- Queryable Encryption beta. Backwards-breaking changes may be made before the
661- final release.
711+ .. note:: `query_type`, `contention_factor` and `range_opts` are part of the Queryable Encryption beta.
712+ Backwards-breaking changes may be made before the final release.
662713
663714 :Returns:
664715 The encrypted value, a :class:`~bson.binary.Binary` with subtype 6.
@@ -667,23 +718,66 @@ def encrypt(
667718 Added the `query_type` and `contention_factor` parameters.
668719
669720 """
670- self ._check_closed ()
671- if key_id is not None and not (
672- isinstance (key_id , Binary ) and key_id .subtype == UUID_SUBTYPE
673- ):
674- raise TypeError ("key_id must be a bson.binary.Binary with subtype 4" )
721+ return self ._encrypt_helper (
722+ value = value ,
723+ algorithm = algorithm ,
724+ key_id = key_id ,
725+ key_alt_name = key_alt_name ,
726+ query_type = query_type ,
727+ contention_factor = contention_factor ,
728+ range_opts = range_opts ,
729+ is_expression = False ,
730+ )
675731
676- doc = encode ({"v" : value }, codec_options = self ._codec_options )
677- with _wrap_encryption_errors ():
678- encrypted_doc = self ._encryption .encrypt (
679- doc ,
680- algorithm ,
681- key_id = key_id ,
682- key_alt_name = key_alt_name ,
683- query_type = query_type ,
684- contention_factor = contention_factor ,
685- )
686- return decode (encrypted_doc )["v" ] # type: ignore[index]
732+ def encrypt_expression (
733+ self ,
734+ expression : Mapping [str , Any ],
735+ algorithm : str ,
736+ key_id : Optional [Binary ] = None ,
737+ key_alt_name : Optional [str ] = None ,
738+ query_type : Optional [str ] = None ,
739+ contention_factor : Optional [int ] = None ,
740+ range_opts : Optional [RangeOpts ] = None ,
741+ ) -> RawBSONDocument :
742+ """Encrypt a BSON expression with a given key and algorithm.
743+
744+ Note that exactly one of ``key_id`` or ``key_alt_name`` must be
745+ provided.
746+
747+ :Parameters:
748+ - `expression`: **(BETA)** The BSON aggregate or match expression to encrypt.
749+ - `algorithm` (string): The encryption algorithm to use. See
750+ :class:`Algorithm` for some valid options.
751+ - `key_id`: Identifies a data key by ``_id`` which must be a
752+ :class:`~bson.binary.Binary` with subtype 4 (
753+ :attr:`~bson.binary.UUID_SUBTYPE`).
754+ - `key_alt_name`: Identifies a key vault document by 'keyAltName'.
755+ - `query_type` (str): **(BETA)** The query type to execute. See
756+ :class:`QueryType` for valid options.
757+ - `contention_factor` (int): **(BETA)** The contention factor to use
758+ when the algorithm is :attr:`Algorithm.INDEXED`. An integer value
759+ *must* be given when the :attr:`Algorithm.INDEXED` algorithm is
760+ used.
761+ - `range_opts`: **(BETA)** An instance of RangeOpts.
762+
763+ .. note:: Support for range queries is in beta.
764+ Backwards-breaking changes may be made before the final release.
765+
766+ :Returns:
767+ The encrypted expression, a :class:`~bson.RawBSONDocument`.
768+
769+ .. versionadded:: 4.4
770+ """
771+ return self ._encrypt_helper (
772+ value = expression ,
773+ algorithm = algorithm ,
774+ key_id = key_id ,
775+ key_alt_name = key_alt_name ,
776+ query_type = query_type ,
777+ contention_factor = contention_factor ,
778+ range_opts = range_opts ,
779+ is_expression = True ,
780+ )
687781
688782 def decrypt (self , value : Binary ) -> Any :
689783 """Decrypt an encrypted value.
0 commit comments