Durchsuchbare Dokumentation aufrufen | Zurück zur Dokumentationsübersicht
Navigation: Dokumentationen agorum core
Bei der Arbeit mit Suchen gibt es einige wichtige Best Practices, die Entwickler beachten sollten, um Belastungen des Systems durch überflüssige Suchanfragen zu vermeiden. Eine häufige Fehlanwendung besteht darin, unnötige Suchanfragen auszuführen, obwohl die benötigten Objekte bereits vorhanden sind. Hier sind einige Beispiele, die zeigen, wie Suchoperationen sinnvoll eingesetzt werden können.
Generell gilt:
Falls die ID oder der Pfad eines Objekts bekannt ist, sollte direkt darauf zugegriffen werden, anstatt eine Suche auszuführen. Eine Suche über Solr ist IO-intensiv und sollte vermieden werden, wenn direkte Zugriffsmöglichkeiten bestehen.
let objects = require('common/objects'); let target = objects.find('home:MyFiles'); // DON'T: Search for a known object // objects.search('inpath:' + target + ' name_ci:"test.txt"')[0]; // DO: Directly access the known object objects.find('home:MyFiles/test.txt');
Wenn ein Objekt bereits im Code vorhanden ist oder gerade erstellt wurde, sollte es direkt genutzt werden, anstatt es erneut zu suchen.
// D'ONT: Create a file and then try to search for the file
/*objects.create('file', {
name: 'doctest.txt',
target: target,
});*/
// Attention: this is not possible because the object has not yet been indexed
// objects.search('inpath:' + target + ' name_ci:"doctest.txt"')[0];
// this is also IO-heavy and should be avoided
// target.items().find(item => item.name === 'doctest.txt');
// DO: Create the object and use it immediately
let obj = objects.create('file', {
name: 'doctest2.txt',
target: target,
});
// DO: If the object is not immediately returned, but you know where it is
objects.create('file', {
name: 'doctest3.txt',
target: target,
});
objects.find('home:MyFiles/doctest3.txt');
Wenn eine Operation häufig aufgerufen wird, insbesondere innerhalb von Schleifen, sollten IO-intensive Aufrufe minimiert werden. Versuchen Sie stattdessen, solche Aufrufe außerhalb der Schleife zu platzieren.
let objects = require('common/objects'); // DON'T: Search inside the loop /*objects .search('inpath:9999') .limit(100) .forEach(obj => { let targetFolder = objects.find('home:MyFiles'); // ... do something with targetFolder });*/ // DO: Search outside the loop let targetFolder = objects.find('home:MyFiles'); objects .search('inpath:9999') .limit(100) .forEach(obj => { // ... do something with targetFolder });
Analysieren Sie, welche Teile Ihres Codes häufig aufgerufen werden und ob diese in produktiven Umgebungen große Datenmengen verarbeiten. In solchen Fällen ist es besonders wichtig, IO-intensive Funktionen zu vermeiden oder zu optimieren.
Diese Dokumentation bietet Best Practices für die Arbeit mit aguila-Widgets. Der Fokus liegt darauf, reaktive und benutzerfreundliche Bedienoberflächen zu erstellen, insbesondere im Umgang mit langwierigen Aufgaben, die die Bedienoberfläche blockieren könnten.
Vermeiden Sie es, zeitaufwendige Operationen direkt in der Hauptausführung des Programms auszuführen, da dies die UI einfrieren kann.
let objects = require('common/objects'); let aguila = require('common/aguila'); let form = aguila.create({ type: 'agorum.composite.form.basic', width: 800, height: 600, labelPosition: 'top', elements: [ { type: 'agorum.composite.form.element.button', name: 'button', text: 'Klick mich', }, { type: 'agorum.composite.form.element.number', readOnly: true, name: 'zahl', }, ], }); // DON'T: Use the blocking operation inside the main program /* let doSomething = () => { form.locked = 'Ich tu was'; java.lang.Thread.sleep(2000); form.locked = null; }; */ form;
Verwenden Sie aguila.fork, um zeitaufwendige Operationen auszuführen, während die Bedienoberfläche reaktionsfähig bleibt. Für weitere Informationen zu aguila.fork siehe Beispiel mit aguila.fork().
let objects = require('common/objects'); let aguila = require('common/aguila'); let form = aguila.create({ type: 'agorum.composite.form.basic', width: 800, height: 600, labelPosition: 'top', elements: [ { type: 'agorum.composite.form.element.button', name: 'button', text: 'Klick mich', }, { type: 'agorum.composite.form.element.number', readOnly: true, name: 'zahl', }, ], }); // DO: Use aguila.fork() to separate background operation let doSomething2 = () => { form.locked = 'Ich tu was'; aguila .fork(() => { java.lang.Thread.sleep(2000); }) .finally(() => { form.locked = null; }); }; form;
Wenn ein Benutzer mehrmals auf eine Schaltfläche klickt, sollte immer der letzte Klick verarbeitet werden. Verwenden Sie einen synchronizer, um sicherzustellen, dass alle Klicks verarbeitet werden, und der letzte Klick die endgültige Aktion bestimmt. Für weitere Informationen zur Verwendung eines synchronizers siehe widget.synchronizer().
let objects = require('common/objects'); let aguila = require('common/aguila'); let form = aguila.create({ type: 'agorum.composite.form.basic', width: 800, height: 600, labelPosition: 'top', elements: [ { type: 'agorum.composite.form.element.button', name: 'button', text: 'Klick mich', }, { type: 'agorum.composite.form.element.number', readOnly: true, name: 'zahl', }, ], }); // DO: Use aguila.synchronizer() let globalZahl = 0; let synchronizer = aguila.synchronizer(); let doSomething3 = () => { let localZahl = globalZahl++; synchronizer .fork(() => { java.lang.Thread.sleep(5000 - localZahl * 1000); console.log(localZahl); return localZahl; }) .then(zahl => { form.set('zahl.value', zahl); }); }; form.on('action', action => { if (action.name === 'button') { doSomething3(); } }); form;
Das Management von Transaktionen ist wichtig für die Performance und Stabilität der Anwendung. Regelmäßige Commits innerhalb einer Transaktion helfen, die Datenintegrität sicherzustellen. Vermeiden Sie verschachtelte Transaktionen und achten Sie auf die Größe der Transaktion: führen Sie Änderungen an großen Mengen von Objekten eher in mehreren kleinen Transaktionen durch.
Denken Sie vor der Verwendung von Transaktionen stets daran, die Bibliothek mit require() einzubinden:
let transaction = require('common/transaction');
Für weitere Informationen zur Verwendung von common/transaction siehe JavaScript-Bibliothek common/transaction.
Der unten beschriebene Codeabschnitt zeigt verschiedene Ansätze für das Iterieren und Ändern von Objekten mithilfe von Transaktionen.
Ohne Transaktionen können Änderungen inkonsistent sein und es besteht die Gefahr von halb geänderten Daten bei Fehlern. Das wäre zudem sehr langsam, weil jede Änderung einzeln gespeichert werden muss.
let objects = require('common/objects'); let metadata = require('common/metadata'); let transaction = require('common/transaction'); // DON'T: Use no transaction /* objects .search('inpath:9999') .limit(100) .forEach(obj => { // change obj 1 // change obj 2 // change obj 3 }); */
Allerdings sollten Sie auch nicht eine Transaktion pro Objekt ausführen. Das wäre ebenfalls langsam, da jede Änderung in einer separaten Transaktion erfolgt.
let objects = require('common/objects'); let metadata = require('common/metadata'); let transaction = require('common/transaction'); // DON'T: Use one transaction per object /* objects .search('inpath:9999') .limit(100) .forEach(obj => { transaction(() => { // change obj 1 // change obj 2 // change obj 3 }); }); */
Darüber hinaus sollten Sie es auch vermeiden, alle Änderungen in einer großen Transaktion durchzuführen. Lange laufende Transaktionen können bei großen Datenmengen im schlimmsten Fall zu Memory-Problemen und Systeminstabilität führen.
let objects = require('common/objects'); let metadata = require('common/metadata'); let transaction = require('common/transaction'); // DON'T: Use one transaction for all changes /* transaction(() => { objects .search('inpath:9999') .limit(100) .forEach(obj => { // change obj 1 // change obj 2 // change obj 3 }); }); */
Transaktionen mit regelmäßigen Commits haben sich als Lösung bewährt. Regelmäßige Commits (etwa alle 10 Objekte) sind immer noch schnell und sicher, da die Transaktionen nicht zu lange laufen.
let objects = require('common/objects'); let metadata = require('common/metadata'); let transaction = require('common/transaction'); // DO: Use a transaction with regular, change-based commits transaction(t => { objects .search('inpath:9999') .limit(100) .forEach((obj, index) => { // change obj 1 // change obj 2 // change obj 3 if ((index + 1) % 10 === 0) { t.restart(); } }); });
Anstatt die Commits nach einer bestimmten Zahl von Änderungen durchzuführen, können Sie auch zeitbasierte Commits verwenden (etwa alle 10 Sekunden).
let objects = require('common/objects'); let metadata = require('common/metadata'); let transaction = require('common/transaction'); // DO: Use a transaction with regular, time-based commits transaction(t => { let start = Date.now(); objects .search('inpath:9999') .limit(100) .forEach(obj => { // change obj 1 // change obj 2 // change obj 3 if (Date.now() - start > 10000) { t.restart(); start = Date.now(); } }); });
Vermeiden Sie verschachtelte Transaktionen, um Klarheit und Vorhersehbarkeit zu gewährleisten.
Achtung: Beachten Sie, dass innere Transaktionen in verschachtelten Transaktionen ignoriert werden.
Das folgende Beispiel funktioniert nicht:
let objects = require('common/objects');
let metadata = require('common/metadata');
let transaction = require('common/transaction');
transaction(() => {
// ....
// DON'T:
Use nested transactions, this inner transaction will be ignored!
transaction(t => {
let start = Date.now();
objects
.search('inpath:9999')
.limit(100)
.forEach(obj => {
// change obj 1
// change obj 2
// change obj 3
if (Date.now() - start > 10000) {
t.restart();
start = Date.now();
}
});
});
// ....
});
Wenn Sie Transaktionen in einem Skript für einen Aktiven Ordner verwenden wollen, müssen Sie darauf achten, das Häkchen für die Einstellung Skript in Transaktion ausführen, das standardmäßig gesetzt ist, unbedingt zu entfernen. Sonst ist die Transaktion, die Sie im angegebenen Skript benutzen, eine innere Transaktion und wird ignoriert.
Für weitere Informationen zur Verwendung eines Skripts für einen Aktiven Ordner siehe Skript in Transaktion ausführen.