Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Delphi TcxVirtualTreeList auf Datenebene filtern? (https://www.delphipraxis.net/193423-tcxvirtualtreelist-auf-datenebene-filtern.html)

Zacherl 28. Jul 2017 00:23


TcxVirtualTreeList auf Datenebene filtern?
 
Hallo zusammen,

ich spiele grade ein wenig mit der
Delphi-Quellcode:
TcxVirtualTreeList
Komponente im "provider mode" mit aktiviertem "smart load" (Child-Nodes werden erst beim Aufklappen hinzugefügt). Hierzu habe ich mir eine Data-Source von
Delphi-Quellcode:
TcxTreeListCustomDataSource
abgeleitet und die erforderlichen Methoden implementiert. Klappt soweit auch alles wunderbar.

Nun möchte ich eine Filter-Funktion auf Datenebene einbauen, um nur Datensätze anzeigen zu lassen, die den Filterkriterien entsprechen. Das Problem an dieser Stelle ist, dass meine zugrundeliegende Datenstruktur die "tatsächlichen" Daten nur in den Blättern liegen hat, ich also zum Überprüfen des Filterkriteriums zwangsweise bis ganz ans Ende eines Asts traviersieren muss. Dieses Verfahren muss ich nun in den
Delphi-Quellcode:
GetChildCount
und
Delphi-Quellcode:
GetChildRecordHandle
Methoden der Data-Source implementieren.

Nehmen wir mal an, ich hätte folgende Struktur:
Code:
root
 | - child
       | - child
             | - data
       | - child
             | - data
Dann wird
Delphi-Quellcode:
GetChildCount
insgesamt 4x aufgerufen (theoretisch 6x, aber die Daten-Nodes können trivial 0 zurückgeben). Hierbei muss der Daten-Baum dann jeweils einmal vom entsprechenden Level aus bis zu den Blättern traversiert werden. Das ist schonmal unschön.

Noch problematischer wird es bei
Delphi-Quellcode:
GetChildRecordHandle
. Hier kann ich ja nun nicht mehr einfach auf
Delphi-Quellcode:
Child[Index]
zurückgreifen, sondern muss auch hier jedes Mal zwecks Überprüfung des Filterkriteriums bis zu den Blättern traversieren und dann den Index entsprechend umrechnen, so dass er die rausgefilterten Nodes ignoriert.

Hat hier jemand vielleicht eine performantere Lösung anzubieten? Caching ist natürlich eine Möglichkeit, wobei das bei großer Datenmenge doch schon ordentlich Speicher verbraten würde. Ich befürchte leider, das es aufgrund meiner Datenstruktur aber nicht anders machbar ist.

Viele Grüße
Zacherl

mensch72 29. Jul 2017 10:40

AW: TcxVirtualTreeList auf Datenebene filtern?
 
Ich nutze zwar weder "TcxVirtualTreeList" noch "TcxTreeListCustomDataSource", habe dieses Grundsatzproblem aber selbst logisch wie folgt gelöst:

Mein "linearisierter Baum" arbeitet im GUI und bei der Nutzung stets voll virtuell, es gibt im Speicher eines Nodes nur temporär ChildNodes welche gerade angefragt werden
Es bei mir haben Nodes folgende Minimale Grundstruktur

//stored => so einfach "linear" in der DB-Table
- NodeID: eineindeutige UID, z.B. DB-AutoIncrement
- ParentID: NodeID des ParentNodes oder wenn ein "Root" gleich der eigenen NodeID
- Data1: beliebige Daten
- Data2: beliebige Daten
- Data3: beliebige Daten

//private => nur Temporär im Speicher
- RootPtr als schneller "BackLink"
- ChildIDs als NULL oder TList<NodeID>
- TimeStamp als 0 oder letztes Sync, einfach als "PrimitivCache".. wird mit immer mit dem "globalen" TimeStamp des Root verglichen, TimeStamp des Roots aktualisiert sich bei allen Move,Insert,Delete Operationen(aber nicht bei "DataChange" weil DataValues nie im Cache) wenn sich nicht der ChildSelbst um die Benachrichtigung(zur Aktualisierung) seines Parents kümmert, sodass das globale ungültig machen aller Caches so umgangen werden kann


Das Konzept stammt noch aus Zeiten von Delphi2007 32Bit, da war mir das mit 32Bytes per Node eine Löung um auch Millionen von Nodes als Records in einer simplen DB Struktur zu halten und effektiv per einfachstem SQL anzufordern oder gefiltert abzufragen
Code:
Abfrage aller RootNodes
select * from NodeTable where NodeID = ParentID

Abfrage aller RootNodes mit z.B. Filtervergelichswert :Data1
select * from NodeTable where NodeID = ParentID and Data1 = :Data1

Abfrage aller ChildNodes zu einer NodeID
select * from NodeTable where ParentID = :NodeID

Abfrage aller ChildNodes zu einer :NodeID mit z.B. Filtervergelichswert :Data1
select * from NodeTable where ParentID = :NodeID and Data1 = :Data1
Ich habe mir den SQL Kram je nach NodeDataTyp jeweils direkt in Getter&Setter und Setter der NodePropertys gebracht.. ein universelles ORM oder eine ganz sauberer eigener DB-VirtualTreeView/Node war nie mein Ziel, ich verwende dies auch heute oft noch als CodeTemplate per Copy&Paste und passe fix den Namen der Nodeklasse an und benenne sowie typisiere Data1.., dann nur noch passend auch das SQL dazu und gut is.

Zumindest wäre dein Ziel ein trotz Rekursivität möglichst effektives ZeitVerhalten auch bei Datenfiltern zu behalten aus meiner Erfahrung so durchaus gegeben, der Trick ist hier ja einfach die lineare Indexbasierte Suche und Datenhaltung hinter/neben dem TreeView:)

Ich denke, diese Grundlogik könnest du übernehmen und wirst die passenden Events oder Gett&Setter auch in "TcxVirtualTreeList..." finden.
Ersetze in deinen Gedanken einfach dein Child[Index] gegen ein Child[NodeID] was du bei passender DB-Struktur wie und wo auch immer stets einigermaßen deterministisch auswählen und nutzen kannst.


Sollte ich deine Fragestellung nach Suche und Select mit Filtermöglichkeit doch nicht richtig und für dich passend getroffen haben, dann liegt es eventuell daran, dass ich mich explizit nicht um die Frage Stellung "GetChildCount" & "GetChildRecordHandle" mit einem echt BaumBasierten Filter&Such Algo gekümmert habe, mich also die Baumtiefe bis zu den Blättern bei dieser Zugriffs&Filterlösung garnicht interessiert.

Zacherl 29. Jul 2017 17:27

AW: TcxVirtualTreeList auf Datenebene filtern?
 
Vielen Dank für deine ausführliche Antwort :)

Zitat:

Zitat von mensch72 (Beitrag 1377725)
der Trick ist hier ja einfach die lineare Indexbasierte Suche und Datenhaltung hinter/neben dem TreeView:)

Das ist auf jeden Fall eine gute generelle Lösung für Probleme dieser Art. Auf meinen konkreten Fall kann ich das Prinzip leider nicht trivial übertragen, da meine Datenstruktur inkompatibel ist. Zwar habe ich auch eine lineare Grund-Datenbasis, aber im ersten Schritt wird aus diesen Daten ein Baum aufgezogen welcher ab diesem Zeitpunkt immer komplett im Speicher liegt (sind momentan ca. 7000 Datensätzen und werden in absehbarer Zeit auch keine signifikanten Mengen hinzukommen). Leider ist das Kalkulieren der Äste auch recht rechenaufwändig und muss nach Zielsetzungs zwangsweise dynamisch (basierend auf Grundlage der linearen Datenbasis) geschehen. Demzufolge befinden sich die Knoten nicht in der Datenbank und eine triviale Möglichkeit anhand der Daten auf die Anzahl und Relation der resultierenden Knoten zu schließen gibt es aufgrund der komplexen Berechnung auch nicht.

mensch72 29. Jul 2017 18:54

AW: TcxVirtualTreeList auf Datenebene filtern?
 
Ich würde in deinem Beispiel, wenn es nicht möglich ist, die 7000 Datensätze um zwei "CacheFelder für NodeID und ParentID" zu erweitern und beim einlesen nach deiner komplexen Baumstrukturberechnung das Ergebnis dieser entweder direkt zurück in die DB-Records geben, ober besser nach Berechnung eine JobTabelle als StrukturCache mit: Job(z.B. TimeStamp der Berechnung), RecordID(PK deines Datensatzes) und NodeID+ParentID anlegen. Ob nur im Speicher als wenn SQL fähiges MemTable oder einfach in der DB per Zusatztabelle(z.B. "TreeJobResult")

Dann behältst du dein "komplexes Struktur Konzept" so unverändert wie möglich bei, speicherst dir nur das Ergebnis deiner Berechnung(also die BaumStruktur) so minimalistisch wie möglich ohne Datenredundanz ab. Die GUI greift bei Filtereinsatz einfach über die wieder nun SQL fähig linearisierte SturkturInfo der Zwischenschicht auf die Ausgangsdaten zu.
Das ist bei 7000 Datensätzen sowohl vom Speicher als auch von der "zusätzlichen" Verarbeitungszeit pro Zugriff als auch beim pro Berechnung einmaligen erzeugen der "JobStruktur" völlig unkritisch.
(zufällig habe ich diese Schicht auch oft, denn das gleiche kann man mit den FilterErgebnissen so machen, so kann die GUI dem Anwender ganz simpel erlauben, mal zurück zum vorherigen Filter zu gehen, ohne das der nochmal berechnet werden muss... mit dem simplen Konzept eines im Prinzip globalen CacheTimeStamps bekomme ich zwar keinen optimal reduziertes Nachberechnungsschema hin, aber es ist garantiert das ich nie mit eventuell alter CacheStruktur arbeite, notfals berechnen sich alle Filter eben bei nochmaliger Verwendung sauber neu)

Zacherl 30. Jul 2017 14:28

AW: TcxVirtualTreeList auf Datenebene filtern?
 
Ja ich denke so in der Art werde ich es wohl machen müssen :) Muss ich nur mal noch schauen, wie ich die Cache-Kohärenz gewährleisten kann, da die Daten auch zur Laufzeit geändert/ergänzt werden können. Mein Datenbaum stellt hierfür aber Events bereit, so dass das kein Problem sein sollte.


Alle Zeitangaben in WEZ +1. Es ist jetzt 09:30 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz