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

Brute Force View Tuning

Complex view selection formulas can slowdown your Domino performance substantially. So keeping them simple is a mandate for high performance applications. One of my favorite approaches here is to have a set of hidden fields in my forms that compute to true or false and then simply use SELECT vsf_Demo as selection formula, where " vfs" stands for "View Selection Formula". In large applications that results in a rather nice performance. Downside of the approach: you need to compute these values on each alteration of a document (but that's not too hard). You can use this method even to retrofit existing applications to improve performance. These are the steps:
  1. Create a FORM with one field for each view that contains the view selection formula
  2. Change the view selection formula (short of the SELECT @All ones) to point to that field
  3. Alter/Create the QuerySave event to: switch to that form, do a computeWithForm, switch back to the original form
  4. Create a script library with an easy function that takes a document and tells you if the document's selection formulas have been updated
  5. Add a call to that function to all agents
  6. Add an agent that runs on "documents created or changed" to catch any case where the two calls above didn't work (e.g. @Formula agents)
  7. Test, Test, Test
  8. Enjoy better performance
The code you need to add to the querySave event is rather light: while this is the code in the querySave event of any form:

Sub Querysave (Source AsNotesuidocument, Continue AsVariant)
    Dim handler As DocumentHandler
    Dim doc AsNotesDocument
    Set doc = Source. Document
    Set handler = New DocumentHandler (doc )
      'We recompute here, since we will save anyway we don't care for the result
    Call handler. docHasChangedAfterUpdate()
Being lazy and notoriously prone to typos I created some code that does step 1 and 2 for me in one go.
    Class ViewTuner
    Description: Creates a form with all view selection formulas and updates all views

PublicClass ViewTuner
    Private ourForm AsNotesDOMDocumentNode
    Private formName AsString
    Private fieldNamesUsed ListAsString
    Private resultStream AsNotesStream
    Private parser AsNotesDOMParser
    Private s AsNotesSession
        formName = "ViewSelectionFormulas"
        Setme. s = NewNotesSession
        Property Set formToUse
        Description: Overwrite the formName if necessary
    %END REM

    PublicPropertySet formToUse AsString
        me. formName = formToUse

        Function getEmptyForm
        Description: Get an empty form DOM in DXL
    %END REM

    PrivateFunction getEmptyForm (formName AsString)   AsNotesDOMDocumentNode
        Dim rawform AsNotesStream
        Dim s AsNewNotesSession
        Set rawForm = s. Createstream()
        Set resultStream = s. Createstream()

        Call rawform. Writetext("<?xml version='1.0' encoding='utf-8'?>")
        Call rawform. Writetext("<form name='(" + formName + ")' alias='" + formName + "'>")
        Call rawform. Writetext("<body><richtext>")
        Call rawform. Writetext("<pardef id='1' hide='web mobile'/>")
        Call rawform. Writetext("<par def='1'><run><font color='red'/>&lt;!-- View selection formulas for all views in this application --&gt;</run></par></richtext></body></subform>")
        rawform. Position = 0
        Set parser = s. Createdomparser(rawform, resultStream )
        Call parser. Process()

        Set getEmptyForm = parser. Document

        Call rawform. Close()


        Sub addField
        Description: Add a field to the form DOM
    %END REM

    PublicFunction addField (fieldName AsString, fieldFormula AsString)AsBoolean
        IfIsElement(me. fieldNamesUsed(fieldName ))Then
            'We have that field already, can't use it again
            addField = False
        Dim tmp AsNotesDOMNodeList
        Dim insertionpoint AsNotesDOMNode
        Dim newFieldElement AsNotesDOMNode

        'Lazy initialization
        Ifme. ourFormIsNothingThen
            Setme. ourForm = me. getEmptyForm(me. formName)

        Set tmp = me. ourForm. Getelementsbytagname("run")
        Set insertionpoint = tmp. Getitem(1)
        Set newFieldElement = me. getFieldElement(fieldname,fieldFormula )
        Call insertionpoint. Appendchild(newFieldElement )
        me. fieldNamesUsed(fieldName ) = fieldFormula

        Function getFieldElement
        Description: Generate the XML representing a field with a selection formula
    %END REM

    PrivateFunction getFieldElement (fieldname AsString,fieldFormula AsString)AsNotesDOMNode
            We need to create something like this:
            <field  type='number' kind='computed' name='vsf_DemoView'><code
        %END REM

        Dim result AsNotesDOMElementNode
        Dim code AsNotesDOMElementNode
        Dim formula AsNotesDOMElementNode
        Dim formulaContent AsNotesDOMTextNode

        Set result = me. ourForm. Createelementnode("field")
        Call result. Setattribute("type", "number")
        Call result. Setattribute("kind", "computed")
        Call result. Setattribute("name", fieldname )

        Set code = me. ourForm. Createelementnode("code")
        Call code. Setattribute("event", "defaultvalue")

        Set formula = me. ourForm. Createelementnode("formula")
        Set formulaContent = me. ourForm. Createtextnode(fieldFormula )

        Call formula. appendChild(formulaContent )
        Call code. Appendchild(formula )
        Call result. Appendchild(code )

        Set getFieldElement = result

        Function saveSubformIntoDatabase
        Description: Saves the subform into the database specified
    %END REM

    Function saveSubformIntoDatabase (db AsNotesDatabase)AsString
        Dim importer AsNotesDXLImporter
        'Write the changes into the stream
        Callme. parser. Serialize()
        me. resultStream. Position = 0
        Set importer = s. Createdxlimporter()

        importer. Designimportoption =   6
        Call importer. Setoutput(db )
        Call importer. Setinput(me. resultStream)
        Call importer. Process()
        IfErr <> 0Then
            me. resultstream. Position = 0
            MsgBoxme. resultstream. Readtext()
        saveSubformIntoDatabase = importer. Log


For handling changed documents this agents comes in handy:
    Created Jan 5, 2010 by Stephan H Wissel/Singapore/IBM
    Description: Agent Sample how to update changed view selection formulas


Sub Initialize ()
    Dim support AsNew AgentSupport
    Dim doc AsNotesDocument
    Dim factory AsNew DH_Factory
    Dim dh As DocumentHandler
    Set doc = support. docToProcess()
    DoUntil doc IsNothing
        Set dh = factory. getDocumentHandler(Doc )
        If dh. docHasChangedAfterUpdate()Then
            Call dh. save()
        Call support. Updateprocesseddoc(doc )
        Set doc = support. docToProcess()
You need the CoreHandlerLibrary and the AgentSupport As usual YMMV

Posted by on 24 November 2011 | Comments (3) | categories: Show-N-Tell Thursday


  1. posted by Nathan T. Freeman on Friday 25 November 2011 AD:
    Why not just use folders?
  2. posted by ursus on Friday 25 November 2011 AD:
    Hi Stefan

    Cool idea - love your implementation, well done.

    I have inherited a couple of databases that are quite large (40 - 60 GB on an AS/400 or whatever its called this week :o) and they VERY slow. The reason for this is that every document has reader lists attached to it. I am having a difficult time convincing the users that they do not need them :o( There aren't TOO may views so I could apply your technique but was wondering if you could quantify how much of a speed advantage I would get? I would have about 100 DB's that I would use this on and would have to run the conversion agent by hand as I need to have "Full Access Admin" set!

    Greetings from Austria

    PS: you at LotusPhere this year?

  3. posted by Stephan H. Wissel on Friday 25 November 2011 AD:
    In a Notes client app users can mess up by moving documents in an out of folders. So you need extra code to check for all documents, including the unchanged ones, that they are in all Folders they belong to and out any they are not supposed to be in.
    Also audit gets much harder. Definitely nothing you want to do in a retrofitting exercise.
    Emoticon smile.gif stw