NaviServer - programmable web server
4.99  5.0

[ Main Table Of Contents | Table Of Contents | Keyword Index ]

ns_set(n) 5.0.0a naviserver "NaviServer Built-in Commands"

Name

ns_set - Data Structure for Multimap with Potentially Case-insentitive Key-value Pairs

Table Of Contents

Synopsis

Description

This command provides functions for manipulating sets of key-value pairs, or "fields". Fields in the set are ordered numerically, starting at zero and increasing by one up to the total number of fields. An ns_set is actually a multimap (also called multiset, or bag), meaning the same key can appear multiple times within the same set. Optionally, sets can be defined to be case-insensitive, meaning that mixed and uppercase keys are treated exactly like lowercase keys (for details, see Case Sensitivity).

The ns_set data structure is especially useful for handling data like HTTP headers, which are per definition case insensitive and may have repeated keys.

COMMANDS

ns_set array setId

Returns the contents of the setId in a string format similar to Tcl's array-get syntax. This is helpful for iterating over key-value pairs with foreach. The result can also be used for creating a Tcl dict or a Tcl array. Note that since ns_set can contain duplicate keys, these conversion may lose duplicates, as keys in dicts and arrays are unique.

 # Ouput HTTP request headers as string
 set headers [ns_conn headers]
 set output ""
  foreach {key value} [ns_set array $headers] {
    append output "$key = $value \n"
  }
  set output
ns_set cleanup

Deletes all sets in the current interpreter. See also the section Storage Management below.

ns_set copy setId

Returns a new set with the same name and contents as the specified setId. See also the section Storage Management below.

ns_set cput ?-nocase? ?--? setId key value

Conditionally appends a new field to the set with the specified key and value, only if the key does not already exist in the set. Returns the field number of the newly added field, or the field number of the existing field if it already exists.

ns_set create ?-nocase? ?--? ?name? ?key? ?value? ...

Creates a new set and returns its setId.

If a name is provided, it sets the name of the set. You can also initialize the set with key/value pairs by providing them as additional arguments.

If the -nocase option is used, the set becomes case-insensitive for all commands that accept a key parameter. Keys are stored in case-insensitive sets in lowercase and returned in lowercase.

 # Create a case-insensitive set with initial values
 set mySet [ns_set create -nocase mySetName a 1 B 2 c 3]
ns_set delete setId fieldNumber

Deletes the field in the set at the specified field number.

ns_set delkey ?-nocase? ?--? setId key

Removes the first occurrence of a field in the set with the specified key. Note that multiple fields with the same key may exist; this command only removes the first occurrence.

ns_set find ?-nocase? ?--? setId key

Returns the index of the first field in the specified setId with a matching key. If no matching field is found, returns -1.

ns_set format ?-noname? ?-lead lead? ?-separator separator? ?--? setId

Formats the specified set in a human-friendly way and returns the string as result. Option -noname will omit the name from the output. When lead is specified, every key in the output will be preceded by this lead string (default is ""). When separator is specified, every key in the output will be followed by this separator string (default is ": ").

 # Example of ns_set format
 % set mySet [ns_set create mySetName a 1 b 2 c 3]
 % ns_set format $mySet
 mySetName:
   a: 1
   b: 2
   c: 3
 
 # Using options
 % ns_set format -noname -lead "-> " -separator " = " $mySet
 -> a = 1
 -> b = 2
 -> c = 3
ns_set free setId

Frees (deletes) the specified set. Although sets are automatically freed at the end of the request (see section Storage Management), freeing sets manually can save temporary memory within loops with many iterations.

ns_set get setId ?-all? ?-nocase? ?--? key ?default?

Returns the first value associated with the specified key. If the key is not found, the default value or an empty string is returned. With the -all option, all values for the key are returned as a list.

 % set mySet [ns_set create mySetName a 1 b 2 c 3 a 4]
 # Get the first value for key 'a'
 % ns_set get $mySet a
 1
 
 # Get all values for key 'a' (if there are duplicates)
 % ns_set get -all $mySet a
 1 4
 
 # Case-insensitive get
 % ns_set get -nocase $mySet A
 1
ns_set isnull setId fieldNumber

Returns 1 if the field specified by fieldNumber is null, and 0 if it is not. Note that an empty string is distinct from null; this command returns 0 for an empty string.

ns_set key setId fieldNumber

Returns the key for the field at the specified fieldNumber.

ns_set keys setId ?pattern?

Returns a list of all keys in the specified setId. If a pattern is provided, only keys matching the pattern are returned, following Tcl's string match rules.

ns_set list

Returns a list of all ns_set instances in the current interpreter.

ns_set merge setId1 setId2

Merges setId2 into setId1. Fields from setId2 are appended to setId1 only if their keys do not already exist in setId1.

 # Example of ns_set merge
 # Create two sets
 % set set1 [ns_set create key1 value1 key2 value2]
 % set set2 [ns_set create key2 value2_new key3 value3]
 
 # Merge set2 into set1
 % ns_set merge $set1 $set2
 # Now set1 contains key1, key2 (original value), and key3
 % ns_set keys $set1
 key1 key2 key3
 
ns_set move setId1 setId2

Moves all fields from setId2 to the end of setId1, leaving setId2 as an empty but valid set.

 # Example of ns_set move
 # Create two sets
 % set set1 [ns_set create key1 value1]
 % set set2 [ns_set create key2 value2 key3 value3]
 
 # Move fields from set2 to set1
 % ns_set move $set1 $set2
 
 # Now set1 contains key1, key2, key3
 % ns_set keys $set1
 key1 key2 key3
 
 # set2 is empty
 % ns_set size $set2
 0
ns_set name setId

Returns the name of the set or an empty string if no name has been set.

ns_set put setId key value

Appends a new field with the specified key and value to the set and returns the field number of the new field. Use ns_set update if you want to update a field with a certain key.

ns_set size setId

Returns the number of fields in the set.

ns_set split setId ?splitChar?

Splits one set into multiple sets based on a specific character, splitChar used in the key, and returns a list of IDs for the newly created sets. Defaults to using a period (".") as the split character.

 # Create a set with keys containing periods
 % set s1 [ns_set create]
 % ns_set put $s1 dog.food "Yummy dog food!"
 % ns_set put $s1 cat.food "Yummy cat food!"
 
 % ns_set format $s1
 dog.food: Yummy dog food!
 cat.food: Yummy cat food!
 
 # Split the set into multiple (actually 2) sets based on '.' in the key
 % set newSets [ns_set split $s1]
 d2 d3
 
 # Show the result of the created new sets
 % set result ""
 % foreach s $newSets {
 %   append result [ns_set format $s]
 % }
 % set result
 dog:
   food: Yummy dog food!
 cat:
   food: Yummy cat food!
ns_set truncate setId fieldNumber

Removes all fields from fieldNumber onward.

ns_set unique ?-nocase? ?--? setId key

Returns 1 if the specified key is unique in the set and 0 if it is not.

 # Create a set with duplicate keys
 % set headers [ns_set create]
 % ns_set put $headers Accept "text/html"
 % ns_set put $headers Accept "application/xml"
 % ns_set put $headers Host "www.example.com"
 
 # Check if 'Accept' header is unique
 % ns_set unique $headers Accept
 0
 
 # Check if 'Host' header is unique
 % ns_set unique $headers Host
 1
ns_set update ?-nocase? ?--? setId key value

Updates the first occurrence of a field with the specified key, replacing its value with the new value. Equivalent to ns_set delkey followed by ns_set put.

ns_set value setId fieldNumber

Returns the value of the field at the specified fieldNumber.

ns_set values setId ?pattern?

Returns a list of all values in the set. If a pattern is specified, only values matching the pattern are returned.

OPTIONS

The option -nocase is valid for multiple commands. The options valid for just a single command are described above.

-nocase

When specified on a submethod accepting a key the lookup for the key will be case-insensitive, even if the underlying set is case-sensitive.

EXAMPLES

Basic operations on ns_sets:

 # Create an ns_set with several fields, named "mySetName"
 % set mySet [ns_set create mySetName a b c d e f A Joe B John C Jeff]
 % ns_set size $mySet
 6
 
 # Get the name of the ns_set
 % ns_set name $mySet
 mySetName
 
 # Get all keys and values of the ns_set
 % ns_set array $mySet
 a b c d e f A Joe B John C Jeff
 
 % ns_set keys $mySet
 a c e A B C
 
 % ns_set values $mySet
 b d f Joe John Jeff
 
 # Get the value of the field with key 'A'
 % ns_set get $mySet A
 Joe
 
 # Get the value of the field with key 'a'
 % ns_set get $mySet a
 b
 
 # Is the key 'a' unique in the ns_set?
 % ns_set unique $mySet a
 1
 
 # Is the key 'a' unique in the ns_set, when the case is ignored?
 % ns_set unique -nocase $mySet a
 0
 
 # Truncate the set after the third field
 % ns_set truncate $mySet 3
 % ns_set format $mySet
 mySetName:
   a: b
   c: d
   e: f
 
 # Update the value of the field with key "c"
 % ns_set update $mySet c "Hello World!"
 2
 % ns_set format $mySet
 mySetName:
   a: b
   c: Hello World!
   e: f
 
 # Lookup position of existing key
 % ns_set find $mySet c
 1
 
 # Lookup position of non-existing key
 % ns_set find $mySet nokey
 -1
 
 # Delete the field at position 0
 % ns_set delete $mySet 0
 % ns_set format $mySet
 mySetName:
   c: Hello World!
   e: f

Iterating over the fields of an ns_set:

 # Run through an ns_set based on key/value pairs
 foreach {key value} [ns_set array $myset]} {
   ns_log notice "key: $key, value: $value"
 }
 
 # Run through an ns_set based on indicies
 for {set i 0} {$i < [ns_set size $myset]} {incr i} {
   set key [ns_set key $myset $i]
   set value [ns_set value $myset $i]
   ns_log notice "key: $key, value: $value"
 }

Case Sensitivity

Sets can be defined to be case-sensitive (default) or case-insensitive. When a set is defined case-insensitive, all keys are converted internally to lowercase. Every lookup operation with an upper or mixed case key will then succeed when the key in lowercase exists.

Case-sensitive and case-insensitive sets can be created either from Tcl or from C. To create a case-insensitive set from Tcl, use the option -nocase for the ns_set create command. When a case-insensitive set is duplicated via ns_set copy or ns_set split, the resulting set is also case-insensitive. The sets used for HTTP request headers and configuration values are created automatically from C as case-insensitive.

Even, when a set is defined as case-sensitive, all subcommands of ns_set receiving a key as input include the option -nocase, to make a single lookup case-insensitive.

Legacy: previous versions of NaviServer have used special names for the subcommands where the keys should be treated case-insensitive. These subcommand names were prefixed with the character "i" and were named "icput", "idelkey", "ifind", "iget", "imerge", "iunique", and "iupdate". These names are still supported for compatibility, but it is recommended to use the option -nocase instead.

Storage Management

Sets created via the Tcl interface using ns_set and the subcommands create, copy, and split are automatically deleted after each request, or when ns_set cleanup is executed. The cleanup is performed automatically after every request by the command ns_cleanup.

It is also possible to delete a specified set with the command ns_set free.

NOTES

NULL values, distinct from empty strings, are useful when representing a database row as an ns_set. Currently, obtaining a NULL value in an ns_set through the Tcl API can be challenging. NULL values may occur in sets created by ns_parsequery or by omitting the last value when seeding an ns_set in the create subcommand, among other scenarios. NULL-valued fields can also be created through the C API.

ns_set is intended for relatively small amounts of data. Since keys are found by linear search, loading very large datasets into an ns_set may result in poor performance. In such cases, consider using a Tcl array or dict structure instead.

See Also

ns_cleanup, ns_conn, ns_findset, ns_ictl, ns_parsequery

Keywords

bag, data structure, global built-in, multimap, multiset, ns_set