select()

Returns an ECMModelQuerySync (sync) or ECMModelQueryAsync (async) that can be used to search, filter, and sort objects.

ECM Model reference — for a full description of all system properties (system.id, system.rights, system.base_params, system.file_properties, etc.) and how change tracking works.

1. Signature

  • Sync

  • Async

ecm.dms.select(model_class: type[T]) -> ECMModelQuerySync[T]
await ecm.dms.select(model_class: type[T]) -> ECMModelQueryAsync[T]

2. Parameters

Name Type Description

model_class

type[ECMFolderModel | ECMRegisterModel | ECMDocumentModel]

The model class describing the object type. Can also be created dynamically via make_folder_model() / make_register_model() / make_document_model().

3. Query builder methods

The returned query builder supports the following methods (chainable):

Method Description

.where(*conditions)

Add filter conditions. Multiple arguments are combined with AND. Conditions can be combined with & (AND) and | (OR).

.order_by(*sort_orders)

Set the sort order. Argument position determines sort priority. Each argument is an ECMSortOrder created via .ASC / .DESC on an ECMField.

.limit(n)

Maximum total number of results across all pages.

.pagesize(n)

Number of objects per server request (default: 1000). Affects efficiency for large result sets.

.offset(n)

Zero-based start offset. Skips the first n results.

.rights()

Include access rights data (populates obj.system.rights).

.base_params()

Include audit metadata (populates obj.system.base_params): creator, modification date, etc.

.file_properties()

Include file properties (populates obj.system.file_properties, documents only).

.variants()

Include document variant data.

.icons()

Include icon IDs for returned objects.

.remarks()

Include remarks for returned objects.

.fields(*fields)

Return only the specified fields (sets field_schema="MIN"). Useful for performant queries when only a few fields are needed. Calling without arguments resets the restriction.

.garbage_mode()

Return only objects from the recycle bin.

.with_children(*specs)

Switch to a HOL query that includes child objects. Returns an ECMModelQueryHolSync.

.with_parents(*specs)

Switch to a HOL query that includes parent objects. Returns an ECMModelQueryHolSync.

.execute()

Execute the query and return all results as a list (all pages in memory).

.stream()

Execute the query page by page and return a generator. Recommended for large result sets.

.execute() loads all objects into memory before processing begins. With large result sets this can cause memory problems.

.stream() works page by page — but paging in enaio is not transactional. If objects are modified while iterating, pages may contain inconsistent state or objects may appear twice. In that case .execute() should be preferred.

4. Filter conditions (where)

Conditions are passed to .where(). Multiple arguments in a single .where() call are automatically combined with AND. For OR combinations, conditions are joined with the | operator; for AND with &.

4.1. Comparison operators

Syntax Operator Example

Field == value

Equality

InvoiceFolder.Year == 2024

Field != value

Inequality

InvoiceFolder.Status != "Archived"

Field < value

Less than

InvoiceFolder.Year < 2024

Field ⇐ value

Less than or equal

InvoiceFolder.Year ⇐ 2024

Field > value

Greater than

InvoiceFolder.Year > 2020

Field >= value

Greater than or equal

InvoiceFolder.Year >= 2020

Field.in_(v1, v2, …​)

Matches one of the values

InvoiceFolder.Year.in_(2022, 2023, 2024)

Field.not_in(v1, v2, …​)

Matches none of the values

InvoiceFolder.Status.not_in("Open", "Draft")

Field.between(lower, upper)

Between two values (inclusive)

InvoiceFolder.Year.between(2020, 2024)

4.2. AND combinations

Multiple arguments in .where() are combined with AND. The & operator can also be used explicitly:

# Variant 1: multiple arguments → AND
ecm.dms.select(InvoiceFolder).where(
    InvoiceFolder.Year >= 2020,
    InvoiceFolder.Status == "Approved",
)

# Variant 2: explicit & → same result
ecm.dms.select(InvoiceFolder).where(
    (InvoiceFolder.Year >= 2020) & (InvoiceFolder.Status == "Approved")
)

4.3. OR combinations

Conditions are combined into an OR group using |:

ecm.dms.select(InvoiceFolder).where(
    (InvoiceFolder.Status == "Open") | (InvoiceFolder.Status == "In Progress")
)

For value sets, .in_() is more concise:

ecm.dms.select(InvoiceFolder).where(
    InvoiceFolder.Status.in_("Open", "In Progress")
)

4.4. Mixed AND/OR groups

& and | can be nested arbitrarily. Python parentheses control the evaluation order:

  • Sync

  • Async

# (Year >= 2020 AND Year <= 2024) AND (Status = "Open" OR Status = "In Progress")
for folder in (
    ecm.dms.select(InvoiceFolder)
    .where(
        (InvoiceFolder.Year >= 2020) & (InvoiceFolder.Year <= 2024),
        InvoiceFolder.Status.in_("Open", "In Progress"),
    )
    .stream()
):
    print(folder.Title, folder.Year)
async for folder in (
    ecm.dms.select(InvoiceFolder)
    .where(
        (InvoiceFolder.Year >= 2020) & (InvoiceFolder.Year <= 2024),
        InvoiceFolder.Status.in_("Open", "In Progress"),
    )
    .stream()
):
    print(folder.Title, folder.Year)

4.5. between()

.between(lower, upper) is a compact alternative to >= + :

  • Sync

  • Async

for folder in (
    ecm.dms.select(InvoiceFolder)
    .where(InvoiceFolder.Year.between(2020, 2024))
    .stream()
):
    print(folder.Title)
async for folder in (
    ecm.dms.select(InvoiceFolder)
    .where(InvoiceFolder.Year.between(2020, 2024))
    .stream()
):
    print(folder.Title)

4.6. Combined queries across multiple object types

Conditions in .where() can reference fields from different object types within the same cabinet. The server returns only those objects where all conditions are satisfied — regardless of which object type a condition refers to.

The following combinations are supported:

  • Search for a document with conditions on fields of the document itself, its register, and its folder.

  • Search for a register or folder that contains a child object matching a given condition.

When registers are nested, only the immediately enclosing register is available; parent registers above it are not.

Search for a document — with conditions on register and folder:

  • Sync

  • Async

from tests.models.Unittest_DMS import Unittest_DMS, Unittest_DMS_Register, Unittest_DMS_Document

for doc in (
    ecm.dms.select(Unittest_DMS_Document)
    .where(
        Unittest_DMS_Document.StringField == "Invoice",
        Unittest_DMS_Register.Name == "Incoming invoices",
        Unittest_DMS.Name == "Supplier GmbH",
    )
    .stream()
):
    print(doc.system.id, doc.Name)
async for doc in (
    ecm.dms.select(Unittest_DMS_Document)
    .where(
        Unittest_DMS_Document.StringField == "Invoice",
        Unittest_DMS_Register.Name == "Incoming invoices",
        Unittest_DMS.Name == "Supplier GmbH",
    )
    .stream()
):
    print(doc.system.id, doc.Name)

Search for a folder — that contains a document matching a condition:

  • Sync

  • Async

from tests.models.Unittest_DMS import Unittest_DMS, Unittest_DMS_Document

for folder in (
    ecm.dms.select(Unittest_DMS)
    .where(
        Unittest_DMS.Name == "Supplier GmbH",
        Unittest_DMS_Document.StringField == "Invoice",
    )
    .stream()
):
    print(folder.system.id, folder.Name)
async for folder in (
    ecm.dms.select(Unittest_DMS)
    .where(
        Unittest_DMS.Name == "Supplier GmbH",
        Unittest_DMS_Document.StringField == "Invoice",
    )
    .stream()
):
    print(folder.system.id, folder.Name)

5. Sorting

The sort order is set via .order_by(). Each argument is an ECMSortOrder produced by .ASC or .DESC on an ECMField at class level. Argument position determines sort priority.

  • Sync

  • Async

# Primary sort by year descending, secondary by title ascending
for folder in (
    ecm.dms.select(InvoiceFolder)
    .order_by(InvoiceFolder.Year.DESC, InvoiceFolder.Title.ASC)
    .stream()
):
    print(folder.Year, folder.Title)
async for folder in (
    ecm.dms.select(InvoiceFolder)
    .order_by(InvoiceFolder.Year.DESC, InvoiceFolder.Title.ASC)
    .stream()
):
    print(folder.Year, folder.Title)

6. Pagination

.limit(), .pagesize() and .offset() restrict the result set and control internal page navigation.

Method Default Description

.limit(n)

unlimited

Maximum total number of results across all pages.

.pagesize(n)

1000

Number of objects per server request. Smaller values reduce per-page memory usage but increase the number of requests. Large values can overload the server — see the warning below.

.offset(n)

0

Skip the first n results. Useful for manual page navigation.

  • Sync

  • Async

# Page 3 with 20 entries each (offset = 2 × 20 = 40), sorted by year descending
for folder in (
    ecm.dms.select(InvoiceFolder)
    .order_by(InvoiceFolder.Year.DESC)
    .limit(20)
    .offset(40)
    .stream()
):
    print(folder.system.id, folder.Title)
async for folder in (
    ecm.dms.select(InvoiceFolder)
    .order_by(InvoiceFolder.Year.DESC)
    .limit(20)
    .offset(40)
    .stream()
):
    print(folder.system.id, folder.Title)

A pagesize that is too large can cause the server to crash. Memory consumption per page grows proportionally to the number of objects multiplied by the requested metadata. The combination of large pages with .rights(), .base_params(), .file_properties() or .variants() is particularly critical, as the server performs additional database queries for each object.

As a rule of thumb: keep the default of 1000 and only adjust when a proven performance problem exists — and then reduce rather than increase.

7. Rights information

.rights() retrieves the access rights of the logged-in user for each object. The rights are available in obj.system.rights as an ECMModelRights instance.

Attribute Description

insert

May create child objects (registers/documents in a folder, documents in a register).

edit_metadata

May change the index fields of the object.

read_file

May read the file of the document.

edit_file

May modify the file of the document.

delete

May delete the object.

  • Sync

  • Async

for folder in ecm.dms.select(InvoiceFolder).rights().stream():
    r = folder.system.rights
    if r.edit_metadata:
        print(f"{folder.system.id}: editing allowed")
    if not r.delete:
        print(f"{folder.system.id}: deletion not permitted")
async for folder in ecm.dms.select(InvoiceFolder).rights().stream():
    r = folder.system.rights
    if r.edit_metadata:
        print(f"{folder.system.id}: editing allowed")
    if not r.delete:
        print(f"{folder.system.id}: deletion not permitted")

8. Base parameters

.base_params() retrieves administrative information stored by the server for each object. The data is available in obj.system.base_params as an ECMModelBaseParams instance.

Attribute Description

creator

Username of the creator.

creation_date

Date of creation.

owner

Current owner of the object.

modifier

Username of the last modifier.

modified_date

Timestamp of the last modification.

links_count

Number of links.

text_notice_count

Number of text notices.

  • Sync

  • Async

for folder in ecm.dms.select(InvoiceFolder).base_params().stream():
    bp = folder.system.base_params
    print(f"Created by {bp.creator} on {bp.creation_date}")
    print(f"Last modified by {bp.modifier} on {bp.modified_date}")
async for folder in ecm.dms.select(InvoiceFolder).base_params().stream():
    bp = folder.system.base_params
    print(f"Created by {bp.creator} on {bp.creation_date}")
    print(f"Last modified by {bp.modifier} on {bp.modified_date}")

9. File properties

.file_properties() retrieves metadata about the file of a document. Only available for ECMDocumentModel types; obj.system.file_properties returns an ECMModelFileProperties instance.

Attribute Description

count

Number of files (primary and secondary files).

size

File size in bytes.

extension

File extension (e.g. pdf, docx).

mimetype

MIME type (e.g. application/pdf).

mimetypegroup

MIME group (e.g. application).

iconid

ID of the file type icon.

documentpagecount

Number of pages (if known).

  • Sync

  • Async

for doc in ecm.dms.select(InvoiceDocument).file_properties().stream():
    fp = doc.system.file_properties
    print(f"{doc.system.id}: {fp.extension}, {fp.size} bytes, {fp.documentpagecount} pages")
async for doc in ecm.dms.select(InvoiceDocument).file_properties().stream():
    fp = doc.system.file_properties
    print(f"{doc.system.id}: {fp.extension}, {fp.size} bytes, {fp.documentpagecount} pages")

10. Variants

.variants() retrieves the version branches of a document from the W-module. Only relevant for document types with the W-module enabled. The result is available in obj.system.variants as a list of ECMModelDocumentVariant instances.

Each variant has the attributes doc_id, doc_ver, is_active, doc_parent and children.

  • Sync

  • Async

for doc in ecm.dms.select(InvoiceDocument).variants().stream():
    for variant in doc.system.variants:
        active = "✓" if variant.is_active else " "
        print(f"[{active}] {variant.doc_ver} (ID {variant.doc_id})")
async for doc in ecm.dms.select(InvoiceDocument).variants().stream():
    for variant in doc.system.variants:
        active = "✓" if variant.is_active else " "
        print(f"[{active}] {variant.doc_ver} (ID {variant.doc_id})")

11. Remarks

.remarks() includes the text notices of an object in the response. The data is available in obj.system.remarks.

  • Sync

  • Async

for folder in ecm.dms.select(InvoiceFolder).remarks().stream():
    for remark in folder.system.remarks:
        print(remark)
async for folder in ecm.dms.select(InvoiceFolder).remarks().stream():
    for remark in folder.system.remarks:
        print(remark)

12. Restricting fields (fields)

.fields() loads only the specified index fields from the server. The server internally sets field_schema="MIN" and transfers only the explicitly listed fields. System fields (system.id, system.name, etc.) and sort fields are always included regardless of this list.

This significantly reduces the amount of data transferred when only a few fields are needed. Fields that were not requested are None on the returned object.

Fields can be passed as ECMField class attributes or as internal field name strings. Calling without arguments resets the restriction and returns all fields again.

  • Sync

  • Async

# Load only Title and Year — all other fields are None
for folder in (
    ecm.dms.select(InvoiceFolder)
    .fields(InvoiceFolder.Title, InvoiceFolder.Year)
    .stream()
):
    print(folder.Title, folder.Year)
    # folder.Status is None (not loaded)
async for folder in (
    ecm.dms.select(InvoiceFolder)
    .fields(InvoiceFolder.Title, InvoiceFolder.Year)
    .stream()
):
    print(folder.Title, folder.Year)

Fields can alternatively be passed as strings when no typed model is available:

  • Sync

  • Async

from ecmind_blue_client.ecm.model import make_folder_model

InvoiceFolder = make_folder_model("InvoiceFolder")

for folder in (
    ecm.dms.select(InvoiceFolder)
    .fields("Title", "Year")
    .stream()
):
    print(folder["Title"], folder["Year"])
async for folder in (
    ecm.dms.select(InvoiceFolder)
    .fields("Title", "Year")
    .stream()
):
    print(folder["Title"], folder["Year"])

13. Recycle bin

.garbage_mode() restricts the query to deleted objects. Without this call only non-deleted objects are returned.

  • Sync

  • Async

# List all deleted invoice folders
for folder in ecm.dms.select(InvoiceFolder).garbage_mode().stream():
    print(f"Deleted: {folder.system.id} – {folder.Title}")
async for folder in ecm.dms.select(InvoiceFolder).garbage_mode().stream():
    print(f"Deleted: {folder.system.id} – {folder.Title}")

14. Child objects (with_children)

.with_children() switches to a HOL query that returns each main object together with its child objects. The call returns an ECMModelQueryHolSync. .limit() must be set before execute() or stream() because HOL responses do not support pagination.

Each result is an ECMHolResult with:

  • .main — the main object (e.g. the folder)

  • .children_of(ECMChildSpec(ChildModel)) — list of child objects of that type

  • Sync

  • Async

from ecmind_blue_client.ecm.model import ECMChildSpec

results = (
    ecm.dms.select(InvoiceFolder)
    .where(InvoiceFolder.Year == 2024)
    .with_children(ECMChildSpec(InvoiceDocument))
    .limit(100)
    .execute()
)
for r in results:
    print(f"Folder {r.main.Title}:")
    for doc in r.children_of(ECMChildSpec(InvoiceDocument)):
        print(f"  Document {doc.system.id}")
from ecmind_blue_client.ecm.model import ECMChildSpec

results = await (
    ecm.dms.select(InvoiceFolder)
    .where(InvoiceFolder.Year == 2024)
    .with_children(ECMChildSpec(InvoiceDocument))
    .limit(100)
    .execute()
)
for r in results:
    print(f"Folder {r.main.Title}:")
    for doc in r.children_of(ECMChildSpec(InvoiceDocument)):
        print(f"  Document {doc.system.id}")

15. Parent objects (with_parents)

.with_parents() inverts the response structure: the outermost parent type (first spec) becomes the main object result.main. The originally queried object and all intermediate levels are accessible via .children_of().

Specs are passed from outermost to innermost (e.g. Folder first, then Register).

  • Sync

  • Async

from ecmind_blue_client.ecm.model import ECMChildSpec, ECMParentSpec

results = (
    ecm.dms.select(InvoiceDocument)
    .where(InvoiceDocument.Status == "Approved")
    .with_parents(ECMParentSpec(InvoiceFolder), ECMParentSpec(InvoiceRegister))
    .limit(50)
    .execute()
)
for r in results:
    folder   = r.main                                          # InvoiceFolder
    register = r.children_of(ECMChildSpec(InvoiceRegister))[0]
    doc      = r.children_of(ECMChildSpec(InvoiceDocument))[0]
    print(f"{folder.Title} → {register.Title} → Doc {doc.system.id}")
from ecmind_blue_client.ecm.model import ECMChildSpec, ECMParentSpec

results = await (
    ecm.dms.select(InvoiceDocument)
    .where(InvoiceDocument.Status == "Approved")
    .with_parents(ECMParentSpec(InvoiceFolder), ECMParentSpec(InvoiceRegister))
    .limit(50)
    .execute()
)
for r in results:
    folder   = r.main
    register = r.children_of(ECMChildSpec(InvoiceRegister))[0]
    doc      = r.children_of(ECMChildSpec(InvoiceDocument))[0]
    print(f"{folder.Title} → {register.Title} → Doc {doc.system.id}")

16. Examples

16.1. Basic query with filter and sorting

  • Sync

  • Async

from ecmind_blue_client.ecm.model import ECMFolderModel, ECMField

class InvoiceFolder(ECMFolderModel):
    _internal_name_ = "InvoiceFolder"
    Title: ECMField[str]
    Year:  ECMField[int]

for folder in (
    ecm.dms.select(InvoiceFolder)
    .where(InvoiceFolder.Year >= 2020)
    .order_by(InvoiceFolder.Year.DESC)
    .stream()
):
    print(folder.system.id, folder.Title)
from ecmind_blue_client.ecm.model import ECMFolderModel, ECMField

class InvoiceFolder(ECMFolderModel):
    _internal_name_ = "InvoiceFolder"
    Title: ECMField[str]
    Year:  ECMField[int]

async for folder in (
    ecm.dms.select(InvoiceFolder)
    .where(InvoiceFolder.Year >= 2020)
    .order_by(InvoiceFolder.Year.DESC)
    .stream()
):
    print(folder.system.id, folder.Title)

16.2. Combined conditions (AND / OR)

  • Sync

  • Async

for folder in (
    ecm.dms.select(InvoiceFolder)
    .where(
        (InvoiceFolder.Year >= 2020) & (InvoiceFolder.Year <= 2024),
        InvoiceFolder.Title == "Invoice",
    )
    .stream()
):
    print(folder.Title, folder.Year)
async for folder in (
    ecm.dms.select(InvoiceFolder)
    .where(
        (InvoiceFolder.Year >= 2020) & (InvoiceFolder.Year <= 2024),
        InvoiceFolder.Title == "Invoice",
    )
    .stream()
):
    print(folder.Title, folder.Year)

16.3. Limit, offset and page size

  • Sync

  • Async

# At most 50 results, starting at position 100, in batches of 25 per server request
for folder in (
    ecm.dms.select(InvoiceFolder)
    .limit(50)
    .offset(100)
    .pagesize(25)
    .stream()
):
    print(folder.system.id)
async for folder in (
    ecm.dms.select(InvoiceFolder)
    .limit(50)
    .offset(100)
    .pagesize(25)
    .stream()
):
    print(folder.system.id)

16.4. Rights and audit metadata

  • Sync

  • Async

for folder in (
    ecm.dms.select(InvoiceFolder)
    .rights()
    .base_params()
    .stream()
):
    print(folder.system.rights)
    print(folder.system.base_params)
async for folder in (
    ecm.dms.select(InvoiceFolder)
    .rights()
    .base_params()
    .stream()
):
    print(folder.system.rights)
    print(folder.system.base_params)

16.5. Selective fields

Use .fields() to load only the specified fields from the server. This significantly reduces the amount of data transferred when only a few fields are needed.

  • Sync

  • Async

for folder in (
    ecm.dms.select(InvoiceFolder)
    .fields(InvoiceFolder.Title, InvoiceFolder.Year)
    .stream()
):
    print(folder.Title, folder.Year)
    # folder.OtherField would be None (not loaded)
async for folder in (
    ecm.dms.select(InvoiceFolder)
    .fields(InvoiceFolder.Title, InvoiceFolder.Year)
    .stream()
):
    print(folder.Title, folder.Year)

16.6. Querying the recycle bin

  • Sync

  • Async

# Only deleted objects
for folder in ecm.dms.select(InvoiceFolder).garbage_mode().stream():
    print(folder.system.id, folder.Title)
async for folder in ecm.dms.select(InvoiceFolder).garbage_mode().stream():
    print(folder.system.id, folder.Title)

16.7. Generic model (without a class definition)

When the object type is only known at runtime, a model can be created dynamically:

  • Sync

  • Async

from ecmind_blue_client.ecm.model import make_folder_model

InvoiceFolder = make_folder_model("InvoiceFolder")

for folder in ecm.dms.select(InvoiceFolder).stream():
    print(folder.system.id, folder["Title"])
from ecmind_blue_client.ecm.model import make_folder_model

InvoiceFolder = make_folder_model("InvoiceFolder")

async for folder in ecm.dms.select(InvoiceFolder).stream():
    print(folder.system.id, folder["Title"])

16.8. Combined query across document, register and folder

  • Sync

  • Async

from tests.models.Unittest_DMS import Unittest_DMS, Unittest_DMS_Register, Unittest_DMS_Document

for doc in (
    ecm.dms.select(Unittest_DMS_Document)
    .where(
        Unittest_DMS_Document.StringField == "Invoice",
        Unittest_DMS_Register.Name == "Incoming invoices",
        Unittest_DMS.Name == "Supplier GmbH",
    )
    .stream()
):
    print(doc.system.id, doc.Name)
from tests.models.Unittest_DMS import Unittest_DMS, Unittest_DMS_Register, Unittest_DMS_Document

async for doc in (
    ecm.dms.select(Unittest_DMS_Document)
    .where(
        Unittest_DMS_Document.StringField == "Invoice",
        Unittest_DMS_Register.Name == "Incoming invoices",
        Unittest_DMS.Name == "Supplier GmbH",
    )
    .stream()
):
    print(doc.system.id, doc.Name)

16.9. Search for a folder containing a matching child object

  • Sync

  • Async

from tests.models.Unittest_DMS import Unittest_DMS, Unittest_DMS_Document

for folder in (
    ecm.dms.select(Unittest_DMS)
    .where(
        Unittest_DMS.Name == "Supplier GmbH",
        Unittest_DMS_Document.StringField == "Invoice",
    )
    .stream()
):
    print(folder.system.id, folder.Name)
from tests.models.Unittest_DMS import Unittest_DMS, Unittest_DMS_Document

async for folder in (
    ecm.dms.select(Unittest_DMS)
    .where(
        Unittest_DMS.Name == "Supplier GmbH",
        Unittest_DMS_Document.StringField == "Invoice",
    )
    .stream()
):
    print(folder.system.id, folder.Name)