= select_lol()
:description: Returns a LOL query builder for flat, paginated result lists without parent-child hierarchy. Suitable for large result sets spanning multiple object types.
:keywords: select_lol, LOL, pagination, result list, ECMModelQuerySync, ECMModelQueryAsync, object types
:page-tags: api, dms

Returns an `ECMModelQueryLolSync` (sync) or `ECMModelQueryLolAsync` (async) that queries objects using the **LOL format** (Linear Object List).

LOL queries are typically faster than HOL queries for large, flat result sets because the server returns a compact, column-oriented rowset instead of an object-based XML tree. However, compared to `select()` (HOL), there are some limitations.

== Signature

[tabs]
====
Sync::
+
[source,python]
----
ecm.dms.select_lol(model_class: type[T]) -> ECMModelQueryLolSync[T]
----

Async::
+
[source,python]
----
ecm.dms.select_lol(model_class: type[T]) -> ECMModelQueryLolAsync[T]
----
====

== Parameters

[cols="1,1,3"]
|===
|Name |Type |Description

|`model_class`
|`type[ECMFolderModel \| ECMRegisterModel \| ECMDocumentModel]`
|The model class describing the object type.
|===

== Differences from select() (HOL)

[cols="1,1,1"]
|===
| |`select()` (HOL) |`select_lol()` (LOL)

|Performance
|Slower
|Faster for large, flat result sets

|Sorting
|Available
|Available

|`.file_properties()`
|Available
|Not available

|`.base_params()`
|Available (structured)
|Not available (only via system fields)

|`.variants()`
|Available
|Not available

|`.icons()`
|Available
|Ignored (server does not return icons in LOL mode)

|`.with_children()` / `.with_parents()`
|Available
|Not available

|Table field values
|Typed, with `row_id`
|Untyped raw strings, `row_id` always `None`
|===

[NOTE]
====
If file information, base parameters, variants, or hierarchical queries are needed, xref:api/dms/select.adoc[select()] is the right choice.
====

== Query builder methods

[cols="1,3"]
|===
|Method |Description

|`.where(*conditions)`
|Add filter conditions. Multiple arguments are combined with AND.

|`.order_by(*sort_orders)`
|Set the sort order. Argument order determines sort priority.

|`.limit(n)`
|Maximum total number of results across all pages (maps to `MaxHits`).

|`.pagesize(n)`
|Number of objects per server request (default: 1000).

|`.offset(n)`
|Zero-based start position.

|`.rights()`
|Fetch access rights (populates `obj.system.rights`).

|`.fields(*fields)`
|Return only the specified fields. Without arguments, resets the selection.

|`.garbage_mode()`
|Return only objects from the recycle bin.

|`.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.
|===

== Limitations in detail

=== File information

`.file_properties()` is not available. Partial file metadata is only accessible via system fields explicitly declared on the model:

[cols="1,3"]
|===
|System field |Meaning

|`OBJECT_FILESIZE`
|File size in bytes

|`OBJECT_COUNT`
|Number of files

|`OBJECT_DOCPAGECOUNT`
|Number of document pages
|===

File extension, MIME type, and MIME type group are never returned by the server in LOL mode.

=== Base parameters

`.base_params()` is not available — `obj.system.base_params` is always `None`. Audit metadata is only accessible when the corresponding system fields are declared via `_ecm_system_fields_` on the model:

[cols="1,3"]
|===
|System field |Meaning

|`OBJECT_CRID`
|Creator ID

|`OBJECT_CRDATE`
|Creation date

|`OBJECT_USERGUID`
|Owner GUID

|`OBJECT_MODIFYUSER`
|Last modifier

|`OBJECT_MODIFYTIME`
|Last modification date

|`OBJECT_LINKS`
|Link count

|`OBJECT_TXTNOTICECOUNT`
|Text note count
|===

=== Table fields

Table field values are returned as untyped raw strings and are not converted to the declared Python type. `row_id` is always `None` for table rows returned from LOL queries.

== Examples

=== Simple query

[tabs]
====
Sync::
+
[source,python]
----
for folder in (
    ecm.dms.select_lol(InvoiceFolder)
    .where(InvoiceFolder.Year >= 2020)
    .order_by(InvoiceFolder.Year.DESC)
    .stream()
):
    print(folder.system.id, folder.Title)
----

Async::
+
[source,python]
----
async for folder in (
    ecm.dms.select_lol(InvoiceFolder)
    .where(InvoiceFolder.Year >= 2020)
    .order_by(InvoiceFolder.Year.DESC)
    .stream()
):
    print(folder.system.id, folder.Title)
----
====

=== Selective fields

[tabs]
====
Sync::
+
[source,python]
----
for folder in (
    ecm.dms.select_lol(InvoiceFolder)
    .fields(InvoiceFolder.Title, InvoiceFolder.Year)
    .stream()
):
    print(folder.Title, folder.Year)
----

Async::
+
[source,python]
----
async for folder in (
    ecm.dms.select_lol(InvoiceFolder)
    .fields(InvoiceFolder.Title, InvoiceFolder.Year)
    .stream()
):
    print(folder.Title, folder.Year)
----
====

=== With rights

[tabs]
====
Sync::
+
[source,python]
----
for folder in (
    ecm.dms.select_lol(InvoiceFolder)
    .rights()
    .stream()
):
    if folder.system.rights.edit_metadata:
        print(f"{folder.system.id}: editing allowed")
----

Async::
+
[source,python]
----
async for folder in (
    ecm.dms.select_lol(InvoiceFolder)
    .rights()
    .stream()
):
    if folder.system.rights.edit_metadata:
        print(f"{folder.system.id}: editing allowed")
----
====

=== Recycle bin

[tabs]
====
Sync::
+
[source,python]
----
for folder in ecm.dms.select_lol(InvoiceFolder).garbage_mode().stream():
    print(f"Deleted: {folder.system.id} – {folder.Title}")
----

Async::
+
[source,python]
----
async for folder in ecm.dms.select_lol(InvoiceFolder).garbage_mode().stream():
    print(f"Deleted: {folder.system.id} – {folder.Title}")
----
====

=== When to use select_lol() vs select()?

[tabs]
====
Sync::
+
[source,python]
----
# Recommended for: large sets, index data only (no variants/file properties/hierarchy)
for doc in (
    ecm.dms.select_lol(InvoiceDocument)
    .where(InvoiceDocument.Year == 2024)
    .order_by(InvoiceDocument.Title.ASC)
    .pagesize(500)
    .stream()
):
    print(doc.system.id, doc.Title)

# Recommended for: file info, base params, variants, hierarchies → use select()
for doc in (
    ecm.dms.select(InvoiceDocument)
    .where(InvoiceDocument.Year == 2024)
    .file_properties()
    .stream()
):
    print(doc.system.file_properties.extension)
----

Async::
+
[source,python]
----
# Recommended for: large sets, index data only (no variants/file properties/hierarchy)
async for doc in (
    ecm.dms.select_lol(InvoiceDocument)
    .where(InvoiceDocument.Year == 2024)
    .order_by(InvoiceDocument.Title.ASC)
    .pagesize(500)
    .stream()
):
    print(doc.system.id, doc.Title)

# Recommended for: file info, base params, variants, hierarchies → use select()
async for doc in (
    ecm.dms.select(InvoiceDocument)
    .where(InvoiceDocument.Year == 2024)
    .file_properties()
    .stream()
):
    print(doc.system.file_properties.extension)
----
====
