= select_lol()
:description: Gibt einen LOL-Query-Builder zurück. Liefert eine flache, paginierte Ergebnisliste ohne Hierarchie — geeignet für große Treffermengen über mehrere Objekttypen.
:keywords: select_lol, LOL, Paginierung, Ergebnisliste, ECMModelQuerySync, ECMModelQueryAsync, Objekttypen
:page-tags: api, dms

Gibt einen `ECMModelQueryLolSync` (sync) bzw. `ECMModelQueryLolAsync` (async) zurück, der Objekte über das **LOL-Format** (Linear Object List) abfragt.

LOL-Abfragen sind in der Regel schneller als HOL-Abfragen bei großen, flachen Ergebnismengen, da der Server eine kompakte, spaltenorientierte Zeilenmenge zurückgibt statt eines objektbasierten XML-Baums. Gegenüber `select()` (HOL) gibt es jedoch einige Einschränkungen.

== Signatur

[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]
----
====

== Parameter

[cols="1,1,3"]
|===
|Name |Typ |Beschreibung

|`model_class`
|`type[ECMFolderModel \| ECMRegisterModel \| ECMDocumentModel]`
|Die Modelklasse, die den Objekttyp beschreibt.
|===

== Unterschiede zu select() (HOL)

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

|Performance
|Langsamer
|Schneller bei großen, flachen Mengen

|Sortierung
|Verfügbar
|Verfügbar

|`.file_properties()`
|Verfügbar
|Nicht verfügbar

|`.base_params()`
|Verfügbar (strukturiert)
|Nicht verfügbar (nur via Systemfelder)

|`.variants()`
|Verfügbar
|Nicht verfügbar

|`.icons()`
|Verfügbar
|Ignoriert (Server liefert keine Icons)

|`.with_children()` / `.with_parents()`
|Verfügbar
|Nicht verfügbar

|Tabellenfeld-Werte
|Typisiert, mit `row_id`
|Untypisierte Rohstrings, `row_id` immer `None`
|===

[NOTE]
====
Wenn Dateiinformationen, Basisparameter, Varianten oder hierarchische Abfragen benötigt werden, ist xref:api/dms/select.adoc[select()] die richtige Wahl.
====

== Query-Builder Methoden

[cols="1,3"]
|===
|Methode |Beschreibung

|`.where(*conditions)`
|Filterbedingungen hinzufügen. Mehrere Argumente werden mit AND verknüpft.

|`.order_by(*sort_orders)`
|Sortierrichtung festlegen. Die Reihenfolge der Argumente bestimmt die Sortierpriorität.

|`.limit(n)`
|Maximale Gesamtanzahl Treffer über alle Seiten (entspricht `MaxHits`).

|`.pagesize(n)`
|Anzahl Objekte pro Server-Anfrage (Standard: 1000).

|`.offset(n)`
|Startposition (0-basiert).

|`.rights()`
|Zugriffsrechte mit abrufen (befüllt `obj.system.rights`).

|`.fields(*fields)`
|Gibt nur die angegebenen Felder zurück. Ohne Argumente wird die Einschränkung zurückgesetzt.

|`.garbage_mode()`
|Nur Objekte aus dem Papierkorb zurückgeben.

|`.execute()`
|Führt die Abfrage aus und gibt eine vollständige Liste zurück (alle Seiten im Speicher).

|`.stream()`
|Führt die Abfrage seitenweise aus und gibt einen Generator zurück.
|===

== Einschränkungen im Detail

=== Dateiinformationen

`.file_properties()` steht nicht zur Verfügung. Teilweise Datei-Metadaten sind nur über Systemfelder zugänglich, die explizit im Modell deklariert werden müssen:

[cols="1,3"]
|===
|Systemfeld |Bedeutung

|`OBJECT_FILESIZE`
|Dateigröße in Bytes

|`OBJECT_COUNT`
|Anzahl der Dateien

|`OBJECT_DOCPAGECOUNT`
|Anzahl der Dokumentseiten
|===

Dateiendung, MIME-Typ und MIME-Gruppe werden im LOL-Modus vom Server grundsätzlich nicht zurückgegeben.

=== Basisparameter

`.base_params()` ist nicht verfügbar — `obj.system.base_params` ist immer `None`. Audit-Metadaten sind nur zugänglich, wenn die entsprechenden Systemfelder über `_ecm_system_fields_` am Modell deklariert sind:

[cols="1,3"]
|===
|Systemfeld |Bedeutung

|`OBJECT_CRID`
|Ersteller-ID

|`OBJECT_CRDATE`
|Erstellungsdatum

|`OBJECT_USERGUID`
|GUID des Eigentümers

|`OBJECT_MODIFYUSER`
|Letzter Bearbeiter

|`OBJECT_MODIFYTIME`
|Datum der letzten Änderung

|`OBJECT_LINKS`
|Anzahl Verlinkungen

|`OBJECT_TXTNOTICECOUNT`
|Anzahl Textnotizen
|===

=== Tabellenfelder

Tabellenfeld-Werte werden als untypisierte Rohstrings geliefert und nicht in den deklarierten Python-Typ konvertiert. `row_id` ist für alle Tabellenzeilen aus LOL-Abfragen immer `None`.

== Beispiele

=== Einfache Abfrage

[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)
----
====

=== Selektive Felder

[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)
----
====

=== Mit Rechten

[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}: Bearbeitung erlaubt")
----

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}: Bearbeitung erlaubt")
----
====

=== Papierkorb

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

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

=== Wann select_lol() statt select() verwenden?

[tabs]
====
Sync::
+
[source,python]
----
# Empfohlen für: große Mengen, nur Indexdaten (ohne Varianten/Dateiinfos/Hierarchie)
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)

# Empfohlen für: Dateiinfos, Basisparameter, Varianten, Hierarchien → select() verwenden
for doc in (
    ecm.dms.select(InvoiceDocument)
    .where(InvoiceDocument.Year == 2024)
    .file_properties()
    .stream()
):
    print(doc.system.file_properties.extension)
----

Async::
+
[source,python]
----
# Empfohlen für: große Mengen, nur Indexdaten (ohne Varianten/Dateiinfos/Hierarchie)
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)

# Empfohlen für: Dateiinfos, Basisparameter, Varianten, Hierarchien → select() verwenden
async for doc in (
    ecm.dms.select(InvoiceDocument)
    .where(InvoiceDocument.Year == 2024)
    .file_properties()
    .stream()
):
    print(doc.system.file_properties.extension)
----
====
