# coding: utf-8

"""
    Mixpeek API

    This is the Mixpeek API, providing access to various endpoints for data processing and retrieval.

    The version of the OpenAPI document: 1.3.0
    Contact: info@mixpeek.com
    Generated by OpenAPI Generator (https://openapi-generator.tech)

    Do not edit the class manually.
"""  # noqa: E501


from __future__ import annotations
import pprint
import re  # noqa: F401
import json

from pydantic import BaseModel, ConfigDict, Field, StrictBool, StrictStr
from typing import Any, ClassVar, Dict, List, Optional
from typing_extensions import Annotated
from mixpeek.models.filter_operator import FilterOperator
from mixpeek.models.logical_operator import LogicalOperator
from typing import Optional, Set
from typing_extensions import Self

class StageParamsAttributeFilter(BaseModel):
    """
    Configuration for filtering documents by attribute conditions.  **Stage Category**: FILTER  **Transformation**: N documents → ≤N documents (subset, same schema)  **Purpose**: Produces a subset of input documents by removing those that don't match attribute conditions. Output documents have identical schema to input.  **When to Use**:     - **As First Stage**: To retrieve and filter documents by attributes without semantic search       (e.g., \"get all active products with priority >= 5\"). Fetches up to 1000 documents       per collection from Qdrant, then applies filter conditions.     - **As Subsequent Stage**: To narrow results from previous stages by specific attributes       (status, date range, category, tags). Operates purely on in-memory results.     - When you need to apply business logic filtering (active items, published content)     - Before expensive stages (SORT, APPLY) to reduce processing overhead     - For structured/fast filtering based on document properties  **When NOT to Use**:     - For reordering results (use SORT stages: sort_relevance, sort_attribute)     - For complex semantic filtering (use llm_filter instead)     - For enriching documents with additional data (use APPLY stages)     - For aggregating to single document (use REDUCE stages)  **Operational Behavior**:     - **As First Stage**: Fetches documents directly from Qdrant (up to 1000 per collection)       using scroll API. Supports pre_filters which leverage Qdrant's native filtering       (including full-text search with TEXT operator). Results are then filtered in-memory       using the stage's filter conditions. This allows attribute_filter to be used as an       initial retrieval stage without requiring a prior search/embedding stage.     - **As Subsequent Stage**: Operates purely on in-memory results from previous stages       (no database queries). This is the typical use case for post-filtering.     - Produces subset of documents (removes non-matching)     - Fast operation (simple condition evaluation)     - Processes documents in batches for memory efficiency     - Supports complex boolean logic (AND/OR/NOT)     - Output schema = Input schema (no schema changes)  **Common Pipeline Position**: FILTER (this stage) → SORT → APPLY  **Important Limitations**:     - When used as first stage, maximum 1000 documents per collection are fetched from Qdrant     - For large collections, consider using semantic_search or other retrieval stages first     - Vectors are not fetched from Qdrant (only payloads) to optimize performance  **Two modes of operation:**  1. **Simple mode** (single condition): Specify `field`, `operator`, and `value` 2. **Boolean mode** (multiple conditions): Specify `conditions` with AND/OR/NOT logic  Both modes support template variables that are evaluated for every document.  Examples:     Simple single condition:         ```json         {             \"field\": \"metadata.status\",             \"operator\": \"eq\",             \"value\": \"active\"         }         ```      Boolean AND:         ```json         {             \"conditions\": {                 \"AND\": [                     {\"field\": \"metadata.status\", \"operator\": \"eq\", \"value\": \"active\"},                     {\"field\": \"metadata.priority\", \"operator\": \"gte\", \"value\": 5}                 ]             }         }         ```      Boolean OR:         ```json         {             \"conditions\": {                 \"OR\": [                     {\"field\": \"metadata.urgent\", \"operator\": \"eq\", \"value\": true},                     {\"field\": \"metadata.priority\", \"operator\": \"gte\", \"value\": 8}                 ]             }         }         ```      Nested boolean logic:         ```json         {             \"conditions\": {                 \"AND\": [                     {\"field\": \"metadata.status\", \"operator\": \"eq\", \"value\": \"active\"},                     {                         \"OR\": [                             {\"field\": \"metadata.category\", \"operator\": \"eq\", \"value\": \"urgent\"},                             {\"field\": \"metadata.category\", \"operator\": \"eq\", \"value\": \"critical\"}                         ]                     }                 ]             }         }         ```
    """ # noqa: E501
    var_field: Optional[StrictStr] = Field(default='metadata.status', description="Dot-delimited field path to evaluate on each document. Supports template variables (e.g. '{{DOC.metadata.category}}'). REQUIRED for simple mode. NOT USED when 'conditions' is specified.", alias="field")
    operator: Optional[FilterOperator] = Field(default=None, description="Comparison operator to apply. Supported operators: eq, ne, gt, gte, lt, lte, in, nin, contains, starts_with, ends_with, regex, exists, is_null. REQUIRED for simple mode. NOT USED when 'conditions' is specified.")
    value: Optional[Any] = None
    conditions: Optional[LogicalOperator] = Field(default=None, description="Complex filter conditions using boolean logic (AND/OR/NOT). Use this for combining multiple filter conditions. REQUIRED for boolean mode. Cannot be used with 'field'/'operator'/'value'.")
    batch_size: Optional[Annotated[int, Field(le=1000, strict=True, ge=1)]] = Field(default=100, description="Number of documents to evaluate per batch. The executor streams documents through the filter in chunks to avoid large in-memory spikes.")
    case_insensitive: Optional[StrictBool] = Field(default=False, description="When true, string comparisons are performed case-insensitively where the operator supports it. Applies to both simple and boolean modes.")
    __properties: ClassVar[List[str]] = ["field", "operator", "value", "conditions", "batch_size", "case_insensitive"]

    model_config = ConfigDict(
        populate_by_name=True,
        validate_assignment=True,
        protected_namespaces=(),
    )


    def to_str(self) -> str:
        """Returns the string representation of the model using alias"""
        return pprint.pformat(self.model_dump(by_alias=True))

    def to_json(self) -> str:
        """Returns the JSON representation of the model using alias"""
        # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
        return json.dumps(self.to_dict())

    @classmethod
    def from_json(cls, json_str: str) -> Optional[Self]:
        """Create an instance of StageParamsAttributeFilter from a JSON string"""
        return cls.from_dict(json.loads(json_str))

    def to_dict(self) -> Dict[str, Any]:
        """Return the dictionary representation of the model using alias.

        This has the following differences from calling pydantic's
        `self.model_dump(by_alias=True)`:

        * `None` is only added to the output dict for nullable fields that
          were set at model initialization. Other fields with value `None`
          are ignored.
        """
        excluded_fields: Set[str] = set([
        ])

        _dict = self.model_dump(
            by_alias=True,
            exclude=excluded_fields,
            exclude_none=True,
        )
        # override the default output from pydantic by calling `to_dict()` of conditions
        if self.conditions:
            _dict['conditions'] = self.conditions.to_dict()
        # set to None if value (nullable) is None
        # and model_fields_set contains the field
        if self.value is None and "value" in self.model_fields_set:
            _dict['value'] = None

        return _dict

    @classmethod
    def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
        """Create an instance of StageParamsAttributeFilter from a dict"""
        if obj is None:
            return None

        if not isinstance(obj, dict):
            return cls.model_validate(obj)

        _obj = cls.model_validate({
            "field": obj.get("field") if obj.get("field") is not None else 'metadata.status',
            "operator": obj.get("operator"),
            "value": obj.get("value"),
            "conditions": LogicalOperator.from_dict(obj["conditions"]) if obj.get("conditions") is not None else None,
            "batch_size": obj.get("batch_size") if obj.get("batch_size") is not None else 100,
            "case_insensitive": obj.get("case_insensitive") if obj.get("case_insensitive") is not None else False
        })
        return _obj


