Skip to content

Searching a View Index

Chapter 6-4
Searching a View Index

Views with One Text Key

This section examines TEXTKEY, an example program that finds documents in a collection based on the documents' primary sort key. This is a useful operation because it allows you to build a collection once and use it repeatedly to find documents with different keys.


NIFFindByName

TEXTKEY uses the same functions and data types as the previous sample program and an additional function, NIFFindByName. NIFFindByName searches a collection for the first note whose primary sort key matches a given string. The function sets a COLLECTIONPOSITION structure to indicate the location of this note in the collection. The function also returns the number of notes that have this key.


Restrictions of NIFFindByName

NIFFindByName is subject to the following restrictions:

    • You can search for only those notes whose primary key is equal to or begins with a certain string. You cannot find notes whose sort key simply contains a string. Use the FIND_PARTIAL match rule to find a string at the beginning of the sort key.
    • You can search for matches on the primary key only. You cannot use multiple keys at the same time or search alternate keys.
    • The primary key must be a string. You cannot search key fields that are numeric or time/date.


    If you need more extensive search capabilities, use the C API function NIFFindByKey, discussed later in this chapter. For details about NIFFindByKey, see the Reference.


    The TEXTKEY Sample Program

    The sample program TEXTKEY is invoked with three command line arguments: the name of the database, the name of the view to search, and the text string comprising the primary key to search on. TEXTKEY initially opens the database, finds the note ID of the view, and opens the collection as in previous examples. It then looks for notes that have the given primary sort key.

    NOTE: For the purpose of this discussion, assume that TEXTKEY is searching for all notes in the specified view that have "SMITH, NANCY" as their primary key.

    Example: Find notes in a view by primary TEXT key in TEXTKEY


     char               text_key;   / key to search for in view /
    DBHANDLE           db_handle;   /
    handle of the database /
    HCOLLECTION        coll_handle; /
    collection handle /
    COLLECTIONPOSITION coll_pos;    /
    position within collection /
    DWORD              match_size;  /
    number of notes matching key /

    /

     Look for notes that have the given primary sort key (which must be of
    type text). We get back a COLLECTIONPOSITION structure describing where the
    first such note is in the collection and a count of how many such notes
    there are. Check the return code for "not found" versus a real error.

     /

     error = NIFFindByName (
          coll_handle,     /
    collection to look in /
          text_key,       /
    string to match on /
          FIND_CASE_INSENSITIVE,/
    match rules /
          &coll_pos,      /
    where match begins (return) /
          &match_size);     /
    how many match (return) */


     if (ERR(error) == ERR_NOT_FOUND)
    {
      printf ("\nKey not found in the collection.\n");
      NIFCloseCollection (coll_handle);
      NSFDbClose (db_handle);
      API_RETURN (NOERROR);
    }

    if (error)
    {
      NIFCloseCollection (coll_handle);
      NSFDbClose (db_handle);
      API_RETURN (ERR(error));
    }



    This code fragment shows the C API function NIFFindByName, which uses the sorting order of a view to find specific entries in the collection. This function allows you to find documents whose primary sort key for the view begins with a specified string.

    The first argument (input) to NIFFindByName is the handle of the collection to search. The second argument (input) is the string to look for in the primary key. The third argument (input) contains the matching rules to be used during the search. The fourth argument (output) is a COLLECTIONPOSITION that points to the first place in the collection where the string is found. The fifth argument (output) is the number of matches found.

    The example code searches the primary key of the collection for the string "SMITH, NANCY." The match rules specify that the searching is to be case-insensitive. When the first "SMITH, NANCY" is found in the primary sort key of the collection, the COLLECTIONPOSITION structure is set to point to this entry. The total number of entries whose primary key begins with "SMITH, NANCY" is returned in the fifth argument.

        Example: Reading Collection Entries That Match the Search Criteria in TEXTKEY

     DWORD   notes_found;        / number of notes found /
    DWORD   match_size;         / number of notes matching key /

     DWORD   note_count = 0;     / ordinal number of the note / 
     BOOL    FirstTime = TRUE;   / used in NIFReadEntries loop /
     HCOLLECTION        coll_handle;  / collection handle /
    COLLECTIONPOSITION coll_pos;     / position within collection /

    / Get a buffer of all the note IDs that have this key. /

     if (error = NIFReadEntries(
                     coll_handle,      / handle to this collection /
                     &coll_pos,        / where to start in collection /
                     FirstTime ? NAVIGATE_CURRENT : NAVIGATE_NEXT,
                                       / order to use when skipping /
                     FirstTime ? 0L : 1L, / number to skip /
                     NAVIGATE_NEXT,    / order to use when reading /
                     match_size - note_count,      

                                        / max number to read /
                     READ_MASK_NOTEID, / info we want /
                     &buffer_handle,   / handle to info (return) /
                     NULL,             / length of buffer (return) /
                     NULL,             / entries skipped (return) /
                     &notes_found,     / entries read (return) /
                     &signal_flag))    / signal and share warnings (return) /


     {
        NIFCloseCollection (coll_handle);
        NSFDbClose (db_handle);
        API_RETURN (ERR(error));
    }


    / Check to make sure there was a buffer of information returned.
    (This is just for safety. We know that some notes matched the search
    key.)
    /


     if (buffer_handle == NULLHANDLE)
    {
        NIFCloseCollection (coll_handle);
        NSFDbClose (db_handle);
        printf ("\nEmpty buffer returned by NIFReadEntries.\n");
        API_RETURN (NOERROR);
    }


    / Lock down (freeze the location) of the buffer of notes IDs. Cast
    the resulting pointer to the type we need.
    /


     id_list = (NOTEID ) OSLockObject (buffer_handle);

    /
    Print out the list of note IDs found by this search. */

     printf ("\n");
    for (i=0; i<notes_found; i++)
        printf ("Note count is %lu. \t Note ID is: %lX\n",
            ++note_count, id_list[i]);



    TEXTKEY calls NIFReadEntries to retrieve the IDs of the notes found by the search. The program passes to NIFReadEntries the COLLECTIONPOSITION found by NIFFindByName. When called for the first time, NIFReadEntries skips no notes during its skipping phase. Therefore, during the reading phase, NIFReadEntries starts reading at the first note that has "SMITH, NANCY" as its key.

    The program also passes to NIFReadEntries the number of notes that have "SMITH, NANCY" as their key. NIFReadEntries then reads exactly this number of notes. If the program did not cut off the reading phase in this manner, NIFReadEntries would return information about all the notes following the first "SMITH, NANCY."

    NIFReadEntries returns a handle to a buffer containing a list of the note IDs it found. If the handle is invalid, the code cleans up and exits with an error. Otherwise, it locks the buffer and retrieves the note IDs one by one.

    If there are more notes to be read, NIFReadEntries is called again. This time, the function skips the first entry since it was already read in the previous call.


    Views with Multiple Keys

    This section examines TWOKEY, an example program that finds documents in a view based on two different search keys. Besides calling most of the same C API functions as TEXTKEY, TWOKEY also calls the function NIFFindByKey.


    NIFFindByKEY

    NIFFindByKey searches a collection for the first note whose sort key (or keys) match those specified in the second function parameter. The function sets a COLLECTIONPOSITION structure to indicate the location of this note in the collection. It also returns the number of notes that match the specified keys.

    The second parameter passed to NIFFindByKey is a buffer containing two data types, ITEMs and ITEMTABLEs. These are explained in the "Data Types" section of this chapter.


    Restrictions of NIFFindByKey

    NIFFindByKey is subject to the following restrictions:

    • You cannot use NIFFindByKey to find notes that are categorized under multiple categories, since the resulting position is unpredictable.
    • You cannot use the function to locate responses.
    • You must specify the ITEMs passed into NIFFindByKey in the same order as the sorted columns of the view, from left to right. Other unsorted columns may lie between the sorted columns you want to search. For example, suppose view columns 1, 3, 4, and 5 are sorted. The key buffer may contain search keys for column 1 only; columns 1 and 3; columns 1, 3, and 4; or all the sorted columns.



    Partial-Key Searches

    Using the FIND_PARTIAL match rule allows the supplied string to match any key that begins with that string, whatever may follow in the key. For example, the string "abc" matches "abc", "abcd", "abc7", and so on. When you use FIND_PARTIAL with multiple-key searches, partial matching is used for all the specified keys. The partial match must succeed for all the specified keys in order for a document to be selected.

    Partial-key searches are sensitive to the order in which documents are sorted in the view. When you use multiple search keys, the COLLECTIONPOSITION is set to the first document that matches on all the keys. Additional documents match the search keys until a document is encountered that does not match on all keys. Any documents following the non-matching document cannot be located with a partial-key search, even if the documents match the partial search keys.

    For example, assume the following documents in a view:

      11 Smith 23
      11 Smithson 23
      11 Smithson 37
      11 Smithy 23


    A partial-key search using the keys "11", "Smith", and "23" finds only the first two records; encountering the record "11 Smithson 37" ends the search.


    Data Types

    ITEM

    The ITEM data type contains information about a data field. It contains two USHORTs, one to specify the length in bytes of the name of the field and the other to specify the length in bytes of a field's value. NIFFindByKey does not use the field name information, so the length of the field name should be set to zero.

    ITEM_TABLE

    The ITEM_TABLE data type is a variable-length data type that specifies summary information about the fields in a document. It consists of:

    • A USHORT specifying the total length of this ITEM_TABLE structure
    • A USHORT specifying the number of items in the table
    • An array of ITEMs describing the field in a document
    • An array containing the packed data for each field


      The TWOKEY Sample Program

      The sample program TWOKEY is invoked with two command line arguments: a text key to search on and a number key to search on. It opens the database TWOKEY.NSF, finds all documents in the view KEYVIEW that match on both supplied keys, and prints out the note ID for each matching document.

      NOTE: For the purpose of this discussion, assume that TWOKEY is searching for all notes in the specified view that have "Elvis" as their primary key and "99" as their secondary key.

      Example: Create key_buffer parameter for call to NIFFindByKey in TWOKEY

         char    pTemp;         / working pointer to key                 /
        char    
      pKey;          / saved pointer to key                   /
        char    Key1;          / primary key                            /
        char    
      Key2;          / secondary key                          /
        WORD     Item1ValueLen; / len of actual primary text to match on /
        WORD     Item2ValueLen; / len of actual secondary key to match on/


         WORD     Item1ValueLen, Item2ValueLen, signal_flag;
        BOOL     FirstTime = TRUE;


         ITEM_TABLE  Itemtbl;
        ITEM        Item;
        WORD        Word;





      / ======================================================================
      This is the interesting part of NIFFindByKey() - Building the key.


      Note: Space for Key1 and Key2 is dynamically allocated.
      /

      / Processing input arguments /

      ProcessArgs(argc, argv, Key1, Key2);

      / Translate the input key to LMBCS /
      TranslatedKeyLen = OSTranslate (
                                     OS_TRANSLATE_NATIVE_TO_LMBCS,
                                     Key1,
                                     (WORD) strlen (Key1),
                                     TranslatedKey,
                                     STRING_LENGTH);
      Item1ValueLen = TranslatedKeyLen + sizeof(WORD);
      Item2ValueLen = sizeof(double) + sizeof(WORD);


      / Allocate memory for the key's use. /
      pKey = (char )malloc(MALLOC_AMOUNT);
      if (pKey == NULL)    

      {
        printf("Error: Out of memory.\n");
       
      returnCode=1;
      goto exit1;
      }


      pTemp = pKey;

      /
      ============================================================================
       The key should look like:
       an ITEM_TABLE, followed by an ITEM for every component of the key (a single
       key has one ITEM, a two part key has two ITEMs, etc...) and then packed data
       for each of the ITEMs.


        ITEM_TABLE:
          Length - total length of the key
          Items - number of components to the key
      --
       ITEM 1:
          NameLength - 0 (NIFFindByKey does not use the field name)
          ValueLength - length of the actual primary key + sizeof(the type field)
        .
        .
        .
       ITEM n:
          NameLength - 0 (NIFFindByKey does not use the field name)
          ValueLength - length of the actual nth key + sizeof(the type field)
      --
       Key 1 Type  - type of the primary field, see the include files
       Key 1 Value - actual value of the primary field, w/ no terminating zero
        .
        .
        .
       Key n Type  - type of the nth sort field, see the include files
       Key n Value - actual value of the nth sort field, w/ no terminating zero
      /


      /
      Creating the ITEM_TABLE structure in the key /
       Itemtbl.Length = (   sizeof(Itemtbl) +
              (2
      (sizeof(Item))) + Item1ValueLen + Item2ValueLen);
       Itemtbl.Items = 2;
       memcpy (pTemp, &Itemtbl, sizeof(Itemtbl));
       pTemp += sizeof(Itemtbl);


      / Creating the ITEMs in the key /
      / Initialize first ITEM in the key /


        Item.NameLength = 0;
       Item.ValueLength = Item1ValueLen;
       memcpy (pTemp, &Item, sizeof(Item));
       pTemp += sizeof(Item);


      / Initialize second ITEM in the key /

        Item.NameLength = 0;
       Item.ValueLength = Item2ValueLen;
       memcpy (pTemp, &Item, sizeof(Item));
       pTemp += sizeof(Item);


      / Create the key titles, key types & key values  /

        Word = TYPE_TEXT;                 / key 1 type (found in nsfdata.h /
       memcpy (pTemp, &Word, sizeof(Word));
       pTemp += sizeof(Word);


        memcpy (pTemp, TranslatedKey, TranslatedKeyLen);
       pTemp += TranslatedKeyLen;


        Word = TYPE_NUMBER;                / key 2 type (found in nsfdata.h /
       memcpy (pTemp, &Word, sizeof(Word));
       pTemp += sizeof(Word);

         
        Double = atof(Key2);
       memcpy (pTemp, &Double, sizeof(Double)); / key 2 value  /
       pTemp += sizeof(Double);


      The buffer created should look like this:

      Total length of table
      ITEM_TABLE structure
      Number of items (search keys) in table.
      0
      String length of field name. Field name is not used by NIFFindByKey.)
      First ITEM structure
      String length of the actual primary key value, including the length of the data type.
      0
      String length of field name. Field name is not used in NIFFindByKey.
      Second ITEM structure
      String length of the actual secondary key value, including the length of the data type.
      Type of data in primary field. (TYPE_TEXT)
      Data pertaining to primary key.
      Primary key value to search for. ("Elvis")
      Key value. Not NULL-terminated.
      Type of data in secondary field. (TYPE_NUMBER)
      Data pertaining to secondary key.
      Floating point integer containing the secondary key value to search for. (99)
      Example: Find notes in a view by primary TEXT key and secondary NUMBER key in TWOKEY


       char    pKey;          / saved pointer to key                   /
      HCOLLECTION hCollection;/
      collection handle                      /
      COLLECTIONPOSITION posCollection;   /
      position within collection /
      DWORD    NumNotesMatch; /
      number of notes matching key           /



      /

        Look for notes that have the given two-part sort key.
        A COLECTIONPOSITION structure is returned, describing where the first
        matching note is in the collection, and a count of how many such notes
        there are.
      /
        error = NIFFindByKey(
                 hCollection,
                 pKey,          /
      refer to key   /
                 FIND_CASE_INSENSITIVE,     /
      match rules /
                    /
      FIND_PARTIAL could be added to find wildcard matches /
                &posCollection, /
      where match begins (return) /
                &NumNotesMatch);/
      how many match (return) /


      /
      Done with the key now that posCollection & NumNotesMatch are prepared;
        don't forget to free the memory.
      /

         .
         .
         .
         free(pKey);
        free(Key1);
        free(Key2);

         free(TranslatedKey);


      /
      ======================================================================= */

      The above code fragment illustrates the call to NIFFFindByKey. The third parameter specifies rules to use in deciding whether a note matches the keys. For definitions of these flags, see FIND_FLAGS in the Reference.

      Once NIFFindByKey returns, you could then call NIFReadEntries to get a buffer containing the note IDs of all documents that match both keys.