GeneralManager 0.82.3__tar.gz → 0.82.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {generalmanager-0.82.3/src/GeneralManager.egg-info → generalmanager-0.82.5}/PKG-INFO +1 -1
- {generalmanager-0.82.3 → generalmanager-0.82.5}/pyproject.toml +1 -1
- {generalmanager-0.82.3 → generalmanager-0.82.5/src/GeneralManager.egg-info}/PKG-INFO +1 -1
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/GeneralManager.egg-info/SOURCES.txt +1 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/_types/search.py +4 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/api/graphql.py +7 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/api/graphql_output.py +44 -1
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/api/graphql_search.py +5 -1
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/api/graphql_type.py +8 -1
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/management/commands/search_index.py +2 -12
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/public_api_registry.py +5 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/search/backend.py +7 -2
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/search/backends/dev.py +36 -1
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/search/backends/meilisearch.py +106 -13
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/search/config.py +26 -1
- generalmanager-0.82.5/src/general_manager/search/identity.py +287 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/search/indexer.py +16 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/search/reconciliation.py +14 -1
- {generalmanager-0.82.3 → generalmanager-0.82.5}/LICENSE +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/README.md +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/setup.cfg +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/GeneralManager.egg-info/dependency_links.txt +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/GeneralManager.egg-info/requires.txt +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/GeneralManager.egg-info/top_level.txt +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/_types/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/_types/api.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/_types/bucket.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/_types/cache.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/_types/dataframes.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/_types/factory.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/_types/general_manager.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/_types/interface.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/_types/manager.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/_types/measurement.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/_types/permission.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/_types/rule.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/_types/utils.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/api/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/api/graphql_as_of.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/api/graphql_errors.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/api/graphql_groups.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/api/graphql_identifiers.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/api/graphql_mutations.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/api/graphql_ordering.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/api/graphql_prefetch.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/api/graphql_relations.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/api/graphql_resolvers.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/api/graphql_subscription_consumer.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/api/graphql_subscriptions.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/api/graphql_view.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/api/graphql_warmup.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/api/graphql_warmup_registry.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/api/graphql_warmup_tasks.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/api/mutation.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/api/notification_batching.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/api/property.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/api/registry.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/api/remote_api.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/api/remote_invalidation.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/api/remote_invalidation_client.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/apps.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/as_of.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/bootstrap.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/bucket/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/bucket/_materialized_bucket.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/bucket/_ordering.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/bucket/base_bucket.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/bucket/calculation_bucket.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/bucket/database_bucket.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/bucket/excel_bucket.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/bucket/group_bucket.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/bucket/indexing.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/bucket/projection.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/bucket/request_bucket.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/cache/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/cache/_dependency_graph.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/cache/batch_refresh.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/cache/cache_decorator.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/cache/cache_tracker.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/cache/data_change_context.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/cache/dependency_cache.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/cache/dependency_index.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/cache/dependency_matching.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/cache/dependency_publish.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/cache/dependency_shards.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/cache/model_dependency_collector.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/cache/run_context.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/cache/run_context_lru.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/cache/signals.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/cache/transaction_barrier.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/audit.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/bootstrap.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/checks.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/consumer.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/context.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/errors.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/evals/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/evals/__main__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/evals/baseline.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/evals/datasets/basic_queries.yaml +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/evals/datasets/demo_readiness.yaml +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/evals/datasets/edge_cases.yaml +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/evals/datasets/follow_ups.yaml +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/evals/datasets/large_schema.yaml +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/evals/datasets/multi_hop.yaml +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/evals/datasets/planned_orchestration.yaml +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/evals/diagnostics.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/evals/fingerprints.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/evals/fixtures.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/evals/judges/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/evals/judges/answer_quality.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/evals/judges/contract.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/evals/judges/result_accuracy.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/evals/judges/tool_sequence.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/evals/readiness.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/evals/runner.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/evals/traces.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/grounding.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/models.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/planned/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/planned/budget.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/planned/calculations.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/planned/catalog.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/planned/config.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/planned/events.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/planned/evidence.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/planned/models.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/planned/planner.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/planned/provider_calls.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/planned/resolver.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/planned/routing.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/planned/scheduler.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/planned/synthesis.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/planned/validation.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/providers/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/providers/_shared.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/providers/anthropic.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/providers/base.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/providers/google.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/providers/ollama.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/providers/openai.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/rate_limits.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/routing.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/schema_index.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/settings.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/signals.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/system_prompt.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/tool_metadata.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/tools.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/turns.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/chat/views.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/conf.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/dataframes/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/dataframes/measurements.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/factory/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/factory/auto_factory.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/factory/factories.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/factory/factory_methods.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/base_interface.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/bundles/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/bundles/calculation.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/bundles/database.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/bundles/excel.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/bundles/remote_manager.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/bundles/request.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/base.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/builtin.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/calculation/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/calculation/_compat.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/calculation/input_resolution.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/calculation/lifecycle.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/configuration.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/core/observability.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/core/utils.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/excel/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/exceptions.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/existing_model/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/existing_model/_compat.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/existing_model/resolution.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/factory.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/orm/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/orm/_compat.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/orm/bulk.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/orm/create_many.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/orm/history.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/orm/lifecycle.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/orm/mutations.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/orm/support.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/orm_utils/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/orm_utils/django_manager_utils.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/orm_utils/field_descriptors.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/orm_utils/payload_normalizer.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/orm_utils/update_state.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/read_only/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/read_only/_compat.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/read_only/_types.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/read_only/lifecycle.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/read_only/management.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/registry.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/remote_manager.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/capabilities/request/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/excel.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/excel_store.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/excel_workbook.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/infrastructure/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/infrastructure/startup_hooks.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/infrastructure/system_checks.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/interfaces/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/interfaces/calculation.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/interfaces/database.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/interfaces/excel.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/interfaces/existing_model.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/interfaces/read_only.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/interfaces/remote_manager.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/interfaces/request.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/manifests/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/manifests/capability_builder.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/manifests/capability_manifest.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/manifests/capability_models.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/orm_interface.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/requests.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/utils/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/utils/database_interface_protocols.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/utils/errors.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/utils/history.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/interface/utils/models.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/logging.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/management/commands/chat_cleanup.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/management/commands/cleanup_upload_intents.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/management/commands/graphql_warmup.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/management/commands/graphql_warmup_refresh_due.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/management/commands/search_reconcile.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/management/commands/seed_manager_landscape.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/management/commands/shell.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/management/commands/workflow_drain_outbox.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/management/commands/workflow_replay_dead_letters.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/manager/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/manager/bulk_create.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/manager/general_manager.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/manager/group_manager.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/manager/input.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/manager/meta.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/measurement/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/measurement/measurement.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/measurement/measurement_field.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/metrics/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/metrics/graphql.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/migrations/0001_initial.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/migrations/0002_workflow_outbox_scaling_indexes.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/migrations/0003_workflow_execution_correlation_constraint.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/migrations/0004_chat_persistence_models.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/migrations/0004_search_index_state.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/migrations/0005_merge_chat_search.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/migrations/0006_chat_pending_confirmation_scoped_ids.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/migrations/0007_upload_intent.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/migrations/0008_upload_finalization_versions.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/migrations/0009_upload_cleanup_state.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/migrations/0010_chat_pending_confirmation_portable_uniqueness.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/migrations/0011_search_index_state_dirty_generation.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/migrations/0012_chat_context_watermarks.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/migrations/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/models.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/permission/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/permission/audit.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/permission/base_permission.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/permission/graphql_capabilities.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/permission/manager_based_permission.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/permission/mutation_permission.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/permission/permission_checks.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/permission/permission_data_manager.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/permission/utils.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/py.typed +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/rule/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/rule/handler.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/rule/rule.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/search/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/search/async_tasks.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/search/backend_registry.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/search/backends/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/search/backends/opensearch.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/search/backends/typesense.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/search/checks.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/search/invalidation.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/search/m2m_invalidation.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/search/models.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/search/registry.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/search/tasks.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/search/utils.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/seeding/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/seeding/manager_landscape.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/uploads/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/uploads/adapters.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/uploads/checks.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/uploads/config.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/uploads/errors.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/uploads/finalization.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/uploads/graphql.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/uploads/graphql_types.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/uploads/metrics.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/uploads/models.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/uploads/public.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/uploads/s3.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/uploads/services.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/uploads/tokens.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/uploads/types.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/uploads/urls.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/uploads/views.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/utils/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/utils/_args_to_kwargs.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/utils/_cache_key_encoder.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/utils/_make_cache_key.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/utils/_none_to_zero.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/utils/filter_parser.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/utils/format_string.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/utils/json_encoder.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/utils/path_mapping.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/utils/public_api.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/utils/testing.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/utils/type_checks.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/workflow/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/workflow/actions.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/workflow/backend_registry.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/workflow/backends/__init__.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/workflow/backends/celery.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/workflow/backends/local.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/workflow/backends/n8n.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/workflow/config.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/workflow/engine.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/workflow/event_registry.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/workflow/events.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/workflow/models.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/workflow/signal_bridge.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/workflow/tasks.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/workflow/telemetry.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/tests/test_settings.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/tests/test_urls.py +0 -0
- {generalmanager-0.82.3 → generalmanager-0.82.5}/tests/testing_asgi.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: GeneralManager
|
|
3
|
-
Version: 0.82.
|
|
3
|
+
Version: 0.82.5
|
|
4
4
|
Summary: Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching.
|
|
5
5
|
Author-email: Tim Kleindick <tkleindick@yahoo.de>
|
|
6
6
|
License: MIT License
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "GeneralManager"
|
|
7
|
-
version = "0.82.
|
|
7
|
+
version = "0.82.5"
|
|
8
8
|
description = "Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [{ name = "Tim Kleindick", email = "tkleindick@yahoo.de" }]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: GeneralManager
|
|
3
|
-
Version: 0.82.
|
|
3
|
+
Version: 0.82.5
|
|
4
4
|
Summary: Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching.
|
|
5
5
|
Author-email: Tim Kleindick <tkleindick@yahoo.de>
|
|
6
6
|
License: MIT License
|
|
@@ -274,6 +274,7 @@ src/general_manager/search/backend.py
|
|
|
274
274
|
src/general_manager/search/backend_registry.py
|
|
275
275
|
src/general_manager/search/checks.py
|
|
276
276
|
src/general_manager/search/config.py
|
|
277
|
+
src/general_manager/search/identity.py
|
|
277
278
|
src/general_manager/search/indexer.py
|
|
278
279
|
src/general_manager/search/invalidation.py
|
|
279
280
|
src/general_manager/search/m2m_invalidation.py
|
|
@@ -18,10 +18,12 @@ __all__ = [
|
|
|
18
18
|
"SearchConfigSpec",
|
|
19
19
|
"SearchDocument",
|
|
20
20
|
"SearchHit",
|
|
21
|
+
"SearchIdentityFilterGroup",
|
|
21
22
|
"SearchIndexer",
|
|
22
23
|
"SearchInvalidationRule",
|
|
23
24
|
"SearchResult",
|
|
24
25
|
"TypesenseBackend",
|
|
26
|
+
"build_search_filters",
|
|
25
27
|
"configure_search_backend",
|
|
26
28
|
"configure_search_backend_from_settings",
|
|
27
29
|
"get_search_backend",
|
|
@@ -44,10 +46,12 @@ from general_manager.search.config import SearchConfigProtocol
|
|
|
44
46
|
from general_manager.search.config import SearchConfigSpec
|
|
45
47
|
from general_manager.search.backend import SearchDocument
|
|
46
48
|
from general_manager.search.backend import SearchHit
|
|
49
|
+
from general_manager.search.identity import SearchIdentityFilterGroup
|
|
47
50
|
from general_manager.search.indexer import SearchIndexer
|
|
48
51
|
from general_manager.search.config import SearchInvalidationRule
|
|
49
52
|
from general_manager.search.backend import SearchResult
|
|
50
53
|
from general_manager.search.backends.typesense import TypesenseBackend
|
|
54
|
+
from general_manager.search.identity import build_search_filters
|
|
51
55
|
from general_manager.search.backend_registry import configure_search_backend
|
|
52
56
|
from general_manager.search.backend_registry import (
|
|
53
57
|
configure_search_backend_from_settings,
|
|
@@ -100,6 +100,7 @@ from general_manager.api.graphql_mutations import (
|
|
|
100
100
|
generate_delete_mutation_class as _generate_delete_mutation_class_fn,
|
|
101
101
|
)
|
|
102
102
|
from general_manager.api.graphql_output import (
|
|
103
|
+
GraphQLOutputIdentityMetadataError,
|
|
103
104
|
create_output_field_resolver,
|
|
104
105
|
map_graphql_output_annotation,
|
|
105
106
|
resolve_output_type_hints,
|
|
@@ -518,6 +519,11 @@ class GraphQL:
|
|
|
518
519
|
)
|
|
519
520
|
fields: dict[str, graphene.Field] = {}
|
|
520
521
|
for declared_field in dataclasses.fields(output_class):
|
|
522
|
+
is_identifier = declared_field.metadata.get("is_identifier", False)
|
|
523
|
+
if not isinstance(is_identifier, bool):
|
|
524
|
+
raise GraphQLOutputIdentityMetadataError(
|
|
525
|
+
output_name, declared_field.name, is_identifier
|
|
526
|
+
)
|
|
521
527
|
mapped = map_graphql_output_annotation(
|
|
522
528
|
hints[declared_field.name],
|
|
523
529
|
owner_name=output_name,
|
|
@@ -528,6 +534,7 @@ class GraphQL:
|
|
|
528
534
|
output_type_registry=cls.graphql_output_type_registry,
|
|
529
535
|
measurement_type=MeasurementType,
|
|
530
536
|
scalar_mapper=cls._map_field_to_graphene_base_type,
|
|
537
|
+
is_identifier=is_identifier,
|
|
531
538
|
)
|
|
532
539
|
field = cast(graphene.Field, mapped.field)
|
|
533
540
|
field.resolver = create_output_field_resolver(
|
|
@@ -9,6 +9,7 @@ from decimal import Decimal
|
|
|
9
9
|
import sys
|
|
10
10
|
from types import UnionType
|
|
11
11
|
from typing import Any, ForwardRef, Union, get_args, get_origin, get_type_hints
|
|
12
|
+
from uuid import UUID
|
|
12
13
|
|
|
13
14
|
import graphene
|
|
14
15
|
|
|
@@ -40,6 +41,16 @@ class GraphQLOutputAnnotationError(TypeError):
|
|
|
40
41
|
)
|
|
41
42
|
|
|
42
43
|
|
|
44
|
+
class GraphQLOutputIdentityMetadataError(TypeError):
|
|
45
|
+
"""Raised when an output field's identity flag is not boolean."""
|
|
46
|
+
|
|
47
|
+
def __init__(self, owner_name: str, field_name: str, flag: object) -> None:
|
|
48
|
+
super().__init__(
|
|
49
|
+
f"Invalid is_identifier metadata for {owner_name}.{field_name}: "
|
|
50
|
+
f"expected bool, got {flag!r}"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
43
54
|
@dataclass(frozen=True, slots=True)
|
|
44
55
|
class _MappedAnnotation:
|
|
45
56
|
"""Internal Graphene type metadata before a field wrapper is mounted."""
|
|
@@ -144,6 +155,7 @@ def _map_collection_annotation(
|
|
|
144
155
|
output_type_registry: Mapping[str, type[graphene.ObjectType]],
|
|
145
156
|
measurement_type: type[graphene.ObjectType],
|
|
146
157
|
scalar_mapper: Callable[[type], type],
|
|
158
|
+
is_identifier: bool = False,
|
|
147
159
|
) -> _MappedAnnotation:
|
|
148
160
|
origin = get_origin(annotation)
|
|
149
161
|
if origin not in (list, tuple, set):
|
|
@@ -159,6 +171,13 @@ def _map_collection_annotation(
|
|
|
159
171
|
raise _annotation_error(owner_name, field_name, annotation)
|
|
160
172
|
element_annotation = args[0]
|
|
161
173
|
|
|
174
|
+
if is_identifier:
|
|
175
|
+
_, identity_type = _unwrap_optional_annotation(
|
|
176
|
+
element_annotation, owner_name=owner_name, field_name=field_name
|
|
177
|
+
)
|
|
178
|
+
if identity_type not in (int, str, UUID):
|
|
179
|
+
raise _annotation_error(owner_name, field_name, annotation)
|
|
180
|
+
|
|
162
181
|
element = _map_annotation(
|
|
163
182
|
element_annotation,
|
|
164
183
|
owner_name=owner_name,
|
|
@@ -169,6 +188,7 @@ def _map_collection_annotation(
|
|
|
169
188
|
output_type_registry=output_type_registry,
|
|
170
189
|
measurement_type=measurement_type,
|
|
171
190
|
scalar_mapper=scalar_mapper,
|
|
191
|
+
is_identifier=is_identifier,
|
|
172
192
|
)
|
|
173
193
|
element_type = element.graphene_type
|
|
174
194
|
if not element.nullable:
|
|
@@ -192,6 +212,7 @@ def _map_non_optional_value(
|
|
|
192
212
|
output_type_registry: Mapping[str, type[graphene.ObjectType]],
|
|
193
213
|
measurement_type: type[graphene.ObjectType],
|
|
194
214
|
scalar_mapper: Callable[[type], type],
|
|
215
|
+
is_identifier: bool = False,
|
|
195
216
|
) -> _MappedAnnotation:
|
|
196
217
|
annotation = _resolve_registered_name(
|
|
197
218
|
annotation,
|
|
@@ -220,6 +241,17 @@ def _map_non_optional_value(
|
|
|
220
241
|
output_type_registry=output_type_registry,
|
|
221
242
|
measurement_type=measurement_type,
|
|
222
243
|
scalar_mapper=scalar_mapper,
|
|
244
|
+
is_identifier=is_identifier,
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
if is_identifier:
|
|
248
|
+
if annotation not in (int, str, UUID):
|
|
249
|
+
raise _annotation_error(owner_name, field_name, annotation)
|
|
250
|
+
return _MappedAnnotation(
|
|
251
|
+
graphene.ID,
|
|
252
|
+
annotation,
|
|
253
|
+
nullable=False,
|
|
254
|
+
contains_measurement=False,
|
|
223
255
|
)
|
|
224
256
|
|
|
225
257
|
if safe_issubclass(annotation, GraphQLType):
|
|
@@ -290,6 +322,7 @@ def _map_annotation(
|
|
|
290
322
|
output_type_registry: Mapping[str, type[graphene.ObjectType]],
|
|
291
323
|
measurement_type: type[graphene.ObjectType],
|
|
292
324
|
scalar_mapper: Callable[[type], type],
|
|
325
|
+
is_identifier: bool = False,
|
|
293
326
|
) -> _MappedAnnotation:
|
|
294
327
|
nullable, concrete = _unwrap_optional_annotation(
|
|
295
328
|
annotation,
|
|
@@ -306,6 +339,7 @@ def _map_annotation(
|
|
|
306
339
|
output_type_registry=output_type_registry,
|
|
307
340
|
measurement_type=measurement_type,
|
|
308
341
|
scalar_mapper=scalar_mapper,
|
|
342
|
+
is_identifier=is_identifier,
|
|
309
343
|
)
|
|
310
344
|
return replace(mapped, nullable=nullable)
|
|
311
345
|
|
|
@@ -322,6 +356,7 @@ def _map_non_optional_annotation(
|
|
|
322
356
|
measurement_type: type[graphene.ObjectType],
|
|
323
357
|
scalar_mapper: Callable[[type], type],
|
|
324
358
|
required: bool,
|
|
359
|
+
is_identifier: bool = False,
|
|
325
360
|
) -> MappedGraphQLOutput:
|
|
326
361
|
"""Map one non-optional annotation to a Graphene field."""
|
|
327
362
|
mapped = _map_non_optional_value(
|
|
@@ -334,6 +369,7 @@ def _map_non_optional_annotation(
|
|
|
334
369
|
output_type_registry=output_type_registry,
|
|
335
370
|
measurement_type=measurement_type,
|
|
336
371
|
scalar_mapper=scalar_mapper,
|
|
372
|
+
is_identifier=is_identifier,
|
|
337
373
|
)
|
|
338
374
|
field_kwargs: dict[str, object] = {}
|
|
339
375
|
if mapped.contains_measurement:
|
|
@@ -355,8 +391,14 @@ def map_graphql_output_annotation(
|
|
|
355
391
|
output_type_registry: Mapping[str, type[graphene.ObjectType]],
|
|
356
392
|
measurement_type: type[graphene.ObjectType],
|
|
357
393
|
scalar_mapper: Callable[[type], type],
|
|
394
|
+
is_identifier: bool = False,
|
|
358
395
|
) -> MappedGraphQLOutput:
|
|
359
|
-
"""Map a strict
|
|
396
|
+
"""Map a strict output hint, preserving nullability and native values.
|
|
397
|
+
|
|
398
|
+
Explicit identity fields accept int, str, UUID, and supported flat
|
|
399
|
+
collections of those types. Their resolver type stays native while the
|
|
400
|
+
generated scalar is ID. Unmarked fields retain the strict legacy mapping.
|
|
401
|
+
"""
|
|
360
402
|
nullable, concrete_type = _unwrap_optional_annotation(
|
|
361
403
|
annotation,
|
|
362
404
|
owner_name=owner_name,
|
|
@@ -374,6 +416,7 @@ def map_graphql_output_annotation(
|
|
|
374
416
|
measurement_type=measurement_type,
|
|
375
417
|
scalar_mapper=scalar_mapper,
|
|
376
418
|
required=not nullable,
|
|
419
|
+
is_identifier=is_identifier,
|
|
377
420
|
)
|
|
378
421
|
except GraphQLOutputAnnotationError as error:
|
|
379
422
|
# Preserve the annotation written on the owning field in diagnostics,
|
|
@@ -41,6 +41,7 @@ from general_manager.measurement.measurement import Measurement
|
|
|
41
41
|
from general_manager.utils.type_checks import safe_issubclass
|
|
42
42
|
from general_manager.search.backend_registry import get_search_backend
|
|
43
43
|
from general_manager.search.backend import SearchHit
|
|
44
|
+
from general_manager.search.identity import SearchFilters, build_search_filters
|
|
44
45
|
from general_manager.search.registry import (
|
|
45
46
|
get_search_config,
|
|
46
47
|
get_type_label,
|
|
@@ -1257,7 +1258,7 @@ def register_search_query(
|
|
|
1257
1258
|
permission_plan,
|
|
1258
1259
|
backend_shape=backend_shape,
|
|
1259
1260
|
)
|
|
1260
|
-
filter_groups = (
|
|
1261
|
+
filter_groups: SearchFilters = (
|
|
1261
1262
|
manager_filters or None
|
|
1262
1263
|
if decision == "allow_all"
|
|
1263
1264
|
else merge_permission_filters(
|
|
@@ -1265,6 +1266,9 @@ def register_search_query(
|
|
|
1265
1266
|
permission_plan.filters,
|
|
1266
1267
|
)
|
|
1267
1268
|
)
|
|
1269
|
+
filter_groups = build_search_filters(
|
|
1270
|
+
manager_class, index_name, filter_groups
|
|
1271
|
+
)
|
|
1268
1272
|
total_hits_for_manager = 0
|
|
1269
1273
|
candidate_hits_for_manager = 0
|
|
1270
1274
|
offset_cursor = 0
|
|
@@ -29,7 +29,14 @@ class GraphQLTypeMeta(type):
|
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
class GraphQLType(metaclass=GraphQLTypeMeta):
|
|
32
|
-
"""Frozen annotated value exposed only as a GraphQL output object.
|
|
32
|
+
"""Frozen annotated value exposed only as a GraphQL output object.
|
|
33
|
+
|
|
34
|
+
Use ``dataclasses.field(metadata={"is_identifier": True})`` to declare
|
|
35
|
+
native int, str, or UUID identity fields and their supported homogeneous
|
|
36
|
+
collections. Schema generation preserves annotation nullability and native
|
|
37
|
+
values while exposing ID scalars. Missing or false metadata keeps existing
|
|
38
|
+
output types; other flag values fail schema generation with TypeError.
|
|
39
|
+
"""
|
|
33
40
|
|
|
34
41
|
|
|
35
42
|
def get_registered_graphql_types() -> tuple[type[GraphQLType], ...]:
|
|
@@ -9,9 +9,8 @@ from django.core.management.base import BaseCommand
|
|
|
9
9
|
|
|
10
10
|
from general_manager.logging import get_logger
|
|
11
11
|
from general_manager.search.backend_registry import get_search_backend
|
|
12
|
-
from general_manager.search.indexer import SearchIndexer
|
|
12
|
+
from general_manager.search.indexer import SearchIndexer, _ensure_index
|
|
13
13
|
from general_manager.search.registry import (
|
|
14
|
-
collect_index_settings,
|
|
15
14
|
get_index_names,
|
|
16
15
|
iter_searchable_managers,
|
|
17
16
|
)
|
|
@@ -99,16 +98,7 @@ class Command(BaseCommand):
|
|
|
99
98
|
target_indexes = get_index_names()
|
|
100
99
|
|
|
101
100
|
for index_name in sorted(target_indexes):
|
|
102
|
-
|
|
103
|
-
backend.ensure_index(
|
|
104
|
-
index_name,
|
|
105
|
-
{
|
|
106
|
-
"searchable_fields": settings_payload.searchable_fields,
|
|
107
|
-
"filterable_fields": settings_payload.filterable_fields,
|
|
108
|
-
"sortable_fields": settings_payload.sortable_fields,
|
|
109
|
-
"field_boosts": settings_payload.field_boosts,
|
|
110
|
-
},
|
|
111
|
-
)
|
|
101
|
+
_ensure_index(backend, index_name)
|
|
112
102
|
logger.info(
|
|
113
103
|
"search index ensured",
|
|
114
104
|
context={"index": index_name},
|
|
@@ -793,6 +793,11 @@ RULE_EXPORTS: LazyExportMap = {
|
|
|
793
793
|
}
|
|
794
794
|
|
|
795
795
|
SEARCH_EXPORTS: LazyExportMap = {
|
|
796
|
+
"SearchIdentityFilterGroup": (
|
|
797
|
+
"general_manager.search.identity",
|
|
798
|
+
"SearchIdentityFilterGroup",
|
|
799
|
+
),
|
|
800
|
+
"build_search_filters": ("general_manager.search.identity", "build_search_filters"),
|
|
796
801
|
"FieldConfig": ("general_manager.search.config", "FieldConfig"),
|
|
797
802
|
"IndexConfig": ("general_manager.search.config", "IndexConfig"),
|
|
798
803
|
"SearchConfigProtocol": ("general_manager.search.config", "SearchConfigProtocol"),
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import builtins
|
|
5
6
|
from collections.abc import Mapping, Sequence
|
|
6
|
-
from dataclasses import dataclass
|
|
7
|
+
from dataclasses import dataclass, field
|
|
7
8
|
from typing import Protocol, runtime_checkable
|
|
8
9
|
|
|
9
10
|
|
|
@@ -20,7 +21,10 @@ class SearchDocument:
|
|
|
20
21
|
protocol does not require adapters to reject mismatches between
|
|
21
22
|
`document.index` and an `index_name` argument. `data` contains
|
|
22
23
|
searchable/stored fields. `field_boosts` and `index_boost` are hints for
|
|
23
|
-
adapters that support weighted matching.
|
|
24
|
+
adapters that support weighted matching. `identity_fields` maps explicit
|
|
25
|
+
identity names to native scalar types (`int`, `str`, or `UUID`), including
|
|
26
|
+
nullable/collection element types. It never coerces document values.
|
|
27
|
+
The payload mappings, including
|
|
24
28
|
`identification`, are not copied by the model; callers should treat them as
|
|
25
29
|
immutable after handing them to a backend. Dataclass field annotations are
|
|
26
30
|
the validation boundary; this model does not coerce or validate runtime
|
|
@@ -36,6 +40,7 @@ class SearchDocument:
|
|
|
36
40
|
data: Mapping[str, object]
|
|
37
41
|
field_boosts: Mapping[str, float]
|
|
38
42
|
index_boost: float | None = None
|
|
43
|
+
identity_fields: Mapping[str, builtins.type] = field(default_factory=dict)
|
|
39
44
|
|
|
40
45
|
|
|
41
46
|
@dataclass(frozen=True)
|
|
@@ -10,6 +10,11 @@ from threading import RLock
|
|
|
10
10
|
from general_manager.search.backend import SearchDocument, SearchHit, SearchResult
|
|
11
11
|
from general_manager.bucket._ordering import normalize_ordering, sort_items
|
|
12
12
|
from general_manager.utils.filter_parser import apply_lookup
|
|
13
|
+
from general_manager.search.identity import (
|
|
14
|
+
SearchIdentityFilterGroup,
|
|
15
|
+
identity_lookup,
|
|
16
|
+
canonical_identity,
|
|
17
|
+
)
|
|
13
18
|
|
|
14
19
|
|
|
15
20
|
@dataclass
|
|
@@ -176,6 +181,10 @@ class DevSearchBackend:
|
|
|
176
181
|
case-sensitive, missing fields behave like `None`, incompatible
|
|
177
182
|
mixed-type comparisons and invalid lookup/value combinations
|
|
178
183
|
return `False`, and `None` compares only through `exact`.
|
|
184
|
+
Explicit `SearchIdentityFilterGroup` equality/membership uses
|
|
185
|
+
native casts and owner scope without changing these operator
|
|
186
|
+
rules. Matching document metadata and a present field are
|
|
187
|
+
required. Plain mappings retain raw comparisons.
|
|
179
188
|
filter_expression (str | None): Unsupported in this backend; passing a value raises NotImplementedError.
|
|
180
189
|
sort (Sequence[str] | None): Signed document data field names.
|
|
181
190
|
Prefix a field with ``-`` for descending order. If omitted,
|
|
@@ -392,12 +401,38 @@ class DevSearchBackend:
|
|
|
392
401
|
if not all(isinstance(group, Mapping) for group in filters):
|
|
393
402
|
return False
|
|
394
403
|
return any(self._passes_filters(document, group) for group in filters)
|
|
404
|
+
if isinstance(filters, SearchIdentityFilterGroup):
|
|
405
|
+
if filters.type_label is not None and document.type != filters.type_label:
|
|
406
|
+
return False
|
|
395
407
|
for key, value in filters.items():
|
|
396
|
-
|
|
408
|
+
identity = None
|
|
409
|
+
if isinstance(filters, SearchIdentityFilterGroup):
|
|
410
|
+
identity = identity_lookup(key, filters.identity_fields)
|
|
411
|
+
if identity is not None:
|
|
412
|
+
field_name, lookup, native_type = identity
|
|
413
|
+
if (
|
|
414
|
+
field_name not in document.data
|
|
415
|
+
or document.identity_fields.get(field_name) is not native_type
|
|
416
|
+
):
|
|
417
|
+
return False
|
|
418
|
+
elif "__" in key:
|
|
397
419
|
field_name, lookup = key.split("__", 1)
|
|
398
420
|
else:
|
|
399
421
|
field_name, lookup = key, "exact"
|
|
400
422
|
doc_value = document.data.get(field_name)
|
|
423
|
+
if identity is not None and (
|
|
424
|
+
lookup != "in" or isinstance(value, (list, tuple, set))
|
|
425
|
+
):
|
|
426
|
+
doc_value = (
|
|
427
|
+
[canonical_identity(native_type, item) for item in doc_value]
|
|
428
|
+
if isinstance(doc_value, (list, tuple, set))
|
|
429
|
+
else canonical_identity(native_type, doc_value)
|
|
430
|
+
)
|
|
431
|
+
value = (
|
|
432
|
+
[canonical_identity(native_type, item) for item in value]
|
|
433
|
+
if isinstance(value, (list, tuple, set))
|
|
434
|
+
else canonical_identity(native_type, value)
|
|
435
|
+
)
|
|
401
436
|
if lookup == "exact" and isinstance(value, (list, tuple, set)):
|
|
402
437
|
if isinstance(doc_value, (list, tuple, set)):
|
|
403
438
|
if not set(doc_value).intersection(value):
|
{generalmanager-0.82.3 → generalmanager-0.82.5}/src/general_manager/search/backends/meilisearch.py
RENAMED
|
@@ -8,6 +8,7 @@ from collections.abc import Callable, Iterable
|
|
|
8
8
|
from importlib import import_module
|
|
9
9
|
from inspect import signature
|
|
10
10
|
from typing import Mapping, Protocol, Sequence, cast
|
|
11
|
+
from uuid import UUID
|
|
11
12
|
|
|
12
13
|
from general_manager.search.backend import (
|
|
13
14
|
SearchBackendClientMissingError,
|
|
@@ -17,6 +18,14 @@ from general_manager.search.backend import (
|
|
|
17
18
|
SearchResult,
|
|
18
19
|
)
|
|
19
20
|
from general_manager.bucket._ordering import normalize_ordering
|
|
21
|
+
from general_manager.search.identity import (
|
|
22
|
+
SearchIdentityFilterGroup,
|
|
23
|
+
bind_search_filters,
|
|
24
|
+
identity_field_key,
|
|
25
|
+
identity_token,
|
|
26
|
+
meilisearch_identity_filters,
|
|
27
|
+
SearchIdentityConfigurationError,
|
|
28
|
+
)
|
|
20
29
|
|
|
21
30
|
|
|
22
31
|
def _load_meilisearch_api_error() -> type[Exception] | None:
|
|
@@ -155,6 +164,9 @@ class MeilisearchBackend:
|
|
|
155
164
|
- "searchable_fields": iterable of field names to set as searchableAttributes.
|
|
156
165
|
- "filterable_fields": iterable of field names to set as filterableAttributes.
|
|
157
166
|
- "sortable_fields": iterable of field names to set as sortableAttributes.
|
|
167
|
+
- "identity_fields": contracts keyed by document type; each
|
|
168
|
+
maps identity names to int/str/UUID. Shadow filterability is
|
|
169
|
+
added only for names listed in "filterable_fields".
|
|
158
170
|
|
|
159
171
|
Non-iterable setting values and strings/bytes are ignored; iterable
|
|
160
172
|
values such as lists, tuples, and sets are converted to strings. This
|
|
@@ -169,6 +181,29 @@ class MeilisearchBackend:
|
|
|
169
181
|
index = self._get_or_create_index(index_name)
|
|
170
182
|
searchable_fields = self._string_sequence_setting(settings, "searchable_fields")
|
|
171
183
|
filterable_fields = self._string_sequence_setting(settings, "filterable_fields")
|
|
184
|
+
contracts = settings.get("identity_fields", {})
|
|
185
|
+
if not isinstance(contracts, Mapping):
|
|
186
|
+
raise SearchIdentityConfigurationError(
|
|
187
|
+
"identity_fields", "expected a mapping of type contracts"
|
|
188
|
+
)
|
|
189
|
+
identity_names: set[str] = set()
|
|
190
|
+
for owner, contract in contracts.items():
|
|
191
|
+
if not isinstance(contract, Mapping):
|
|
192
|
+
raise SearchIdentityConfigurationError(
|
|
193
|
+
str(owner), "expected a native field mapping"
|
|
194
|
+
)
|
|
195
|
+
validated = SearchIdentityFilterGroup({}, identity_fields=contract)
|
|
196
|
+
identity_names.update(validated.identity_fields)
|
|
197
|
+
if filterable_fields is not None:
|
|
198
|
+
shadow_fields = {
|
|
199
|
+
"gm_identity." + identity_field_key(name)
|
|
200
|
+
for name in identity_names
|
|
201
|
+
if name in filterable_fields
|
|
202
|
+
}
|
|
203
|
+
filterable_fields = [
|
|
204
|
+
*filterable_fields,
|
|
205
|
+
*sorted(shadow_fields - set(filterable_fields)),
|
|
206
|
+
]
|
|
172
207
|
sortable_fields = self._string_sequence_setting(settings, "sortable_fields")
|
|
173
208
|
ranking_rules = self._ranking_rules_with_sort_first(index, settings)
|
|
174
209
|
task = index.update_settings({"rankingRules": ranking_rules})
|
|
@@ -322,7 +357,13 @@ class MeilisearchBackend:
|
|
|
322
357
|
with structured filters. String rendering uses ``str(value)`` and
|
|
323
358
|
escapes backslashes and double quotes; this also applies to numbers,
|
|
324
359
|
booleans, and ``None``. Empty `in` lists produce an empty
|
|
325
|
-
parenthesized clause.
|
|
360
|
+
parenthesized clause. Declared identity equality/membership uses
|
|
361
|
+
separate versioned tokens without changing source values. Plain
|
|
362
|
+
structured filters inherit registered manager/index contracts;
|
|
363
|
+
`SearchIdentityFilterGroup` supports unregistered documents.
|
|
364
|
+
Old documents without tokens require reindexing; raw
|
|
365
|
+
`filter_expression` bypasses identity transport.
|
|
366
|
+
``sort`` is normalized as signed field names
|
|
326
367
|
and forwarded as ordered Meilisearch ``field:direction`` entries.
|
|
327
368
|
A sorted query reads the index settings and raises
|
|
328
369
|
``MeilisearchOrderingConfigurationError`` unless ``sort`` has the
|
|
@@ -342,7 +383,9 @@ class MeilisearchBackend:
|
|
|
342
383
|
filter_expr = (
|
|
343
384
|
filter_expression
|
|
344
385
|
if filter_expression is not None
|
|
345
|
-
else self._build_filter_expression(
|
|
386
|
+
else self._build_filter_expression(
|
|
387
|
+
bind_search_filters(index_name, filters, types), types
|
|
388
|
+
)
|
|
346
389
|
)
|
|
347
390
|
if filter_expr:
|
|
348
391
|
payload["filter"] = filter_expr
|
|
@@ -447,22 +490,48 @@ class MeilisearchBackend:
|
|
|
447
490
|
- `data`: the original data mapping,
|
|
448
491
|
- additional top-level keys copied from `document.data` except
|
|
449
492
|
reserved keys (`id`, `gm_document_id`, `type`,
|
|
450
|
-
`identification`, `data`).
|
|
493
|
+
`identification`, `data`, `gm_identity`).
|
|
494
|
+
- `gm_identity`: bounded equality tokens for declared present
|
|
495
|
+
identity fields. UUID/set JSON conversion affects only the
|
|
496
|
+
payload copy, never the source document.
|
|
451
497
|
"""
|
|
452
|
-
reserved_keys = {
|
|
498
|
+
reserved_keys = {
|
|
499
|
+
"id",
|
|
500
|
+
"gm_document_id",
|
|
501
|
+
"type",
|
|
502
|
+
"identification",
|
|
503
|
+
"data",
|
|
504
|
+
"gm_identity",
|
|
505
|
+
}
|
|
453
506
|
extra_data = {
|
|
454
507
|
key: value
|
|
455
508
|
for key, value in document.data.items()
|
|
456
509
|
if key not in reserved_keys
|
|
457
510
|
}
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
511
|
+
identities: dict[str, object] = {}
|
|
512
|
+
for name, native_type in document.identity_fields.items():
|
|
513
|
+
if name not in document.data:
|
|
514
|
+
continue
|
|
515
|
+
value = document.data[name]
|
|
516
|
+
identities[identity_field_key(name)] = (
|
|
517
|
+
[identity_token(native_type, item) for item in value]
|
|
518
|
+
if isinstance(value, (list, tuple, set))
|
|
519
|
+
else identity_token(native_type, value)
|
|
520
|
+
)
|
|
521
|
+
return cast(
|
|
522
|
+
dict[str, object],
|
|
523
|
+
_json_search_value(
|
|
524
|
+
{
|
|
525
|
+
"id": MeilisearchBackend._normalize_document_id(document.id),
|
|
526
|
+
"gm_document_id": document.id,
|
|
527
|
+
"type": document.type,
|
|
528
|
+
"identification": document.identification,
|
|
529
|
+
"data": document.data,
|
|
530
|
+
**extra_data,
|
|
531
|
+
"gm_identity": identities,
|
|
532
|
+
}
|
|
533
|
+
),
|
|
534
|
+
)
|
|
466
535
|
|
|
467
536
|
@staticmethod
|
|
468
537
|
def _build_filter_expression(
|
|
@@ -485,6 +554,9 @@ class MeilisearchBackend:
|
|
|
485
554
|
fields in one mapping are combined with AND; multiple mappings
|
|
486
555
|
are combined with OR. Empty `in` lists produce the exact clause
|
|
487
556
|
``()``.
|
|
557
|
+
Explicit identity groups instead target versioned `gm_identity`
|
|
558
|
+
fields. Null has its own token; empty identity `in` operands
|
|
559
|
+
produce an always-false clause.
|
|
488
560
|
types (Sequence[str] | None):
|
|
489
561
|
Sequence of type names to restrict results to; these are combined with OR against the `type` field.
|
|
490
562
|
|
|
@@ -502,12 +574,22 @@ class MeilisearchBackend:
|
|
|
502
574
|
group_clauses: list[str] = []
|
|
503
575
|
for group in filter_groups:
|
|
504
576
|
parts: list[str] = []
|
|
505
|
-
|
|
577
|
+
if (
|
|
578
|
+
isinstance(group, SearchIdentityFilterGroup)
|
|
579
|
+
and group.type_label is not None
|
|
580
|
+
):
|
|
581
|
+
parts.append(f'type = "{_escape_filter_value(group.type_label)}"')
|
|
582
|
+
for key, value in meilisearch_identity_filters(group):
|
|
506
583
|
if "__" in key:
|
|
507
584
|
field_name, lookup = key.split("__", 1)
|
|
508
585
|
else:
|
|
509
586
|
field_name, lookup = key, "exact"
|
|
510
587
|
if lookup == "in" and isinstance(value, (list, tuple, set)):
|
|
588
|
+
if field_name.startswith("gm_identity.") and not value:
|
|
589
|
+
parts.append(
|
|
590
|
+
f"({field_name} EXISTS AND {field_name} NOT EXISTS)"
|
|
591
|
+
)
|
|
592
|
+
continue
|
|
511
593
|
options = " OR ".join(
|
|
512
594
|
[
|
|
513
595
|
f'{field_name} = "{_escape_filter_value(item)}"'
|
|
@@ -808,6 +890,17 @@ def _is_meilisearch_already_exists(error: Exception) -> bool:
|
|
|
808
890
|
return status == 409 or "already_exists" in code
|
|
809
891
|
|
|
810
892
|
|
|
893
|
+
def _json_search_value(value: object) -> object:
|
|
894
|
+
"""Copy native document values into JSON-compatible provider payloads."""
|
|
895
|
+
if isinstance(value, UUID):
|
|
896
|
+
return str(value)
|
|
897
|
+
if isinstance(value, Mapping):
|
|
898
|
+
return {key: _json_search_value(item) for key, item in value.items()}
|
|
899
|
+
if isinstance(value, (list, tuple, set)):
|
|
900
|
+
return [_json_search_value(item) for item in value]
|
|
901
|
+
return value
|
|
902
|
+
|
|
903
|
+
|
|
811
904
|
def _escape_filter_value(value: object) -> str:
|
|
812
905
|
"""
|
|
813
906
|
Escape a value for inclusion in a Meilisearch filter expression.
|
|
@@ -5,12 +5,13 @@ from __future__ import annotations
|
|
|
5
5
|
from collections.abc import Callable, Iterable, Mapping, Sequence
|
|
6
6
|
from dataclasses import dataclass, field
|
|
7
7
|
from typing import Literal, Protocol, cast
|
|
8
|
+
from uuid import UUID
|
|
8
9
|
|
|
9
10
|
from general_manager.manager.general_manager import GeneralManager
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
_RESERVED_DOCUMENT_FIELD_NAMES = frozenset(
|
|
13
|
-
{"id", "gm_document_id", "type", "identification", "data"}
|
|
14
|
+
{"id", "gm_document_id", "type", "identification", "data", "gm_identity"}
|
|
14
15
|
)
|
|
15
16
|
|
|
16
17
|
|
|
@@ -110,6 +111,10 @@ class IndexConfig:
|
|
|
110
111
|
sorts: Field names allowed for backend sorting.
|
|
111
112
|
boost: Optional positive index-level boost.
|
|
112
113
|
min_score: Optional non-negative minimum relevance score.
|
|
114
|
+
identity_fields: Native scalar kinds (`int`, `str`, `UUID`) for configured
|
|
115
|
+
document aliases, including nullable/collection element types.
|
|
116
|
+
Manager identity metadata is inherited automatically; conflicting
|
|
117
|
+
overrides fail when the index contract is resolved.
|
|
113
118
|
|
|
114
119
|
Raises:
|
|
115
120
|
InvalidIndexBoostError: If `boost` is not `None` and is less than or
|
|
@@ -126,6 +131,7 @@ class IndexConfig:
|
|
|
126
131
|
sorts: Sequence[str] = field(default_factory=tuple)
|
|
127
132
|
boost: float | None = None
|
|
128
133
|
min_score: float | None = None
|
|
134
|
+
identity_fields: Mapping[str, type] = field(default_factory=dict)
|
|
129
135
|
|
|
130
136
|
def __post_init__(self) -> None:
|
|
131
137
|
"""Validate index-level boost and minimum score after initialization."""
|
|
@@ -143,6 +149,25 @@ class IndexConfig:
|
|
|
143
149
|
for field_name in (*self.filters, *self.sorts):
|
|
144
150
|
if field_name in _RESERVED_DOCUMENT_FIELD_NAMES:
|
|
145
151
|
raise ReservedDocumentFieldNameError(field_name)
|
|
152
|
+
from general_manager.search.identity import SearchIdentityConfigurationError
|
|
153
|
+
|
|
154
|
+
serialized_fields = {
|
|
155
|
+
*(
|
|
156
|
+
entry.name if isinstance(entry, FieldConfig) else entry
|
|
157
|
+
for entry in self.fields
|
|
158
|
+
),
|
|
159
|
+
*self.filters,
|
|
160
|
+
*self.sorts,
|
|
161
|
+
}
|
|
162
|
+
for field_name, native_type in self.identity_fields.items():
|
|
163
|
+
if field_name not in serialized_fields:
|
|
164
|
+
raise SearchIdentityConfigurationError(
|
|
165
|
+
field_name, "field is not configured"
|
|
166
|
+
)
|
|
167
|
+
if native_type not in (int, str, UUID):
|
|
168
|
+
raise SearchIdentityConfigurationError(
|
|
169
|
+
field_name, "expected int, str or UUID"
|
|
170
|
+
)
|
|
146
171
|
if self.boost is not None and self.boost <= 0:
|
|
147
172
|
raise InvalidIndexBoostError
|
|
148
173
|
if self.min_score is not None and self.min_score < 0:
|