Reusable code that treats TWiki forms as if they were table rows in a database.
<--
PLEASE DO NOT EDIT THIS TOPIC
It is automatically generated from the subversion repository, and any
changes you make will simply be overwritten the next time a release is
generated.
Instead, you could check your fix in, raise a bug in the Bugs web, or
mail the author.
-->
WARNING: TWiki-4 only. If you want to use this extension with an earlier version of TWiki, please see an earlier version of this topic
Summary of Contents
This module supports structured queries over a database built on the fly from the forms in TWiki topics. It does not support any tags, as it is provided as a service for other plugins that want to treat a TWiki web as a simple database; for example, the TWiki:Plugins/FormQueryPlugin, which supports the display of query results.
The plugin encapsulates code that was formerly in the "engine room" of the FormQueryPlugin?. It has been abstracted out in the belief that it will be useful to anyone who wants to do simple search operations from a plugin.
Features
Perform complex queries on the TWiki database
Cache TWiki topics for rapid queries
How the database gets built
You can think of the database as an array of all the topics in a web. Each array entry is a map (or hash in perl terms) that maps a set of field names to values.
Each topic in the web automatically gets a number of standard fields, generated by reading the metadata from the topic (see TWikiMetaData)
name - name of the topic
parent - name of parent topic
_up - reference to the Map of the parent topic, if it exists
attachments - array of Maps, each of which contains:
_up - reference to the Map for the topic
name - attachment name
attr - e.g hidden
comment - attachment comment
path - client path used to upload attachment
size - size in Kb
user - who uploaded the attachment
version - e.g. 1.3
info - Map containing:
_up - reference to the Map for the topic
author - most recent author
date - date of last change
format - topic format version
version - topic version number
moved - Map containing:
_up - reference to the Map for the topic
by - who moved it
date - when they moved it
from - where they moved it from
to - where they moved it to
form - form type
form name - e.g. if a "MyForm" is attached, this will be MyForm. This is a reference to a Map containing a key for each field in the form. Each key maps to the value in the form data for that key. The Map will also have an _up reference to the Map for the topic.
text - raw text of the topic)
The sub-Maps created for info, form name, moved, and each row in attachments also have a reference back to the topic Map, called _up.
Other fields may be added by subclasses. Refer to the documentation for the plugin that is using the DBCache for more details.
The cache
To achieve best perfomance the plugin caches the database read from the TWiki topics. It creates this cache in a file in the web, _DBCache. If any topic changes in the web, this cache is automatically rebuilt. The cache file can be deleted at any point with no ill effects.
Extending or customising
Extension or customisation is welcome, as long as all extensions are described and code provided back to the author.
The module is shipped with a perl build file, which should be used for installation and testing. Testing is done using CPAN:Test::Unit, and is invoked using the 'test' build target. Writing tests is a useful way of feeding back bugs as well. I can't encourage you enough to maintain and extend the tests!
Detailed Documentation
Clients use the DBCache by defining a subclass of the TWiki::Contrib::DBCache class. The following POD documentation describes the methods of this class and the various other classes provided by the plugin..
General purpose cache that treats TWiki topics as hashes. Useful for
rapid read and search of the database. Only works on one web.
Typical usage:
use TWiki::Contrib::DBCacheContrib;
$db = new TWiki::Contrib::DBCacheContrib( $web ); # always done
$db->load(); # may be always done, or only on demand when a tag is parsed that needs it
# the DB is a hash of topics keyed on their name
foreach my $topic ($db->getKeys()) {
my $attachments = $db->get($topic)->get("attachments");
# attachments is an array
foreach my $val ($attachments->getValues()) {
my $aname = $attachments->get("name");
my $acomment = $attachments->get("comment");
my $adate = $attachments->get("date");
...
}
}
As topics are loaded, the readTopicLine method gives subclasses an opportunity to apply special processing to indivual lines, for example to extract special syntax such as %ACTION lines, or embedded tables in the text. See FormQueryPlugin? for an example of this.
new($dataDir, $web)
$dataDir location of cache file
$web name of web to create the object for.
Construct a new DBCache object.
readTopicLine($topic, $meta, $line, $fh) --> text
$topic - name of the topic being read
$meta - reference to the hash object for this topic
line - the line being read
$fh - the file handle of the file
return text to insert in place of line in the text field of the topic
Called when reading a topic that is being cached, this method is invoked on each line
in the topic. It is designed to be overridden by subclasses; the default implementation
does nothing. The sort of expected activities will be (for example) reading tables and
adding them to the hash for the topic.
onReload($topics)
$topics - perl array of topic names that have just been loaded (or reloaded)
Designed to be overridden by subclasses. Called when one or more topics had to be
read from disc rather than from the cache. Passed a list of topic names that have been read.
load()
Load the web into the database.
Returns a string containing 3 numbers that give the number of topics
read from the cache, the number read from file, and the number of previously
cached topics that have been removed.
write($archive)
$archive - the TWiki::Contrib::DBCacheContrib::Archive being written to
Writes this object to the archive. Archives are used only if Storable is not available. This
method must be overridden by subclasses is serialisation of their data fields is required.
read($archive)
$archive - the TWiki::Contrib::DBCacheContrib::Archive being read from
Reads this object from the archive. Archives are used only if Storable is not available. This
method must be overridden by subclasses is serialisation of their data fields is required.
class Search
Search operators work on the fields of a TWiki::Contrib::DBCacheContrib::Map. The fields are given by name, and the values by strings or numbers. Strings should always be surrounded by 'single-quotes'. Strings which are regular expressions (RHS of , ~ operators) use 'perl' re syntax (see =man perlre for help). Numbers can be signed integers or decimals.
Warning single and double quotes are not allowed in values!
The following operators are available:
Operator
$Result*
Meaning
=
Boolean
LHS exactly matches the regular expression on the RHS. The expression must match the whole string.
!=
Boolean
Inverse of =
=~
Boolean
LHS contains RHS i.e. the RHS is found somewhere in the field value.
<
Boolean
Numeric <
>
Boolean
Numeric >
>=
Boolean
Numeric >=
<=
Boolean
Numeric <=
lc
String
Unary lower case
uc
String
unary UPPER CASE
EARLIER_THAN
BOOLEAN
Date is earlier than the given date
LATER_THAN
Boolean
LHS is later than the given date (string containing a date e.g. '1 Apr 2003')
WITHIN_DAYS
Boolean
Date (which must be in the future) is within n working days of todays date
!
Boolean
Unary NOT
AND
Boolean
AND
OR
Boolean
OR
()
any
Bracketed subexpression
Dates for EARLIER_THAN, LATER_THAN and WITHIN_DAYS must be dates in the format expected by Time::ParseDate (like the ActionTrackerPlugin?). WITHIN_DAYS works out the number of working days (i.e. excluding Saturday and Sunday). Apologies in advance if your weekend is offset ± a day!
A search object implements the "matches" method as its general
contract with the rest of the world.
Example
Get a list of attachments that have a date earlier than 1st January 2000
$db = new TWiki::Contrib::DBCacheContrib::DBCache( $web ); # always done
$db->load();
my $search = new TWiki::Contrib::DBCacheContrib::Search("date EARLIER_THAN '1st January 2000'");
foreach my $topic ($db->getKeys()) {
my $attachments = $topic->get("attachments");
foreach my $val ($attachments->getValues()) {
if ($search->matches($val)) {
print $val->get("name") . "\n";
}
}
}
new($string)
$string - string containing an expression to parse
Construct a new search node by parsing the passed expression.
matches($object) -> boolean
$object - object to test; must implement get
See if object matches the search. $object can actually be any object that provides
the method "get" that returns a value given a string key.
Object that handles a file/time tuple for use in Storable and
TWiki::Contrib::DBCacheContrib::Archive.
new($file)
$file - filename
Construct from a file name
uptodate() -< boolean
Check the file time against what is seen on disc. Return 1 if consistent, 0 if inconsistent.
toString() -> string
Generates a string representation of the object.
write()
TWiki::Contrib::DBCacheContrib::Archive hook
read()
TWiki::Contrib::DBCacheContrib::Archive hook
class Array
Generic array object. This is required because perl arrays are not objects, and
cannot be subclassed e.g. for serialisation. To avoid lots of horrid code to handle
special cases of the different perl data structures, we use this array object instead.
new()
Create a new, empty array object
add($object)
$object any perl data type
Add an element to the end of the array
find($object) -> integer
$object datum of the same type as the content of the array
Uses "==" to find the given element in the array and return it's index
remove($index)
$index - integer index
Remove an entry at an index from the array.
get($key, $root) -> datum
$k - key
$root - what # refers to
Subfield syntax
get("9", $r) where $n is a number will get the 9th entry in the array
get("[9]", $r) will also get the 9th entry
get(".9", $r) will also get the 9th entry
get(".X", $r) will return the sum of the subfield X of each entry
get("[?search]", $r) will perform the given search over the entries in the array. Always returns an array result, even when there is only one result. For example: [?name='Sam'] will return an array of all the entries that have their subfield name set to Sam.
# means "reset to root". So get("#[3]", $r) will return the 4th entry of $r (assuming $r is an array!).
get("[*X]", $r) will get a new array made from subfield X of each entry in this array.
Where the result of a subfield expansion is another object (a Map or an Array) then further subfield expansions can be used. For example,
See also TWiki::Contrib::DBCacheContrib::Map for syntax that applies to maps.
size() -> integer
Get the size of the array
sum($field) -> number
$field - name of a field in the class of objects stored by this array
Returns the sum of values of the given field in the objects stored in this array.
search($search) -> search result
$search - TWiki::Contrib::DBCacheContrib::Search object to use in the search
Search the array for matches with the given object.
values. Return a TWiki::Contrib::DBCacheContrib::Array of matching entries.
getValues() -> perl array
Get a "perl" array of the values in the array, suitable for use with foreach
toString($limit, $level, $strung) -> string
$limit - recursion limit for expansion of elements
$level - currentl recursion level
Generates an HTML string representation of the object.
write($archive)
$archive - the TWiki::Contrib::DBCacheContrib::Archive being written to
Writes this object to the archive. Archives are used only if Storable is not available. This
method must be overridden by subclasses is serialisation of their data fields is required.
read($archive)
$archive - the TWiki::Contrib::DBCacheContrib::Archive being read from
Reads this object from the archive. Archives are used only if Storable is not available. This
method must be overridden by subclasses is serialisation of their data fields is required.
class Map
Generic map object for mapping names to things. A name is defined as
name = \w+ | \w+ "." name
The . indicates a field reference in a sub-map.
Objects in the map are either strings, or other objects that must
support toString.
new($string)
$string - optional attribute string in standard TWiki syntax
Create a new, empty array object. Optionally parse a standard attribute
string containing name=value pairs. The
value may be a word or a quoted string (no escapes!)
fastget($k) -> datum
$k - key
Get the value for a key, but without any subfield field expansion
get($k, $root) -> datum
$k - key
$root what # refers to
Get the value corresponding to key $k; return undef if not set.
Subfield syntax
get("X",$r) will get the subfield named X.
get("X.Y",$r) will get the subfield Y of the subfield named X.
get("[X]",$r) = will get the subfield named =X (so X[Y] and X.Y are synonymous)..
# means "reset to root". So get("#.Y", $r) will return the subfield =Y of $r (assuming $r is a map!), as will get("#[Y]".
Where the result of a subfield expansion is another object (a Map or an Array) then further subfield expansions can be used. For example,
get("UserTable[0].Surname", $web);
See also TWiki::Contrib::DBCacheContrib::Array for syntax that applies to arrays.
set($k, $v)
$k - key
$v - value
Set the given key, value pair in the map.
size() -> integer
Get the size of the map
remove($index) -> old value
$index - integer index
Remove an entry at an index from the array. Return the old value.
getKeys() -> perl array
Get a "perl" array of the keys in the map, suitable for use with foreach
getValues() -> perl array
Get a "perl" array of the values in the Map, suitable for use with foreach
search($search) -> search result
$search - TWiki::Contrib::DBCacheContrib::Search object to use in the search
Search the map for keys that match with the given object.
values. Return a TWiki::Contrib::DBCacheContrib::Array of matching keys.
toString($limit, $level, $strung) -> string
$limit - recursion limit for expansion of elements
$level - currentl recursion level
Generates an HTML string representation of the object.
write($archive)
$archive - the TWiki::Contrib::DBCacheContrib::Archive being written to
Writes this object to the archive. Archives are used only if Storable is not available. This
method must be overridden by subclasses is serialisation of their data fields is required.
read($archive)
$archive - the TWiki::Contrib::DBCacheContrib::Archive being read from
Reads this object from the archive. Archives are used only if Storable is not available. This
method must be overridden by subclasses is serialisation of their data fields is required.
class Archive
Simple file archive storer and restorer. Handles serialising objects
using their "write" and "read" methods. Serialisable objects must
have a no-parameters constructor.
This module is only used if Storable isn't available. Storable is
much faster, because it is implemented in C.
new($file, $rw)
$file - archive file path
$rw - mode "r" or "w"
Create a new archive, using filename $file and
mode $rw which must be "r" or "w". The archive will remain
in existence (and the file remain open) until "close" is
called. An exclusive lock is taken for write as long as the file
is open. Throws an exception if the archive cannot be opened.
close()
Close this archive. MUST be called to
close the file.
writeByte($b)
$b - byte to write
Write a byte to the archive
writeString($s)
$s - string to write
Write a string to the archive
writeInt($i)
$i integer to write
Write a 32-bit integer to the archive
writeObject()
Write an object to the archive. An object must implement
read($archive) and write($archive), or may be undef or a string. No other types
are supported.
readByte() -> byte
Read a byte from the archive
readString() -> string
Read a UTF8 string from the archive
readInt() -> integer
Read a 32-bit integer from the archive
readObject() -> object
Read an object from the archive
<--
Set STUB = TWiki::Contrib::DBCacheContrib
Set SHORTDESCRIPTION = Reusable code that treats TWiki forms as if they were table rows in a database
-->
Installation Instructions
You do not need to install anything in the browser to use this extension. The following instructions are for the administrator who installs the extension on the server where TWiki is running.
Like many other TWiki extensions, this module is shipped with a fully automatic installer script written using the BuildContrib.
If you have TWiki 4.1 or later, and Perl 5.8, you can install from the configure interface (Go to Plugins->Find More Extensions)
If you have a permanent connection to the internet (and Perl 5.8), you are recommended to use the automatic installer script
Just download the DBCacheContrib_installer perl script and run it.
If the $TWIKI_PACKAGES environment variable is set to point to a directory, the installer will try to get archives from there. Otherwise it will try to download from twiki.org or cpan.org, as appropriate.
(Developers only: the script will look for twikiplugins/DBCacheContrib/DBCacheContrib.tgz before downloading from TWiki.org)
If you don't have a permanent connection, you can still use the automatic installer, by downloading all required TWiki archives to a local directory.
Point the environment variable $TWIKI_PACKAGES to this directory, and the installer script will look there first for required TWiki packages.
$TWIKI_PACKAGES is actually a path; you can list several directories separated by :
As required for the publication of all extensions to TWiki, this software is published under the terms of the GNU General Public License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details, published at http://www.gnu.org/copyleft/gpl.html
Version:
11537
Change History:
11537
Added lc and uc operators for case-insensitive searches