Das Notes Forum

Domino 9 und frühere Versionen => ND8: Entwicklung => Thema gestartet von: schroederk am 07.12.10 - 10:04:20

Titel: Vorgehensweise Dokumente synchronisieren
Beitrag von: schroederk am 07.12.10 - 10:04:20
Hallo,

ich würde mich gerne an ein für mich noch recht neues Thema heranwagen und Dokumente in Ansichten zwischen zwei Datenbanken synchronisieren.
Also herausfinden, ob Dokumente neu, geändert oder gelöscht.

Jedes Dokument in einer Ansicht in der Source-DB durchzugehen und zu prüfen ob es in der Target-DB zu finden ist und wenn, welches letzte Änderungsdatum es hat, halte ich noch für meine Verhältnisse gut realisierbar.
Aber wie sieht es mit gelöschten Einträgen aus?
Und ist es überhaupt ein guter Ansatz durch alle Dokumente in der Source-DB durchzugehen?
Muss dann nicht dasselbe nochmal mit vertauschten Rollen (also Target-DB gleich Source-DB und umgekehrt) durchgegangen werden?

Titel: Re: Vorgehensweise Dokumente synchronisieren
Beitrag von: eknori am 07.12.10 - 10:06:14
Guckst Du http://www.eknori.de/2005-09-03/adressbucher-konsolidieren/
Titel: Re: Vorgehensweise Dokumente synchronisieren
Beitrag von: schroederk am 07.12.10 - 10:42:05
Vielen Dank für den Link, aber auch auf die Gefahr hin, das Rad zweimal zu erfinden, hilft es mir sicherlich den Prozess und die Vorgehensweise zu verstehen, wenn ich es doch selber programmieren möchte.
Das kann dieses fertige Tool (was einem ersten Blick nach auch komplexer ist) kaum leisten.

Ich habe auch nicht vor ein Field-Mapping einzubauen oder den Sync auf andere Art und Weise komplizierter zu machen.

Wenn es die Sach deutlich vereinfacht, bin ich auch mit einem One-Way-Sync einverstanden, der aber nicht (das wäre die einfachste Form) erst alle Dokumente im Ziel löscht und dann alle Dokumente aus Quelle nach Ziel kopiert.

Wie finde ich heraus, welche Dokumente nach der letzten Synchronisation gelöscht wurden? Muss ich mich hier über "Deletion Stubs" einlesen?
Titel: Re: Vorgehensweise Dokumente synchronisieren
Beitrag von: Peter Klett am 07.12.10 - 11:04:36
Ich mache sowas über Listen. Angenommen, Du willst nur die Dokumente aus der einen Datenbank in die andere übertragen (also One-Way), dazu hier ein sinngemäßer Auszug:

Dim zielcol As NotesDocumentCollection
Set zielcol = zieldb.Search (alle relevanten Dokumente der ZielDB)
Dim zieldoks List As Integer
Alle Dokumente (zieldoc) aus zielcol durchgehen
   zieldoks (zieldoc.UniversalID) = 1

Beim Abgleich nehme ich dann alle relevanten Dokumente aus der QuellDB und lösche beim Abgleich die DokID aus der Zielliste

If IsElement (zieldoks (quelldoc.UniversalID)) Then
   'Dokument abgleichen
   Erase zieldoks (quelldoc.UniversalID)
Else
   'Dokument in Ziel neu erstellen
End If

Zum Schluss verbleiben in der Liste alle Dokumente der ZielDB, die nicht mehr in der QuellDB enthalten sind, die lösche ich dann

Forall dok in doks
   Set zieldoc= zieldb.GetDocumentByUnid (Listtag (dok))
   'zieldoc löschen
End Forall

Voraussetzung meiner Darstellung ist, dass Quell- und Zieldokumente die gleiche DokumentID (UniversalID) besitzen (die kann beim Erstellen des Dokuments VORM SPEICHERN gesetzt werden (-> zieldoc.UniversalID = quelldoc.UniversalID)), ansonsten benötigst Du einen anderen eindeutigen Schlüssel.
Titel: Re: Vorgehensweise Dokumente synchronisieren
Beitrag von: schroederk am 07.12.10 - 11:30:12
Vielen Dank. Deinen Ansatz habe ich verstanden.  :)
Datu noch ein paar Fragen...

Wie findest Du hier damit neue Einträge in der Quell-DB?

Einträge die mit
Zitat
Call dbQuelldoc.CopyToDatabase(dbZiel)
kopiert werden, haben doch die gleiche UniversalID oder?
So zumindest hatte ich vor, neue Dokumente von Quelle nach Ziel zu übertragen.

Wie gleichst Du die Dokumente ab? Löschen und Neuanlegen (oben genanntes Copy)?
Titel: Re: Vorgehensweise Dokumente synchronisieren
Beitrag von: m3 am 07.12.10 - 11:40:20
Nein. Eine Kopie ist ein neues Dokument ist gleich eine neue DocumentUniqueID.

Die DocumentUniqueID (UniversalID) bleibt nur bei der Replikation eines Dokuments/Datenbank gleich.

http://www.nsftools.com/tips/NotesTips.htm#noteids
Titel: Re: Vorgehensweise Dokumente synchronisieren
Beitrag von: Peter Klett am 07.12.10 - 11:43:27
1. Neue Dokumente sind alle Dokumente, die nicht in der Liste der Zieldokumente enthalten sind
2. doc.CopyToDatabase (db) produziert m.W. nicht die gleiche ID, ich verwende

Set zieldoc = New NotesDocument (zieldb)
zieldoc.UniversalID = quelldoc.UniversalID
Call quelldoc.CopyAllItems (zieldoc)
Call zieldoc.Save (True, True)

3. Abgleichen niemals mit Löschen und Neuanlegen von Dokumenten! Du könntest alle Items im zieldoc löschen und dann mit CopyAllItems aus der Quelle neu kopieren, da bist Du auf der sicheren Seite was Dateianhänge, Richtext usw. angeht
Titel: Re: Vorgehensweise Dokumente synchronisieren
Beitrag von: schroederk am 07.12.10 - 11:55:06
Vielen Dank nochmals für die Tips, dann werde ich das mal angehen...   :)
Titel: Re: Vorgehensweise Dokumente synchronisieren
Beitrag von: schroederk am 10.12.10 - 08:38:33
Ich muss nochmal um Eure Unterstützung bitten...

Ich hatte (wohl fälschlicherweise) angenommen, dass ich das Feld "Lastmodified" heranziehen kann, um herauszufinden, auf welcher Seite sich etwas geändert hat. Das funktioniert aber leider nicht wirklich, da dieses Feld ja automatisch beim Speichern eines Dokuments aktulisiert wird.
Wenn im Quell-Dokument die letzte Änderung 01.12.2010 war und dieses im Ziel angelegt wird, dann ist das Änderungsdatum im Ziel nicht auch der 01.12.2010.
Ich könnte vielleicht ein Hilfsfeld benutzen oder wenn im Ziel geschrieben wird auch immer das Dokument in der Quelle "touchen". Beides erscheint mir ein wenig "gestrickt".
Gibt es noch einen anderen Weg?
Titel: Re: Vorgehensweise Dokumente synchronisieren
Beitrag von: schroederk am 10.12.10 - 09:46:51
Ich habs jetzt erstmal so gelöst, dass dann das Dokument in der Quelle auch geändert wird.

Code
					dbQdoc.form=dbQdoc.form(0)
					Call dbQdoc.Save (True, True)
So richtig glücklich bin ich nicht damit.  :-\
Falls jemand eine bessere Idee hat, immer her damit  ;)
Titel: Re: Vorgehensweise Dokumente synchronisieren
Beitrag von: Peter Klett am 10.12.10 - 09:48:27
Du willst also doch in beide Richtungen abgleichen? Dann wird Dir nichts anderes übrig bleiben, als Quell- und Zieldokument zu speichern. Das Hilfsfeld wird Dir nicht helfen, außer, wenn Du dann in der Maske beim Speichern ebenfalls das Hilfsfeld änderst. Damit verlierst Du aber die Allgemeingültigkeit Deiner Lösung.
Titel: Re: Vorgehensweise Dokumente synchronisieren
Beitrag von: Peter Klett am 10.12.10 - 09:49:41
Speichern alleine müsste auch reichen, denn mit doc.Form = doc.Form (0) änderst Du nicht wirklich etwas
Titel: Re: Vorgehensweise Dokumente synchronisieren
Beitrag von: schroederk am 10.12.10 - 09:55:59
Speichern alleine müsste auch reichen, denn mit doc.Form = doc.Form (0) änderst Du nicht wirklich etwas

Nein, reichte leider nicht aus. Offenbar ist Notes so intelligent festzustellen, dass nichts geändert wurde und hat das Änderungsdatum nicht aktualisiert.
Und ändern wollte ich ja auch nicht wirklich was ;)
Ist eher so ähnlich, als ob ich das Dokument bearbeite, ein Zeichen anfüge und wieder lösche und dann speichere.

Die Idee mit doc.Form = doc.Form (0) stammt von offizieller IBM-Seite
http://www-01.ibm.com/support/docview.wss?uid=swg21095636 (http://www-01.ibm.com/support/docview.wss?uid=swg21095636)

Auch wenn es dort mehr um den "gelesen"-Status geht
Titel: Re: Vorgehensweise Dokumente synchronisieren
Beitrag von: Peter Klett am 10.12.10 - 09:59:23
Eine Idee hätte ich noch:

Wenn Dein Abgleich-Agent von einem technischen User unterzeichnet wird, brauchst Du nur das Zieldokument zu speichern. Dann gleichst Du nur die Dokumente ab, die zuletzt von einem anderen als dem technischen User geändert wurden (also LastModified in db1 ist jünger als in db2 und letzter Bearbeiter ist nicht der technische User. Den letzten Bearbeiter findest Du in $UpdatedBy, dort der letzte Eintrag (nur 99% sicher, bitte überprüfen)).

Das ist allerdings etwas wackelig, da Du wirklich sicherstellen musst, dass der Agent nur von diesem einen User gestartet wird (Stichwort Rezertifizierung, neue Certifier usw.). Du könntest aber in den Agenten eine Überprüfung einbauen, ob der richtige User den Agenten startet und im Fehlerfall mit einer Fehlermeldung im Log abbrechen. Wenn dann irgendwann einmal nicht mehr synchronisiert wird, muss nur noch die Fehlermeldung gefunden und verstanden werden.
Titel: Re: Vorgehensweise Dokumente synchronisieren
Beitrag von: Peter Klett am 10.12.10 - 10:03:01
Offenbar ist Notes so intelligent festzustellen, dass nichts geändert wurde und hat das Änderungsdatum nicht aktualisiert.
Aber zum Glück nicht intelligent genug, um festzustellen, dass die vorgenommene Änderung nichts geändert hat.  :)
Titel: Re: Vorgehensweise Dokumente synchronisieren
Beitrag von: schroederk am 10.12.10 - 10:17:45
Eine Idee hätte ich noch:

Wenn Dein Abgleich-Agent von einem technischen User unterzeichnet wird, brauchst Du nur das Zieldokument zu speichern. Dann gleichst Du nur die Dokumente ab, die zuletzt von einem anderen als dem technischen User geändert wurden (also LastModified in db1 ist jünger als in db2 und letzter Bearbeiter ist nicht der technische User. Den letzten Bearbeiter findest Du in $UpdatedBy, dort der letzte Eintrag (nur 99% sicher, bitte überprüfen)).

Das ist allerdings etwas wackelig, da Du wirklich sicherstellen musst, dass der Agent nur von diesem einen User gestartet wird (Stichwort Rezertifizierung, neue Certifier usw.). Du könntest aber in den Agenten eine Überprüfung einbauen, ob der richtige User den Agenten startet und im Fehlerfall mit einer Fehlermeldung im Log abbrechen. Wenn dann irgendwann einmal nicht mehr synchronisiert wird, muss nur noch die Fehlermeldung gefunden und verstanden werden.

An die Idee hatte ich auch schon gedacht und auch an die von Dir genannten Probleme.
Daher hab ich mich doch zur Zeit für die jetzige Lösung entschieden.

Momentan haperts bei mir nur noch an der Problematik, wie ich darauf reagiere, wenn ich durch die Ziel-DB durchgehe und ein Dokument in der Ziel-DB lösche, nicht den "Error 4207: Function requires a valid ADT argument " zu erhalten.
Funktioniert ein
Code
Set dummydoc = dbZdoc
Call dummydoc.Remove( True )
?
Titel: Re: Vorgehensweise Dokumente synchronisieren
Beitrag von: Peter Klett am 10.12.10 - 10:41:49
Bist Du sicher, dass der Fehler an genau der Stelle auftritt?

Vermute mal, dass Du eine Ansicht durchgehst, dann ein Dokument löschst und mit set doc = view.GetNextDocument (doc) auf das nächste zugreifen willst, dabei kracht es dann.

Falls das stimmt, mach es z.B. so:

Set dummydoc = dbZdoc
Set dbZdoc = dbZview.GetNextDocument (dbZdoc)
Call dummydoc.Remove (true)
Titel: Re: Vorgehensweise Dokumente synchronisieren
Beitrag von: schroederk am 10.12.10 - 10:54:07
Du hast völlig recht, der Fehler tritt dort auf, wo ich in der Ansicht ein Dokument lösche und dann das nächste Dokument holen möchte.

Muss ich denn das nächte Dokument zwingend VOR dem Löschen holen? Denn dann muss ich die Struktur des Scripts überarbeiten, das momentan noch so aussieht:

Code
			While Not(dbZdoc Is Nothing)										' Alle Dokumente in Ziel-DB-Ansicht abarbeiten
				' UniversalID in Quell-DB suchen
				strUNID = dbZdoc.Universalid
				On Error 4091 Resume Next										' Fehler abfangen, sollte ID nicht gefunden werden
				Set dbQdoc = dbQuelle.GetDocumentByUNID( strUNID )
				If dbQdoc Is Nothing Then										' nicht gefunden, also kann in Ziel-DB gelöscht werden.
					Set dummydoc = dbZdoc
					Call dummydoc.Remove (True)					
				Else
					If dbQdoc.size = 0 Then
						' ghost document gefunden also aus Ziel entfernen 
						Set dummydoc = dbZdoc
						Call dummydoc.Remove (True)					
					Else
						' Last Modified von Quelle und Ziel vergleichen
						If dbQdoc.Lastmodified <> dbZdoc.Lastmodified Then		' Änderungsdatum unterschiedlich
							Call dbQdoc.CopyAllItems (dbZdoc, True)				' Daten von Quelle nach Ziel übertragen.
							Call dbZdoc.Save (True, True)
							dbQdoc.form=dbQdoc.form(0)							' damit Quelle "geändert" ist und Speichern funktioniert
							Call dbQdoc.Save (True, True)						' Quelle auch speichern, damit Änderungsdatum gesetzt ist
						End If
					End If
				End If
				Set dbQdoc = Nothing											' dbQdoc wieder löschen
				Set dbZdoc = Zview.GetNextDocument(dbZdoc)
			Wend
Titel: Re: Vorgehensweise Dokumente synchronisieren
Beitrag von: ascabg am 10.12.10 - 10:55:32
Wirst Du nicht umhin kommen.


Andreas
Titel: Re: Vorgehensweise Dokumente synchronisieren
Beitrag von: Peter Klett am 10.12.10 - 11:15:45
Ich lösche immer erst ganz zum Schluss. Anstatt das Dokument zu löschen, merke ich es mir in einer Liste

Dim loeschdocs List As NotesDocument

dann in der Schleife statt löschen

Set loeschdocs (doc.UniversalID) = doc

und dann am Ende des Scripts

Forall loeschdoc in loeschdocs
   Call loeschdoc.Remove (True)
End Forall

Wenn Du das so umbaust, brauchst Du Deine Struktur nicht zu verändern
Titel: Re: Vorgehensweise Dokumente synchronisieren
Beitrag von: Thomas Schulte am 10.12.10 - 11:44:56
Und wenn du anstelle der Liste eine documentcollection aufbaust und die Dokumente da reinlegst, dann kannst du mit
Dc.removeall
Die Dokumente in einem Rutsch löschen ohne noch eine Schleife durchlaufen zu müssen.
Das spart ein wenig Zeit.
Titel: Re: Vorgehensweise Dokumente synchronisieren
Beitrag von: Thomas Schulte am 10.12.10 - 11:50:10
Noch eine Anmerkung zum Thema löschen in Ansichten wÄhrend der Arbeit in Script.

Das ist ganz grundsätzlich keine gute Idee, weil es die Performance satt in den Keller zieht.
Je nachdem wo du das machst fängst du da nämlich an den indexer des servers in die Knie zu zwingen.
Titel: Re: Vorgehensweise Dokumente synchronisieren
Beitrag von: Peter Klett am 10.12.10 - 12:08:31
Das mit der Collection ist eine sehr gute Idee, denn gerade das Löschen von vielen Dokumenten ist damit viel schneller.

NotesDocumentCollection.AddDocument ist (für mich) aber ganz neu (gibt es ja auch erst seit Notes 5), da ist es manchmal nicht schlecht, gelegentlich die Hilfe zu lesen (oder dieses Forum)  ;D
Titel: Re: Vorgehensweise Dokumente synchronisieren
Beitrag von: schroederk am 10.12.10 - 12:48:34
Vielen Dank für Deinen Tip Peter.
Ich habs jetzt aber doch schon umgebaut, war nicht soviel Aufwand und die Struktur ist quasi noch dieselbe. Nur die Lesbarkeit hat imho darunter gelitten  ;)

Falls es jemanden interessiert:

Code
			While Not(dbZdoc Is Nothing)										' Alle Dokumente in Ziel-DB-Ansicht abarbeiten
				' UniversalID in Quell-DB suchen
				strUNID = dbZdoc.Universalid
				On Error 4091 Resume Next										' Fehler abfangen, sollte ID nicht gefunden werden
				Set dbQdoc = dbQuelle.GetDocumentByUNID( strUNID )
				If dbQdoc Is Nothing Then										' nicht gefunden, also kann in Ziel-DB gelöscht werden.
					Print "** Sync Tool ** Step 1 ** " & dbZdoc.Getitemvalue("FirstName")(0) & " " & dbZdoc.Getitemvalue("LastName")(0) & " -> deleted"
					Set dummydoc = dbZdoc
					Set dbZdoc = Zview.GetNextDocument(dbZdoc)
					Call dummydoc.Remove (True)					
				Else
					If dbQdoc.size = 0 Then
						' ghost document gefunden also aus Ziel entfernen 
						Print "** Sync Tool ** Step 1 ** " & dbZdoc.Getitemvalue("FirstName")(0) & " " & dbZdoc.Getitemvalue("LastName")(0) & " -> deleted"
						Set dummydoc = dbZdoc
						Set dbZdoc = Zview.GetNextDocument(dbZdoc)
						Call dummydoc.Remove (True)					
					Else
						' Last Modified von Quelle und Ziel vergleichen
						If dbQdoc.Lastmodified <> dbZdoc.Lastmodified Then		' Änderungsdatum unterschiedlich
							Print "** Sync Tool ** Step 1 ** " & dbQdoc.Getitemvalue("FirstName")(0) & " " & dbQdoc.Getitemvalue("LastName")(0) & " -> updated"
							Call dbQdoc.CopyAllItems (dbZdoc, True)				' Daten von Quelle nach Ziel übertragen.
							Call dbZdoc.Save (True, True)
							dbQdoc.form = dbQdoc.form(0)						' damit Quelle "geändert" ist und Speichern funktioniert
							Call dbQdoc.Save (True, True)						' Quelle auch speichern, damit Änderungsdatum gesetzt ist
						Else
							Print "** Sync Tool ** Step 1 ** " & dbQdoc.Getitemvalue("FirstName")(0) & " " & dbQdoc.Getitemvalue("LastName")(0) & " -> skipped"
						End If
						Set dbZdoc = Zview.GetNextDocument(dbZdoc)
					End If
				End If
				Set dbQdoc = Nothing											' dbQdoc wieder löschen
			Wend