Bob Balaban's Blog

     
    alt

    Bob Balaban

     

    Show ’n’ Tell Thursday - what folders is a document in?

    Bob Balaban  March 24 2008 02:43:04 PM
    Image:Show ’n’ Tell Thursday - what folders is a document in?
    Greetings, Geeks!

    I'm very late to the SNTT club, but I'm hoping to be able to throw in some decent techie tips at least semi-regularly, in addition to the more high-falutin' thought-pieces and questionnaires.

    So! Have you ever needed to figure out what folders a document is in? Here's a brief synopsis of the documented way to do it, and then I'll show you a neat way I discovered that works better and faster.

    You can go to the Designer Help database, select the Index tab, and find the "FolderReferences property" entry. You'll see in the doc that this is a property on NotesDocument (works in Java, too), but there are some constraints: the db has to have the "FolerReferencesEnabled" property turned on. But, there's no UI to turn it on (no InfoBox checkbox or anything), you have to do it from a program. And the db has to include 2 hidden views, which you can copy from a recent mail template: $FolderInfo and $FolderRefInfo. Eh, not great, but (so far) not so bad. Then you should go look at Technote number 1092899, which gives a bit more information on how folder references work.

    It turns out there are a couple of limitations. For one thing, references to folders containing a document are only accumulated once you turn the database-level feature on, it doesn't go back and figure out anything about documents that were put into folders before the feature was enabled. And, having documents in privatefolders causes problems (like, doesn't work....). And they warn you that turning on this feature can slow down "database operations".

    So, for the project I'm working on, as I did this research it sure started to sound like "not the best way to peel this onion" (meaning, I started to get the idea that if I went down this road, it was going to mean a lot of tears for me).

    Luckily I came up with another way to do it that seems to work pretty well, and wasn't too hard to code up. I'll describe the algorithm, then show you some code snippets.

    Algorithm:

    1. Get a list of all the folders in the database (scan the array of View objects returned by db.Views, save the actual folders (view.IsFolder) in an array
    2. Figure out what documents I want to test for folder-ization (this will vary depending on the application, but that's not important)
    3. For each document in whatever list (or DocumentCollection, or whatever):
      4. Iterate over each folder object (which is really an instance of the View class), and for each one:
           5. Create a ViewNavigator object, using the View.CreateViewNavFrom()
           6. Call ViewNav.Count. If the result is 0, the document is NOT in the folder, otherwise, it is

    Cool, huh? I must say, I was very pleased to have figured this out. Why does it work? The trick is in using the ViewNavigator class. When you call any of the CreateViewNavXXX methods on the View class, you will ALWAYS get a non-null result, it always creates the object. BUT, if you use CreateViewNavFrom(document), the navigator class has nothing to do if the document is not in the view/folder. So Count will be 0. It's really nice that the View and ViewNavigator classes were coded to be tolerant of what is essentially an error condition! You're asking for a navigator object where the first position in the navigator's "list" is the document you specify. There is a call in the CAPI that this logic uses (NIFLocateNote(), you can look it up) which, given a document id, finds that document in a view or folder collection.

    Code:

    This code is written in C#, because the project I happen to be working on requires a .NET implementation. However, this would work just as well if it were in LotusScript or java.

          // "allFolders" is a previously-populated array of NotesView objects representing all the folders in a db

                     string f = null;    // list of folder names, delimited by ";"
                     for (int i = 0; i < allFolders.Count; i++)
                     {
                         NotesView v = (NotesView)allFolders[i];
                         try
                         {
                             NotesViewNavigator nav = v.CreateViewNavFrom(doc, 0);
                             if (nav != null && nav.Count > 0)
                                 f += (f == null) ? v.Name : "; " + v.Name;
                         }
                         catch (Exception e) {} // guess it didn't work, keep going
                     }

    Simple, huh? Note that this is a doubly-nested loop: outer loop over documents, inner loop over array of folders. If you can keep the numbers reasonable, this isn't too slow.

    Enjoy!
    lol, g2g, ttyl

    (Need expert application development architecture/coding help? Contact me at: bbalaban, gmail.com)
    Follow me on Twitter @LooseleafLLC
    This article ©Copyright 2009 by Looseleaf Software LLC, all rights reserved. You may link to this page, but may not copy without prior approval.
    Comments

    1Jens Polster  04/05/2008 5:34:43 AM  Performance

    Steve McDonagh did some performance testing with this and two alternative methods. It seems that this is the fastest method to accomplish this.

    The results are in the commments to his entry: https://www.blogger.com/comment.g?blogID=1527573980897631948&postID=8812759020184408304&pli=1