wissel.net

Usability - Productivity - Business - The web - Singapore & Twins

Syncing documents in 2 views


I got quite some requests on the details of the syncing documents in views pattern/anti-pattern.So this is what you need to do:
  1. Have 2 views with identical keys, sorted by the key
  2. Julian's OpenLog database
  3. The main function SyncAllDocument
  4. 3 auxiliary functions: getCompareKeySource, getCompareKeyTarget and updateSourceDoc
In my example code I made the assumption, that the documents contain the same fields and have a single key. You might have different requirements like computing things and/or only coying part of the items or do a computewithform before saving. Nevertheless you don't need to touch the main routine for that. You get the idea.
 
Option Public Option Declare Use "OpenLogFunctions" Function getCompareKey (doc As NotesDocument ) As String getCompareKey = doc .getitemvalues ( "someidfield" ) (0 ) End Function Sub updateSourcedoc (curDoc As NotesDocument , newDoc As NotesDocument ) 'We only save if the items have changed! Dim saveFlag As Boolean Dim curItem As NotesItem Dim newItem As NotesItem On Error Goto Err_updateSourcedoc saveFlag = False Forall it In newDoc .Items Set newItem = it If curDoc .HasItem (newItem . Name ) Then Set curItem = curDoc .GetFirstItem (newItem . Name ) If curItem . Text < > newitem . Text Then Call curItem . Remove Call newItem .CopyItemToDocument (curDoc ,newItem . Name ) saveFlag = True End If Else Call newItem .CopyItemToDocument (curDoc ,newItem . Name ) saveFlag = True End If End Forall 'Todo Optional: remove items that don't exist anymore If saveFlag Then Call curDoc .Save ( True , True ) End If Exit_updateSourcedoc : Exit Sub Err_updateSourcedoc : Call LogErrorEx ( "" ,SEVERITY_HIGH ,curDoc ) Resume Exit_updateSourcedoc End Sub Function SyncAllDocuments (sourceView As NotesView , targetView As NotesView ) As Boolean 'The documents to be compared Dim SourceDoc As NotesDocument Dim nextSourceDoc As NotesDocument Dim targetDoc As NotesDocument Dim nextTargetDoc As NotesDocument 'Auxiliary variables Dim gotoNextSource As Boolean Dim gotoNextTarget As Boolean Dim sourceKey As String Dim targetKey As String On Error Goto Err_SyncAllDocuments 'Important: the two views are designed that at the end of this algorythm both views have the same number of documents 'with the same data in it. gotoNextSource = True 'We need to set this to get the loop started gotoNextTarget = True 'We need to set this to get the loop started Set sourceDoc = SourceView .GetFirstDocument Set targetDoc = TargetView .GetFirstDocument 'This is the outer loop running through the documents in this database 'The "inner" loop is for the target database Do Until sourceDoc Is Nothing If gotoNextSource Then 'Since we just advanced to the current source we need to know the next one Set nextSourceDoc = SourceView .GetNextDocument (sourceDoc ) 'This must be BEFORE any action with the doc End If If targetDoc Is Nothing Then 'We don't have any target documents, so we copy point blank Call sourceDoc .CopyToDatabase (targetView .Parent ) gotoNextSource = True 'We can read the next source document gotoNextTarget = False 'We are already at nothing for the target Else 'Since targetdocument exists we can get the next target document now if we need it If gotoNextTarget Then 'Since we just advanced to the current target we need to know the next one Set nextTargetDoc = targetView .GetNextDocument (targetDoc ) 'This must be BEFORE any action with the doc End If 'Now we need to compare the keys sourceKey = getCompareKeySource (sourceDoc ) targetKey = getCompareKeyTarget (targetDoc ) If sourceKey = targetKey Then 'We found an existing document and would need to update it if values have changed Call updateSourcedoc (sourceDoc , targetDoc ) 'We can move our pointer one up for source and target documents gotoNextSource = True gotoNextTarget = True Elseif sourceKey > targetKey Then 'We found a document that doesn't exist in the source database anymore 'so we need to remove that from the target database Call targetDoc . Remove ( True ) gotoNextSource = False 'We stay on the source until we get even or less with the key gotoNextTarget = True 'We just removed the document Else 'We have a source document that is not yet in the target database, We just copy it over. Call sourceDoc .CopyToDatabase (targetView .Parent ) gotoNextSource = True 'We just copied this document we move on gotoNextTarget = False 'They key was too big, so we need to wait End If End If 'Advance the pointers If gotoNextSource Then Set sourceDoc = nextSourceDoc End If If gotoNextTarget Then Set targetDoc = nextTargetDoc End If Loop 'Finally we need to remove all documents in the target view that are still here since 'We ran out of source documents 'If we ran out of target documents already targetDoc is nothing aready Do Until targetDoc Is Nothing Set nextTargetDoc = targetView .getNextDocument (targetDoc ) Call targetDoc . Remove ( True ) Set targetDoc = nextTargetDoc Loop SyncAllDocuments = True 'If we got here it worked Exit_SyncAllDocuments : Exit Function Err_SyncAllDocuments : SyncAllDocuments = False LogError Resume Exit_SyncAllDocuments End Function Function getCompareKeySource (doc As NotesDocument ) As String getCompareKeySource = getCompareKey (doc ) End Function Function getCompareKeyTarget (doc As NotesDocument ) As String getCompareKeyTarget = getCompareKey (doc ) End Function
This LotusScript was converted to HTML using the ls2html routine,
provided by Julian Robichaux at nsftools.com.

Posted by on 21 September 2007 | Comments (3) | categories: Show-N-Tell Thursday

Comments

  1. posted by Stephan H. Wissel on Monday 24 September 2007 AD:
    @William,
    thx for pointing out. You are right. Code fixed.
    Emoticon smile.gif stw
  2. posted by William Ryken on Monday 24 September 2007 AD:
    thanks for the contribution to the community.
    one small logic typo... in the inner loop of the SyncAllDocuments function.
    Right after If targetDoc is Nothing, you're setting gotoNextSource twice.
    I'm thinking that you meant gotoNextSource = True, and then gotoNextTarget = False.
    You may want to make a small edit, just in case someone tries to cut-and-paste the code.
    Cheers.
  3. posted by Nick Wall on Monday 24 September 2007 AD:
    Very nice.

    What I am about to mention takes away some of the "dynamicness" of the code, but if you are running into performance issues...just throwing another idea out there...

    If the documents that have changed versus total docs in the view is small, in the updateSourceDoc routine, what you can also do is have column 2 of each view concactenate the fields, e.g.
    View1, column2 = Field1 + "~" + Field2 + "~" + @Implode(MultiField1;"^") + Field4...etc,

    View2 column2 = Field1 + "~" + Field2...

    Since the docs are in a view, you can compare the doc.ColumnValues( 1 ), and if they are different, then start the cycle through the fields. This should speed up the iteration. If you used NotesViewEntries, and only referenced the NotesDocument if you began the iteration through the NotesItems, this should speed it up even more. The side effect will be that the view indeces will go up on each view, plus it's only checking if the fields you have concatenated in the view column have changed.