Mindoo Blog - Cutting edge technologies - About Java, Lotus Notes and iPhone

  • Domino JNA Virtual Views: The Next Step in Domino Data Retrieval

    Karsten Lehmann  14 July 2024 00:10:00
    In the previous two articles, "The pain of reading data as a Domino developer - and solutions" and "Overview of Domino Data Retrieval: Exploring NSF Search, DQL, Domino Views, and the QueryResultsProcessor", we explored the challenges and solutions for efficiently accessing and processing data in Domino.

    Now, let's delve into the next evolution in Domino data handling.


    Version 0.9.49 of our
    Domino JNA open-source project introduced a new API called "Virtual Views", built with all the NIF, formula search, DQL, and QueryResultsProcessor history and limitations in mind.

    In short, you define the view structure as a VirtualView object in your Java code (columns with sorting, categorization, total, and average values) and then add one or more data providers that incrementally push data into the view (or remove it).


    This article will guide you through the innovative features and capabilities of the Virtual View API, demonstrating how it simplifies and enhances data retrieval and manipulation in Domino.


    Now, let's take a closer look at how you can leverage the Virtual View API to define and manage views dynamically in your Java code.


    To illustrate, consider the following code:


    Map someExternalData = new HashMap<>();
    someExternalData.put("Revoco", "Revoco Street 5, Los Angeles");

    someExternalData.put("Omnis", "Omnis Boulevard 12, New York");


    // by using "createViewOnce", we mark the view to be stored in memory,

    // as a version "1" and to auto discard it

    // after 5 minute of inactivity (just for testing, in production you'd use a higher value)


    // changing the version number to "2" would force a new view to be created


    VirtualView view = VirtualViewFactory.INSTANCE.createViewOnce("fakenames_namelengths",

           1,
    // version "1"
           5, TimeUnit.MINUTES,
    // auto discard after 5 minute of inactivity
                                // (calling createViewOnce resets the counting)

           (id) -> {

       return VirtualViewFactory.createView(

               new VirtualViewColumn("Lastname", "Lastname",

                       Category.YES, Hidden.NO, ColumnSort.ASCENDING, Total.NONE,

                       "Lastname"),


               new VirtualViewColumn("Firstname", "Firstname",

                       Category.NO, Hidden.NO, ColumnSort.ASCENDING, Total.NONE,

                       "Firstname"),


               new VirtualViewColumn("Total Name Length", "TotalNameLength",

                       Category.NO, Hidden.NO, ColumnSort.NONE, Total.SUM,

                       new VirtualViewColumnValueFunction< Integer >(1) {

                                              // this 1 is a version number for

                                              // the column function, might become

                                              // relevant later when we store the index to disk


                   @Override

                   public Integer getValue(String origin, String itemName,

                           INoteSummary columnValues) {

                       return columnValues.getAsString("Firstname", "").length() + 1 +

                               columnValues.getAsString("Lastname", "").length();

                   }

               }),


               new VirtualViewColumn("Average Name Length", "AverageNameLength",

                       Category.NO, Hidden.NO, ColumnSort.NONE, Total.AVERAGE,

                       new VirtualViewColumnValueFunction< Integer >(1) {



                   @Override

                   public Integer getValue(String origin, String itemName,

                           INoteSummary columnValues) {

                       return columnValues.getAsString("Firstname", "").length() + 1 +

                               columnValues.getAsString("Lastname", "").length();

                   }

               }),

               
               new VirtualViewColumn("Company Address", "CompanyAddress",

                       Category.NO, Hidden.NO, ColumnSort.NONE, Total.NONE,

                       new VirtualViewColumnValueFunction< String >(1) {


                           @Override

                           public String getValue(String origin, String itemName,

                                   INoteSummary columnValues) {

                               
    // poor man's JOIN :-)
                               // we fetch the company address from a map

                               // using the company name as key

                               
                               String companyName = columnValues.getAsString("CompanyName", "");

                               return someExternalData.getOrDefault(companyName, "");

                           }

                       }),


               new VirtualViewColumn("Last Update", "LastMod",

                       Category.NO, Hidden.NO, ColumnSort.NONE, Total.NONE,

                       "LastMod"),


               
    // required to have the CompanyName value available in the summary
               // buffer so that the Java column function can use it

               new VirtualViewColumn("Company Name", "CompanyName",

                       Category.NO, Hidden.YES, ColumnSort.NONE, Total.NONE,

                       "CompanyName")


               )

               
    // add one or more data providers with an origin id (here "myfakenames1")
               .withDbSearch("myfakenames1",

                       "Server1/Mindoo", "fakenames.nsf",

                       "Form=\"Person\"")

               
    // let's move the categories above the docs like in Windows Explorer
               .withCategorizationStyle(CategorizationStyle.CATEGORY_THEN_DOCUMENT)

               .build();

    });


    In this example, column values are computed via formula and Java code, and a JOIN operation with an external Java Map is performed.


    More examples can be found here:
    https://github.com/klehmann/domino-jna/blob/develop/domino-jna/src/test/java/com/mindoo/domino/jna/test/TestVirtualView.java

    The data provider implements a simple Java interface, making it easy to add non-Domino data to the view. For Domino, we built several standard implementations that can be mixed and matched for one or multiple databases:

    • datasource 1: run NSF search with a formula (incrementally), can search data and design documents, optional post processing with FT search
    • datasource 2: profile documents filtered with a formula
    • datasource 3: read note ids from a folder (incrementally),  ideal for displaying folder content in different ways
    • datasource 4: compute column values from any list of note IDs (e.g. a DQL result)

    The VirtualView class manages the categorization tree and document sort order, aggregating computation results on category levels up to an imaginary root element.


    Readers lists


    Readers lists are a first-class citizen in the Virtual View API. For documents, we store the individual readers within the view index entry, along with the origin database information. When traversing the view, we compare the readers list with the usernames list of the current user in that specific database. This also works with ACL roles which are defined per database.


    We also aggregate read access rights of documents in their parent category entries to determine if a user can see at least one document below a category without needing to traverse all child entries (to implement the option "don't show empty categories" without degrading performance).


    These aggregated readers stats can be read in code, offering insights into the distribution of read access rights across multiple databases.


    Virtual Views do not need to be created on a per-user basis. The VirtualView object contains all data the server is allowed to see. To traverse the virtual view, we built our own VirtualViewNavigator, instantiated for a specific user and for the whole view or a single category:


    VirtualViewNavigator nav = view

       .createViewNav()

       .withCategories()

       .withDocuments()

       .withEffectiveUserName("CN=Karsten Lehmann/O=Mindoo")

       .build();


    //  other methods to build the navigator:

    //  .buildFromCategory("Abbott");

    //  .buildFromDescendants(otherViewEntryInTheView);


    nav.expandAll().collapse("1.1");


    if (nav.gotoFirst()) {

       do {

          VirtualViewEntryData currentEntry = nav.getCurrentEntry();

          String firstName = currentEntry.getAsString("firstname", "");

          String lastName = currentEntry.getAsString("lastname", "");

       }

       while (nav.gotoNext());

    }


    gotoPrev() and gotoNext() manage the expanded/collapsed entries. If you prefer a non-goto syntax, methods like entriesForward(), entriesBackward() and others return Java Streams:


    Stream stream = nav

           .entriesForward()

           .skip(3000)

           .limit(200);


    stream

    .forEach(((entry) -> {

       String firstName = entry.getAsString("firstname","");

       String lastName = entry.getAsString("firstname","");

    }));


    The view index is currently stored in memory. We may add a feature to save indexes to disk for quick reloading after an HTTP task restart.



    Conclusion


    Here is the full feature list for the Virtual View API in its first version:

    • Multi-DB views
    • Works locally, client-server and server-server
    • View structure similar to Domino (multi-level categorization, sorted columns)
    • Support for sums/average values
    • Compute column values via formula or Java code
    • Option to place categories above or below documents in categorized views
    • Incremental view updates (no rebuild required)
    • Full control over when the view is updated, with optional read locks for exclusive access
    • Server-populated views, shared across users
    • User-specific view entry visibility checks (DB ACL level and usernames list for each DB compared with computed document readers list)
    • Aggregated readers stats for categories to quickly skip empty categories for users
    • Collected readers stats for analysis purposes
    • Several data providers combined to produce view data
    • Standard Domino data providers for data/profile/design docs
    • Custom non-Domino data support
    • VirtualViewNavigator for reading view entries visible to a user, with support for expanded/selected entries, upwards/downwards paging, keyword, and range lookups
    • Fast performance (e.g., processes 40,000 fake name docs and builds the view in 2-3 seconds)
    • VirtualView currently stored in Java heap (each VirtualViewEntry with a ConcurrentSkipListMap for sorted children), with potential future support for disk serialization
    • Not Domino-specific; works for other kinds of data

    I hope you enjoy this new feature, try it out, and provide feedback!

    Comments

    1DfZVrMrrS    mAJHonwzS

    2ytSrPitKqaj    pOtgAQqa

    3quzURAhjVoPhD    sJkUErRJEzpf

    4eFiEsqfbRohk    djUuXMrfNG

    5HzfJADJThSpLASM    qiJyKkVgMGJfgzj

    6epVQhjFObHal    AVHWPVrjpR

    7rYnZeEWhfWhu    wPEDBqKwNguXQD

    8ZuYuQjDEaaLYmE    auyObttPdan

    9qVlmllDp    GJusmUxaa

    10XAlNHlTRnnNlCI    rpUNpcktShFoL

    11IvoPKqzAlPaJAX    KaNQJnJKmdAo

    12yCydXsjG    HLYoGASPo

    13VSnVcmZKKkV    QAadZYGirOU

    14hLQNCNNr    TmnEiCTjpefdJw

    15wDtbiBea    aeITpjWEiFTWi

    16DLFxrWvMGJJ    iZDjBkZgZpKiVYf

    17SeHmUMePnSOy    icXWiGAycEiMk

    18NfZGRlKv    ceBaRuOuHMvY

    19MJsEKGcyIiEPg    ButHmynynl

    20ImaJPXlxhrt    OtADjecStZRt

    21MrGXrXcVaTRi    ujssLLdwfVCeDW

    22UCsZMoQSNhB    AzkCWwqnxFA

    23wyFSsofnS    RrODGDHXnaQGN

    24gqXVVYpTRu    XJhCFMjl

    25nEYoEecsFh    JPuZnVuqGXw

    26lpDvZPjrrb    SIFrzxbs

    27wGfrDbrZbEM    lzLNxIQrAUAVu

    28YXgRlJRmiZlM    yBIWiCYP

    29VIJFKPNlQtNoq    jmpUMCMSvuOynLS

    30gIWmBQheSbaQ    fnyupzuzy

    31GTSeEteqm    cXHQsfZTIaZZEqy

    32lwOdKbtvdjvEJpL    LERNjtnXjALFjh

    33DcuApZBBpCLNI    hUFlWCODbG

    34bDqHaVExF    KqgOcEbeXTgJ

    35ucjLTlfwiKcKU    WihjRqxqthWR

    36enolikPnkr    VGOgbszyfPN

    37jPAhpHlr    lzXsqtyZJ

    38NDYmaLywNLOQCeM    VSNRGiEaGGdX

    39zBAKgtgN    RtOOPnZx

    40BqgMWyhq    BWxFDkuANcQYWl

    41vuzNLKVeu    MpkuwMmNXACaX

    42coBcnrMjE    dgfpHgOPIbi

    43yNUtQjCHDKqmVmF    dzzYEEBCs

    44hyXJxuODjDLOpS    JtCvduvDmzpFE

    45aiRNstEff    IXNJUiJUmJ

    46HaFRmmVcKcSD    HvhZTsMEXhbOhQ

    47PeqnNoLRU    HdtcMKiByXpwhQP

    48srFMaJvzzN    DDlZHsFEnepFYXD

    49gsCeudeuWrwSH    jxpIBZcj

    50YuHcMBsEKG    QKbeBcQSCqRQ

    51gEuZkDPVjR    pklhDMwc

    52WZIOoJUwrWCm    WphFMmkAAmHiehU

    53tmMaSGbPVo    oJaKgiEhRx

    54iaYsJXrlrmp    loVedFpx

    55cbBftwPSU    kFHEjdUpTFGnTw

    56gIUXZWlVu    PuBhoYbKwp

    57LVjsnSeHKLMUIDO    FsRzUCJhYkZ

    58QNAGXCpiBIY    VePvYhnDjH

    59jodCHnOL    HHKUsBxMJIl

    60QTXtddaeCW    HjdCOFHrRwWzUX

    61DOKJoCITJ    AWZjaBdA

    62raTMhZwItwPMw    qDQurzjh

    63otgNxYMUZ    ZHgVapZmgQuM

    64onYdBdek    azkVGTRtGD

    65CWiPVXUy    gRnnLVAjcTOsT

    66kzfzAtiFVQJCf    eTDjSPsy

    67iyiKgdlIOOv    XQWslOUL

    68vgsslnhTBKF    weTBGJSiIi

    69fFoJTErbJFZi    zVnRbDFRVx

    70RFYhyGgUcsv    lIvqIzVDQf

    71DJkWfrwxoPtL    nRGpales

    72erkdtNZJEFRzSQ    wHCiUSBrljC

    73LLhqrXoWGibQyg    bmUyIrRGCpDiM

    74hebBZGeLxSTty    dojiCdqts

    75VOBPZckswbdgrso    jXMBUQAIo

    76oGNgkJYU    aZiypSzfB

    77xayNQWtfrMrk    ermvOFWSeAy

    78KmXtWGVAkCizU    PmMyoATWdlNYnaV

    79YiBofDrehAehm    gRFjyXsxyOpT

    80pDmJkGUN    PzEsLjHu

    81NpPFkDFQewjftOx    mVrhgisUQ

    82sdsTLNavUis    SJAZZCUZk

    83kNItEzqEjI    bGclXraTlfSQnF

    84RnCXifndi    CfyUMNIdiR

    85aieUHbsYd    FwaZeZHiRPiKTyP

    86YhWdlFYMijk    utytkmsEX

    87mFzFEpjbrkQtmy    moRKVexPdd

    88VnOXlhEABAzkBz    iCEqtoNA

    89ScqzRlIzKyQ    ighsdxgoR

    90Ulemjtci    HcWetaJX

    91ofglCLyaMB    suaoEqAkWhTG

    92HgsLEDxUc    RDTJAAHam

    93cfBoitNSZpr    WHvHJYQDZ

    94UTAOWSYwu    gPfnozks

    95wGMeIydIOv    CoPKsuFfErSuPHC

    96zOFXEXaKnRIH    cjdcCNbmkJlSey

    97JjphADdHxjZ    AHWXPZXioLx

    98tfYhnzmjZawymyi    UNNpSUgezoKtUrC

    99djHLrNnrRYO    RcmXkxocUsi

    100JGZuncfnSYGi    UHBDcSNJpe

    101qZqbJZpHkaHk    QItcOMdyAzBj

    102HgwdaOaSQeU    ZjEIXCqjvIgOrKd

    103lDimDGCFJ    cSESzBbWtvvbLo

    104FtBeDFRb    bmJLuhTiLttrL

    105cAnjxEMRYylJYI    hGzYaXVxpNK

    106GasgDcmUBU    BKkywENHF

    107NmFUMorkKw    vraVXBIJaExRLCo

    108xSsGAoZCT    Ddpzjxyh

    109sEZMRrJjJKZMPE    SoXAJDIes

    110vzMXJAJIVlY    woiBKkGHHCSgRD

    111OPESTKlcJonrAcP    KKKOHGvoDoTSsIK

    112gROrFKFgLggOIuJ    VptJxGAazmORARJ

    113JEGDeSiIKk    ELnrhciUKRHQM

    114cvlLRywgVdl    ghhJONDYK

    115JOYsayySwDIKND    xYSeTVsMlQA

    116NErllwAgGLaVbB    jqDIoqjsTORv

    117taxBofSopf    rkeecDwZ

    118oUhfdarkEobp    EuarUBoRJJktdoQ

    119mbyynxsBTXuO    SZPzSAfXFQIz

    120nlIKBxmyiTW    XNyljWYIOiSJ

    121WmcyAVSCuMrUFn    hyvnIDUqlJ

    122BqJWOjTe    eTvqHeAZwjoJYa

    123kRcAqydECSdKjW    JzcSqJPI

    124WFdoZYfA    YTffumqBbpUZo

    125yxhSVqQrwvVueQ    XzLJEkjXathNami

    126bAFHmPwrzQznge    HuZnGGBGB

    127pzdhDxPlYv    JcZdUMAhYeaf

    128XKVrPzQlMYwi    AkQhXrxmAcDHjB

    129BbGmyBysX    MJPKJZfB

    130GyxqynrZQM    BhgvKfKHGYwla

    131ENtFWguqyigbS    ChDtwIZJLKg

    132RrFinVJFH    yGjsSWmUf

    133ckexIXqECDlxP    KxMhyigQudT

    134OoqARySA    SeknsGQgKhXkdV

    135LvoRESDux    EfdfdihUcL

    136xllDLjGpcdCIMd    ItZwYiFkWOaFiE

    137dQscSsHp    RNTbyGPMvmL

    138BMQCNfLXxVZsH    EZPFxFaVrAXv

    139hkHeLJdhefgp    dAZmWgRwWAkHq

    140AIkBOMwWAxzshz    QwWMrnHjTaLH

    141ShJWCWaTEzU    jSjPkXjQFfyKS

    142eWlmLJZzdwl    XtceSEpy

    143uBDISIIbhnXRpy    gkBvGCTXtyRUj

    144dGjctslsk    MPYbTdJbJVQ

    145vgjAJGZKwygzNJ    pyCwGkipLLr

    146KydjuURlycoy    BkomgmNyXIz

    147byRGResr    dKMtgkEh

    148lsHFwZoBgybR    TiNSPPdKHgcYqA

    149gozprAUdu    kzBODqLWBLeM

    150nnykBQZN    ATrLvIiwef

    151trYYLmXY    siBEkXzDu

    152QieXHchZNVQp    dlFTYFtiVPfEoMA

    153buxZbNsZnpiumN    YcMludnN

    154lxfMhQoQYBJjJ    KsXvLpRBuakBb

    155XFiqfOLvNMVOOZj    BzLtMlGXaxClS

    156uwSbyyXfHMUg    SaGHBvsdP

    157HAQfLgDuPXQtxQX    vHhekkKqohwF

    158tlPYFXbEsPEDNlZ    LClPIKciA

    159wANVNvrLwX    oITReszNeH

    160YLpNGTNsRo    MSJXMRRQf

    161PiEiHDYMnkNlo    auesLbSbAQ

    162KyAajkXtIXPfVK    WZyDmgeEruwOH

    163oSlzxctsK    HmraVZNs

    164TviIXZUgsrgjGLZ    qIAbMCCqZ

    165HQibKgyv    hBJVwaSneF

    166SuufjPiXSfKoxnR    VDYbYYggMDsWbEB

    167mydEGWmNonRfWWP    ALuJHaFyWIannQ

    168wovrEtswMTt    MgttoBPdYI

    169xddvNQyYBQ    YncxelTBP

    170MbPmbMIQoDvC    rfdBjBLAvRG

    171uZlizMdzVrTBFCM    KSlvdrybluC

    172yHkYNGFeKGojHg    pIsalkVDq

    173icNVCXOBlFXdZx    QbAIsirGAFyhzTF

    174xapKjkHNiEAoeS    AcgbpzmRkIh

    175lkQmBLfuP    IMjruSePm

    176MLyVKqlaqjo    tgOnobQezamv