In the Caché object DBMS, bitmap and bitslice indexes are supported. It is very easy to use them in Caché classes: it is enough to indicate
in the description of the index, and the performance of some SQL queries is improved dramatically. But how does this work?
This article describes how bitmap indexes are organized, how to create a bitmap index on an arbitrary global structure, how to use bit logic functions and how to use them effectively when working with NoSQL in Caché.
Since the very beginning of the time, when developing applications to quickly find interesting records in globals, type indices were most often built
Such indexes allow you to easily and quickly find records for which the property of interest has a specified value:
will give the identifiers of all records that have the value of the property “Property” equal to value, and you can check for the existence of records with this property value, for example, using this code:
It is easy to come up with an algorithm for the effective search of records that satisfy several conditions at once, combined by the logic of "AND". But for more complex queries, it was necessary either to develop (and to debug each time) a more complex algorithm, or to abandon the use of indices in favor of directly iterating over all records in the database.
(). Now you can build bitmap-indices, in the simplest case - of the form
Of course, since the length of the bit string is limited to 256K, in real-world applications, the bitmap index has to be placed not in a single bit string, but in an array.
The $ bitlogic () functions make it easy to create a resultant bitmap index corresponding to a sample of records that satisfy any arbitrary query, $ bitcount () determines the number of records included in the sample, and $ bitfind () provides easy navigation through received sample.
For example, to collect records for which the property “Pr1” has the values “A” or “B”, and the property “Pr2” - the value “C”:
This is not all that can be squeezed out of the bit functions in Caché. To calculate the values of the grouping function (sum, arithmetic average, minimum, maximum), it was still necessary to cycle through all the records in the sample, which, with a sufficient sample size, was not fast. But a small modification of the structure of bitmap-indices significantly speeds up this procedure, if the property values are integer or with a fixed decimal point.
The essence of this modification is the construction of separate bitmap-indices for each bit of the property value. Such constructions are called bit-slices:
In reality, again, instead of a bit string, an array is used.
When using bitslices, it is somewhat more difficult, for example, to search for “more” or “less” restrictions, but the gain in calculating grouping functions outweighs this inconvenience. In fact, to calculate the sum of the values of the property of interest for the selected records, the cycle of records is no longer needed. It is enough to sum up with the corresponding weights $ bitcount () in the resulting bitmap-index for all bits of the value of the property of interest. With a sufficient sample size, the performance gain can be called amazing without exaggeration.
Class code/// A set of methods for creating and working with bitmap and bit-slice indexes
///
/// bitmap index structure
/// array of the form array (value) whose elements are
/// bit strings with 1 in positions corresponding to record numbers,
/// for which some parameter takes the value value,
/// split into an array of strings with a maximum length $$$ MAXBITLENGTH
/// numbering of records - with 1, array elements - with 0,
/// (further, for brevity - bit arrays).
/// example of filling
/// let the record number idfact par parameter is val
/// set idf0 = idfact-1 \ $$$ MAXBITLENGTH, idf1 = idfact-1 # $$$ MAXBITLENGTH + 1
/// set $ bit (^ BM ("I", par, val, idf0), idf1) = 1
/// set $ bit (^ BM ("EXIST", idf0), idf1) = 1
/// index of existing records is useful, first, because
/// makes it easy to perform logical deletion, and secondly,
/// allows you to find the number of records in the repository without
/// calls to other structures, which is necessary for correct
/// work procedure BMNot (see below)
/// structure of the bayslay-index:
/// array of bitmap indexes. 1st array element - character index
/// of values, the 2nd is the index of the least significant bit of the values (1/0), the 3rd is
/// index of the last but one bit (2/0), etc.
///
/// example of filling
/// let the record number idfact parameter par has an integer
/// value of val
/// set idf0 = idfact-1 \ $$$ MAXBITLENGTH, idf1 = idfact-1 # $$$ MAXBITLENGTH + 1
/// set val = $$ CvtToBin (val)
/// for ix = 1: 1: $ length (val) set: $ extract (val, ix) $ bit (^ BM ("S", par, ix, idf0), idf1) = 1
/// when mass filling, in order to avoid a catastrophic fall
/// performance, it is recommended to impose the corresponding fragment
/// code functions $ sortbegin (^ BM) and $ sortend (^ BM)
/// for sampling the facts according to the given constraints and calculations for the obtained
/// samples of grouping functions "sum", "maximum" and "minimum"
/// are intended functions BSSum (), BSMax () and BSMin ()
Class User.BitMapSlice Extends% RegisteredObject [ ProcedureBlock ]
{
/// Name ($ na) of bitmaps and bits
/// Default value is "^ BM"
Property BMGLOB As% String ( TRUNCATE = 1 ) [ InitialExpression = "^ BM" ];
/// Maximal length of $ bit string to use
Parameter MAXBITLENGTH = 64000 ;
/// Creates and returns the name ($ na ()) of the temporary global subnode
/// to store intermediate bitmaps
ClassMethod GetNewTMP () As% String
{
quit $ name (^ CacheTemp ( "BM" , $ job _ $ zutil (110), $ increment (^ CacheTemp ( "BM" , $ job _ $ zutil (110)))))
}
/// Removes all temporary subnodes created by this process.
ClassMethod KillAllTMP ()
{
kill ^ CacheTemp ( "BM" , $ job _ $ zutil (110)) quit
}
/// Operations on bitmaps
/// here and hereinafter, in order to be flexible, bit indices,
/// arrays and slices are passed by name ($ na ())
///
/// finds the position next to pos with a single bit
/// in bm - transmitted by the name of the bitmap
Method BMOrder ( bm As% String , pos As% String ) As% String
{
set sub = pos \ .. #MAXBITLENGTH , ix = $ bitfind ( $ get (@ bm @ ( sub )), 1, pos # .. #MAXBITLENGTH +1)
quit : ix sub * .. #MAXBITLENGTH + ix
for set sub = $ order (@ bm @ ( sub )) quit : ' sub set ix = $ bitfind ( $ get (@ bm @ ( sub )), 1) quit : ix
quit : ix sub * .. #MAXBITLENGTH + ix
quit ""
}
/// bmdest = bmdest & bmsrc
Method BMAnd ( bmdest As% String , bmsrc As% String )
{
set sub1 = $ order (@ bmdest @ ( $ char (0)), - 1), sub = $ order (@ bmsrc @ ( $ char (0)), - 1)
set : sub < sub1 sub = sub1
for ix = 0: 1: sub set : $ data (@ bmdest @ ( ix )) & $ data (@ bmsrc @ ( ix )) @ bmdest @ ( ix ) = $ bitlogic (@ bmdest @ ( ix ) & @ bmsrc @ ( ix )) kill : ' $ data (@ bmsrc @ ( ix )) @ bmdest @ ( ix )
quit
}
/// bmdest = bmdest | bmsrc
Method BMOr ( bmdest As% String , bmsrc As% String )
{
set sub1 = $ order (@ bmdest @ ( $ char (0)), - 1), sub = $ order (@ bmsrc @ ( $ char (0)), - 1)
set : sub < sub1 sub = sub1
for ix = 0: 1: sub set : $ data (@ bmsrc @ ( ix )) @ bmdest @ ( ix ) = $ select ( $ data (@ bmdest @ ( ix )): $ bitlogic (@ bmdest @ ( ix ) | @ bmsrc @ ( ix )), 1: @ bmsrc @ ( ix ))
quit
}
Method BMNot ( bm As% String )
{
set maxblk = $ order (@ (.. BMGLOB ) @ ( "EXIST" , "" ), - 1), blklen = $ bitcount (@ (.. BMGLOB ) @ ( "EXIST" , maxblk ))
for ix = maxblk : -1: 0 set blk = $ get (@ bm @ ( ix )) set : $ bitcount ( blk ) < blklen $ bit ( blk , blklen ) = 0 set @ bm @ ( ix ) = $ bitlogic (~ blk ), blklen = .. #MAXBITLENGTH
do .. BMAnd ( bm , $ name (@ (.. BMGLOB ) @ ( "EXIST" )))
quit
}
/// returns the number of single bits in the array
Method BMCount ( bm As% String ) As% Integer
{
set ix = "" , bmcret = 0
for set ix = $ order (@ bm @ ( ix )) quit : ix '= + ix set bmcret = bmcret + $ bitcount (@ bm @ ( ix ), 1)
quit bmcret
}
/// places in the bmdest bitmap a sample of the vbmsrc bitmap index,
/// where the value of the parameter is val
///
Method BMEq ( bmdest As% String , vbmsrc As% String , val As% String )
{
kill @ bmdest merge @ bmdest = @ vbmsrc @ ( val )
quit
}
/// places in the bmdest bitmap a sample of the vbmsrc bitmap index,
/// where the parameter value is not equal to val
Method BMNe ( bmdest As% String , vbmsrc As% String , val As% String )
{
do .. BMEq ( bmdest , vbmsrc , val ) do .. BMNot ( bmdest )
quit
}
/// places in the bmdest bitmap a sample of the vbmsrc bitmap index,
/// where the parameter value is less (sorted to) val
/// bmdest: = U (vbmsrc (v): v <val)
Method BMLt ( bmdest As% String , vbmsrc As% String , val As% String )
{
kill @ bmdest set ix = val
for set ix = $ order (@ vbmsrc @ ( ix ), - 1) quit : ix = "" do .. BMOr ( bmdest , $ name (@ vbmsrc @ ( ix )))
quit
}
/// similar to BMLt () but "less than or equal to"
Method BMLe ( bmdest As% String , vbmsrc As% String , value As% String )
{
kill @ bmdest merge @ bmdest = @ vbmsrc @ ( val ) set ix = val
for set ix = $ order (@ vbmsrc @ ( ix ), - 1) quit : ix = "" do .. BMOr ( bmdest , $ name (@ vbmsrc @ ( ix )))
quit
}
/// similar to BMLe, but "greater or equal"
Method BMGe ( bmdest As% String , vbmsrc As% String , val As% String )
{
kill @ bmdest merge @ bmdest = @ vbmsrc @ ( val ) set ix = val
for set ix = $ order (@ vbmsrc @ ( ix )) quit : ix = "" do .. BMOr ( bmdest , $ name (@ vbmsrc @ ( ix )))
quit
}
/// similarly, the value is greater than or equal to min and less than or equal to max
Method BMGeLe ( bmdest As% String , vbmsrc As% String , min As% String , max As% String )
{
kill @ bmdest merge @ bmdest = @ vbmsrc @ ( min ) set ix = min
for set ix = $ order (@ vbmsrc @ ( ix )) quit : ix ]] max quit : ix = "" do .. BMOr ( bmdest , $ name (@ vbmsrc @ ( ix )))
quit
}
/// similar to BMGe (), but "strictly more"
Method BMGt ( bmdest As% String , vbmsrc As% String , val As% String )
{
kill @ bmdest set ix = val
for set ix = $ order (@ vbmsrc @ ( ix )) quit : ix = "" do .. BMOr ( bmdest , $ name (@ vbmsrc @ ( ix )))
quit
}
/// similarly, values greater than min and less than max
Method BMGtLt ( bmdest As% String , vbmsrc As% String , min As% String , max As% String )
{
kill @ bmdest set ix = min
for set ix = $ order (@ vbmsrc @ ( ix )) quit : max ']] ix quit : ix = "" do .. BMOr ( bmdest , $ name (@ vbmsrc @ ( ix )))
quit
}
/// similarly, the value is greater than or equal to min and less than max
Method BMGeLt ( bmdest As% String , vbmsrc As% String , min As% String , max As% String )
{
kill @ bmdest merge @ bmdest = @ vbmsrc @ ( min ) set ix = min
for set ix = $ order (@ vbmsrc @ ( ix )) quit : max ']] ix quit : ix = "" do .. BMOr ( bmdest , $ name (@ vbmsrc @ ( ix )))
quit
}
/// similarly, values are greater than min and less than or equal to max
Method BMGtLe ( bmdest As% String , vbmsrc As% String , min As% String , max As% String )
{
kill @ bmdest set ix = min
for set ix = $ order (@ vbmsrc @ ( ix )) quit : ix ]] max quit : ix = "" do .. BMOr ( bmdest , $ name (@ vbmsrc @ ( ix )))
quit
}
/// Bit-Sliced Data Operations
/// converts an integer value to a text bit string
/// {sign, 1,2,4, ..., 2 ** N}
ClassMethod CvtToBin ( value As% Integer ) As% String
{
set value = $ fnumber (+ value , "" , 0), res = ( value <0)
for quit : ' value set res = res _ ( value # 2), value = value \ 2
quit res
}
/// converts the bit list $ lb (sign, 1,2,4, ... 2 ** N) into an integer
ClassMethod CvtFromSlice ( slice As% String ) As% Integer
{
set res = 0
for i = $ listlength ( slice ): - 1: 2 set res = res + res set res = res + $ listget ( slice , i , 0)
quit $ select ( $ listget ( slice ): - res , 1: res )
}
/// puts the bits from the pos position of the slbs vbs to the list ($ lb ())
Method GetSlice ( vbs As% String , pos As% Integer ) As% String
{
set sub = pos \ .. #MAXBITLENGTH , ix = pos -1 # .. #MAXBITLENGTH +1
for i = 1: 1: $ order (@ vbs @ ( "" ), - 1) set $ list ( slice , i ) = $ bit ( $ get (@ vbs @ ( i , sub )), ix )
quit slice
}
/// places in the bmdest bitmap a sample of the vbs bestslay index,
/// where the value of the parameter is val
Method BSEq ( bmdest As% String , vbs As% String , val As% Integer )
{
set bswork = .. GetNewTMP () set vbit = .. CvtToBin ( val )
kill @ bmdest merge @ bmdest = @ (.. BMGLOB ) @ ( "EXIST" )
set maxbit = $ order (@ vbs @ ( "" ), - 1) set : maxbit < $ length ( vbit ) maxbit = $ length ( vbit )
for ix = 1: 1: maxbit kill @ bswork merge @ bswork = @ vbs @ ( ix ) do : ' $ extract ( vbit , ix ) .. BMNot ( bswork ) do .. BMAnd ( bmdest , bswork )
kill @ bswork quit
}
/// places in the bmdest bitmap a sample of the vbs bestslay index,
/// where the parameter value is greater than or equal to val
Method BSGe ( bmdest As% String , vbs As% String , val As% Integer )
{
do .. BSLt ( bmdest , vbs , val ), .. BMNot ( bmdest ) quit
}
/// places in the bmdest bitmap a sample of the vbs bestslay index,
/// where the parameter value is not equal to val
Method BSNe ( bmdest As% String , vbs As% String , val As% Integer )
{
do .. BSEq ( bmdest , vbs , val ), .. BMNot ( bmdest )
quit
}
/// places in the bmdest bitmap a sample of the vbs bestslay index,
/// where the value of the parameter is greater than val
///
Method BSGt ( bmdest As% String , vbs As% String , val As% Integer )
{
do .. BSLe ( bmdest , vbs , val ), .. BMNot ( bmdest ) quit
}
/// places in the bmdest bitmap a sample of the vbs bestslay index,
/// where the parameter sign is equal to sign (zero is considered positive)
Method BSSign ( bmdest As% String , vbs As% String , sign As% Integer )
{
set bswork = .. GetNewTMP () kill @ bmdest
merge @ bmdest = @ (.. BMGLOB ) @ ( "EXIST" ), @ bswork = @ vbs @ (1)
do : $ get ( sign ) '<0 .. BMNot ( bswork ) do .. BMAnd ( bmdest , bswork )
kill @ bswork quit
}
/// places in the bmdest bitmap a sample of the vbs bestslay index,
/// where the parameter value is less than or equal to val
Method BSLe ( bmdest As% String , vbs As% String , val As% Integer )
{
set tmpLe = .. GetNewTMP ()
do .. BSLtAbs ( bmdest , vbs , val ), .. BSSign ( tmpLe , vbs , -1)
if val '<0 do .. BMOr ( bmdest , tmpLe ), .. BSEq ( tmpLe , vbs , val ), .. BMOr ( bmdest , tmpLe ) if 1
else do .. BMNot ( bmdest ), .. BMAnd ( bmdest , tmpLe )
kill @ tmpLe quit
}
/// places in the bmdest bitmap a sample of the vbs bestslay index,
/// where the parameter value is less than val
Method BSLt ( bmdest As% String , vbs As% String , val As% Integer )
{
set tmpLt = .. GetNewTMP ()
do .. BSLtAbs ( bmdest , vbs , val ), .. BSSign ( tmpLt , vbs , -1)
if val '<0 do .. BMOr ( bmdest , tmpLt ) if 1
ELSE do .. BMNot ( bmdest ), .. BMAnd ( bmdest , tmpLt ), .. BSNe ( tmpLt , vbs , val ), .. BMAnd ( bmdest , tmpLt )
kill @ tmpLt quit
}
/// places in the bmdest bitmap a sample of the vbs bestslay index,
/// where the parameter value is greater than or equal to val1 and less than or equal to val2
Method BSGeLe ( bmdest As% String , vbs As% String , val1 As% Integer , val2 As% Integer )
{
set tmpGeLe = .. GetNewTMP ()
do .. BSGe ( bmdest , vbs , val1 ), .. BSLe ( tmpGeLe , vbs , val2 ), .. BMAnd ( bmdest , tmpGeLe )
kill @ tmpGeLe quit
}
/// places in the bmdest bitmap a sample of the vbs bit-slice index,
/// where the parameter value is greater than or equal to val1 and less than val2
Method BSGeLt ( bmdest As% String , vbs As% String , val1 As% Integer , val2 As% Integer )
{
set tmpGeLt = .. GetNewTMP ()
do .. BSGe ( bmdest , vbs , val1 ), .. BSLt ( tmpGeLt , vbs , val2 ), .. BMAnd ( bmdest , tmpGeLt )
kill @ tmpGeLt quit
}
/// places in the bmdest bitmap a sample of the vbs bestslay index,
/// where the parameter value is greater than val1 and less than or equal to val2
Method BSGtLe ( bmdest As% String , vbs As% String , val1 As% Integer , val2 As% Integer )
{
set tmpGtLe = .. GetNewTMP ()
do .. BSGt ( bmdest , vbs , val1 ), .. BSLe ( tmpGtLe , vbs , val2 ), .. BMAnd ( bmdest , tmpGtLe )
kill @ tmpGtLe quit
}
/// places in the bmdest bitmap a sample of the vbs bit-slice index,
/// where the parameter value is greater than val1 and less than val2
Method BSGtLt ( bmdest As% String , vbs As% String , val1 As% Integer , val2 As% Integer )
{
set tmpGtLt = .. GetNewTMP ()
do .. BSGt ( bmdest , vbs , val1 ), .. BSLt ( tmpGtLt , vbs , val2 ), .. BMAnd ( bmdest , tmpGtLt )
kill @ tmpGtLt quit
}
/// places in the bmdest bitmap a sample of the vbs bit-slice index,
/// where the value of the parameter in absolute value is less than val
Method BSLtAbs ( bmdest As% String , vbs As% String , val As% Integer )
{
set bswork = .. GetNewTMP (), test = .. GetNewTMP ()
kill @ bmdest set vbit = .. CvtToBin ( val ), ixmax = $ order (@ vbs @ ( "" ), - 1)
kill @ test merge @ test = @ (.. BMGLOB ) @ ( "EXIST" )
if ixmax < $ length ( vbit ) {
merge @ bmdest = @ test
} else {
for ix = ixmax : -1: 2 {
kill @ bswork merge @ bswork = @ vbs @ ( ix )
do .. BMNot ( bswork ), .. BMAnd ( bswork , test )
do : $ extract ( vbit , ix ) .. BMOr ( bmdest , bswork ), .. BMNot ( bswork )
do .. BMAnd ( test , bswork )
}
}
kill @ test @ bswork quit
}
/// grouping functions for bit-slips
/// returns the maximum value (with the optional parameter
/// bsmin '= 0 - minimum) for sampling a bitmap from the vbs bit-time-index
Method BSMax ( vbs As% String , bitmap As% String , bsmin As% String ) As% Integer
{
set bsmin = '' $ get ( bsmin ), bswork = .. GetNewTMP ()
set resBSM = .. GetNewTMP (), tmpBSM = .. GetNewTMP ()
merge @ resBSM = @ vbs @ (1) do : ' bsmin .. BMNot ( resBSM ) do .. BMAnd ( resBSM , bitmap )
if .. BMCount ( resBSM ) set min = 0
ELSE set min = 1 kill @ resBSM merge @ resBSM = @ vbs @ (1) do : bsmin .. BMNot ( resBSM ) do .. BMAnd ( resBSM , bitmap )
for ix = $ order (@ vbs @ ( "" ), - 1): - 1: 2 {
kill @ tmpBSM , @ bswork merge @ tmpBSM = @ resBSM , @ bswork = @ vbs @ ( ix )
do : min .. BMNot ( bswork ) do .. BMAnd ( tmpBSM , bswork )
if .. BMCount ( tmpBSM ) kill @ resBSM merge @ resBSM = @ tmpBSM
}
set pos = .. BMOrder ( resBSM , 0) quit : ' pos 0
set val = .. CvtFromSlice (.. GetSlice ( vbs , pos ))
kill @ bswork , @ resBSM , @ tmpBSM quit val
}
Method BSMin ( vbs As% String , bitmap As% String ) As% Integer
{
quit .. BSMax ( vbs , bitmap , 1)
}
/// returns the sum of values for sampling a bitmap from a vbs bit-time-index
Method BSSum ( vbs As% String , bitmap As% String ) As% Integer
{
set bswork = .. GetNewTMP (), resBSSum = .. GetNewTMP (), tmpBSSum = .. GetNewTMP ()
merge @ resBSSum = @ vbs @ (1) do .. BMNot ( resBSSum ), .. BMAnd ( resBSSum , bitmap ) set slice = ""
for ix = 2: 1: $ order (@ vbs @ ( "" ), - 1) kill @ tmpBSSum merge @ tmpBSSum = @ vbs @ ( ix ) do .. BMAnd ( tmpBSSum , resBSSum ) set $ list ( slice , ix ) = .. BMCount ( tmpBSSum )
set val = .. CvtFromSlice ( slice )
kill @ resBSSum merge @ resBSSum = @ vbs @ (1) do .. BMAnd ( resBSSum , bitmap ) set slice = ""
for ix = 2: 1: $ order (@ vbs @ ( "" ), - 1) kill @ tmpBSSum merge @ tmpBSSum = @ vbs @ ( ix ) do .. BMAnd ( tmpBSSum , resBSSum ) set $ list ( slice , ix ) = .. BMCount ( tmpBSSum )
set val = val - .. CvtFromSlice ( slice )
kill @ bswork , @ resBSSum , @ tmpBSSum quit val
}
/// methods to fill in bitmap and bitlist indexes
///
/// sets the corresponding bit in the bitmap index
/// of the given value of this property
/// with optional parameter setexist = 1 also sets the bit to
/// bitmap index of existing records (necessary for correct
/// work method BMNot ())
Method SetBitMap ( idfact As% Integer , property As% String , value As% String , setexist As% String )
{
set idf0 = idfact -1 \ .. #MAXBITLENGTH , idf1 = idfact -1 # .. #MAXBITLENGTH +1
set : $ get ( setexist ) $ bit (@ (.. BMGLOB ) @ ( "EXIST" , idf0 ), idf1 ) = 1
set $ bit (@ (.. BMGLOB ) @ ( "I" , property , value , idf0 ), idf1 ) = 1
quit
}
/// sets the corresponding bit in the bit-slice index
/// of the given value of this property.
/// Optional setexist parameter - similar to SetBitMap () method
Method SetBitSlice ( idfact As% Integer , property As% String , value As% Integer , setexist As% String )
{
set idf0 = idfact -1 \ .. #MAXBITLENGTH , idf1 = idfact -1 # .. #MAXBITLENGTH +1
set : $ get ( setexist ) $ bit (@ (.. BMGLOB ) @ ( "EXIST" , idf0 ), idf1 ) = 1
set v = .. CvtToBin (+ value )
for ix = 1: 1: $ length ( v ) set : $ extract ( v , ix ) $ bit (@ (.. BMGLOB ) @ ( "S" , property , ix , idf0 ), idf1 ) = 1
quit
}
/// returns the name of the index global subnode containing
/// bitmap-index (if slice = 0 or not defined) or bit-slice (ghb slice '= 0)
/// for this property. If property is empty or undefined -
/// then the bitmap index of existing records
Method GetBitMapName ( property As% String , slice As% String ) As% String
{
quit : $ get ( property ) = "" $ name (@ (.. BMGLOB ) @ ( "EXIST" ))
quit $ name (@ (.. BMGLOB ) @ ( $ select (+ $ get ( slice ): "S" , 1: "I" ), property ))
}
/// demo fill and sample
///
ClassMethod Populate ( count As% String = 10000 )
{
set ix = .. % New ()
set ix . BMGLOB = $ name (^ BMI)
set count = $ get ( count , 10000), m = 0
set names = $ listbuild ( "SantaClause" , "Crocodile" , "Simba" )
set colors = $ listbuild ( "Cyan" , "Magenta" , "Yellow" , "Black" )
if $ sortbegin (^ BMI)
for idfact = 1: 1: count {
set name = $ list ( names , 1 + $ random ( $ listlength ( names )))
set color = $ list ( colors , 1 + $ random ( $ listlength ( colors )))
set length = 10 + $ random (90)
set weight = (10+ $ random (40)) * 100
; for color we create bitmap index
do ix . SetBitMap ( idfact , "C" , color , 1)
; for weight and length create bit-slides indices
do ix . SetBitSlice ( idfact , "L" , length )
do ix . SetBitSlice ( idfact , "W" , weight )
; to check the test sample (see below) we consider
; total weight of black and yellow crocodiles 45-70 cm long
; inclusive
set : (( color = "Black" )! ( color = "Yellow" )) & ( length '<45) & ( length '> 70) m = m + weight
}
if $ sortend (^ BMI)
write m _ "gramms total",!
kill ix quit
}
/// test sample
ClassMethod TestGetData ()
{
set ix = .. % New ()
set ix . BMGLOB = $ name (^ BMI)
set b = .. GetNewTMP (), b1 = .. GetNewTMP ()
do ix . BMEq ( b , ix . GetBitMapName ( "C" ), "Black" )
do ix . BMEq ( b1 , ix . GetBitMapName ( "C" ), "Yellow" )
do ix . BMOr ( b , b1 )
do ix . BSGeLe ( b1 , ix . GetBitMapName ( "L" , 1), 45.70)
do ix . BMAnd ( b , b1 )
set count = ix . BMCount ( b )
write count _ "items selected",!
; determine the total weight of the selected
write ix . BSSum ( ix . GetBitMapName ( "W" , 1), b ) _ "gramms total",!
do .. KillAllTMP ()
kill ix quit
}
}
contains a complete set of functions for developing a data warehouse using bitmap indexes and bitslices in classes and routines, supporting all the required manipulations with these structures. Also included is a demonstration method for filling the test repository with data, during which the value of the grouping function is calculated directly - the sum for the subsequent test request:
and a method for executing this test query, in which the value of the same grouping function is computed using bitslices:
You can compare the result and the time spent on getting it.
For those who are accustomed to the abbreviated COS syntax, I recall that Caché Studio, starting with version 5.2, allows you to
.
In conclusion, you need to comment on one subtlety. It is recommended to build intermediate and resulting bitmap indices for sampling in globals with ^ CacheTemp or ^ mtemp prefixes. Such globals are physically located in the CACHETEMP database and due to the fact that
The code described in this article owes its appearance to the information kindly provided by InterSystems at one of the presentations of the DeepSee product, and was subsequently applied by MakovaSoft in its Orthofact data storage.