from django.core.exceptions import ImproperlyConfigured
from django.core.management.base import CommandError
from django.db import NotSupportedError
from django.db.backends.base.features import BaseDatabaseFeatures
from django.utils.functional import cached_property
from pymongo.errors import OperationFailure

try:
    from .gis.features import GISFeatures
except ImproperlyConfigured:
    # GIS libraries (GDAL/GEOS) not installed.
    class GISFeatures:
        pass


class DatabaseFeatures(GISFeatures, BaseDatabaseFeatures):
    minimum_database_version = (7, 0)
    allow_sliced_subqueries_with_in = False
    allows_multiple_constraints_on_same_fields = False
    can_create_inline_fk = False
    can_introspect_foreign_keys = False
    can_return_rows_from_bulk_insert = True
    greatest_least_ignores_nulls = True
    has_json_object_function = False
    has_native_json_field = True
    rounds_to_even = True
    supports_boolean_expr_in_select_clause = True
    supports_collation_on_charfield = False
    supports_column_check_constraints = False
    supports_date_lookup_using_string = False
    supports_deferrable_unique_constraints = False
    supports_explaining_query_execution = True
    supports_expression_defaults = False
    supports_expression_indexes = False
    supports_foreign_keys = False
    supports_ignore_conflicts = False
    supports_json_field_contains = False
    # BSON Date type doesn't support microsecond precision.
    supports_microsecond_precision = False
    supports_nulls_distinct_unique_constraints = True
    supports_paramstyle_pyformat = False
    supports_sequence_reset = False
    supports_slicing_ordering_in_compound = True
    supports_table_check_constraints = False
    supports_temporal_subtraction = True
    # MongoDB stores datetimes in UTC.
    supports_timezones = False
    # While MongoDB supports transactions in some configurations (see
    # DatabaseFeatures._supports_transactions), django.db.transaction.atomic() is a
    # no-op on this backend.
    supports_transactions = False
    supports_unspecified_pk = True
    uses_savepoints = False

    _django_test_expected_failures = {
        # $concat only supports strings, not int
        "db_functions.text.test_concat.ConcatTests.test_concat_non_str",
        # QuerySet.order_by() with annotation transform doesn't work:
        # "Expression $mod takes exactly 2 arguments. 1 were passed in"
        # https://github.com/django/django/commit/b0ad41198b3e333f57351e3fce5a1fb47f23f376
        "aggregation.tests.AggregateTestCase.test_order_by_aggregate_transform",
        # 'NulledTransform' object has no attribute 'as_mql'.
        "lookup.tests.LookupTests.test_exact_none_transform",
        # BaseExpression.convert_value() crashes with Decimal128.
        "aggregation.tests.AggregateTestCase.test_combine_different_types",
        "annotations.tests.NonAggregateAnnotationTestCase.test_combined_f_expression_annotation_with_aggregation",
        # Pattern lookups that use regexMatch don't work on JSONField:
        # Unsupported conversion from array to string in $convert
        "model_fields.test_jsonfield.TestQuerying.test_icontains",
        # Unexpected alias_refcount in alias_map.
        "queries.tests.Queries1Tests.test_order_by_tables",
        # Pattern lookups (startswith, regex, etc.) don't work on non-string
        # fields: https://jira.mongodb.org/browse/INTPYTHON-734
        "admin_changelist.tests.ChangeListTests.test_pk_in_search_fields",
        "admin_changelist.tests.ChangeListTests.test_related_field_multiple_search_terms",
        "lookup.tests.LookupTests.test_lookup_int_as_str",
        "lookup.tests.LookupTests.test_regex_non_string",
        # The $sum aggregation returns 0 instead of None for null.
        "aggregation.test_filter_argument.FilteredAggregateTests.test_plain_annotate",
        "aggregation.tests.AggregateTestCase.test_aggregation_default_passed_another_aggregate",
        "aggregation.tests.AggregateTestCase.test_annotation_expressions",
        "aggregation.tests.AggregateTestCase.test_reverse_fkey_annotate",
        "aggregation_regress.tests.AggregationTests.test_annotation_disjunction",
        "aggregation_regress.tests.AggregationTests.test_decimal_aggregate_annotation_filter",
        # subclasses of BaseDatabaseWrapper may require an is_usable() method
        "backends.tests.BackendTestCase.test_is_usable_after_database_disconnects",
        # Connection creation doesn't follow the usual Django API.
        "backends.tests.ThreadTests.test_pass_connection_between_threads",
        "backends.tests.ThreadTests.test_default_connection_thread_local",
        # Object of type ObjectId is not JSON serializable.
        "auth_tests.test_views.LoginTest.test_login_session_without_hash_session_key",
        # GenericRelation.value_to_string() assumes integer pk.
        "contenttypes_tests.test_fields.GenericRelationTests.test_value_to_string",
        # ArrayField's contained_by lookup crashes with Exists: "both operands "
        # of $setIsSubset must be arrays. Second argument is of type: null"
        # https://jira.mongodb.org/browse/SERVER-99186
        "model_fields_.test_arrayfield.QueryingTests.test_contained_by_subquery",
        # Value.as_mql() doesn't call output_field.get_db_prep_save():
        # https://github.com/mongodb/django-mongodb-backend/issues/282
        "model_fields.test_jsonfield.TestSaveLoad.test_bulk_update_custom_get_prep_value",
        # CheckConstraint(condition=models.Q(price__gte=0)) doesn't accept null
        # values because {'$gte': [None, 0]} returns False instead of NULL like
        # in SQL.
        "constraints.tests.CheckConstraintTests.test_validate_nullable_field_with_none",
        # bulk_create() population of _order doesn't work because of ObjectId
        # type mismatch when querying object_id CharField.
        # https://github.com/django/django/commit/953095d1e603fe0f8f01175b1409ca23818dcff9
        "contenttypes_tests.test_order_with_respect_to.OrderWithRespectToGFKTests.test_bulk_create_allows_duplicate_order_values",
        "contenttypes_tests.test_order_with_respect_to.OrderWithRespectToGFKTests.test_bulk_create_mixed_scenario",
        "contenttypes_tests.test_order_with_respect_to.OrderWithRespectToGFKTests.test_bulk_create_respects_mixed_manual_order",
        "contenttypes_tests.test_order_with_respect_to.OrderWithRespectToGFKTests.test_bulk_create_with_existing_children",
    }

    @cached_property
    def django_test_expected_failures(self):
        expected_failures = super().django_test_expected_failures
        expected_failures.update(self._django_test_expected_failures)
        return expected_failures

    _django_test_skips = {
        "Database defaults aren't supported by MongoDB.": {
            # bson.errors.InvalidDocument: cannot encode object:
            # <django.db.models.expressions.DatabaseDefault
            "basic.tests.ModelInstanceCreationTests.test_save_primary_with_db_default",
            "basic.tests.ModelInstanceCreationTests.test_save_primary_with_falsey_db_default",
            "constraints.tests.UniqueConstraintTests.test_database_default",
            "field_defaults.tests.DefaultTests",
            "migrations.test_operations.OperationTests.test_add_field_both_defaults",
            "migrations.test_operations.OperationTests.test_add_field_database_default",
            "migrations.test_operations.OperationTests.test_add_field_database_default_special_char_escaping",
            "migrations.test_operations.OperationTests.test_alter_field_add_database_default",
            "migrations.test_operations.OperationTests.test_alter_field_change_blank_nullable_database_default_to_not_null",
            "migrations.test_operations.OperationTests.test_alter_field_change_default_to_database_default",
            "migrations.test_operations.OperationTests.test_alter_field_change_nullable_to_database_default_not_null",
            "migrations.test_operations.OperationTests.test_alter_field_change_nullable_to_decimal_database_default_not_null",
            "schema.tests.SchemaTests.test_db_default_output_field_resolving",
            "schema.tests.SchemaTests.test_rename_keep_db_default",
            "validation.test_unique.PerformUniqueChecksTest.test_unique_db_default",
        },
        "Insert expressions aren't supported.": {
            "basic.tests.ModelTest.test_save_expressions",
            "bulk_create.tests.BulkCreateTests.test_bulk_insert_now",
            "bulk_create.tests.BulkCreateTests.test_bulk_insert_expressions",
            "expressions.tests.BasicExpressionsTests.test_new_object_create",
            "expressions.tests.BasicExpressionsTests.test_new_object_save",
            "expressions.tests.BasicExpressionsTests.test_object_create_with_aggregate",
            "expressions.tests.BasicExpressionsTests.test_object_create_with_f_expression_in_subquery",
            "expressions.tests.BasicExpressionsTests.test_object_update_unsaved_objects",
            # PI()
            "db_functions.math.test_round.RoundTests.test_decimal_with_precision",
            "db_functions.math.test_round.RoundTests.test_float_with_precision",
        },
        "MongoDB doesn't rename an index when a field is renamed.": {
            "migrations.test_operations.OperationTests.test_rename_field_index_together",
            "migrations.test_operations.OperationTests.test_rename_field_unique_together",
        },
        "AutoField not supported.": {
            "bulk_create.tests.BulkCreateTests.test_bulk_insert_nullable_fields",
            "introspection.tests.IntrospectionTests.test_sequence_list",
            "lookup.tests.LookupTests.test_filter_by_reverse_related_field_transform",
            "lookup.tests.LookupTests.test_in_ignore_none_with_unhashable_items",
            "m2m_through_regress.tests.ThroughLoadDataTestCase.test_sequence_creation",
            "many_to_many.tests.ManyToManyTests.test_add_remove_invalid_type",
            "many_to_one.tests.ManyToOneTests.test_fk_to_smallautofield",
            "many_to_one.tests.ManyToOneTests.test_fk_to_bigautofield",
            "migrations.test_operations.OperationTests.test_autofield__bigautofield_foreignfield_growth",
            "migrations.test_operations.OperationTests.test_model_with_bigautofield",
            "migrations.test_operations.OperationTests.test_smallfield_autofield_foreignfield_growth",
            "migrations.test_operations.OperationTests.test_smallfield_bigautofield_foreignfield_growth",
            "model_fields.test_autofield.AutoFieldTests",
            "model_fields.test_autofield.BigAutoFieldTests",
            "model_fields.test_autofield.SmallAutoFieldTests",
            "queries.tests.TestInvalidValuesRelation.test_invalid_values",
            "schema.tests.SchemaTests.test_alter_autofield_pk_to_bigautofield_pk",
            "schema.tests.SchemaTests.test_alter_autofield_pk_to_smallautofield_pk",
        },
        "Converters aren't run on returning fields from insert.": {
            # Unsure this is needed for this backend. Can implement by request.
            # https://github.com/django/django/commit/d9de74141e8a920940f1b91ed0a3ccb835b55729
            "custom_pk.tests.CustomPKTests.test_auto_field_subclass_bulk_create",
            "custom_pk.tests.CustomPKTests.test_auto_field_subclass_create",
        },
        "MongoDB does not enforce PositiveIntegerField constraint.": {
            "model_fields.test_integerfield.PositiveIntegerFieldTests.test_negative_values",
        },
        "Test assumes integer primary key.": {
            "custom_managers.tests.CustomManagersRegressTestCase.test_save_clears_annotations_from_base_manager",
            "db_functions.comparison.test_cast.CastTests.test_cast_to_integer_foreign_key",
            "expressions.tests.BasicExpressionsTests.test_nested_subquery_outer_ref_with_autofield",
            "model_fields.test_foreignkey.ForeignKeyTests.test_to_python",
            "queries.test_qs_combinators.QuerySetSetOperationTests.test_order_raises_on_non_selected_column",
            "queries.tests.RelatedLookupTypeTests.test_values_queryset_lookup",
            "queries.tests.ValuesSubqueryTests.test_values_in_subquery",
            "sites_tests.tests.CreateDefaultSiteTests.test_no_site_id",
        },
        "Test inspects query for SQL": {
            "aggregation.tests.AggregateAnnotationPruningTests.test_non_aggregate_annotation_pruned",
            "aggregation.tests.AggregateAnnotationPruningTests.test_unreferenced_aggregate_annotation_pruned",
            "aggregation.tests.AggregateAnnotationPruningTests.test_unused_aliased_aggregate_pruned",
            "aggregation.tests.AggregateAnnotationPruningTests.test_referenced_aggregate_annotation_kept",
            "aggregation.tests.AggregateTestCase.test_count_star",
            "delete.tests.DeletionTests.test_only_referenced_fields_selected",
            "expressions.tests.ExistsTests.test_optimizations",
            "lookup.tests.LookupTests.test_in_ignore_none",
            "lookup.tests.LookupTests.test_lookup_direct_value_rhs_unwrapped",
            "lookup.tests.LookupTests.test_textfield_exact_null",
            "many_to_many.tests.ManyToManyQueryTests.test_count_join_optimization_disabled",
            "many_to_many.tests.ManyToManyQueryTests.test_exists_join_optimization_disabled",
            "many_to_many.tests.ManyToManyTests.test_custom_default_manager_exists_count",
            "many_to_one.tests.ManyToOneTests.test_selects",
            "migrations.test_commands.MigrateTests.test_migrate_syncdb_app_label",
            "migrations.test_commands.MigrateTests.test_migrate_syncdb_deferred_sql_executed_with_schemaeditor",
            "model_fields.test_jsonfield.TestQuerying.test_key_sql_injection_escape",
            "prefetch_related.test_prefetch_related_objects.PrefetchRelatedObjectsTests.test_foreignkey_reverse",
            "prefetch_related.tests.MultiTableInheritanceTest.test_child_link_prefetch",
            "queries.tests.ExistsSql.test_exists",
            "queries.tests.Queries6Tests.test_col_alias_quoted",
            "schema.tests.SchemaTests.test_rename_column_renames_deferred_sql_references",
            "schema.tests.SchemaTests.test_rename_table_renames_deferred_sql_references",
        },
        "Test checks for SQL in str(queryset.query)": {
            "aggregation_regress.tests.AggregationTests.test_more_more5",
            "aggregation_regress.tests.AggregationTests.test_reverse_join_trimming",
            "aggregation_regress.tests.JoinPromotionTests",
            "custom_lookups.tests.YearLteTests",
            "custom_lookups.tests.CustomizedMethodsTests",
            "expressions.tests.BasicExpressionsTests.test_subquery_sql",
            "expressions.tests.BasicExpressionsTests.test_ticket_18375_chained_filters",
            "expressions.tests.BasicExpressionsTests.test_ticket_18375_join_reuse",
            "expressions.tests.BasicExpressionsTests.test_ticket_18375_kwarg_ordering",
            "expressions.tests.BasicExpressionsTests.test_ticket_18375_kwarg_ordering_2",
            "expressions_case.tests.CaseExpressionTests.test_m2m_reuse",
            "generic_relations_regress.tests.GenericRelationTests.test_join_reuse",
            "lookup.tests.LookupTests.test_in_keeps_value_ordering",
            "model_forms.tests.ModelMultipleChoiceFieldTests.test_clean_does_deduplicate_values",
            "model_inheritance.tests.ModelInheritanceTests.test_inherited_ordering_pk_desc",
            "model_inheritance_regress.tests.ModelInheritanceTest.test_inheritance_joins",
            "ordering.tests.OrderingTests.test_order_by_f_expression_duplicates",
            "queries.tests.DisjunctionPromotionTests",
            "queries.tests.JoinReuseTest.test_fk_reuse",
            "queries.tests.JoinReuseTest.test_fk_reuse_annotation",
            "queries.tests.JoinReuseTest.test_fk_reuse_disjunction",
            "queries.tests.JoinReuseTest.test_fk_reuse_order_by",
            "queries.tests.JoinReuseTest.test_fk_reuse_select_related",
            "queries.tests.JoinReuseTest.test_revfk_noreuse",
            "queries.tests.JoinReuseTest.test_revo2o_reuse",
            "queries.tests.NullJoinPromotionOrTest.test_null_join_demotion",
            "queries.tests.NullableRelOrderingTests.test_join_already_in_query",
            "queries.tests.Queries1Tests.test_order_by_join_unref",
            "queries.tests.Queries1Tests.test_subquery_condition",
            "queries.tests.Queries4Tests.test_order_by_resetting",
            "queries.tests.Queries6Tests.test_nested_queries_sql",
            "queries.tests.Queries6Tests.test_ticket_11320",
            "queries.tests.ReverseJoinTrimmingTest.test_reverse_trimming",
            "queries.tests.ValuesJoinPromotionTests.test_non_nullable_fk_not_promoted",
            "queries.tests.ValuesJoinPromotionTests.test_values_no_promotion_for_existing",
            "queries.tests.Ticket18785Tests.test_ticket_18785",
            "select_for_update.tests.SelectForUpdateTests.test_ordered_select_for_update",
            "select_related_regress.tests.SelectRelatedRegressTests.test_null_join_promotion",
            "select_related_regress.tests.SelectRelatedRegressTests.test_regression_7110",
        },
        "Custom aggregations/functions with SQL don't work on MongoDB.": {
            "aggregation.tests.AggregateTestCase.test_add_implementation",
            "aggregation.tests.AggregateTestCase.test_multi_arg_aggregate",
            "aggregation.tests.AggregateTestCase.test_empty_result_optimization",
            "annotations.tests.NonAggregateAnnotationTestCase.test_custom_functions",
            "annotations.tests.NonAggregateAnnotationTestCase.test_custom_functions_can_ref_other_functions",
        },
        "Bilateral transform not implemented.": {
            "db_functions.tests.FunctionTests.test_func_transform_bilateral",
            "db_functions.tests.FunctionTests.test_func_transform_bilateral_multivalue",
        },
        "MongoDB does not support this database function.": {
            "db_functions.math.test_sign.SignTests",
            "db_functions.text.test_chr.ChrTests",
            "db_functions.text.test_md5.MD5Tests",
            "db_functions.text.test_ord.OrdTests",
            "db_functions.text.test_pad.PadTests",
            "db_functions.text.test_repeat.RepeatTests",
            "db_functions.text.test_reverse.ReverseTests",
            "db_functions.text.test_right.RightTests",
            "db_functions.text.test_sha1.SHA1Tests",
            "db_functions.text.test_sha224.SHA224Tests",
            "db_functions.text.test_sha256.SHA256Tests",
            "db_functions.text.test_sha384.SHA384Tests",
            "db_functions.text.test_sha512.SHA512Tests",
        },
        "MongoDB can't annotate ($project) a function like PI().": {
            "aggregation.tests.AggregateTestCase.test_aggregation_default_using_decimal_from_database",
            "db_functions.math.test_pi.PiTests.test",
        },
        "Can't cast from date to datetime without MongoDB interpreting the new value in UTC.": {
            "db_functions.comparison.test_cast.CastTests.test_cast_from_db_date_to_datetime",
            "db_functions.comparison.test_cast.CastTests.test_cast_from_db_datetime_to_time",
        },
        "Casting datetime/timedelta literals has microsecond differences.": {
            "db_functions.comparison.test_cast.CastTests.test_cast_from_python_to_datetime",
            "db_functions.comparison.test_cast.CastTests.test_cast_to_duration",
        },
        "inspectdb is not supported.": {
            "inspectdb.tests.InspectDBTestCase",
            "inspectdb.tests.InspectDBTransactionalTests",
        },
        "DatabaseIntrospection.get_table_description() not supported.": {
            "introspection.tests.IntrospectionTests.test_bigautofield",
            "introspection.tests.IntrospectionTests.test_get_table_description_col_lengths",
            "introspection.tests.IntrospectionTests.test_get_table_description_names",
            "introspection.tests.IntrospectionTests.test_get_table_description_nullable",
            "introspection.tests.IntrospectionTests.test_get_table_description_types",
            "introspection.tests.IntrospectionTests.test_smallautofield",
        },
        "MongoDB can't introspect primary key.": {
            "introspection.tests.IntrospectionTests.test_get_primary_key_column",
            "schema.tests.SchemaTests.test_alter_primary_key_the_same_name",
            "schema.tests.SchemaTests.test_primary_key",
        },
        "Known issue querying JSONField.": {
            # An ExpressionWrapper annotation with KeyTransform followed by
            # .filter(expr__isnull=False) doesn't use KeyTransformIsNull as it
            # needs to.
            "model_fields.test_jsonfield.TestQuerying.test_expression_wrapper_key_transform",
            # There is no way to distinguish between a JSON "null" (represented
            # by Value(None, JSONField())) and a SQL null (queried using the
            # isnull lookup). Both of these queries return both nulls.
            "model_fields.test_jsonfield.TestSaveLoad.test_json_null_different_from_sql_null",
            # Some queries with Q objects, e.g. Q(value__foo="bar"), don't work
            # properly, particularly with QuerySet.exclude().
            "model_fields.test_jsonfield.TestQuerying.test_lookup_exclude",
            "model_fields.test_jsonfield.TestQuerying.test_lookup_exclude_nonexistent_key",
            # Queries like like QuerySet.filter(value__j=None) incorrectly
            # returns objects where the key doesn't exist.
            "model_fields.test_jsonfield.TestQuerying.test_none_key",
            "model_fields.test_jsonfield.TestQuerying.test_none_key_exclude",
        },
        "Queries without a collection aren't supported on MongoDB.": {
            "queries.test_q.QCheckTests",
            "queries.test_query.TestQueryNoModel",
        },
        "MongoDB doesn't use CursorDebugWrapper.": {
            # It prevents RuntimeWarning for import-time queries from working.
            # https://github.com/django/django/commit/fbd16438f46bc2128926958ad24331da5d1b406f
            "apps.tests.QueryPerformingAppTests",
            "backends.tests.LastExecutedQueryTest.test_last_executed_query",
            "backends.tests.LastExecutedQueryTest.test_last_executed_query_with_duplicate_params",
            "backends.tests.LastExecutedQueryTest.test_query_encoding",
        },
        "Test not applicable for MongoDB's SQLCompiler.": {
            "queries.test_iterator.QuerySetIteratorTests",
        },
        "Support for views not implemented.": {
            "introspection.tests.IntrospectionTests.test_table_names_with_views",
        },
        "Connection health checks not implemented.": {
            "backends.base.test_base.ConnectionHealthChecksTests",
        },
        "transaction.atomic() is not supported.": {
            "backends.base.test_base.DatabaseWrapperLoggingTests",
            "basic.tests.SelectOnSaveTests.test_select_on_save_lying_update",
            "migrations.test_executor.ExecutorTests.test_atomic_operation_in_non_atomic_migration",
            "migrations.test_operations.OperationTests.test_run_python_atomic",
        },
        "transaction.rollback() is not supported.": {
            "transactions.tests.AtomicMiscTests.test_mark_for_rollback_on_error_in_autocommit",
            "transactions.tests.AtomicMiscTests.test_mark_for_rollback_on_error_in_transaction",
            "transactions.tests.NonAutocommitTests.test_orm_query_after_error_and_rollback",
        },
        "migrate --fake-initial is not supported.": {
            "migrations.test_commands.MigrateTests.test_migrate_fake_initial",
            "migrations.test_commands.MigrateTests.test_migrate_fake_split_initial",
            "migrations.test_executor.ExecutorTests.test_soft_apply",
        },
        "Test works in isolation but fails on CI.": {
            # Probably something to do with lack of transaction support.
            "migration_test_data_persistence.tests.MigrationDataNormalPersistenceTestCase.test_persistence",
        },
        "Database caching not implemented.": {
            "cache.tests.CreateCacheTableForDBCacheTests",
            "cache.tests.DBCacheTests",
            "cache.tests.DBCacheWithTimeZoneTests",
        },
        "FilteredRelation not supported.": {
            # https://github.com/mongodb/django-mongodb-backend/issues/157
            "filtered_relation.tests.FilteredRelationAggregationTests",
            "filtered_relation.tests.FilteredRelationAnalyticalAggregationTests",
            "filtered_relation.tests.FilteredRelationTests",
            "queryset_pickle.tests.PickleabilityTestCase.test_pickle_filteredrelation",
        },
        "Broken test.": {
            # This test uses a database router that only allows the auth_user
            # table to be created on "other", but then writes users to
            # "default". It can raise IntegrityError when run alongside other
            # tests because the auth_user table isn't flushed between tests.
            "multiple_database.tests.AuthTestCase.test_dumpdata",
        },
        "ForeignObject is not supported.": {
            "foreign_object.test_agnostic_order_trimjoin.TestLookupQuery.test_deep_mixed_backward",
            "foreign_object.test_empty_join.RestrictedConditionsTests",
            "foreign_object.tests.MultiColumnFKTests",
            "foreign_object.tests.TestExtraJoinFilterQ",
        },
        "Tuple lookups are not supported.": {
            "foreign_object.test_tuple_lookups.TupleLookupsTests",
        },
        "ColPairs is not supported.": {
            "auth_tests.test_views.CustomUserCompositePrimaryKeyPasswordResetTest",
            "composite_pk.test_aggregate.CompositePKAggregateTests",
            "composite_pk.test_create.CompositePKCreateTests",
            "composite_pk.test_delete.CompositePKDeleteTests",
            "composite_pk.test_filter.CompositePKFilterTests",
            "composite_pk.test_get.CompositePKGetTests",
            "composite_pk.test_models.CompositePKModelsTests",
            "composite_pk.test_order_by.CompositePKOrderByTests",
            "composite_pk.test_update.CompositePKUpdateTests",
            "composite_pk.test_values.CompositePKValuesTests",
            "composite_pk.tests.CompositePKTests.test_get_primary_key_columns",
            "composite_pk.tests.CompositePKFixturesTests",
        },
        "Custom lookups are not supported.": {
            "custom_lookups.tests.BilateralTransformTests",
            "custom_lookups.tests.LookupTests.test_basic_lookup",
            "custom_lookups.tests.LookupTests.test_custom_lookup_with_subquery",
            "custom_lookups.tests.LookupTests.test_custom_name_lookup",
            "custom_lookups.tests.LookupTests.test_div3_extract",
            "custom_lookups.tests.SubqueryTransformTests.test_subquery_usage",
        },
        "connection.close() does not close the connection.": {
            "servers.test_liveserverthread.LiveServerThreadTest.test_closes_connections",
            "servers.tests.LiveServerTestCloseConnectionTest.test_closes_connections",
        },
        "Disallowed query protection doesn't work on MongoDB.": {
            # Because this backend doesn't use cursor(), chunked_cursor(), etc.
            # https://github.com/django/django/blob/045110ff3089aefd9c3e65c707df465bacfed986/django/test/testcases.py#L195-L206
            "test_utils.test_testcase.TestTestCase.test_disallowed_database_queries",
            "test_utils.test_transactiontestcase.DisallowedDatabaseQueriesTests.test_disallowed_database_queries",
            "test_utils.tests.DisallowedDatabaseQueriesTests.test_disallowed_database_chunked_cursor_queries",
            "test_utils.tests.DisallowedDatabaseQueriesTests.test_disallowed_database_queries",
            "test_utils.tests.DisallowedDatabaseQueriesTests.test_disallowed_thread_database_connection",
        },
        "search lookup not supported on non-Atlas until MongoDB 8.2.": {
            "expressions.tests.BasicExpressionsTests.test_lookups_subquery",
        },
        "MongoDB does not support cursor.execute().": {
            # Test catches another exception with assertRaises.
            "backends.tests.BackendTestCase.test_duplicate_table_error",
            # AssertionError: <django_mongodb_backend.base.Cursor object at 0x77a8c8b24350>
            # is not an instance of <class 'django.db.backends.utils.CursorWrapper'>
            "backends.tests.BackendTestCase.test_cursor_contextmanager",
            # Many test methods that don't all need to be added to
            # django_test_expected_raises.
            "backends.base.test_base.ExecuteWrapperTests",
            # Test doesn't cleanup properly when it fails, causing a cascade of
            # failures in other tests.
            "migrations.test_commands.MigrateTests.test_migrate_plan",
        },
        "MongoDB does not support cursor.callproc().": {
            # Test catches another exception with assertRaises.
            "backends.test_utils.CursorWrapperTests.test_unsupported_callproc_kparams_raises_error",
        },
        "RawSQL is not supported on MongoDB.": {
            # django_test_expected_raises doesn't work with subTest.
            "annotations.tests.NonAggregateAnnotationTestCase.test_raw_sql_with_inherited_field",
            "expressions.tests.BasicExpressionsTests.test_order_by_multiline_sql",
            # django_test_expected_raises doesn't work with async def.
            "async.test_async_queryset.AsyncQuerySetTest.test_raw",
        },
        "QuerySet.prefetch_related() is not supported on MongoDB.": {
            # django_test_expected_raises doesn't work with subTest.
            "prefetch_related.test_prefetch_related_objects.PrefetchRelatedObjectsTests.test_m2m_forward",
            "prefetch_related.test_prefetch_related_objects.PrefetchRelatedObjectsTests.test_prefetch_related_objects_with_various_iterables",
            "prefetch_related.tests.CustomPrefetchTests.test_filter_deferred",
            "prefetch_related.tests.PrefetchRelatedTests.test_filter_deferred",
        },
    }

    @cached_property
    def django_test_skips(self):
        skips = super().django_test_skips
        skips.update(self._django_test_skips)
        return skips

    # Tests that are expected to raise certain exceptions:
    _django_test_expected_raises = {
        (NotSupportedError, "Pattern lookups on UUIDField are not supported."): {
            "model_fields.test_uuid.TestQuerying.test_contains",
            "model_fields.test_uuid.TestQuerying.test_endswith",
            "model_fields.test_uuid.TestQuerying.test_filter_with_expr",
            "model_fields.test_uuid.TestQuerying.test_icontains",
            "model_fields.test_uuid.TestQuerying.test_iendswith",
            "model_fields.test_uuid.TestQuerying.test_iexact",
            "model_fields.test_uuid.TestQuerying.test_istartswith",
            "model_fields.test_uuid.TestQuerying.test_startswith",
        },
        (
            CommandError,
            "Unable to serialize database: QuerySet.prefetch_related() is not "
            "supported on MongoDB.",
        ): {
            "fixtures.tests.FixtureLoadingTests.test_dumpdata_objects_with_prefetch_related",
        },
        (NotSupportedError, "QuerySet.prefetch_related() is not supported on MongoDB."): {
            "backends.base.test_creation.TestDeserializeDbFromString.test_serialize_db_to_string_base_manager_with_prefetch_related",
            "fixtures.tests.FixtureLoadingTests.test_loading_and_dumping",
            "m2m_through_regress.test_multitable.MultiTableTests.test_m2m_prefetch_proxied",
            "m2m_through_regress.test_multitable.MultiTableTests.test_m2m_prefetch_reverse_proxied",
            "many_to_many.tests.ManyToManyQueryTests.test_prefetch_related_no_queries_optimization_disabled",
            "many_to_many.tests.ManyToManyTests.test_add_after_prefetch",
            "many_to_many.tests.ManyToManyTests.test_add_then_remove_after_prefetch",
            "many_to_many.tests.ManyToManyTests.test_clear_after_prefetch",
            "many_to_many.tests.ManyToManyTests.test_create_after_prefetch",
            "many_to_many.tests.ManyToManyTests.test_remove_after_prefetch",
            "many_to_many.tests.ManyToManyTests.test_set_after_prefetch",
            "model_forms.tests.OtherModelFormTests.test_prefetch_related_queryset",
            "prefetch_related.test_prefetch_related_objects.PrefetchRelatedObjectsTests.test_m2m_reverse",
            "prefetch_related.test_prefetch_related_objects.PrefetchRelatedObjectsTests.test_m2m_then_m2m",
            "prefetch_related.test_prefetch_related_objects.PrefetchRelatedObjectsTests.test_prefetch_object",
            "prefetch_related.test_prefetch_related_objects.PrefetchRelatedObjectsTests.test_prefetch_object_to_attr",
            "prefetch_related.test_prefetch_related_objects.PrefetchRelatedObjectsTests.test_prefetch_object_to_attr_twice",
            "prefetch_related.test_prefetch_related_objects.PrefetchRelatedObjectsTests.test_prefetch_object_twice",
            "prefetch_related.test_prefetch_related_objects.PrefetchRelatedObjectsTests.test_prefetch_queryset",
            "prefetch_related.test_uuid.UUIDPrefetchRelated.test_prefetch_related_from_uuid_model",
            "prefetch_related.test_uuid.UUIDPrefetchRelated.test_prefetch_related_from_uuid_model_to_uuid_model",
            "prefetch_related.test_uuid.UUIDPrefetchRelated.test_prefetch_related_to_uuid_model",
            "prefetch_related.test_uuid.UUIDPrefetchRelatedLookups.test_from_integer_pk_lookup_integer_pk_uuid_pk_uuid_pk",
            "prefetch_related.test_uuid.UUIDPrefetchRelatedLookups.test_from_integer_pk_lookup_uuid_pk_integer_pk",
            "prefetch_related.test_uuid.UUIDPrefetchRelatedLookups.test_from_uuid_pk_lookup_integer_pk2_uuid_pk2",
            "prefetch_related.test_uuid.UUIDPrefetchRelatedLookups.test_from_uuid_pk_lookup_uuid_pk_integer_pk",
            "prefetch_related.tests.CustomPrefetchTests.test_ambiguous",
            "prefetch_related.tests.CustomPrefetchTests.test_custom_qs",
            "prefetch_related.tests.CustomPrefetchTests.test_m2m",
            "prefetch_related.tests.CustomPrefetchTests.test_m2m_through_fk",
            "prefetch_related.tests.CustomPrefetchTests.test_nested_prefetch_related_are_not_overwritten",
            "prefetch_related.tests.CustomPrefetchTests.test_nested_prefetch_related_with_duplicate_prefetch_and_depth",
            "prefetch_related.tests.CustomPrefetchTests.test_nested_prefetch_related_with_duplicate_prefetcher",
            "prefetch_related.tests.CustomPrefetchTests.test_o2m_through_m2m",
            "prefetch_related.tests.CustomPrefetchTests.test_reverse_m2m",
            "prefetch_related.tests.CustomPrefetchTests.test_to_attr_cached_property",
            "prefetch_related.tests.CustomPrefetchTests.test_traverse_multiple_items_property",
            "prefetch_related.tests.CustomPrefetchTests.test_traverse_qs",
            "prefetch_related.tests.CustomPrefetchTests.test_traverse_single_item_property",
            "prefetch_related.tests.DefaultManagerTests.test_m2m_then_m2m",
            "prefetch_related.tests.ForeignKeyToFieldTest.test_m2m",
            "prefetch_related.tests.GenericRelationTests.test_traverse_GFK",
            "prefetch_related.tests.LookupOrderingTest.test_order",
            "prefetch_related.tests.MultiDbTests.test_using_is_honored_m2m",
            "prefetch_related.tests.MultiTableInheritanceTest.test_m2m_to_inheriting_model",
            "prefetch_related.tests.PrefetchRelatedMTICacheTests.test_add_clears_prefetched_objects_in_grandparent",
            "prefetch_related.tests.PrefetchRelatedMTICacheTests.test_add_clears_prefetched_objects_in_parent",
            "prefetch_related.tests.PrefetchRelatedMTICacheTests.test_grandparent_m2m_available_in_child",
            "prefetch_related.tests.PrefetchRelatedMTICacheTests.test_parent_m2m_available_in_child",
            "prefetch_related.tests.PrefetchRelatedMTICacheTests.test_remove_clears_prefetched_objects_in_grandparent",
            "prefetch_related.tests.PrefetchRelatedMTICacheTests.test_remove_clears_prefetched_objects_in_parent",
            "prefetch_related.tests.PrefetchRelatedTests.test_attribute_error",
            "prefetch_related.tests.PrefetchRelatedTests.test_foreign_key_then_m2m",
            "prefetch_related.tests.PrefetchRelatedTests.test_forward_m2m_to_attr_conflict",
            "prefetch_related.tests.PrefetchRelatedTests.test_get",
            "prefetch_related.tests.PrefetchRelatedTests.test_invalid_final_lookup",
            "prefetch_related.tests.PrefetchRelatedTests.test_m2m_forward",
            "prefetch_related.tests.PrefetchRelatedTests.test_m2m_join_reuse",
            "prefetch_related.tests.PrefetchRelatedTests.test_m2m_prefetching_iterator_with_chunks",
            "prefetch_related.tests.PrefetchRelatedTests.test_m2m_reverse",
            "prefetch_related.tests.PrefetchRelatedTests.test_m2m_then_m2m",
            "prefetch_related.tests.PrefetchRelatedTests.test_m2m_then_m2m_object_ids",
            "prefetch_related.tests.PrefetchRelatedTests.test_m2m_then_reverse_fk_object_ids",
            "prefetch_related.tests.PrefetchRelatedTests.test_m2m_then_reverse_one_to_one_object_ids",
            "prefetch_related.tests.PrefetchRelatedTests.test_overriding_prefetch",
            "prefetch_related.tests.PrefetchRelatedTests.test_reverse_m2m_to_attr_conflict",
            "prefetch_related.tests.PrefetchRelatedTests.test_reverse_one_to_one_then_m2m",
            "prefetch_related.tests.ReadPrefetchedObjectsCacheTests.test_retrieves_results_from_prefetched_objects_cache",
            "prefetch_related.tests.Ticket21410Tests.test_bug",
            "queryset_pickle.tests.PickleabilityTestCase.test_pickle_prefetch_related_with_m2m_and_objects_deletion",
            "serializers.test_json.JsonSerializerTestCase.test_serialize_prefetch_related_m2m",
            "serializers.test_json.JsonSerializerTestCase.test_serialize_prefetch_related_m2m_with_natural_keys",
            "serializers.test_jsonl.JsonlSerializerTestCase.test_serialize_prefetch_related_m2m",
            "serializers.test_jsonl.JsonlSerializerTestCase.test_serialize_prefetch_related_m2m_with_natural_keys",
            "serializers.test_xml.XmlSerializerTestCase.test_serialize_prefetch_related_m2m",
            "serializers.test_xml.XmlSerializerTestCase.test_serialize_prefetch_related_m2m_with_natural_keys",
            "serializers.test_yaml.YamlSerializerTestCase.test_serialize_prefetch_related_m2m",
            "serializers.test_yaml.YamlSerializerTestCase.test_serialize_prefetch_related_m2m_with_natural_keys",
        },
        (
            NotSupportedError,
            "Cannot use QuerySet.update() when querying across multiple collections on MongoDB.",
        ): {
            "custom_managers.tests.CustomManagersRegressTestCase.test_refresh_from_db_when_default_manager_filters",
            "expressions.tests.BasicExpressionsTests.test_filter_with_join",
            "model_inheritance.tests.ModelInheritanceDataTests.test_update_works_on_parent_and_child_models_at_once",
            "queries.tests.Queries4Tests.test_ticket7095",
            "queries.tests.Queries5Tests.test_ticket9848",
            "update.tests.AdvancedTests.test_update_annotated_multi_table_queryset",
            "update.tests.AdvancedTests.test_update_ordered_by_m2m_annotation",
            "update.tests.AdvancedTests.test_update_ordered_by_m2m_annotation_desc",
            "update.tests.AdvancedTests.test_update_values_annotation",
        },
        (
            NotSupportedError,
            "Cannot use QuerySet.delete() when querying across multiple collections on MongoDB.",
        ): {
            "admin_changelist.tests.ChangeListTests.test_distinct_for_many_to_many_at_second_level_in_search_fields",
            "admin_changelist.tests.ChangeListTests.test_distinct_for_through_m2m_at_second_level_in_list_filter",
            "delete.tests.FastDeleteTests.test_fast_delete_aggregation",
            "delete.tests.FastDeleteTests.test_fast_delete_empty_no_update_can_self_select",
            "delete.tests.FastDeleteTests.test_fast_delete_full_match",
            "delete.tests.FastDeleteTests.test_fast_delete_joined_qs",
            "delete_regress.tests.DeleteTests.test_meta_ordered_delete",
            "delete_regress.tests.Ticket19102Tests.test_ticket_19102_annotate",
            "delete_regress.tests.Ticket19102Tests.test_ticket_19102_defer",
            "delete_regress.tests.Ticket19102Tests.test_ticket_19102_select_related",
            "one_to_one.tests.OneToOneTests.test_o2o_primary_key_delete",
        },
        (NotSupportedError, "Cannot use QuerySet.delete() when a subquery is required."): {
            "custom_managers.tests.CustomManagerTests.test_removal_through_default_m2m_related_manager",
            "custom_managers.tests.CustomManagerTests.test_removal_through_specified_m2m_related_manager",
            "delete_regress.tests.DeleteTests.test_self_reference_with_through_m2m_at_second_level",
            "many_to_many.tests.ManyToManyTests.test_assign",
            "many_to_many.tests.ManyToManyTests.test_assign_ids",
            "many_to_many.tests.ManyToManyTests.test_clear",
            "many_to_many.tests.ManyToManyTests.test_remove",
            "many_to_many.tests.ManyToManyTests.test_reverse_assign_with_queryset",
            "many_to_many.tests.ManyToManyTests.test_set",
            "many_to_many.tests.ManyToManyTests.test_set_existing_different_type",
        },
        (NotSupportedError, "QuerySet.extra() is not supported on MongoDB."): {
            "aggregation.tests.AggregateTestCase.test_exists_extra_where_with_aggregate",
            "annotations.tests.NonAggregateAnnotationTestCase.test_column_field_ordering",
            "annotations.tests.NonAggregateAnnotationTestCase.test_column_field_ordering_with_deferred",
            "basic.tests.ModelTest.test_extra_method_select_argument_with_dashes",
            "basic.tests.ModelTest.test_extra_method_select_argument_with_dashes_and_values",
            "defer.tests.DeferTests.test_defer_extra",
            "delete_regress.tests.Ticket19102Tests.test_ticket_19102_extra",
            "extra_regress.tests.ExtraRegressTests",
            "lookup.tests.LookupTests.test_values",
            "lookup.tests.LookupTests.test_values_list",
            "ordering.tests.OrderingTests.test_extra_ordering",
            "ordering.tests.OrderingTests.test_extra_ordering_quoting",
            "queries.test_qs_combinators.QuerySetSetOperationTests.test_union_multiple_models_with_values_list_and_order_by_extra_select",
            "queries.test_qs_combinators.QuerySetSetOperationTests.test_union_with_extra_and_values_list",
            "queries.tests.EscapingTests.test_ticket_7302",
            "queries.tests.Queries1Tests.test_tickets_1878_2939",
            "queries.tests.Queries1Tests.test_tickets_7087_12242",
            "queries.tests.Queries5Tests.test_extra_select_literal_percent_s",
            "queries.tests.Queries5Tests.test_ticket7256",
            "queries.tests.ValuesQuerysetTests.test_extra_multiple_select_params_values_order_by",
            "queries.tests.ValuesQuerysetTests.test_extra_select_params_values_order_in_extra",
            "queries.tests.ValuesQuerysetTests.test_extra_values",
            "queries.tests.ValuesQuerysetTests.test_extra_values_list",
            "queries.tests.ValuesQuerysetTests.test_extra_values_order_multiple",
            "queries.tests.ValuesQuerysetTests.test_extra_values_order_twice",
            "queries.tests.ValuesQuerysetTests.test_flat_extra_values_list",
            "select_related.tests.SelectRelatedTests.test_select_related_with_extra",
        },
        (NotSupportedError, "RawSQL is not supported on MongoDB."): {
            "aggregation.tests.AggregateTestCase.test_coalesced_empty_result_set",
            "aggregation_regress.tests.AggregationTests.test_annotate_with_extra",
            "aggregation_regress.tests.AggregationTests.test_annotation",
            "aggregation_regress.tests.AggregationTests.test_more_more3",
            "aggregation_regress.tests.AggregationTests.test_more_more_more3",
            "async.test_async_queryset.AsyncQuerySetTest.test_raw",
            "custom_methods.tests.MethodsTests.test_custom_methods",
            "expressions.tests.BasicExpressionsTests.test_annotate_values_filter",
            "expressions.tests.BasicExpressionsTests.test_filtering_on_rawsql_that_is_boolean",
            "model_fields.test_jsonfield.TestQuerying.test_key_transform_raw_expression",
            "model_fields.test_jsonfield.TestQuerying.test_nested_key_transform_raw_expression",
            "ordering.tests.OrderingTests.test_extra_ordering_with_table_name",
            "queries.tests.Queries1Tests.test_order_by_rawsql",
            "queries.tests.ValuesQuerysetTests.test_named_values_list_with_fields",
            "queries.tests.ValuesQuerysetTests.test_named_values_list_without_fields",
            "raw_query.tests.RawQueryTests",
        },
        (NotImplementedError, ""): {
            # SchemaEditor.quote_value() isn't implemented.
            "schema.test_logging.SchemaLoggerTests.test_extra_args"
        },
        (NotSupportedError, "MongoDB does not support cursor.execute()."): {
            "backends.tests.BackendTestCase.test_queries",
            "backends.tests.BackendTestCase.test_queries_bare_where",
            "backends.tests.BackendTestCase.test_queries_limit",
            "backends.tests.BackendTestCase.test_queries_logger",
            "backends.tests.BackendTestCase.test_unicode_fetches",
            "backends.tests.EscapingChecks.test_parameter_escaping",
            "backends.tests.EscapingChecks.test_paramless_no_escaping",
            "composite_pk.tests.CompositePKTests.test_raw",
            "composite_pk.tests.CompositePKTests.test_raw_missing_PK_fields",
            "delete_regress.tests.DeleteLockingTest.test_concurrent_delete",
            "migrations.test_multidb.MultiDBOperationTests.test_run_sql_migrate_foo_router_with_hints",
            "migrations.test_operations.OperationTests.test_run_sql",
            "migrations.test_operations.OperationTests.test_run_sql_params",
            "migrations.test_operations.OperationTests.test_separate_database_and_state",
            "multiple_database.tests.QueryTestCase.test_raw",
            "prefetch_related.tests.RawQuerySetTests.test_prefetch_before_raw",
            "prefetch_related.tests.RawQuerySetTests.test_basic",
            "prefetch_related.tests.RawQuerySetTests.test_clear",
            "schema.tests.SchemaTests.test_remove_constraints_capital_letters",
            "test_utils.tests.AllowedDatabaseQueriesTests.test_allowed_database_copy_queries",
            "timezones.tests.LegacyDatabaseTests.test_cursor_execute_accepts_naive_datetime",
            "timezones.tests.LegacyDatabaseTests.test_cursor_execute_returns_naive_datetime",
            "timezones.tests.LegacyDatabaseTests.test_raw_sql",
            "timezones.tests.NewDatabaseTests.test_cursor_execute_accepts_naive_datetime",
            "timezones.tests.NewDatabaseTests.test_cursor_execute_returns_naive_datetime",
            "timezones.tests.NewDatabaseTests.test_cursor_explicit_time_zone",
            "timezones.tests.NewDatabaseTests.test_raw_sql",
        },
        (NotSupportedError, "MongoDB does not support cursor.executemany()."): {
            "backends.tests.BackendTestCase.test_cursor_executemany",
            "backends.tests.BackendTestCase.test_cursor_executemany_with_empty_params_list",
            "backends.tests.BackendTestCase.test_cursor_executemany_with_iterator",
        },
        (
            NotSupportedError,
            "TruncDate with tzinfo (America/Vancouver) isn't supported on MongoDB.",
        ): {
            "model_fields.test_datetimefield.DateTimeFieldTests.test_lookup_date_with_use_tz",
        },
        (NotSupportedError, "TruncDate with tzinfo (Africa/Nairobi) isn't supported on MongoDB."): {
            "timezones.tests.NewDatabaseTests.test_query_convert_timezones",
        },
        (NotSupportedError, "ExtractQuarter is not supported."): {
            "db_functions.datetime.test_extract_trunc.DateFunctionTests.test_extract_quarter_func",
            "db_functions.datetime.test_extract_trunc.DateFunctionTests.test_extract_quarter_func_boundaries",
        },
        (NotSupportedError, "StringAgg is not supported."): {
            "aggregation.tests.AggregateTestCase.test_distinct_on_stringagg",
            "aggregation.tests.AggregateTestCase.test_string_agg_escapes_delimiter",
            "aggregation.tests.AggregateTestCase.test_string_agg_filter",
            "aggregation.tests.AggregateTestCase.test_string_agg_filter_in_subquery",
            "aggregation.tests.AggregateTestCase.test_stringagg_default_value",
        },
        (NotSupportedError, "ColPairs is not supported."): {
            "composite_pk.tests.CompositePKTests.test_in_bulk",
            "composite_pk.tests.CompositePKTests.test_in_bulk_batching",
            "composite_pk.tests.CompositePKTests.test_only",
            "composite_pk.tests.CompositePKTests.test_pk_must_be_list_or_tuple",
            "composite_pk.tests.CompositePKTests.test_pk_updated_if_field_updated",
            "composite_pk.tests.CompositePKTests.test_query",
            "composite_pk.tests.CompositePKTests.test_select_related",
        },
        (NotSupportedError, "MongoDB does not support the 'startswith' lookup in indexes."): {
            "schema.tests.SchemaTests.test_unique_constraint_nulls_distinct_condition",
        },
    }

    @cached_property
    def django_test_expected_raises(self):
        try:
            expected_raises = super().django_test_expected_raises
        except AttributeError:
            # No extra tests if GIS backend is inactive.
            return self._django_test_expected_raises
        # Combine GIS backend's tests with this class's.
        for (exception, msg), tests in self._django_test_expected_raises.items():
            try:
                expected_raises[exception, msg].update(tests)
            except KeyError:
                expected_raises[exception, msg] = tests
        return expected_raises

    @cached_property
    def mongodb_version(self):
        return self.connection.get_database_version()  # e.g., (6, 3, 0)

    @cached_property
    def is_mongodb_8_0(self):
        return self.mongodb_version >= (8, 0)

    @cached_property
    def supports_search(self):
        """Does the server support MongoDB search queries and indexes?"""
        try:
            self.connection.get_collection("__null").list_search_indexes()
        except OperationFailure:
            # It would be best to check the error message or error code to
            # avoid hiding some other exception, but the message/code varies
            # across MongoDB versions. Example error message:
            # "$listSearchIndexes  stage is only allowed on MongoDB Atlas".
            return False
        else:
            return True

    @cached_property
    def _supports_transactions(self):
        """
        Transactions are enabled if MongoDB is configured as a replica set or a
        sharded cluster. This is a MongoDB-specific feature flag distinct from Django's
        supports_transactions (without the underscore prefix).
        """
        self.connection.ensure_connection()
        client = self.connection.connection.admin
        hello = client.command("hello")
        # a replica set or a sharded cluster
        return "setName" in hello or hello.get("msg") == "isdbgrid"

    @cached_property
    def supports_queryable_encryption(self):
        """
        For testing purposes, Queryable Encryption requires a MongoDB 8.0 or
        later replica set or sharded cluster, as well as MongoDB Atlas or
        Enterprise. This flag must not guard any non-test functionality since
        it would prevent MongoDB 7.0 from being used, which also supports
        Queryable Encryption. The models in tests/encryption_ aren't compatible
        with MongoDB 7.0 because {"queryType": "range"} is "rangePreview".
        """
        self.connection.ensure_connection()
        build_info = self.connection.connection.admin.command("buildInfo")
        is_enterprise = "enterprise" in build_info.get("modules")
        return (
            # self.supports_search is a proxy for "is Atlas" until MongoDB 8.2
            # when search support was added in Community Edition. Thus, this
            # may need to be reworked, however, it only affects when tests run.
            (is_enterprise or self.supports_search)
            and self._supports_transactions
            and self.is_mongodb_8_0
        )
