Factorizer/Cluster ERP WikiCluster ERP Türkçe
Home / Platform

Reference

Search, pagination, and bulk operations

The shared helpers for language-independent text search, capped counts, recency pages, and bulk deactivation.

List services in Cluster ERP share three small core modules: core/search.py for text search, core/pagination.py for counts on large tables, and core/bulk_operations.py for bulk deactivation. This page lists their functions and constants. The HTTP parameters that list routes expose are documented on API conventions.

Scope and source

  • Defined in: src/cluster_erp/core/search.py, src/cluster_erp/core/pagination.py, src/cluster_erp/core/bulk_operations.py
  • Count: 7 public functions and 2 constants in search.py; 3 functions and 1 constant in pagination.py; 2 functions and 2 schemas in bulk_operations.py (read from the files). Usage counts below were made with grep -rlw <name> over src/cluster_erp and migrations, excluding the defining file.

The module docstring states two problems it solves: user-typed % and _ must be literal in LIKE, and the database collation folds only ASCII letters, so upper-case non-ASCII search terms did not match (src/cluster_erp/core/search.py:1-33).

Name Kind Behaviour Users Source
escape_like(term) function Escapes the escape character, %, and _ used by text_match src/cluster_erp/core/search.py:58-65
NORMALIZED_SQL constant The SQL text of the normalised expression, for migrations that build matching indexes 3 files src/cluster_erp/core/search.py:76-82
normalized(expr) function translate(lower(expr COLLATE "und-x-icu"),...): Unicode lower case through ICU, and the dotted and dotless i forms folded to i used by text_match src/cluster_erp/core/search.py:85-88
text_match(column, term) function normalized(column) LIKE %normalized(escaped term)% 69 files src/cluster_erp/core/search.py:91-97
fold(value) function The same normalisation in Python, for lists filtered after the query used by text_contains src/cluster_erp/core/search.py:100-111
text_contains(haystack, term) function Python-side substring match with fold 1 file src/cluster_erp/core/search.py:114-118
FENCE_MAX_MATCHES constant 20,000; above it a search page walks the recency index used by recency_page src/cluster_erp/core/search.py:143
execute_page(session, stmt, force_index_walk) function Runs a page query, optionally with bitmap scans turned off for the transaction 3 files src/cluster_erp/core/search.py:146-161
recency_page(base, entity, order_attr,...) function Builds a newest-first page; with a search and at most FENCE_MAX_MATCHES uncapped matches it filters through a materialised CTE first 3 files src/cluster_erp/core/search.py:164-195

The docstring states that accent folding is not done on purpose: the unaccent extension is not installed, and ignoring accents would merge distinct letters in some languages (src/cluster_erp/core/search.py:31-33). The literals of the fold are embedded in the SQL text, not bound, so that the expression matches an index built on the same expression (src/cluster_erp/core/search.py:68-74).

Capped counts

Name Kind Behaviour Users Source
DEFAULT_COUNT_CAP constant 10,000 n/a src/cluster_erp/core/pagination.py:23
configure(cap) function Installs the process-wide cap from list_count_cap at start-up; 0 means exact counts called by create_app src/cluster_erp/core/pagination.py:27-30, src/cluster_erp/server/app.py:1311-1313
count_capped(session, base, cap=None, prefer_index=False) function Counts at most cap + 1 rows and returns (total, capped); prefer_index turns sequential scans off for the transaction 14 files src/cluster_erp/core/pagination.py:33-69
capped_total(session, count_stmt, cap=None, prefer_index=False) function The same for an existing select(func.count(...)) statement 58 files src/cluster_erp/core/pagination.py:72-84

The docstring states the reason: on a table of millions of rows a full count(*) costs hundreds of milliseconds, while the page itself costs under a millisecond, so the pager shows "10 000+" instead (src/cluster_erp/core/pagination.py:1-13).

Bulk operations

Name Kind Behaviour Source
BulkDeleteRequest schema ids: list[UUID] src/cluster_erp/core/bulk_operations.py:12-13
BulkDeleteResponse schema deleted_count: int src/cluster_erp/core/bulk_operations.py:16-17
bulk_soft_delete(session, model_class, ids, deleted_by=None) function Loads the live rows and sets deleted_at (and deleted_by) on each through the ORM; returns the count src/cluster_erp/core/bulk_operations.py:20-55
orm_delete_where(session, model_class, *where) function Physically deletes matching rows through the ORM and flushes; for child rows only src/cluster_erp/core/bulk_operations.py:58-75

Both functions go through the ORM so that the change-history hook records every row (src/cluster_erp/core/bulk_operations.py:26-34, src/cluster_erp/core/bulk_operations.py:59-66). See soft delete and deactivation.

Constraints and validation

  • SET LOCAL and RESET change planner settings only for the current transaction (src/cluster_erp/core/pagination.py:60-66, src/cluster_erp/core/search.py:155-161).
  • recency_page applies LIMIT and OFFSET only when limit is greater than 0 (src/cluster_erp/core/search.py:193-194).

Terms introduced

Term Code identifier Definition Turkish term Source
capped count count_capped A row count that stops at a configured cap and reports that it stopped. tavanlı sayım src/cluster_erp/core/pagination.py:33
text match text_match A case-insensitive, language-independent substring filter. metin eşleşmesi src/cluster_erp/core/search.py:91

Verified against commit 92f8051 on 2026-11-13.

Verified against commit 92f8051 · 2026-11-13