feat: implement durable permission-aware search
This commit is contained in:
@@ -5,9 +5,11 @@ from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import (
|
||||
Boolean,
|
||||
DateTime,
|
||||
ForeignKey,
|
||||
Index,
|
||||
Integer,
|
||||
JSON,
|
||||
String,
|
||||
Text,
|
||||
@@ -15,7 +17,7 @@ from sqlalchemy import (
|
||||
)
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from govoplan_core.db.base import Base, TimestampMixin
|
||||
from govoplan_core.db.base import Base, TimestampMixin, utcnow
|
||||
|
||||
|
||||
def new_uuid() -> str:
|
||||
@@ -39,11 +41,23 @@ class SearchIndexDocument(Base, TimestampMixin):
|
||||
"resource_type",
|
||||
),
|
||||
Index("ix_search_document_visibility", "tenant_id", "visibility"),
|
||||
Index(
|
||||
"ix_search_document_provider",
|
||||
"tenant_id",
|
||||
"provider_id",
|
||||
"resource_type",
|
||||
"active",
|
||||
),
|
||||
)
|
||||
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid)
|
||||
tenant_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True)
|
||||
module_id: Mapped[str] = mapped_column(String(100), nullable=False, index=True)
|
||||
provider_id: Mapped[str] = mapped_column(
|
||||
String(200),
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
resource_type: Mapped[str] = mapped_column(
|
||||
String(100), nullable=False, index=True
|
||||
)
|
||||
@@ -66,6 +80,44 @@ class SearchIndexDocument(Base, TimestampMixin):
|
||||
"metadata", JSON, default=dict, nullable=False
|
||||
)
|
||||
content_hash: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
source_revision: Mapped[str] = mapped_column(
|
||||
String(255),
|
||||
nullable=False,
|
||||
)
|
||||
change_cursor: Mapped[str | None] = mapped_column(
|
||||
String(500),
|
||||
nullable=True,
|
||||
)
|
||||
source_updated_at: Mapped[datetime | None] = mapped_column(
|
||||
DateTime(timezone=True),
|
||||
nullable=True,
|
||||
)
|
||||
language: Mapped[str] = mapped_column(
|
||||
String(32),
|
||||
default="simple",
|
||||
nullable=False,
|
||||
)
|
||||
index_version: Mapped[int] = mapped_column(
|
||||
Integer,
|
||||
default=1,
|
||||
nullable=False,
|
||||
)
|
||||
requires_authorization_recheck: Mapped[bool] = mapped_column(
|
||||
Boolean,
|
||||
default=False,
|
||||
nullable=False,
|
||||
)
|
||||
rebuild_id: Mapped[str | None] = mapped_column(
|
||||
String(36),
|
||||
nullable=True,
|
||||
index=True,
|
||||
)
|
||||
active: Mapped[bool] = mapped_column(
|
||||
Boolean,
|
||||
default=True,
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
indexed_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), nullable=False
|
||||
)
|
||||
@@ -100,4 +152,204 @@ class SearchIndexAclToken(Base):
|
||||
)
|
||||
|
||||
|
||||
__all__ = ["SearchIndexAclToken", "SearchIndexDocument", "new_uuid"]
|
||||
class SearchIndexState(Base, TimestampMixin):
|
||||
__tablename__ = "search_index_states"
|
||||
__table_args__ = (
|
||||
UniqueConstraint(
|
||||
"tenant_id",
|
||||
"provider_id",
|
||||
"resource_type",
|
||||
name="uq_search_index_state_source",
|
||||
),
|
||||
Index(
|
||||
"ix_search_index_state_status",
|
||||
"tenant_id",
|
||||
"status",
|
||||
),
|
||||
)
|
||||
|
||||
id: Mapped[str] = mapped_column(
|
||||
String(36),
|
||||
primary_key=True,
|
||||
default=new_uuid,
|
||||
)
|
||||
tenant_id: Mapped[str] = mapped_column(
|
||||
String(36),
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
provider_id: Mapped[str] = mapped_column(
|
||||
String(200),
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
module_id: Mapped[str] = mapped_column(
|
||||
String(100),
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
resource_type: Mapped[str] = mapped_column(
|
||||
String(100),
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
index_version: Mapped[int] = mapped_column(
|
||||
Integer,
|
||||
default=1,
|
||||
nullable=False,
|
||||
)
|
||||
status: Mapped[str] = mapped_column(
|
||||
String(30),
|
||||
default="idle",
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
rebuild_id: Mapped[str | None] = mapped_column(
|
||||
String(36),
|
||||
nullable=True,
|
||||
)
|
||||
checkpoint_cursor: Mapped[str | None] = mapped_column(
|
||||
String(500),
|
||||
nullable=True,
|
||||
)
|
||||
high_watermark: Mapped[str | None] = mapped_column(
|
||||
String(500),
|
||||
nullable=True,
|
||||
)
|
||||
last_change_cursor: Mapped[str | None] = mapped_column(
|
||||
String(500),
|
||||
nullable=True,
|
||||
)
|
||||
indexed_documents: Mapped[int] = mapped_column(
|
||||
Integer,
|
||||
default=0,
|
||||
nullable=False,
|
||||
)
|
||||
rejected_documents: Mapped[int] = mapped_column(
|
||||
Integer,
|
||||
default=0,
|
||||
nullable=False,
|
||||
)
|
||||
rebuild_started_at: Mapped[datetime | None] = mapped_column(
|
||||
DateTime(timezone=True),
|
||||
nullable=True,
|
||||
)
|
||||
rebuild_completed_at: Mapped[datetime | None] = mapped_column(
|
||||
DateTime(timezone=True),
|
||||
nullable=True,
|
||||
)
|
||||
last_success_at: Mapped[datetime | None] = mapped_column(
|
||||
DateTime(timezone=True),
|
||||
nullable=True,
|
||||
)
|
||||
last_error: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
|
||||
|
||||
class SearchIndexChangeQueue(Base, TimestampMixin):
|
||||
__tablename__ = "search_index_change_queue"
|
||||
__table_args__ = (
|
||||
UniqueConstraint(
|
||||
"change_id",
|
||||
name="uq_search_index_change_queue_change",
|
||||
),
|
||||
Index(
|
||||
"ix_search_index_change_queue_pending",
|
||||
"status",
|
||||
"available_at",
|
||||
"created_at",
|
||||
),
|
||||
Index(
|
||||
"ix_search_index_change_queue_source",
|
||||
"tenant_id",
|
||||
"provider_id",
|
||||
"resource_type",
|
||||
),
|
||||
)
|
||||
|
||||
id: Mapped[str] = mapped_column(
|
||||
String(36),
|
||||
primary_key=True,
|
||||
default=new_uuid,
|
||||
)
|
||||
change_id: Mapped[str] = mapped_column(
|
||||
String(255),
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
tenant_id: Mapped[str] = mapped_column(
|
||||
String(36),
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
provider_id: Mapped[str] = mapped_column(
|
||||
String(200),
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
module_id: Mapped[str] = mapped_column(
|
||||
String(100),
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
resource_type: Mapped[str] = mapped_column(
|
||||
String(100),
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
resource_id: Mapped[str] = mapped_column(
|
||||
String(255),
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
kind: Mapped[str] = mapped_column(
|
||||
String(20),
|
||||
nullable=False,
|
||||
)
|
||||
source_revision: Mapped[str] = mapped_column(
|
||||
String(255),
|
||||
nullable=False,
|
||||
)
|
||||
source_cursor: Mapped[str] = mapped_column(
|
||||
String(500),
|
||||
nullable=False,
|
||||
)
|
||||
document_: Mapped[dict[str, Any] | None] = mapped_column(
|
||||
"document",
|
||||
JSON,
|
||||
nullable=True,
|
||||
)
|
||||
occurred_at: Mapped[datetime | None] = mapped_column(
|
||||
DateTime(timezone=True),
|
||||
nullable=True,
|
||||
)
|
||||
status: Mapped[str] = mapped_column(
|
||||
String(30),
|
||||
default="queued",
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
attempts: Mapped[int] = mapped_column(
|
||||
Integer,
|
||||
default=0,
|
||||
nullable=False,
|
||||
)
|
||||
available_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True),
|
||||
default=utcnow,
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
processed_at: Mapped[datetime | None] = mapped_column(
|
||||
DateTime(timezone=True),
|
||||
nullable=True,
|
||||
)
|
||||
error: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
|
||||
|
||||
__all__ = [
|
||||
"SearchIndexAclToken",
|
||||
"SearchIndexChangeQueue",
|
||||
"SearchIndexDocument",
|
||||
"SearchIndexState",
|
||||
"new_uuid",
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user