You are on page 1of 95

DBreezeDatabaseDocumentation.

(DBreezeversion01.074.20160329doc.version01.031.20160329)

Professional,opensource,NoSql
(embeddedKey/Valuestorage),
transactional,ACIDcompliant,
multithreadeddatabasemanagement
systemfor.NET3.5>/XamarinMONO/
.NETCore.
WritteninC#.

Copyright2012dbreeze.tiesky.com
AlexeySolovyov
<
hhblaze@gmail.com
>
IvarsSudmalis
<
zikills@gmail.com
>

It'safreesoftwareforthose,whothinksthatitshouldbefree.

Please,notifyusaboutoursoftwareusage,sowecanevaluateandvisualizeitsefficiency.

Documentevolution.

Thisdocumentevolvesdownside.Allnewfeatures,ifyouhavereadthebasedocument
before,willbereflectedunderneath.Newevolutionalwaysstartsfromamarkinformat[year
monthday][20120521]foreasysearch.

Evolutionhistory[yyyyMMdd]

20160329DBreeze.DataStructures.DataAsTreeanotherwaytorepresentstoreddata.
20160320Quickstartguide.Customersandorders.
20160304ExampleofDBreezeinitializationforUniversalWindowsPlatform
20140603Storingbyte[]serializedobjects(Protobuf.NET).
20130812InsertkeyoverloadforMasterandNestedtable,lettingnottooverwritekeyifit
alreadyexists.
SpeedingupselectoperationsandtraversalswithValuesLazyLoadingIsOn.
20130811RemoveKey/Valueandgetdeletedvalueandnotificationifvalueexistedinone
round.
20130613Fulllockingoftablesinsideoftransaction.
20130610Restoringtablefromtheothertable.
20130529Speedingupbatchmodifications(updates,randominserts)with
Technical_SetTable_OverwriteIsNotAllowedinstruction.
20121111Alternativetablesstoragelocations.
20121101Addednewiteratorsfortransactionmasterandnestedtables
SelectForwardStartsWithClosestToPrefixandSelectBackwardStartsWithClosestToPrefix.
20121023DBreezelikeinmemorydatabase.Outoftheboxbulkinsertspeedincrease.
20121016SecondaryIndexes.Goingdeeper.Part2.

20121015SecondaryIndexes.Goingdeeper.
20121012Behaviouroftheiteratorswiththemodificationinstructionsinside.
20120922!!!!Important,attachnewDBreezeandrecompileyourproject,ifyouhaveerrors
concerningDateTimeconversionfunctionsreadthedocuarticle.
Storingvirtualcolumnsinthevalue,nullabledatatypesandnullabletextof
fixedlength.
20120905Supportofincrementalbackup.
20120628RowhaspropertyLinkToValue
20120601Storinginsideofarowacolumnofdynamicdatalength.InsertDataBlock
HashFunctionsofcommonusage.Fastaccesstolongstringsandbytearrays.
(
updated20121012
)
.
20120529Nestedtablesmemorymanagement.NestedTableClose(),controllingmemory
consumption.
SecondaryIndex.Directkeyselect.
20120526InsertDictionary/SelectDictionaryInsertHashSet/SelectHashSetcontinuation.
20120525Row.GetTable().
InsertDictionary/SelectDictionaryInsertHashSet/SelectHashSet
20120521FractalTablesstructuredescriptionandusagetechniques.
20120509Basictechniquesdescription

[20120509]
Gettingstarted.

DBreeze.dllcontainsfullymanagedcodewithoutreferencestootherlibraries.CurrentDLL
sizeisaround274KB.Startusingitbyaddingitsreferencetoyourproject.Dontforget
DBreeze.XMLfromReleasefoldertogetVSIntelliSensehelp.

DBreezeisadiskbaseddatabasesystem,thoughitalsocanworklikeinmemorystorage.

DbreezedoesnthavevirtualfilesystemunderneathandresidesallworkingfilesinyourOS
filesystem,thatswhyyoumustinstantiateitsenginebysupplyingafoldernamewhereall
fileswillbelocated.

MainDBreeze
namespace
is
DBreeze
.

usingDBreeze

DBreezeEngineengine=null

if(engine==null)
engine=newDBreezeEngine(@"D:\temp\DBR1")

Its
important
inthe
Dispose
functionofyourapplicationorDLLtocallDBreezeengine
Dispose,tohavegracefulapplicationtermination.

if(engine!=null)

engine.Dispose()

Afteryouhaveinstantiatedtheenginetwooptionswillbeavailableforyou,eithertowork
withthedatabaseschemeortoworkwiththetransactions.

Scheme.

Youdontneedtocreatetablesviascheme,itsneededtomakemanipulationswithalready
existingobjects.

Deletingtable:
engine.Scheme.DeleteTable(stringuserTableName)

Gettingspecifictablesnames:
engine.Scheme.GetUserTableNamesStartingWith(stringmask)

Renamingtable:
engine.Scheme.RenameTable(stringoldTableName,stringnewTableName)

Checkingiftableexists:
engine.Scheme.IfUserTableExists(stringtableName)

Gettingphysicalpathtothefileholdingthetable:
engine.Scheme.GetTablePathFromTableName(stringtableName)

Latermorefunctionswillbeaddedthereandtheirdescriptionhere.

Transactions

InDBreezealloperationswiththedata,whichresidesinsideofthetables,mustoccurinside
ofthetransaction.

Weopentransactionlikethis:

using(vartran=engine.GetTransaction())
{

Pleasenote
,thatits
important
todisposetransactionafterallnecessaryoperationsare
done(usingstatementmakesitautomatically).
Pleasenote
,thatonetransactioncanberunonlyin
one
.NETmanagedthreadandcannot
bedelegatedtootherthreads.
Pleasenote
,thatnestedtransactionsare
not
allowed(parenttransactionwillbeterminated)

Duringintransactionaloperationsdifferentthingscanhappenthatswhywe
highly
recommend
tousetrycatchblocktogetherwiththetransactionand
logexceptions
forthe
futureanalysis.

try
{
using(vartran=engine.GetTransaction())
{
}
}
catch(Exceptionex)
{
Console.WriteLine(ex.ToString())
}

Tabledatatypes

EverytableinDBreezeisakey/valuestorage.Onthelowlevel,keysandvaluesrepresent
arraysofbytesbyte[].

Onthetoplevelyoucanchooseyourowndatatype,fromallowedlist,tobestoredasakey
orvalue.

TherearesomenotstandarddatatypesinDBreeze,addedforusability,theyareaccessible
insideofDBreeze.DataTypesnamespace.

usingDBreeze.DataTypes

Tabledatatypes.Keydatatypes

Keys
cannot
contain
NULLABLE
datatypes.

Note,thatkeyinthetableisalwaysunique.

Hereisalistofavailabledatatypesforthekey:

Keysdatatypes

byte[]
int
uint
long
ulong
short
ushort
byte
sbyte
DateTime
double
float
decimal
string
thisonewillbeconvertedintobyte[]usingUTF8encoding
DbUTF8
thisonewillbeconvertedintobyte[]usingUTF8encoding
DbAscii
thisonewillbeconvertedintobyte[]usingAsciiencoding
DbUnicode
thisonewillbeconvertedintobyte[]usingUnicodeencoding
char

Valuedatatypes

byte[]
int
int?
uint
uint?
long
long?
ulong
ulong?
short
short?
ushort
ushort?
byte
byte?
sbyte
sbyte?

DateTime
DateTime?
double
double?
float
float?
decimal
decimal?
string
thisonewillbeconvertedintobyte[]usingUTF8encoding
DbUTF8
thisonewillbeconvertedintobyte[]usingUTF8encoding
DbAscii
thisonewillbeconvertedintobyte[]usingAsciiencoding
DbUnicode
thisonewillbeconvertedintobyte[]usingUnicodeencoding
bool
bool?
char
char?

Andsomemoreexoticdatatypeslike:

DbXML<T>
DbMJSON<T>
DbCustomSerializer<T>

theyareusedforstoringobjectsinsideofthevalue,wewilltalkaboutthemlater.

Tableoperations.Insertingdata

Alloperationswiththedata,exceptoperationswhichcanbedoneviascheme,mustbedone
insideofthetransactionscope.Bypressingtran.intellisensewillgiveyoualistofall
possibleoperations.Westartfrominsertingdataintothetable.

publicvoidExample_InsertingData()
{

using(vartran=engine.GetTransaction())

tran.Insert<int,int>("t1",1,1)
tran.Commit()

}
}

Inthisexamplewehaveinserteddataintothetablewiththenamet1.
Table
willbe

createdautomatically
,ifitdoesntexist.

Keytypeforourtableisint1,valuetypeoftableisalsoint(also1).

AfteroneorseriesofmodificationsinsideofthetransactionwemusteitherCommitthemor
Rollbackthem.
Note,
Rollback
functionwill
automaticallyrun
inthe
transactionDispose
function,soall
notcommittedmodificationsofthedatabaseinsideoftransactionwillbeautomatically
rolledback.

Youcanbesurethatthismodificationwillnotbeappliedtothetable,butneverthelessempty
tablewillbecreated,ifitdoesntexistbefore.

using(vartran=engine.GetTransaction())
{
tran.Insert<int,int>("t1",1,1)

//NOCOMMIT
}

Wedontstoreinthetabledatatypes,whichyouassumemustbethere,tableholdsonly
bytearraysofkeysandvaluesandonlyontheupperlevelacquiredbyte[]willbeconverted
intokeysorvaluesoftheappropriatedatatypesfromgenericconstructions.

Youcanmodifymorethenonetableinsideofthetransaction.

using(vartran=engine.GetTransaction())

tran.Insert<int,int>("t1",1,1)
tran.Insert<uint,string>("t2",1,hello)

tran.Commit()
//or
//tran.Rollback()

tran.Insert<int,int>("t1",2,1)
tran.Insert<uint,string>("t2",2,world)

tran.Commit()

CommitsandRollbacks

UsedCommitorRollbackwillbeappliedtoallmodificationsinsideofthetransaction.If
somethinghappensduringCommitalldatawillbeautomaticallyrolledbackforall
modifications.


TheonlyacceptablereasonforRollbackfailcanbethedamageofthephysicalstorage,and
exceptionsintherollbackprocedurewillbringdatabasetothenotoperablestate.

DBreezedatabase,afteritsstart,checkstransactionsjournalandrestorestablesintotheir
previousstate,sothereshouldbenoproblemswiththepowerlossoranyotheraccidental
softwareterminationinanyprocessexecutionpoint.

DBreezedatabaseisfully
ACID
compliant.

Commit
operationisalwaysvery
fast
andtakesthesameamountoftimeindependentof
thequantityofmodificationsmade.

Rollback
cantake
longer
,dependinguponthe
quantityofdataandcharacterof
modifications,
whichweremadewithinthedatabase.

Tableoperations.Updates

Updatekeyoperationisthesameasinsertoperation

tran.Insert<int,int>("t1",2,1)
tran.Insert<int,int>("t1",2,2)

wehaveupdatedkey2andsetupnewvalue2.

Tableoperations.Bulkoperations

Ifyouaregoingtoinsertorupdateabigdatasetthenfirstexecuteinsert,update,remove
commandasmanytimesasyouneedandthencalltran.Commit()

Callingtran.
Commit
aftereveryoperation,
willnotmaketablephysicalfilebigger
butwill
takemoretimethenoneCommitafteralloperations.

using(vartran=engine.GetTransaction())

//THISISFASTER

for(inti=0i<1000000i++)
{
tran.Insert<int,int>(t1,i,i)
}

tran.Commit()

//THISISSLOWER


for(inti=0i<1000000i++)
{
tran.Insert<int,int>(t1,i,i)
tran.Commit()
}

Tableoperations.Randomkeyswhilebulkinsert.

Dbreezealgorithmsarebuilttoworkwithmaximumefficiencywhileinsertinginbulksorted
ascendingdata.

for(inti=0i<1000000i++)
{
tran.Insert<int,int>(t1,i,i)
}

tran.Commit()

//or

DateTimedt=DateTime.Now

for(inti=0i<1000000i++)
{
tran.Insert<DateTime,int>(t1,dt,i)
dt=dt.AddSeconds(7)
}

tran.Commit()
Theabovecodewillexecute9seconds(year2012and1.5secondsinyear2015).

Ifyoustarttoinsertdatainrandomorderitcantakeuptosomeminutes.Thatswhy,ifyou
haveinmemorybigdataset,beforesavingittothedatabase,sortitascendinginmemory
bykeyandtheninsert.Itwillspeedupyourprogram.

IfyoumakecopyfromotherdatabasestoDBreeze,takeachunk(e.g.1MLNrecords),sort
itinmemorybykeyascending,insertintoDBreeze,themtakeanotherchunkandsoon.

Tableoperations.PartialInsertorUpdate

InDBreeze
maximalkeylength
inbytesis65535(UInt16.MaxValue)andmaximal
value
length
is2147483647(Int32.MaxValue).

Itsnotpossibletosaveasavaluebytearraybiggerthan2GB.Forbiggerdataelementswe

willhavetodevelopinthefutureotherstrategy.

InDbreezewehaveabilityofapartialvalueupdateorinsert.Itspossiblebecausevalues
arestoredasbyte[].Itdoesntmatterwhichdatatypeisstoredalreadyinthetableyoucan
alwaysaccessitandchangeasbytearray.

DBreezehasspecialnamespaceinside,whichallowsyoueasilytoworkwithbytearrays.

usingDBreeze.Utils

Nowyoucanconvertanystandarddatatypeintobytearrayandback.

Wewillachievethesameeffectinallfollowingrecords:

tran.Insert<int,
int
>("t1",10,1)
//or
tran.Insert<int,
byte[]
>("t1",10,((int)1).To_4_bytes_array_BigEndian())
//or
tran.Insert<int,
byte[]
>("t1",10,newbyte[]{0x80,0x00,0x00,0x01})

Aboveinstructionscanberunonebyoneandwillbringtotheresultthenunderkey10we
willhavevalue1.

Andthesameresultwewillachievehavingrun4followinginstructions:

tran.InsertPart<int,byte[]>("t1",10,newbyte[]{0x80},0)
tran.InsertPart<int,byte[]>("t1",10,newbyte[]{0x00},1)
tran.InsertPart<int,byte[]>("t1",10,newbyte[]{0x00},2)
tran.InsertPart<int,byte[]>("t1",10,newbyte[]{0x01},3)

//orthesame
tran.InsertPart<int,byte[]>("t1",10,newbyte[]{0x80,0x00},0)
tran.InsertPart<int,byte[]>("t1",10,newbyte[]{0x00,0x01},2)

Thefourthparameteroftran.InsertPartisexactlytheindexfromwhichwewanttoinsertour
byte[]array.

This
technique
canbeusedbyus,ifwethinkaboutthevalueasaboutthe
setofcolumns
oftheknownlength,likein
standardSQLdatabases
,andgivesusabilitytochangeevery
columnseparately,withoutchangingallothervalues.

Note,
youcanalways
switchtobyte[]datatype
invaluesandinkeys

tran.Insert<int,
int
>
//or

tran.Insert<int,
byte[]
>

ifitsinterestingforyou.

Note,
Ifyouwanttoinsertorupdatevaluestartingfromthe
indexwhichisbiggerthen
currentvalue
length,theemptyspacewillbefilledwithbyte[]{0}.

Wedidnthavebeforekey12andnowweareexecutingfollowingcommands:

tran.InsertPart<int,byte[]>("t1",12,newbyte[]{0x80},
0
)
tran.InsertPart<int,byte[]>("t1",12,newbyte[]{0x80},
10
)

Valueasbyte[]willlooklikethis:

"0x800x000x000x000x000x000x000x000x000x000x80"

Note
,Dbreezewilltrytousethe
samephysicalfilespacewhilerecordupdate,
ifexisting
recordlengthissuitableforthis.

Tableoperations.DataFetching.Select.

Methodtran.Selectisdesignedforgettingonesinglekey:

using(vartran=engine.GetTransaction())

tran.Insert<int,int>("t1",10,2)

tran.Commit()

varrow=tran.Select<int,
int
>("t1",10)
//
orwillworkalsogood

varrow=tran.Select<int,
byte[]
>("t1",10)

Afterselectyoumustsupplyingenericformatdatatypesforthekeyandvalue.

Inourcase,wewanttoreadfromtablet1keyoftypeint(itsvalue10).

SelectalwaysreturnsavalueoftypeDBreeze.DataTypes.Row
.

Wecan
starttovisualize
thekeyvalue
onlyafterchecking,
iftablehassuchvalueinside.

Row
has
propertyExists
:

using(vartran=engine.GetTransaction())
{

tran.Insert<int,int>("t1",10,2)

tran.Commit()

varrow=tran.Select<int,int>("t1",10)

byte[]btRes=null

intres=0

intkey=0

if(
row.Exists
)

key=row.Key

res=row.Value

//btReswillbenull,becausewehaveonly4bytes

btRes=row.GetValuePart(12)

//btReswillbenull,becausewehaveonly4bytes

btRes=row.GetValuePart(12,1)
//willreturn4bytes
btRes=row.GetValuePart(0)
//willreturn4bytes
btRes=row.GetValuePart(0,4)
}

So,ifrowexists,wecanstarttofetchitskey(
row.Key
),fullrecord
row.Value
(itwillbe
automaticallyconvertedfrombyte[]tothedatatype,whichyougavewhileformingSelect).
Andindependentfromtherecorddatatype,Rowhasmethod
GetValuePart
withoverloads
whichwillhelpyoutogetvaluepartiallyandalwaysasbyte[].DBreeze.Utilsextensionscan
helptoconvertvaluestootherdatatypes.

Ifwehadinthevalue,startingfromindex4storedsomekindofulong,whichresides8
bytes,wecansay:

ulongx=row.GetValuePart(4,8).To_UInt64_BigEndian()

Note
,that
DBreeze.UtilsconversionalgorithmsareexactlysharpenedforDBreeze
datatypes,becausetheycreatesortablebyte[]sequencesincomparewith.NETbuilt
inbyte[]conversionfunctions
.

Tableoperations.DataFetching.NULL

tran.Insert<int,int?>(t1,10,null)

varrow=tran.Select<int,int?>(t1,10)

if(row.Exists)
{
int?val=row.Value//valwillbenull
}

Tableoperations.DataFetching.Orderby.OrderbyDescending.

When
Dbreezestoresdata
inthetableits
automatically
storedinthe
sorted
order.Thats
whyallrangeselectsareveryfast.Thisexampleistakenfromsatelliteprojectintegrated
intoDBreezesolution,whichiscalledVisualTesterfromclassDocuExamples:

publicvoidExample_FetchingRange()
{

engine.Scheme.DeleteTable("t1")

using(vartran=engine.GetTransaction())

DBreeze.Diagnostic.SpeedStatistic.StartCounter("INSERT")

DateTimedt=DateTime.Now

for(inti=0i<1000000i++)

tran.Insert<DateTime,byte?>("t1",dt,null)

dt=dt.AddSeconds(7)

tran.Commit()

DBreeze.Diagnostic.SpeedStatistic.StopCounter("INSERT")

DBreeze.Diagnostic.SpeedStatistic.PrintOut(true)

DBreeze.Diagnostic.SpeedStatistic.StartCounter("FETCH")

foreach(varrowintran.SelectForward<DateTime,byte?>("t1"))

//Console.WriteLine("K:{0}V:{1}",
row.Key.ToString("dd.MM.yyyyHH:mm:ss"),(row.Value==null)?"NULL":
row.Value.ToString())

DBreeze.Diagnostic.SpeedStatistic.StopCounter("FETCH")

DBreeze.Diagnostic.SpeedStatistic.PrintOut(true)

Asmallbenchmarkforthisprocedure:
INSERT:10361ms28312951ticks
FETCH:4700ms12844468ticks

AllrangeselectsmethodsinDBreezereturnIEnumerable<Row<TKey,TValue>>,so
theycanbeusedinforeachstatements.

Ifyouwant,youcanbreakfromforeachinanymoment.

Tolimitthequantityofthedatayoucanuse,eitherbreakiterationoruse
Take
statement
:

foreach(varrowintran.SelectForward<DateTime,byte?>("t1").
Take
(100))

SelectForward
startsfromthefirstkeyanditeratesforwardtothelastkeyinsorted
ascending
order.

SelectBackward
startsfromthelastkeyanditeratesbackwardtothefirstkeyinsorted
descending
order.

Transactionhasmoreselfexplainedmethods:

IEnumerable<Row<TKey,TValue>>
SelectForwardStartFrom
<TKey,TValue>(string
tableName,TKeykey,boolincludeStartFromKey)
Note,ifkeyisnotfoundthenitstarts
fromthenextavailablekeyforwardinascendingorder,ideaofnonexistingsupplied
parameterconcernsalliterationalmethods.

SelectBackwardStartFrom
<TKey,TValue>(stringtableName,TKeykey,bool
includeStartFromKey)iteratesfromthegivenkeydownindescendingorder.

SelectForwardFromTo
<TKey,TValue>(stringtableName,TKeystartKey,bool
includeStartKey,TKeystopKey,boolincludeStopKey)

SelectBackwardFromTo
<TKey,TValue>(stringtableName,TKeystartKey,bool
includeStartKey,TKeystopKey,boolincludeStopKey)

DONT
USE
LINQ
after
SelectForwardorSelectBackwardforfilteringlikethis:

tran.SelectForward<int,int>(t1).Where(r=>r.Key>10).Take(10)


Becauseitwillwork
muchmuchmuchslower
thenspeciallysharpenedmethods,
useinstead:

tran.
SelectForwardStartFrom
<int,int>("t1",10,false).Take(10)

Andfinallytwomorespecialmethods:

SelectForwardStartsWith
<TKey,TValue>(stringtableName,TKeystartWithKeyPart)

and

SelectBackwardStartsWith
<TKey,TValue>(stringtableName,TKeystartWithKeyPart)

Yourememberthatalldatatypeswillbeconvertedintobyte[].

Soifintablewehavekeys

byte[]{0x12,0x15,0x17}
byte[]{0x12,0x16,0x17}
byte[]{0x12,0x15,0x19}
byte[]{0x12,0x17,0x18}

then
SelectForwardStartsWith<byte[],int>(t1,newbyte[]{0x12})

willreturnusallkeys
SelectForwardStartsWith<byte[],int>(t1,newbyte[]{0x12,0x15})

willreturnusonly2keys

byte[]{0x12,0x15,0x17}
byte[]{0x12,0x15,0x19}

SelectBackwardStartsWith
<byte[],int>(t1,newbyte[]{0x12,0x15})

willreturnusonly2keysindescendingorder

byte[]{0x12,0x15,0x19}
byte[]{0x12,0x15,0x17}

SelectForwardStartsWith<byte[],int>(t1,newbyte[]{0x12,0x17})

willreturnus1key
byte[]{0x12,0x17,0x18}

and
SelectForwardStartsWith<byte[],int>(t1,newbyte[]{0x10,0x17})

willreturnnothing.

Havingthisideawecaneffectivelyworkwithstrings:

tran.Insert<string,string>(t1,w,w)
tran.Insert<string,string>(t1,ww,ww)
tran.Insert<string,string>(t1,www,www)

then

SelectForwardStartsWith
<string,string>(t1,ww)

willreturnus

ww
www

and
SelectBackwardStartsWith
<string,string>(t1,ww)

willreturnus

www
ww

Tableoperations.Skip

InDbreezewehaveabilitytostartiterationsafterSkippingsomeotherkeys:

thiscommandskipsskippingQuantityelementsandthenstartsenumerationinascending
order:

SelectForwardSkip
<TKey,TValue>(stringtableName,ulongskippingQuantity)

thiscommandskipsskippingQuantityelementsbackwardandthenstartsenumerationin
descendingorder:

IEnumerable<Row<TKey,TValue>>
SelectBackwardSkip
<TKey,TValue>(string
tableName,ulongskippingQuantity)

thiscommandskipsskippingQuantityelementsfromthespecifiedkey(ifkeyisnotfound
thennextoneafteritwillbetakenasskipped1)andthenstartsenumerationinascending
order:

SelectForwardSkipFrom
<TKey,TValue>(stringtableName,TKeykey,ulong
skippingQuantity)

thiscommandskipsskippingQuantityelementsbackwardfromthespecifiedkeyandthen
startsenumerationindescendingorder:

SelectBackwardSkipFrom
<TKey,TValue>(stringtableName,TKeykey,ulong
skippingQuantity)

Note,thatskipneedstoiterateviakeys,tocalculateexactskippingquantity
.Thats
whydeveloperhasalwaystotakeintoconsiderationtheideaofthefindingcompromise
betweenspeedandskippingquantity.Skipping1MLN,ofelementsin
anydirection
starting
from
anykey
willtake4secondswithInteli78coresandSCSIdrive8GBRAM(year2012).
Skipof100000recordswilltake400ms,10000willtake40msrespectively.

So,ifyouaregoingtoimplementgridpaging,thenjustrememberfirstshowninthegridkey
andthenskipfromitquantityofshowninthegridelementsusing
SelectForwardSkipFrom
or
SelectBackwardSkipFrom
.

Tableoperations.Count.

Forgetting
Table
records
quantity
use:

ulongcnt=tran.
Count
("t1")

Countiscalculatedwhileinsertingandremovingoperationsandisalwaysavailable.

Tableoperations.Max.

varrow=tran.
Max
<int,int>("t1")

if(row.Exists)
{
//etc...
}

Tableoperations.Min.


varrow=tran.
Min
<int,int>("t1")

if(row.Exists)
{
//etc...
}

Tableoperations.Readingfromnonexistingtable

Ifyoutrytoreadfromnonexistingtable,this
tablewillnobecreated
inthefilesystem.

tran.Countwillreturn0
tran.Select,tran.Min,tran.Maxwillreturnrowwithrow.Exists==false

Rangeselectsliketran.SelectForwardetc.willreturnnothinginyourforeachstatement.

Tableoperations.Removingkeys

To
removeonekey
use

tran.RemoveKey<int>("t1",10)
tran.Commit()

To
Removeallkeys
use
tran.RemoveAllKeys
(stringtableName,boolwithFileRecreation)

Note
,if
withFileRecreation
parameterissetto
true
,thenwe
dont
needto
Commit
this
modification,itwillbedoneautomatically.Thefilewhoholdsthetablewillberecreated.

Note
,if
withFileRecreation
parameterissetto
false,
theolddatawillbenotvisibleany
more,buttheoldinformationwillstillresideinthetable.We
needCommit
afterthis
modification.

Tableoperations.Changekey

Wehaveanabilitytochangethekey.

Afterthesecommands:

tran.Insert<int,int>(t1,10,10)
tran.ChangeKey<int>("t1",10,11)
tran.Commit()


wewillhaveinthetableonekey11withthevalue10.

Afterthesecommands:

tran.Insert<int,int>(t1,10,10)
tran.Insert<int,int>(t1,11,11)
tran.ChangeKey<int>("t1",10,11)
tran.Commit()

wewillhaveinthetableonekey11withthevalue10.(oldvalueforthekey11willbelost)

Storingobjectsinthedatabase

Forstoringobjectsinthetablewehave3extradatatypeswhichareaccessiblevia
DBreeze.DataTypesnamespace.

DbXML<T>
willautomaticallyusebuiltin.NETXMLserializeranddeserializerforobjects.
Slowerthenothersinbothoperationsfurthermoredataresidesmuchmorephysicalspace,
thenothers.

DbMJSON<T>
MicrosoftJSON,willautomaticallyusebuiltin.NETJSON
(System.Web.Script.Serialization.JavaScriptSerializer)serializeranddeserializerfor
objects.MuchbetterthenXMLbutnotsogoodasserializerprovidedby
http://json.codeplex.com/
JSON.NET,thoughresidesapproximatelythesamephysical
spaceonthedisk.

DbCustomSerializer<T>
givesyouabilitytoattachyourownserializerlike
http://json.codeplex.com/
.

ToattachJSON.NET,downloadit,refertoyourprojectandfillsomelines:

DBreeze.Utils.CustomSerializator.Serializator=JsonConvert.SerializeObject
DBreeze.Utils.CustomSerializator.Deserializator=JsonConvert.DeserializeObject

nowyoucanuseserializationanddeserializationprovidedbyJSON.NET.

ButifyoudontwanttouseJSON.NET,tryMicrosoftJSON.Itsabout40%sloweron
deserializationand510%sloweronserializationthenJSON.NET.

Usealloftheminfollowingmanner:

publicclassArticle
{


publicuintId{getset}

publicstringName{getset}
}

publicvoidExample_InsertingObject()
{

engine.Schema.DeleteTable("Articles")

using(vartran=engine.GetTransaction())

tran.SynchronizeTables("Articles")

uintidentity=0

varrow=tran.Max<uint,byte[]>("Articles")

if(row.Exists)

identity=row.Key

identity++

Articleart=newArticle()

Id=identity,

Name="PC"

tran.Insert<uint,DbMJSON<Article>>("Articles",identity,art)

tran.Commit()

}
}

Note
,DbMJSON,DbXML,DbMJSON,DbCustomSerializerhaveoverloadedoperatorand
youcanspecifyartwithoutsayingnewDbMJSON<Article>,justsayart:

tran.Insert<uint,DbMJSON<Article>>("Articles",identity,
art
)
//or
tran.Insert<uint,DbXML<Article>>("Articles",identity,
art
)
//or
tran.Insert<uint,DbCustomSerializer<Article>>("Articles",identity,
art
)

Gettingobjects:

foreach(varrowintran.SelectForward<uint,DbMJSON<Article>>("Articles").Take(10))

{
//Note
row.Value
willreturnusDbMJSON<Article>

//row.Value
//ButweneedArticle
//
Article
a=row.Value.
Get
//Oritsserializedrepresentation
//
string
aSerialized=row.Value.
SerializedObject

Multithreading

InDbreezetablesarealwaysaccessibleforparallelREADoflastcommitteddatafrom
multiplethreads.

Note
,while
onethread
is
writingdata
intothetable,
otherthreads
will
not
be
able
to
write
datainthesametable(tablelock),tillwritingthreadreleasesitstransaction,theywill
waitinaqueue.

Note
,while
onethread
is
writingdata
intotable,
otherthreads

can
inparallel
read
already
committeddata
.

Note
,if
one
of
threads
needs,insideofthetransaction,to
read
datafromthetablesandit
wantstobesurethattilltheendoftransaction
otherthreads
will
notmodify
thedata,this
threadmust
reservetablesforsynchronizedread
.

using(vartran=engine.GetTransaction())
{

tran.SynchronizeTables("table1","table2")

Transactionalsohasmethodfortablessynchronization.

tran.SynchronizeTables

Thismethodhasoverloadsandyoucansupplyasparameters:List<string>orparams
string[].

SynchronizeTablescanberunonlyonceinsideofthetransaction.

All
reads
canbedividedon
twocategories
byusagetype:

Readforreporting

Readformodification

Basedonthisideathewholemultithreadedlayerisbuilt.

Multithreading.Readforreporting

Ifyouthinkthatthereisnonecessitytoblocktable(s)andotherthreadscouldwritedatain
paralleljustdontusetran.SynchronizeTables.

Thistechniqueisapplicableinallreportingcases.Ifuserneedstoknowhisbankaccount
state,wedontneedtoblockthetablewithaccountinformation,justreadaccountstateand
returnit.Doesntmatterthatinthismomenthisaccountstateischangingitsaquestionof
amoment.Ifuserrequestshisaccountstatein5minuteshewillgetalreadymodified
account.

Therearesomethingswhichmustbeunderstood.

ForexamplewemakeiterationviatableItems,becausesomeonehasrequesteditsfulllist.

Letsassumethatthereare100items

List<Item>items=newList<Item>()

foreach(varrowintran.SelectForward<ulong,DbMJSON<Item>>(Items))
{
items.Add(row.Value.Get)

//wehaveiteratedover50itemsandinthismomentotherthreaddeleteditemId1
andcommittedtransaction
//Result:itsaquestionofthemomentthisitemwillbeaddedtothefinalList,it
doesntmatterinthiscase.

//wehaveiteratedalready75itemsandinthismomentotherthreaddeleteditemId
90andcommittedtransaction
//after89wewillgetitem91
//Result:itsaquestionofthemoment,item90willnotbeaddedtothefinalList,it
doesntmatterinthiscase.

AndifyouwanttobesurethatotherthreadswillnotmodifyItemstable,whileyouare
fetchingthedata,use

tran.SynchronizeTables(Items)

Ifyoutakearowfromatablealwayscheckifitexists.

Ifyourdataprojectionisspreadamongmanytables,firstgetallpiecesofthedata
fromdifferenttables,alwayscheckingifrow.Exists,incaseofdirectselects,andonly
whenyouhavefullobjectconstructedthenreturnittothefinalprojectionasaready
element.

Noteifyouhavereceivedrowanditexists.Itdoesntmeanthatyouhavealready
acquiredthevalue.Valuewillbereadonlywhenyouchoosepropertyrow.Value(lazy
valueloading).Ifotherthreadremovesvalueinbetween,afteryouhaveacquiredthe
row,butstilldidntacquiredvalue,thenvaluewillbereturnedinanycase,because
afterremovingdatastillstaysonthedisk,onlykeysaremarkedasdeleted.Andthis
behaviourfornotsynchronizedreadshouldbeok,becauseitsaquestionofthe
moment.

Ifyouhaveacquiredrowanditexists,inonethread,nowyouaregoingtogetthe
value,butinthismomentotherthreadupdatesvalue,thenyouthreadwillreceive
updatedvalue.

IncaseifyourthreadisgoingtoretrievevalueandinthismomentDBreeze.Scheme
deletestabletheninsideoftransactionexceptionwillberaised,controlledby
trycatchintegratedintousingstatement.

Thesamewillhappenifotherthreadexecutestran.RemoveAllKeys(yourreading
table,truewithFileRecreation).Yourreadingthreadwillgetexceptioninsideofthe
transaction.Butallwillbeokifotherthreadsremovesdatawithoutfilerecreation,if
tran.RemoveAllKeys(yourreadingtable,falsewithFileRecreation).

YoumustuseScheme.DeleteTable,Scheme.RenameTableandtran.RemoveAllKeys
withtablerecreationsemantically.

Eitherinconstructor,afterengineinitialization,orfortemporarytables,whichareusedfor
subcomputationwiththehelpofdatabase,anddefinitelyonlybyonethread.Fortables
whichareunderreadwritepressure,bettertousetran.RemoveAll(false)andthenoneday
tocompactthistablebycopyingexistingvaluesintonewtable,andrenamingnewtableto
oldtable.

Tablescopying/compaction

Copyingofthedatabettertomakeonbyte[]level,itwillbefasterthentocastandserialize/
deserializeobjects.

IfyouhadtableArticles<ulong,DbMJSON<Atricle>>

Copyitlikethis:

foreach(varrowintran.SelectForward(<byte[],byte[]>(Articles)))
{
tran.Insert<byte[],byte[]>(ArticlesCopy,row.Key,row.Value)
}

tran.Commit()

thenyoucanrenameoldtableScheme.RenameTable(ArticlesCopy,Articles)

andgoontoworkwithArticletable

foreach(varrowintran.SelectForward(<long,DbMJSON<Atricle>>(Articles)))
{
...
}

Note
,wecreateforeachloopwhichreadsfromonetableandafterthatwritesintotheother
table.FromHDDpointovviewwemakesuchoperation:
RWRWRWRW..

IfyouhavemechanicalHDD,itsheadmustalwaysmovebetweentwofilestocompletethis
operation,whatisnotsoefficient.

Toincreaseperformanceofthecopyprocedureweneedfollowingsequence:

RRRRWRRRRWRRRRW.

So,firstwereadtothememoryabigchunk(1K/10K/100K/1MLNofrecords)andthensortit
bykeyinascendingorderandinsertitinbulktothecopytable.

Dictionary<TKey,TValue>willnotbeabletosortbyte[].Forthisweneedtoconstruct
hashstringusingDBreeze.Utils:

byte[]bt=newbyte[]{0x08,0x09}

stringhash=bt.ToBytesString()

thenputthishashakeyforDictionary.Copyprocedurewith
RRRRWRRRRWRRRRW.
sequence:

usingDBreeze.Utils

inti=0
intchunkSize=100000
Dictionary<string,KeyValuePair<byte[],byte[]>>cacheDict=new
Dictionary<string,KeyValuePair<byte[],byte[]>>()

foreach(varrowintran.SelectForward(<byte[],byte[]>(Articles)))
{
cacheDict.Add(
row.Key.ToBytesString()
,newKeyValuePair<byte[],byte[]>
(
row.Key,
row.Value
)
)

i++

if(i==chunkSize)
{
//savingsortedvaluestothenewtableinbulk
foreach(varkvpincacheDict.OrderBy(r=>r.Key))
{
tran.Insert<byte[],byte[]>(ArticlesCopy,kvp.Value.Key,
kvp.Value.Value)
}

cacheDict.Clear()
i=0
}
}

//Ifsomethingleftincacheflushit
foreach(varkvpincacheDict.OrderBy(r=>r.Key))
{
tran.Insert<byte[],byte[]>(ArticlesCopy,kvp.Value.Key,kvp.Value.Value)
}
cacheDict.Clear()

tran.Commit()

Note
,actuallywedontneedtosortdictionary,becauseSelectForwardfromtableArticles

givesusalreadysortedvaluesandinsortedsequencetheywillmigrateinto
cacheDictionary,soourcompletecodewilllooklikethis:

inti=0
intchunkSize=100000
Dictionary<byte[],byte[]>cacheDict=newDictionary<byte[],byte[]>()

foreach(varrowintran.SelectForward(<byte[],byte[]>(Articles)))
{
cacheDict.Add(row.Key,row.Value)
i++

if(i==chunkSize)
{
//savingsortedvaluestothenewtableinbulk
foreach(varkvpincacheDict)
{
tran.Insert<byte[],byte[]>(ArticlesCopy,kvp.Key,kvp.Value)
}

cacheDict.Clear()
i=0
}
}

//Ifsomethingleftincacheflushit
foreach(varkvpincacheDict)
{
tran.Insert<byte[],byte[]>(ArticlesCopy,kvp.Key,kvp.Value)
}
cacheDict.Clear()

tran.Commit()

Multithreading.Readformodification

Thistechniqueisusedwhenyouneedtogetdata(select)beforemodification(insertor
updateetc.):

privateboolAddMoneyOnAccount(uintuserId,decimalsum)
{
using(vartran=engine.GetTransaction())
{


try

stringtableUserInfo=UserInfo+userId

tran.SynchronizeTables(tableUserInfo)

//afterSynchronizeTables,besurethatnoneoftheotherthreadswillwritein
tabletableUserInfo,tillthetransactionwillbereleased.

//nowwereadthestateoftheuseraccount

varrow=tran.Select<string,decimal>(tableUserInfo,Account)

decimalaccountState=0

if(row.Exists)
accountState=row.Value

//nowwechangethesumoftheusersaccount

accountState+=sum

tran.Insert<string,decimal>(tableUserInfo,Account,accountState)

tran.Commit()

catch(Exceptionex)

Console.WriteLine(ex.ToString())
returnfalse

}
}

returntrue
}

TableWRITE,ResolvingDeadlockSituation

Ifwewriteonlyinonetableinsideoftransactionandforothertablesuseunsynchronized
read,wedontneedtouse
SynchronizeTables

using(vartran=engine.GetTransaction())
{
tran.Insert<int,int>(t1,1,1)

}

Butwhenwehaveinserted/updated/Removedakeyinthetable,DBreezewillautomatically
blockthewholetableforWrite,likeSynchronizeTables(t1)wouldbeused,tilltheendofthe
transaction.

Infollowingexample,transactionfirstblockstablet1andthent2

using(vartran=engine.GetTransaction())
{
tran.Insert<int,int>(t1,1,1)

tran.Insert<int,int>(t2,1,1)

Imagine,thewehaveparallelthreadwhichwritesinthesametablesbutinothersequence:

using(vartran=engine.GetTransaction())
{
tran.Insert<int,int>(t2,1,1)

tran.Insert<int,int>(t1,1,1)

Thread2hasblockedtablet2,whichisgoingtobereadbyThread1,andThread1has
blockedtablet1,whichisgoingtobereadbyThread2.

Suchsituationiscalleddeadlock.

DbreezeautomaticallydropsoneofthesethreadswithDeadlockException,andtheother
threadwillbeablesuccessfullyfinishitsjob.

Butthisisonlyapartofthesolution.Tomaketheprogramdeadlocksafeuseinboth
threads
SynchronizeTables
construction:

Thread1:

using(vartran=engine.GetTransaction())
{

tran.SynchronizeTables(t1,t2)

tran.Insert<int,int>(t1,1,1)

tran.Insert<int,int>(t2,1,1)

Thread2:

using(vartran=engine.GetTransaction())
{
tran.SynchronizeTables(t1,t2)

tran.Insert<int,int>(t2,1,1)

tran.Insert<int,int>(t1,1,1)

Boththreadswillbeexecutedwithoutexceptions,onebyoneabsolutedefencefrom
thedeadlocksituation.

TableWRITE,READorSYNCHROREAD,Datavisibilityscope

Inthefollowingexamplewereadarowfromtablet1.

using(vartran=engine.GetTransaction())
{
varrow=tran.Select<int,int>(t1,1)
}

Wedidntusetran.SynchronizeTablesconstructionandwedidntwritetothistablebefore,
sowewillseeonlylastcommitteddata,evenifotherthreadischangingthesamedatain
parallel,thistransactionwillreceiveonlylastcommitteddataforthistable.

Buteverythingchangeswhentransactionhasatableinmodificationlist:

using(vartran=engine.GetTransaction())
{
tran.Insert<int,int>(t1,1,157)
//Tablet1isinmodificationlistofthistransactionandallreadsfrom
thistableautomaticallyreturnactualdata,evenbeforecommit


//thisrow.Valuewillreturn157
varrow=tran.Select<int,int>(t1,1)
}

Allreadsofthetable(onlyinsidecurrenttransaction),ifitsinmodificationlist(by
SynchronizeTablesorjustinsert/update/remove)willreturnmodifiedvaluesevenifthedata
wasnotcommittedyet:

using(vartran=engine.GetTransaction())
{
tran.Insert<int,int>(t1,1,99)
tran.Commit()
}

using(vartran=engine.GetTransaction())
{

//row.Valuewillreturn99likeotherparallelthreadswhichread
thistable
varrow=tran.Select<int,int>(t1,1)
//butthisthreadwantsalsotomodifythistable
tran.Insert<int,int>(t1,1,117)

//row.Valuewillreturn117(otherthreadswillsee99)
varrow=tran.Select<int,int>(t1,1)

tran.RemoveKey(t1,1)

//row.Existswillbefalse(otherthreadswillsee99)
varrow=tran.Select<int,int>(t1,1)

/
tran.Insert<int,int>(t1,1,111)

//row.Valuewillreturn111(otherthreadswillsee99)
varrow=tran.Select<int,int>(t1,1)

tran.Commit()

//row.Valuewillreturn111(otherthreadswillsee111)
varrow=tran.Select<int,int>(t1,1)
}

TableSynchronizationbyPATTERN

BecauseinNoSqlconceptwehavetohavedealswithmanytablesinsideofone
transaction,DBreezehasspecialconstructionsfortableslocking.Alltheseconstructionsare
availableviatran.SynchronizeTables
.

Again,tran.SynchronizeTablescanbeusedonlyonceinsideofanytransactionbeforeany
modificationcommand,butcanbeusedafterreadcommands:

ALLOWED:

using(vartran=engine.GetTransaction())
{

tran.SynchronizeTable(t1)
tran.Insert<int,int>(t1,1,99)
tran.Commit()
}
using(vartran=engine.GetTransaction())
{

tran.SynchronizeTable(t1,t2)
tran.Insert<int,int>(t1,1,99)
tran.Insert<int,int>(t2,1,99)
tran.Commit()
}

using(vartran=engine.GetTransaction())
{
List<string>ids=newList<string>()
foreach(varrowintran.SelectForward<int,int>(Items))
{
ids.Add(Article+row.Value.ToString())
}

tran.SynchronizeTable(ids)

tran.Insert<int,int>(t1,1,99)
tran.Commit()
}

Note
,itspossibletoinsertdataintotableswhichwerenotsynchronizedby
SynchronizeTable

using(vartran=engine.GetTransaction())
{

tran.SynchronizeTable(t1)

tran.Insert<int,int>(t1,1,99)
tran.Insert<int,int>(
t2
,1,99)
tran.Commit()
}

Butthisisbettertousefotemporarytables,foravoidingdeadlocks.Toadduniquenessto
thetablename(temporarytablename)addThreadId:

using(vartran=engine.GetTransaction())
{
try{

tran.SynchronizeTable(t1)
tran.Insert<int,int>(t1,1,99)
string
tempTable
=temp+tran.ManagedThreadId+_more

//
incaseifpreviousprocesswasinterruptedandtempTablewasnot
deleted
engine.Scheme.DeleteTable(
tempTable
)

tran.Insert<int,int>(
tempTable
,1,99)

//dooperationswithtemptable.....

engine.Scheme.DeleteTable(
tempTable
)

tran.Commit()
}catch(System.Exceptionex)
{
//exhandle
engine.Scheme.DeleteTable(
tempTable
)
}
}

NOTALLOWED:

using(vartran=engine.GetTransaction())
{
tran.Insert<int,int>(t2,1,99)

tran.SynchronizeTable(t1)

tran.Insert<int,int>(t1,1,99)
tran.Commit()
}

Tosynchronizetablesbypatternweusespecialsymbols:

*allothersymbols
#allothersymbolsexceptslash,followedbyslashandanyothercharacter
$allothersymbols,exceptsslash
tran.SynchronizeTable(Articles
*
)willmeanthatweblockforwritingalltableswhichstart
fromthewordArticles,like:

Articles123
Articles231
etc.
Articles123/SubItems123/SubItems123
andsoon.

tran.SynchronizeTable(Articles
#
/Items
*
)willmeanthatweblockforwritingfollowing
tables,like:

Articles123/Items1257/IOo4564

butwedontblock

Articles123/SubItems546

tran.SynchronizeTable(Articles
$
)willmeanthatweblockforwritingfollowingtables,like:

Articles123
Articles456

andwedontblock

Articles456/Items...

Slashcanbeeffectivelyusedforcreatinggroups.

Surewecancombinepatternsinonetran.SynchronizeTablecommand:

tran.SynchronizeTable(Articles1/Items$,Articles#/SubItems*,
Price1,Price#/Categories#/El*)

NonUniqueKeys


InDBreezetablesallkeysmustunique.

Buttherearealotofmethodshowtostorenonuniquekeys.

Oneofthemisforeverynonuniquekeycreateaseparatetableandstoreallreferenceto
thiskeyinside.Sometimesthisapproachisgood.

Butthereisanotherusefulapproach.

Note,thatDBreezeisaprofessionaldatabaseforhighperformanceandmissioncritical
applications.DeveloperspendsalittlebitmoretimefortheDataAccessLayer,butgets
backveryfastresponsesfromdatabase.

ImaginethatyouhaveaplentyofArticlesandeveryofithaspriceinside.Youknowthat
oneoftherequirementsofyourapplicationistoshowarticlessortedbyprice.Another
requirementistoshowarticlesinpricerange.

Itcanmeanthatexceptthetablewhoholdsarticlesyouwillneedaspecialtablewhereyou
willstorepricesaskeys,tobeabletouseDBreezeSelectForwardStartFromor
SelectForwardFromTo.

Developer,whileinsertingonearticle,hastofilltwotables(itsaminimumforthisexample)
ArticlesandPrices.

Buthowwecanstorepricesaskeytheyarenotunique.

Thenwewillmakethemunique.

usingDBreeze
usingDBreeze.Utils
usingDBreeze.DataTypes

publicclassArticle
{

publicArticle()

Id=0

Name=String.Empty

Price=0f

publicuintId{getset}

publicstringName{getset}

publicfloatPrice{getset}
}


publicvoidExample_NonUniqueKey()
{

engine.Schema.DeleteTable("Articles")

using(vartran=engine.GetTransaction())

uintid=0

Articleart=newArticle()

Name="Notebook",

Price=100.0f

id++

tran.Insert<uint,DbMJSON<Article>>("Articles",id,art)

byte[]idAsByte=id.To_4_bytes_array_BigEndian()

byte[]priceKey=art.Price.To_4_bytes_array_BigEndian().Concat(idAsByte)

Console.WriteLine("{0}Id:{1}IdByte[]:{2}btPriceKey:{3}",art.Name,id,
idAsByte.ToBytesString(""),priceKey.ToBytesString(""))

tran.Insert<byte[],byte[]>("Prices",priceKey,null)

art=newArticle()

Name="Keyboard",

Price=10.0f

id++

tran.Insert<uint,DbMJSON<Article>>("Articles",id,art)

idAsByte=id.To_4_bytes_array_BigEndian()

priceKey=art.Price.To_4_bytes_array_BigEndian().Concat(idAsByte)

Console.WriteLine("{0}Id:{1}IdByte[]:{2}btPriceKey:{3}",art.Name,id,
idAsByte.ToBytesString(""),priceKey.ToBytesString(""))

tran.Insert<byte[],byte[]>("Prices",priceKey,null)

art=newArticle()

Name="Mouse",

Price=10.0f

id++

tran.Insert<uint,DbMJSON<Article>>("Articles",id,art)

idAsByte=id.To_4_bytes_array_BigEndian()

priceKey=art.Price.To_4_bytes_array_BigEndian().Concat(idAsByte)

Console.WriteLine("{0}Id:{1}IdByte[]:{2}btPriceKey:{3}",art.Name,id,
idAsByte.ToBytesString(""),priceKey.ToBytesString(""))

tran.Insert<byte[],byte[]>("Prices",priceKey,null)

art=newArticle()

Name="Monitor",

Price=200.0f

id++

tran.Insert<uint,DbMJSON<Article>>("Articles",id,art)

idAsByte=id.To_4_bytes_array_BigEndian()

priceKey=art.Price.To_4_bytes_array_BigEndian().Concat(idAsByte)

Console.WriteLine("{0}Id:{1}IdByte[]:{2}btPriceKey:{3}",art.Name,id,
idAsByte.ToBytesString(""),priceKey.ToBytesString(""))

tran.Insert<byte[],byte[]>("Prices",priceKey,null)

//thisarticlewasaddedlaterandnotreflectedinthepostexplanation

art=newArticle()

Name="MousePad",

Price=3.0f

id++

tran.Insert<uint,DbMJSON<Article>>("Articles",id,art)

idAsByte=id.To_4_bytes_array_BigEndian()

priceKey=art.Price.To_4_bytes_array_BigEndian().Concat(idAsByte)

Console.WriteLine("{0}Id:{1}IdByte[]:{2}btPriceKey:{3}",art.Name,id,
idAsByte.ToBytesString(""),priceKey.ToBytesString(""))

tran.Insert<byte[],byte[]>("Prices",priceKey,null)

tran.Commit()

Console.WriteLine("***********************************************")

//Fetchingdata>=

using(vartran=engine.GetTransaction())

//WeareinterestehereinArticleswiththecost>=10

floatprice=10f

uintfakeId=0

byte[]searchKey=
price.To_4_bytes_array_BigEndian().Concat(fakeId.To_4_bytes_array_BigEndian())

Articleart=null

foreach(varrowintran.SelectForwardStartFrom<byte[],byte[]>("Prices",
searchKey,true))

Console.WriteLine("Foundkey:{0}",row.Key.ToBytesString(""))

varartRow=tran.Select<uint,DbMJSON<Article>>("Articles",
row.Key.Substring(4,4).To_UInt32_BigEndian())

if(artRow.Exists)

art=artRow.Value.Get

Console.WriteLine("Articel:{0}Price:{1}",art.Name,art.Price)

Console.WriteLine("***********************************************")

//Fetchingdata>

using(vartran=engine.GetTransaction())

//WeareinterestehereinArticleswiththecost>10

floatprice=10f

uintfakeId=UInt32.MaxValue

byte[]searchKey=
price.To_4_bytes_array_BigEndian().Concat(fakeId.To_4_bytes_array_BigEndian())

Articleart=null


foreach(varrowintran.SelectForwardStartFrom<byte[],byte[]>("Prices",
searchKey,true))

Console.WriteLine("Foundkey:{0}",row.Key.ToBytesString(""))

varartRow=tran.Select<uint,DbMJSON<Article>>("Articles",
row.Key.Substring(4,4).To_UInt32_BigEndian())

if(artRow.Exists)

art=artRow.Value.Get

Console.WriteLine("Articel:{0}Price:{1}",art.Name,art.Price)

EveryarticlewhenisinsertedtoArticlestablereceivesitsuniqueidottypeuint:

Articles<uint,DbMJSON<Article>>(Articles)

YourememberthatinnamespaceDBreeze.Utilstherearealotofextensionforconverting
differentdatatypestobyte[]andback.Wecanconvertdecimals,doubles,floats,integers
etc.tobyte[]andback.

Articlepriceisfloatinourexampleandcanbeconvertedtobyte[4](sortablebytearrayfrom
DBreeze.Utils,System.BitConverterwillnotgiveyousuchresults).

Asyouseewehad4articles2ofthemhadthesameprice.
Weachieveuniquenessofthepriceonthebytelevelbyconcatenatingtwobytearray.
Firstpartisapriceconvertedtobytearray(forArticle
Keyboard
):
float10.0f>AE0F4240
SecondpartisuintIdfromtableArticlesconvertedtobytearray(forArticle
Keyboard
):
uint2>00000002

whenweconcatenatebothbytearraysforeveryarticlewewillhavesuchresult:

NotebookId:1btPriceKey:AF0F424000000001
//100f
KeyboardId:2btPriceKey:AE0F424000000002
//10f
MouseId:3btPriceKey:AE0F424000000003
//10f
MonitorId:4btPriceKey:AF1E848000000004
//200f

Thatsallexactlythesefinalbytearraysweinsertintotableprices.

Nowfetchingdata

SelectForwardandBackwardfromtablePriceswillgiveyoualreadysortedbypriceresults.

MoreinterestingistogetAllpricesstartingfrom10f.

Forthiswewillusetran.SelectForwardStartFrom(Prices,btKey,true)

weneedtogetbtKey.

Wetakeourdesirable10fandconverttobyte[]
floatfindPrice=10f
byte[]btKey=findPrice.To_4_bytes_array_BigEndian()

thenweneedtoconcatenatewiththebtKeyfullarticleidandhereisatrick:

uintid=0
btKey=btKey.Concat(id.To_4_bytes_array_BigEndian())

willgiveussuchbtKey:
AE0F424000000000

ifweuseitintran.SelectForwardStartFrom(Prices,btKey,true)

wewillreceiveallprices>=10f.

Ifwe

uintid=UInt32.MaxValue
btKey=btKey.Concat(id.To_4_bytes_array_BigEndian())

willgiveussuchbtKey:
AE0F4240FFFFFFFF

applyingsuchkeyintran.SelectForwardStartFrom(Prices,btKey,true)

wewillreceivepriceonly>10f.

Surewhenyougotthekeyfromvalueprice(itsbyte[]),youcanmake
row.Value.Substring(4,4).To_UInt32_BigEndian()receiveyouuintidfromtableArticlesand
retrievevaluefromtableArticlesbythiskey.

[20120521]

Fractaltablesstructure.

Wecallitwithafancywordfractal,becauseithasselfsimilarstructure.

Actually,itsanabilitytostoreinanykindofavalue(ofaKey/Valuetable)from1toNother
tables+extradata.Andinanykindofanestedtablekeysvaluesotherfrom1toNtables+
extradataandsoon,tillyouresourcesletyoudothat.Suchmultidimensionalstorage
concept.

Itcanalsomeanthatinonevaluewecanstoreobjectofanycomplexitykind.Everyproperty
ofthisobjectwhichcanberepresentedasatable(ListorDictionary)inheritsallpossibilities
ofthemastertable.WecanmakeagainfavoriteoperationslikeForward,BackwardSkip,
Remove,Addetc.andthesamewithsubnestedtablesandsubsub....subnestedtables.

Toinsertatableinavalueweneed64bytesitsasizeoftableroot.

Table"t1"
Key|Value
1|/...64byte..../
/...64byte..../
/...64byte..../

Key<int>Value
Key<string> Value

1
/...64byte..../
a5
/...64byte....//...64byte..../

2
/...64byte....//...64byte..../ b6
string

3
t7
int

h8
long
2|/...64byte..../
3|/...64byte....//...64byte..../extradata/...64byte..../extradata/...64byte..../

Note,
itsnotpossibletocopythetablewhichhasinvaluesnestedtableswiththe
techniquesdescribedbefore(simplebytescopying).Butitispossibletoautomatethis
process,becausethetableroothasamarkdbreeze.tiesky.comalwaysstartingatthe
samepointfromtablerootstart,alsotherootlengthisfixedwith64bytes,soonedaywe

willmakethisrecursivecopyfunction.

Note
,wearestillthinkingaboutthemethodsnameswhichweusewhilefetchingnested
tablesandweknowthatthetimewillplacecorrectemphasisherealso.

Fractaltablesstructure.GettingStarted

Everyoperationstartsfromthemastertable.Mastertableisatablewhichisstoredinthe
Schemeandyouperfectlyknowitsname.

tran.Insert<int,string>(t1,1,Hello)
tran.Insert<int,string>(t1/Points,1,HelloAgain)

t1andt1/Pointsaremastertables.

So,letsassumewehavemastertablewiththenamet1.Keysofthistableareofinteger
type.Valuescanbedifferent.

using(vartran=engine.GetTransaction())

tran.Insert<int,string>("t1",1,"hello")

tran.Insert<int,byte[]>("t1",2,newbyte[]{1,2,3})

tran.Insert<int,decimal>("t1",3,324.34M)

Ifyouknowwhatisstoredunderdifferentkeysyoucanalwayscorrectlyfetchthevalues,on
thelowestleveltheyarealwaysbyte[]bytearray.

Toinsertatablewehavedesignednewmethod

tran.InsertTable<int>("t1",4,0)

youneedtosupplyonetypeforkeyresolving,valuewillbeautomaticallyresolvedasbyte
array.Asparametersyouneedtosupplymastertablename,key(4inourexample)and
tableindex.

Asyourememberwecanputmorethen1tableinthevalueandeveryofitwillreside64
bytes.
So,ifindex=0thentablewillresidevaluebytesfrom063,ifindex=1thentablewillreside
valuebytesfrom64127etc....

Inbetweenyoucanputyourownvalues,justremembernottooverlapnestedtablesroots.

Again,wecansay

tran.InsertTable<int>("t1",4,0)
tran.InsertPart<int,int>("t1",4,587,64)

Key4willhave64bytesofatableandthen4reservedbytesforthevalue587.Youcan
workseparatelywiththem.

Note
,method
InsertTable
givesusextraloadtellingthatwewanttoinsert/change/modify.If
the
table
didntexistinthatplaceitwillbe
automaticallycreated
.AlsoInsertTablewill
notifythesystemthatthread,whoisusingit,triestomodifytablet1,thatswhyall
necessarytechniquesliketran.SynchronizeTables,ifyoumodifymorethenonemaster
table,mustbeused.Theyaredescribedinpreviouschapters.

Wehaveanothermethod

tran.SelectTable<int>("t1",4,0)

InoppositetoInsertTableiftableisnotfounditwillnotbecreated.

Note
,method
SelectTable
willnotcreatetableifitdoesntexistandthismethodis
recommendedfor
READINGTHREADS.
ButalsocanbeusedbyWRITINGthreadsjustto
getthetablewithoutitscreation.

Note,
tran.
InsertTable
and
SelectTable
alwaysreturnvalueoftype
DBreeze.DataTypes.NestedTable

NestedTable
repeatsbyfunctionalityTransactionclassinthescopeoftableoperations.You
willfindthereallwellknownmethods:SelectSelectForwardBackward,Insert,InsertPart,
RemoveKey,RemoveAlletc.

Firstdifferenceisthatyoudontneedtosupplytablenameasparameter.

KeyValue
1
2
3
4/*....64byte...table*//*4bytesinteger*/
KeyValue
1Hi1
2Hi2

3Hi3

Tobuildupsuchstructurewedofollowingcode:

tran

.InsertTable<int>("t1",4,0)

.Insert<int,string>(1,"Hi1")

.Insert<int,string>(2,"Hi2")

.Insert<int,string>(3,"Hi3")

tran.Commit()

ThisfunctionalprogrammingtechniqueispossibleduetoreturnsofInsertItreturnsthe
underlyingNestedTable.

Toreadthedatawedofollowing:

tran
.SelectTable<int>("t1",4,0)

.Select<int,string>(1)

.PrintOut()

WewillreceiveHi1

PrintOutisasmallconsoleouthelperforcheckingthecontent.

Letsiterate

foreach(varrowintran

.SelectTable<int>("t1",4,0)

.SelectForward<int,string>()

row.PrintOut()

Note,ifyoutrytoInsertintonestedtableaftermasterSelectTableyouwillreceivean
exception.Inserting(Removing,changingetcallmodifications)intoallnestedtables
generationsisallowedonlystartingfrommasterInsertTablemethod.

Letstrymorecomplexstructure

KeyValue
1
2
3
4/*....64byte...table*/

KeyValue
1Hi1
2/*....64byte...table*//*....64byte...table*/
KeyValue
KeyValue
1Xi17Piar7
2Xi28Piar8

3Hi3

varhorizontal=
tran

.InsertTable<int>("t1",4,0)

horizontal.Insert<int,string>(1,"Hi1")

horizontal

.GetTable<int>(2,0) //weuseittoaccessnexttablegenearation

.Insert(1,"Xi1")

.Insert(2,"Xi2")

horizontal

.GetTable<int>(2,1)

.Insert(7,"Piar7")

.Insert(8,"Piar8")

horizontal.Insert<int,string>(3,"Hi1")

//Hereallvaluesforallnestedtableswillbecommitted

tran.Commit()

//Fetchingvalue

tran.SelectTable<int>("t1",4,0)

.GetTable<int>(2,1)

.Select<int,string>(7)

.PrintOut()

//ReturnwillbePiar7

Note
,thereisnoseparateCommitorRollbackofthenestedtablestheyaredoneviamaster
tableCommitorRollback.

[20120525]

SelectreturnsDBreeze.DataTypes.Row

This
Row
weknowfrompreviousexamples,butnowitsenhancedwithnewmethod
GetTable(uinttableIndex),whereyoucangetnestedtablestoredinsideofthisrowby
tableIndex.Itworksformasterandfornestedtables.

using(vartran=engine.GetTransaction())
{
tran.InsertTable<int>("t1",1,1)
.Insert<uint,string>(1,"Test1")
.Insert<uint,string>(2,"Test2")
.Insert<uint,string>(3,"Test3")

tran.Commit()

//foreach(varrowintran.SelectTable("t1",1,1))alsopossiblebut...

foreach(varrowintran.SelectForward<int,byte[]>("t1"))
{

foreach(varr1inrow.GetTable(1).SelectForward<uint,string>())

r1.PrintOut()

}
}
}

//Resultwillbe
1Test1
2Test2
3Test3

InsertDictionary.SelectDictionary.InsertHashSet.SelectHashSet

Wehavecreatedextrainsertandselectstatementsformastertableandnestedtableto
supportdirectcastsoftheDBreezetablesasaC#DictionaryandHashSet(listofunique
keys).

Dictionary<uint,string>_d=newDictionary<uint,string>()

_d.Add(10,"Hello,myfriends")

_d.Add(11,"Sehrgut!")

Dictionary<uint,string>_b=null

using(vartran=engine.GetTransaction())

//InsertintoMasterTableRow

tran.InsertDictionary<int,uint,string>("t1",10,_d,0,true)

//InsertintoNestedTableDictionary

tran.InsertTable<int>("t1",15,0)

.InsertDictionary<int,uint,string>(10,_d,0,true)

tran.Commit()

//Selectfrommastertable

_b=tran.SelectDictionary<int,uint,string>("t1",10,0)

_b=tran.SelectTable<int>("t1",15,0)

.SelectDictionary<int,uint,string>(10,0)

tran.InsertDictionary<int,uint,string>("t1",10,_d,0,true)

willcreatefollowingstructure:

t1
Key<int>Value<byte[]>
1
2
..
10/*063bytesnewtable*/
Key<uint>Value<string>
10Hello,myfriends
11Sehrgut!

tran
.InsertTable<int>("t1",15,0)
.InsertDictionary<int,uint,string>(10,_d,0,true)

willcreatefollowingstructure:

t1
Key<int>Value<byte[]>
1

2
..
15/*063bytesnewtable*/
Key<int>Value<byte[]>
......
10/*063bytesnewtable*/

Key<uint>Value<string>
10Hello,myfriends
11Sehrgut!

Selectwillbeusedtogetthesevalues,Hashsethasthesamesemantic.

Note
,thereisoneimportantflaginInsertDictionaryandInsertHashSet.Itslastparameter
boolwithValuesRemove.

IfyousuppliedbeforeDictionarywithkeys1,2,3....commit......thennexttimeyousupply
Dictionarywithvalues2,3,4

ifwithValuesRemove=true
thenindbwillstaykeys2,3,4
ifwithValuesRemove=false
thenindbwillstaykeys1,2,3,4

Thesestructuresdesignedashelpfunctionsfor:

Thequickmethodtostoreasetofkeys/valuesintothenestedtablesfromDictionaryor
HashSet(InsertDictionary(....,false)).
HelpfunctionsforsmallDictionaries/HashSetstobestoredandSelectedwithautomatic
removalandupdate(InsertDictionary(....,true)).

AbillitytogetthefulltableofanyKey/ValuetypeasDictionaryorHashSetrightin
memory.

[20120526]

WehavealsoaddedInsert/SelectDictionary/HashSetforthetablesthemselves(notjust
movedbylevels)

Wecanmakefollowing:

insertingrightintot1tablevaluesrepresentedasDictionary:

tran.InsertDictionary<int,int>("t1",newDictionary<int,int>(),false)

insertingintot1row1atablewhichlocatesfrom0byteofrowaDictionary:

tran.InsertTable<int>("t1",1,0).InsertDictionary<uint,uint>(new
Dictionary<uint,uint>(),false)

Correspondingselects:
tran.SelectDictionary<int,int>("t1")
tran.SelectTable<int>("t1",1,0).SelectDictionary<uint,uint>()

ThesameforHashSets.

[20120529]

Nestedtablesmemorymanagement.

Wehaveasituationofmemorygrowthincaseifweuselotsofnestedtablesinside
ofonetransaction.Supportofatabletakesamemoryamount.

Mastertableandnestedintoittablessharethesamephysicalfile.Currentengine
automaticallydisposesmastertableandallnestedtableswhentransaction(workingwith
mastertable)isfinished.Butonlyincasewhenparallelthreadsdontreadfromthesame
tableinthesametime.Mastertableandnestedintoittableswillbedisposedtogetherwith
thelastworkingwiththistabletransaction.Ifwewriteintothetableonceper7secondsand
readonceper2seconds,definitelythistablewillbeabletofreeresidingmemory
inbetween.

Somemoresituations.Forexampleweinsertdatainsuchmanner:

using(vartran=engine.GetTransaction())
{
for(inti=0i<100000i++)
{

tran.InsertTable<int>("t1",i,1)

.Insert<uint,uint>(1,1)
}

tran.Commit()
}

Reallybadcaseforthememory.Inthiscasewehavetoopen100000+1(master)tablesand

holdtheminmemorytilltran.Commit()

Inourtestsusedmemoryhasgrownupfrom30MB(basicrunofatestprogram)upto
350MB...aftertransactionwasfinishedtheprocesssizedidntchange,butthose320MB
weremarkedtobecollectedby.NETGarbageCollector,socallingGC.Collect(orusingthe
processfurther)bringsbackto30MB.

Andfornowitshardtofindoutthewayshowtoavoidthismemorygrowth.Itsnotsocritical
whenyouinsertinsmallchunks(100records).Soyoumustrememberaboutthat.

Anothercase:

Looksevenmoreinteresting.Whenweselectdata

using(vartran=engine.GetTransaction())
{
for(inti=0i<100000i++)
{

varrow=tran.SelectTable<int>("t1",i,1)

.Select<uint,uint>(1,1)
if(row.Exists)
{
//..do
}
}
}

Here,aftereveryloopiterationwedontneedanymoreusedtable,butitstillstaysin
memoryandmakeitgrowing.Inthisexamplememoryhasgrownupfrom30MBupto
135MB,sureifyouselectmorerecordsitwillneedmorememoryresource.

Exactlyforsuchcasewehadtointegratetable.Closemethod.

TouseClose,weneedavariableforaccessingthistable.Ourcodewilllooklikethisnow:

using(vartran=engine.GetTransaction())
{

foreach(varrowintran.SelectForward<int,byte[]>("t1"))
{

vartbl=row.GetTable(1)

if(!tbl.Select<uint,uint>(1).Exists)

Console.WriteLine("not")

tbl.CloseTable()

}
}

Nowmemoryholdsthenecessarylevel.

Note
,WhenwecallNestedTable.Close,method,wewanttoclosecurrenttableandall
nestedinittables.EverymastertableInsertTableorSelectTable(and
nestedTable.GetTable)increaseopenquantityvariableby1,everyCloseTabledecreses
valueby1,whenvalueislessthen1,thenthetablewithallnestedinittableswillbe
closed.
Ifweforgettoclosetablethenitwillbeopentillalloperationswithmastertablearefinished
andautomaticdisposeworks.

Note,NestedTable.DisposecallsCloseTableautomatically,
sowecanmake:

using(vartran=engine.GetTransaction())
{

using(vartbl=row.GetTable(1))
{

if(!tbl.Select<uint,uint>(1).Exists)

Console.WriteLine("not")

}
}
}

Rules.

DontclosethetablebeforeyouCommitorRollbackit.
Transactionendwillclosemasterandnestedtablesautomaticallyifnoother
threadsareworkingwithit,probablyparallelthreadwillcloseitafterfinish.
Closetableinstancesmanuallyifoperationswiththetableareveryintensiveand
thereisnochancethatitwillbeclosedautomatically.
ControlInsertTablethesamewayasSelectTable.
Itspossibletoclosetablesofallnestinggenerations,dependinguponyourtable
structure.Theywillbeclosedstartingfromcalledgeneration.

Thischapterisontheleveloftheexperiment.

SecondaryIndex.Directkeyselect.

Herewepresentanotherexperimentalapproach.

Ifweneedtosupportotherindicesthenourtablekey,wherewestoreourobjectsweneed
tocreateothertableswherekeyswillbesecondaryindexetc.Insecondaryindextablewe
canstoredirectpointerthefirsttablewiththeobjectincontrastwiththekey.

Whenweinsertorchangethekeywehaveanabilitytoobtainitsfilepointer:

byte[]ptr=null

using(vartran=engine.GetTransaction())
{

tran.Insert<int,int>("t1",12,17,outptr)

tran.SelectDirect<int,int>("t1",ptr).PrintOut()

tran.ChangeKey<int>("t1",12,15,outptr)

tran.SelectDirect<int,int>("t1",ptr).PrintOut()
}

thenwecangetthevaluebypointereconomizingtimeforthesearchofthefirsttablekey.

Note
,whenweupdateprimarytable,whoholdsfullinformationabouttheobject,itspointer
canbemoved,thatswhyourDALmustupdatevalue(pointertotheprimarytablekey)in
thesecondarytablealso.Whenwedeletefromprimarytable,wemustdeleteinthesame
transactionfromsecondaryindextablealso.

Thesamewecanmakeinsideofnestedtables.

Note,fornestedtablesSelectDirectmustbeusedexactlyfromthetablewhereyouare
searchinginformationtoavoidcollisions:

byte[]ptr=null

using(vartbl=tran.InsertTable<int>("t3",15,0))
{
tbl.Insert<int,int>(12,17,outptr)
tran.Commit()

using(vartbl=tran.SelectTable<int>("t3",15,0))
{
varrow=tbl.SelectDirect<int,int>(ptr)
row.PrintOut()

Note
,wecangetpointertothevalueinsideof
Insert
,
InsertPart
and
ChangeKey
for
primaryandnestedtables.

[20120601]

DynamiclengthdatablocksandbindingthemtoRow.Column.

Insideofthetablewehavekeyandvalue.Iftothinkaboutthevalueasrowwithcolumns,
thatgivesusabilitytostoreinonerowindependentdatatypes,whichwecanaccessusing
Row.GetValuePart(uintstartIndex,uintlength)andeverythingseemstobegood,whenour
datatypeshavefixedlength.Butsometimesweneedtostoreinsideofcolumns
dynamiclengthdatastructures.

Forthiswehavedevelopedfollowingmethodinsideofthetransactionclass:

publicbyte[]InsertDataBlock(stringtableName,byte[]initialPointer,byte[]data)

Datablocksliveinparallelwiththetableitselfandinheritthesamedatavisibilitybehaviour
fordifferentthreadslikeotherstructures.

NestedtablesalsohaveInsertDataBlockmethod.
Note
,InsertDataBlockalwaysreturnbyte[]ofthesamelength16bytesitsadefinitionof
thestoredvalue,becausereturnedvaluelengthisfixedwecanuseitascolumninsideofa
Row.

Note,
if2parameterinitialPointerisNULLthennewdatablockwillbecreatedforthetable,if
notNULLitcanmeanthatsuchdatablockalreadyexistsandDBreezewilltrytooverwriteit.
Note,datablocksobeytransactionrules,sotillyoucommitupdateddatablock,parallel
readingthreadswillcontinuetoseeitslastcommittedvalue.Wecanalsorollbackchanges.

Afterweinsertdatablockwewanttostoreitspointerinsideofarow,tohaveanabilitytoget
itlater:

byte[]dataBlockPtr=tran.InsertDataBlock("t1",null,newbyte[]{1,2,3})


herewehavereceiveddatablockpointerandwewanttostorethispointerint1row

tran.InsertPart<int,byte[]>("t1",17,dataBlock,10)

wehavestoredpointertothedatablockinsideoft1key(17)startingfromindex10,pointer
hasalwaysfixedlength16byte,startingfromindex26wecangoontostoreothervalues.

Nowwewanttoretrievethedataback:

ItspossibleviaRowobject:

varrow=tran.Select<int,byte[]>("t1",17)

byte[]res=row.
GetDataBlock
(10)

Note,DataBlockcanstorenullvalue.

Updated:
Also,wecannowdirectlygetDataBlocksfromtransaction:
//Whendatablockissavedinmastertable
tran.SelectDataBlock("t1",dataBlockPointer)
//Whendatablockissavedinnestedtable
tran.SelectTable<int>("t1",1,0).SelectDataBlock(dataBlockPointer)

Ifwewanttostorelinktothedatablockinsideofnestedtablerow,wemustmakeitvia
NestedTablemethod:

vartbl=tran.InsertTable<int>("t1",18,0)

byte[]dbp=tbl.InsertDataBlock(null,newbyte[]{1,2,3})

tbl.InsertPart<int,byte[]>(19,dbp,10)

tran.Commit()

tbl.CloseTable()

tbl=tran.SelectTable<int>("t1",18,0)
varrow=tbl.Select<int,byte[]>(19)
byte[]fr=row.GetDataBlock(10)

if(fr==null)
Console.WriteLine("T1NULL")
else

Console.WriteLine("T1"+fr.ToBytesString())

Systemunderstandsemptypointerstothedatablock.Infollowingexamplewetrytoget
notexistingdatablock,thenupdateitandwritepointerback:

varrow=tran.Select<int,byte[]>("t1",17)

byte[]dataBlock=row.GetDataBlock(10)

dataBlock=tran.InsertDataBlock("t1",dataBlock,newbyte[]{1,2,3,7,8})

tran.InsertPart<int,byte[]>("t1",17,dataBlock,10)

tran.Commit()

HashFunctionsofcommonusage.Fastaccesstolongstringsandbytearrays.

DBreezesearchtrieisavariationofradixtrie,optimizedbyallparameters
LianaTrie
.
So,ifwehavekeysoftypeint(4bytes),wewillneedfrom1upto4HDDhitstogetrandom
key(wedonttalkaboutHDDpossibleproblemsandOSfilesystemfragmentationshere).If
wehavekeysoftypelong(8bytes)wewillneedfrom1upto8hits,dependinguponkeys
quantityandcharacter.Ifwestorelongerbytearrays,wewillneedfrom1uptomaxlength
ofthebiggestkeyhits.Ifwestoreinonetable4suchstringkeys:

key1:
http://google.com/hi
key2:
http://google.com/bye
key3:
http://dbreeze.tiesky.com
key4:abrakadabra

togetrandomlykey1wewillneed
http://google.com/
h19hits
togetrandomlykey2wewillneed
http://google.com/
b19hits
togetrandomlykey3wewillneed
http://
d8hits
togetrandomlykey4wewillneedonly1hit

(afteryoufindakeyinrangeselects,searchingofothers,insideofiterationwillworkfast)

So,ifweneedtouseStartsWith,orweneedsortingofsuchtable,wehavetostorekeyslike
theyare.

Butifweneedjustrandomaccesstosuchkeys,thebestapproachwillbetostorenotthe
fullkeysbutonlytheir4/8or16bytesHASHCODES.Also,hashedkeysandvalueswith
directphysicalpointers,canrepresentsecondaryindex.Forexample,infirsttablewestore
keys,liketheyare,withthecontentandinsecondtablewestorehashesofthosekeysand
physicalpointerstothefirsttable.Nowwecangetsortedviewandhavefastestrandom
access(from1upto8hits,ifhashisof8bytes).

Hashescanhavecollisions.WehaveintegratedintoDBreezesourcesMurMurHash3
algorithm(whichreturnsback4byteshash)andaddedtwomorefunctionstoget8bytes
and16byteshashcode.Werecommendtousethose8bytesor16bytesfunctionstostay
collisionsafewithaveryhighprobability.Ifyouneed1000%guarantee,usenestedtable
undereveryhashandstoreinitrealkey(orkeysincaseofcollisions),forcheckingorsome
kindofothertechnique,likeserializedlistofkeyswiththesamehashcode.

DBreeze.Utils.Hash.MurMurHash.MixedMurMurHash3_648bytereturnsulong
and
DBreeze.Utils.Hash.MurMurHash.MixedMurMurHash3_12816bytereturnbyte[]

[20120628]

RowhaspropertyLinkToValue(actuallyitsalinktoKey/Value),forgettingdirectlinktothe
rowandusingittogetherwithSelectDirect.Alllinks(pointerstokey/valuepairs)nowreturn
fixed8bytesandcanbestoredasvirtualcolumninrows.

Also,wecannowdirectlygetDataBlocksfromtransaction:
//Whendatablockissavedinmastertable
tran.SelectDataBlock("t1",dataBlockPointer)
//Whendatablockissavedinnestedtable
tran.SelectTable<int>("t1",1,0).SelectDataBlock(dataBlockPointer)

[20120905]

Integratedincrementaldatabasebackupability.

Tomakeitworkinginstantiatedbreezelikethis:

DBreezeConfigurationconf=newDBreezeConfiguration()

DBreezeDataFolderName=@"D:\temp\DBreezeTest\DBR1",

Backup=newBackup()


BackupFolderName=@"D:\temp\DBreezeTest\DBR1\Bup",

IncrementalBackupFileIntervalMin=30

engine=newDBreezeEngine(conf)

IfBackupobjectisnotincludedinconfigurationorDBreezeisinstantiatedwithout
configuration,likeitwasbefore,incrementalbackupwillbeswitchedoff.Sure,thereisstill
DBreezeconstructorwithoutconfigurationparameteratall.

Ifyouhaveexistingdatabasesyoucanmakeitsfullcopy(snapshot)andstarttocontinue
toworkwiththeincrementalbackupoptionswitchedon.Backupwillcreateonceper
IncrementalBackupFileIntervalMinanewfile(oldfilesarereleasedandcanbecopiedout
anddeleted).Currentbackupfileisalwayslockedbydbreeze.Youhavetospecifyfolderfor
dbreezeincrementalbackupfilesBackupFolderName.Thatsall.

Ifyoustartnewdatabasewithincrementalbackupoption,thenlateryouwillbeableto
recreatethewholedbfrombackupfiles,ifyouhavestartedfromasnapshotthenbackup
filescanbringyoursnapshottocurrentdbstate.

Youcanrestorebackupinthefolderwhereyoursnapshotresidesor,ifincrementalbackup
wasswitchedonfromthebeginning,intotheemptyfolder.

ExampleofbackuprestorationisshowninVisualTestersatelliteprojecttoDBreeze
solution,underbuttonRestoreBackup.

SwitchedonincrementalbackupoptionbringstoWritespeeddecrease,Readspeedis
untouched.

Insertingonemillionofintegerswithoutbackupoption9secwithoption17sec.

[20120922]

!!!!!!!!!!!!!!!!!!!!!!!!IMPORTANTWHOUSEDDATABASETILLTHISPERIOD
!!!!!!!!!!!!!!!!!!!!!!!!ANDUSEDDBreeze.UtilsByteProcessingextensions:DateTime
!!!!!!!!!!!!!!!!!!!!!!!!.To_8_bytes_array_BigEndian()and
!!!!!!!!!!!!!!!!!!!!!!!!byte[]To_DateTime_BigEndian()

AfterattachingnewDBreezeandrecompilationoftheprojectyouwillseeerrors,because
suchfunctionsdontexistanymoreinDBreeze.

Why?
Itsanissue,historicalissue.OurDBreezegenerictypeconverter(weuseitin

tran.Insert<DateTime,DateTime..tran.InsertPart<DateTimeetc.)waswrittenbeforesome
ByteProcessingUtilsfunctionsandsomehowDateTimewasconvertedfirstto
ulong
and
thentobyte[].Otherwise,To_DateTime_BigEndian()andTo_8_bytes_array_BigEndian()
fromDBreeze.Utilsused
long
,suchunpleasantthing.

Well,nowwhat?
So,wehavedecidedtoleaveDateTimeconvertertoworkwithulong.Itdoesnthave
influenceonthespeed,andwedontneedtorecreatemanyexistingdatabases.

WehavecreatedinsteadsuchfunctionsinDBreeze.Utils.ByteProcessing:
publicstatic
DateTimeTo_DateTime(thisbyte[]value)
andthiswillworkwith
ulong
and
publicstatic
byte[]To_8_bytes_array(thisDateTimevalue)
whichrecreatesDateTimefrom8byte
array.
Withthisfunctionswerecommendtoworkinthefuture
.Thesamealgorithmsare
usedbygenericconverter.

But,ifyouhavealreadyusedmanualDateTimeconversions,wehavelefttwofunctionsfor
compatibility:

publicstaticbyte[]
To_8_bytes_array_zCompatibility
(thisDateTimevalue)
(thisyoumustputinthecodeinsteadofoldTo_8_bytes_array_BigEndianconcerning
DateTime)and

DateTime
To_DateTime_zCompatibility
(thisbyte[]value)(thisyoucanuseinsteadofold
To_DateTime_BigEndian)

TheybothgoontoworkwithDateTimeaslongtobyte[].

So,thinkaboutthatanddowhatyoushoulddo:)
Actually,nothingshouldstopusonthelightwayoftheGodsLove!

Storinginthevaluecolumnsofthefixedsize.

Forthelastsomemonthswehavecreatedmanytableswithdifferentvalueconfigurations,
combiningwaysofthedatastorage.Oneofthemostpopularwayishandlingvaluebyte[]as
setofcolumnsoffixedlength.Wefoundoutthatwehavelackofnullabledatatypesandfor
thiswehaveaddedinDBreeze.Utils.ByteProcessingarangeofextensionsforallstandard
nullabledatatypes:

Youtakeanystandardnullabledatatypeint?,bool?,DateTime?,decimal?,float?uint?etc.
andconvertitintobyte[]usingDBreeze.Utilsextensions:

publicstaticbyte[]To_5_bytes_array_BigEndian(thisint?value)
or
publicstaticbyte[]To_16_bytes_array_BigEndian(thisdecimal?input)

etc...

andthesamebackward:

publicstaticDateTime?To_DateTime_NULL(thisbyte[]value)
or
publicstaticushort?To_UInt16_BigEndian_NULL(thisbyte[]value)

etc.withNULLintheend

Note,thatpracticallyallnullableconverterscreatebyte[]on1bytelongerthennotnullable.

SometimesinonevalueweholdsomecolumnsoffixedlengththensomeDataBlocks,which
representpicturesorsoandthenDataBlockswhichrepresentbigtextorjsonserialized
objectparts.Butwefoundout,thatwemissstoringoftextintheway,likestandardRDBMS
makethat:nvarchar(50)NULLorvarchar(75).SurewecanuseDataBlocksforthat,but
sometimeswedontwantit,especiallyhavingthatDataBlockreferencewillreside16bytes.

WehaveaddedinDBreeze.UtilsByteProcessingtwomoreextensions:

publicstaticbyte[]To_FixedSizeColumn(thisstringvalue,shortfixedSize,bool
isASCII)

and

publicstaticstringFrom_FixedSizeColumn(thisbyte[]value,boolisASCII)

TheybothwillemulatebehaviourofRDBMStextfieldsofthefixedreservationlength.
Maximum32KB.Minimum1byteforASCIItextand4bytesforUTF8text.

Takeastring(itcanbealsoNULL)andsay:

stringa=mytext
byte[]bta=a.
To_FixedSizeColumn(50,true)

andyouwillreceivebytearrayof50+2=
52bytes
thisyoucanstoreinyourvaluefrom
specificplace(letssay10).

Note,returnedsizewillbealways2byteslongerweneedthemtostorelengthofthereal
textinsideofthefixedsizearrayandNULLflag.

Thentakeyourvalue.Substring(10,52).
From_FixedSizeColumn(true)
andyouwillreceive
yourmytext.isASCIImustbesettofalseifyoustoreUTF8value.Ifsizeofthetext
exceedsthefixedSizeparameter,thenvaluewillbetruncated(correctalgorithmisused,so
onlyfullUTF8charswillbestoredwithoutanygarbagebytesintheend).

Sometimes,itsveryusefulasafirstbyteofthevaluetosetuparowversion,then,
dependinguponthisversion,thefurthercontentofthevaluecanhavedifferent
configurationsofthecontent.

[20121012]

Behaviouroftheiteratorswiththemodificationinstructionsinside.

Letsassumethatbeforeeveryfollowingexample,wedeletetablet1andthenexecute
suchinsert:

using(vartran=engine.GetTransaction())
{

for(inti=200000i<800000i++)
{
tran.Insert<int,int>("t1",i,i)
}

tran.Commit()
}

Sometimesitsinterestingforustomaketablemodificationswhileiteration,likehere:

using(vartran=engine.GetTransaction())
{
//t1isnotinmodificationlist,enumeratorsvisibilityscopeisparallelread

foreach(varrowintran.SelectForward<int,int>("t1"))
{

tran.RemoveKey<int>("t1",row.Key)

tran.Commit()

insuchexampleitwillworkgood.

Inthenextexampleitwillalsowork:

using(vartran=engine.GetTransaction())
{

tran.SynchronizeTables("t1")

//t1isinmodificationlist,enumeratorsvisibilityscopeissynchronizedread/write
//probablywecanseechangesmadeinsideofiterationprocedure.

var
en
=tran.SelectForward<int,int>("t1").
GetEnumerator
()

while(
en
.
MoveNext
())
{

tran.RemoveKey<int>("t1",en.Current.Key)
}

tran.Commit()
}

Enumeratoren,referstowritingrootatthismoment,becauseourtablewasaddedinto
modificationlist(bySynchronizeTableoranyothermodificationcommand,likeinsert,
removeetc...),andchangesofthetable,evenbeforecommitting,canbereflectedinsidethe
enumerator.

But,wedeletethesamekeywhichweread,thatswhythistaskwillbeaccomplishedgood.
Wedontinsertordeleteelementsofthefutureiterations.

Inthenextexamplewecanhavenotdesiredbehaviour:

using(vartran=engine.GetTransaction())
{

tran.SynchronizeTables("t1")

//t1isinmodificationlist,enumeratorsvisibilityscopeissynchronizedread/write
//probablywecanseechangesmadeinsideofiterationprocedure.

intpq=799999


varen=tran.SelectForward<int,int>("t1").GetEnumerator()

while(en.MoveNext())
{

tran.RemoveKey<int>("t1",
pq
)
pq
}

tran.Commit()
}

Wewillnotdeleteallkeysinthepreviousexample.Enumeratorwillstoptoiterate
somewhereinthemiddle,whereexactlydependsuponkeystructureandnotreallyuseful
forus.

So,ifyouaregoingtoiteratesomethingandchangepossibleelementsofthefuture
iterations,thereisnoguaranteeforthecorrectlogicexecution.Thisconcernssynchronized
iterators.

Tomakeitcorrect,wehaveaddedforeveryrangeselectfunctionanoverloadwiththe
parameter
boolAsReadVisibilityScope
.Itconcernsnestedtablesrangeselectfunctions
also.

Nowwecanmakesomethinglikethis:

using(vartran=engine.GetTransaction())
{

tran.SynchronizeTables("t1")

//t1isinmodificationlist,enumeratorsvisibilityscopeissynchronizedread/write
//probablywecanseechangesmadeinsideofiterationprocedure.

intpq=799999

varen=tran.SelectForward<int,int>("t1"
,true
).GetEnumerator()

while(en.MoveNext())
{

tran.RemoveKey<int>("t1",
pq
)
pq
}

tran.Commit()
}


Allkeyswillbedeletedcorrectly.Becauseourenumeratorsvisibilityscopewillbethesame
asinparallelthread,soitwillseeonlycommitteddataprojection,beforethestartofthe
currenttransaction.

Nowwecanvarywhichvisibilityscopefortheenumerator,whosetableisinsideof
modificationlist,wewanttochoose,synchronizedorparallel.Defaultrangeselects,without
extraparameter,iftableisinmodificationlistwillreturnsynchronizedview.

[20121015]

SecondaryIndexes.Goingdeeper.

Transaction/NestedTablemethod
Select
nowisalsooverloadedwith
bool
AsReadVisibilityScope,
forthesamepurposesasdescribedinthepreviouschapter.

Letsassumethatwehaveanobject:

publicclassArticle
{
[PrimaryKey]
publiclongId=12

publicstringName=A1

[SecondaryKey]
publicfloatPrice=15f
}

PrimaryandSecondarykeysattributes,fornow,dontexistinDBreeze.Butideaisfollowing:
fromfieldIdwewanttomakePrimaryindex/keyandfromfieldPricewewanttocreate
oneofoursecondaryindexes.

FornowDBreezedoesnthaveextraobjectlayer,sowewouldmakesuchsaveinthe
followingformat:

usingDBreeze
usingDBreeze.Utils

publicvoidSaveObject(Articlea)
{
byte[]ptr=null

using(vartran=engine.GetTransaction())

{
//InsertingintoPrimaryTable
tran.Insert<long,byte[]>
(Article,
a.Id.To_8_bytes_array_BigEndian(),
//Idprimarykey
a.Name.To_FixedSizeColumn(50,false)
//letitbenotDataBlock
.Concat(
a.Price.To_4_bytes_array_BigEndian()
),
outptr
//gettingbackaphysicalpointer
)

//InsertingintoSecondaryIndextable

tran.Insert<byte[],byte[]>
(ArticleIndexPrice,
a.Price.To_4_bytes_array_BigEndian()
//compoundkey:price+Id
.Concat(
a.Id.To_8_bytes_array_BigEndian()
,
ptr
//valueisapointertotheprimarytable
)
)

tran.Commit()
}
}

Somethinglikethis.Inthereallifeallprimaryandsecondaryindexescouldbepackedinto
thenestedtablesofoneMasterTableunderdifferentkeys.

Wehavefilled2tables.FirstisArticle.AskeytherewestoreArticle.Idasvaluewestore
articlenameandprice.SecondtableisArticleIndexPrice.Itskeyisconstructedfrom
(float)Price+(long)ArticleIditsunique,sortable,comparableandsearchable.Such
techniquewasdescribedinpreviousarticles.Asavaluewestorephysicalpointertothe
primarykeyinsideoftheArticletable.Whenwehavesuchphysicalpointer,searchingof
Key/ValueofthePrimaryTableArticleisonlyoneHDDhit.

Butkeysandvaluesarenotalwaysstatic.Sometimesweremovearticles,sometimeswe
changethepriceorevenexpandthevalue(inthelastcase,weneedtosavenewphysical
pointerintosecondaryindextable).

IfweremoveArticle,wemustremovecompoundkeyfromthetableArticleIndexPricealso.
Whenweupdateprice,insideoftableArticle,wemustdeleteoldcompoundkeyfromthe
tableArticleIndexPriceandcreatenewone.


Itmeans,thateverytimewhenweinsertsomethingintotableArticleitcanbecountedasa
probableupdate,andwemustcheck,ifrowwithsuchIdexistsbeforeinsert.Ifyesthenwe
mustreadit,deletecompoundkey,constructandinsertnewcompoundkeyintothetable
ArticleIndexPriceandfinallyupdatevalueinthetableArticle.

Thisallcanslowdowninsertprocessverymuch.

Thatswhywehaveaddedforeverymodificationcommand,insideofthetransactionclass
andnestedtableclass,usefuloverloads:

Modificationcommandsoverloads(thesamefornestedtables):

publicvoid
Insert
<TKey,TValue>(stringtableName,TKeykey,TValuevalue,outbyte[]
refToInsertedValue,
outboolWasUpdated
)

publicvoid
InsertPart
<TKey,TValue>(stringtableName,TKeykey,TValuevalue,uint
startIndex,outbyte[]refToInsertedValue,
outboolWasUpdated
)
publicvoid
ChangeKey
<TKey>(stringtableName,TKeyoldKey,TKeynewKey,outbyte[]
ptrToNewKey,
outboolWasChanged
)

publicvoid
RemoveKey
<TKey>(stringtableName,TKeykey,
outboolWasRemoved
)

Actually,Dbreeze,wheninsertsdata,knows,ifitsgoingtobeanupdateornewinsert.
ThatswhyDbreezecannotifyusaboutthis.

Wegoontoinsertdatainusualmanner.Ifflag
WasUpdated
equalstotrue,thenweknow
thatitwasanupdate.Wecanuseournew,
overloaded
withvisibilityscopeparameter,
Select
togetkey/valuepair,whichwasbeforemodificationandchangesecondaryindex
table.Weneedtomakethisactiononlyincaseofupdate/remove/changecommand,butnot
incaseofthenewinsert.

[20121016]

SecondaryIndexes.Goingdeeper.Part2

IfwestoreinsideofvalueDataBlocks(notjustserializedvalueorcolumnsoffixedlength),
beforewemakeanupdateofsuchvalue,wemustreaditinanycasepreviousvaluecontent
(togetDataBlocksinitialpointersforupdates).So,againeveryinsertcanbecountedas
probableupdate.Followingtechnique/benchmarkshowsustimeconsumptionforreading
previousrowvalueversionbeforeinsert:

Thisisastandardinsert:

using(vartran=engine.GetTransaction())

byte[]ptr=null

boolwasUpdated=false

DBreeze.Diagnostic.SpeedStatistic.StartCounter("a")

for(inti=200000i<800000i++)

tran.Insert<int,int>("t1",i,i,outptr,outwasUpdated)

}
DBreeze.Diagnostic.SpeedStatistic.PrintOut("a",true)

tran.Commit()

Operationtook
9300ms(9sec2012y,1.5sec2015y)
.
1MLN
of
inserts
.

Thisisaninsertwithgettingpreviousrowversionbeforeinsert:

using(vartran=engine.GetTransaction())
{
byte[]ptr=null

DBreeze.DataTypes.Row<int,int>row=null

DBreeze.Diagnostic.SpeedStatistic.StartCounter("a")

for(inti=200000i<800000i++)
{

//Note,weuseSelectwithVisibilityScope=ParallelRead

row=tran.Select<int,int>("t1",i,
true
)

if(row.Exists)

//doupdate
tran.Insert<int,int>("t1",i,i,outptr)

else

{
//doinsert

tran.Insert<int,int>("t1",i,i,outptr)

}
}

DBreeze.Diagnostic.SpeedStatistic.PrintOut("a",true)

tran.Commit()
}

Thisoperationtook
10600ms(10sec)
.
1MLN
of
inserts,
distinguishingbetweenupdates
andinserts.
Remember,thatDBreezeinsertandselectalgorithmsworkwithmaximumefficiency
inbulkoperations,whenkeysaresuppliedsortedinascendingorder(descendingis
abitslower).So,sortbulkchunksinmemorybeforeinserts/selects.

Previous2exampleswereaboutpureinserts,andwerunthemagainhavingdataalreadyin
thetable,soallrecordshavetobeupdated:

1example1MLNofupdatestook28sec
2example1MLNofupdateswith(gettingrowpreviousversion)took36sec.

[20121023]

Dbreezelikeinmemorydatabase.

Dbreezecanresidealsofullyinmemory.Itsjustafeature.Havingthesamefunctionalityas
diskbasedversion.

Instantiatingexample:

DBreeze.DBreezeEnginememeng=newDBreezeEngine(newDBreezeConfiguration()

Storage=DBreezeConfiguration.eStorage.MEMORY

})

using(vartran=memeng.GetTransaction())
{

for(inti=0i<1000000i++)

tran.Insert<int,int>("t1",i,i)

Console.WriteLine(tran.Count("t1"))

tran.Commit()

Itworksabitslowerthen.NETDictionaryorSortedDictionary,becausehaslotsof
subsystemsinside,whichmustbesupported,anddesignedtoworkwithverylargedata
sets,withoutindexfragmentationaftercontinuousinserts,updatesanddeletes.

Outoftheboxbulkinsertspeedincrease.

WehaveincreasedstandardbulkinsertspeedofDBreeze(about5times),byaddinga
specialmemorycachelayerbeforeflushingdataonthedisk.Bystandardconfiguration,20
tables,whicharewritteninparallel,receivesuchmemorybufferofsize1MBeach,before
diskflush.The21th(andsoon,parallel)willwritewithoutbuffer.Afterdisposingofthe
writingtransactionsothertablescanreceivesuchbuffer,soitsnotboundtothetables
namestablesarechosenautomaticallyrightintimeoftheinsert.

NowDBreeze,instandardconfiguration,canstoreinbulk(ascendingordered)500Krecords
per1seconds(BenchmarkPCistaken).6parallelthreadscouldwriteinto6differenttables
1MLNofrecordseach,forthe3.4seconds,whatwasabout40MB/sand1.7MLNsimple
recordspersecond(seeBenchmarkingdocument).

[20121101]

IterationsSelectBackwardStartsWithClosestToPrefixand
SelectForwardStartsWithClosestToPrefix.

Theybothconcernmasterandnestedtables.

Ifwehaveinthetablestringkeys:

"check
"sam"
"slash
"slam"
"what"
stringprefix="slap"

foreach(varrowintran.SelectForwardStartsWithClosestToPrefix<string,byte>("t1",prefix))
{
Console.WriteLine(row.Key)
}

Result:


slam
slash

andfor
foreach(varrowintran.SelectBackwardStartsWithClosestToPrefix<string,byte>("t1",prefix))

Result:

slash
slam

[20121111]

Alternativetablesstoragelocations.

StartingfromcurrentDBreezeversionweareabletosetuptableslocationsbytablenames
patternsglobally.WecanmixtablesphysicallocationsinsideofoneDBreezeinstance.
Tablescanresideindifferentfolders,ondifferentharddrivesandeveninmemory.

DBreezeConfigurationobjectisenrichedwiththepublicaccessibleDictionary
AlternativeTablesLocations.

NowwecancreateDBreezeconfigurationinthefollowingformat:

DBreezeConfigurationconf=newDBreezeConfiguration()

DBreezeDataFolderName=@"D:\temp\DBreezeTest\DBR1",

Storage=DBreezeConfiguration.eStorage.DISK,

//SETTINGUPALTERNATIVEFOLDERFORTABLEt11
conf.AlternativeTablesLocations.Add(
"t11
",@"D:\temp\DBreezeTest\DBR1\INT")

//SETTINGUPTHATALLTABLESSTARTINGFROMmem_mustresideinmemory
conf.AlternativeTablesLocations.Add("
mem_*
",
String.Empty
)

//SETTINGUPTablepatterntoresideindifferentfolder
conf.AlternativeTablesLocations.Add(
t#/Items
",@"D:\temp\DBreezeTest\DBR1\EXTRA")

engine=newDBreezeEngine(conf)

So,if
value
oftheDictionaryAlternativeTablesLocationskeyis
empty
,tablewillbe

automaticallyforcedtowork
inmemory
.Ifpatternforthetableisnotfound,tablewillbe
created,overridingDBreezemainconfigurationsettings(DBreezeDataFolderNameand
StorageType).

If
onetable
corresponds
tosomepatterns
,the
first
onewillbe
taken
.

PatternslogicisthesameasinTransactionSynchronizeTables:

$*#patternextrasymbols
"U"intersects,!Udoesntintersect

*1ormoreofanysymbolkind(everysymbolafter*willbecutted):Items*U
Items123/Picturesetc...
#symbols(exceptslash)followedbyslashandminimumanothersymbol:Items#/PictureU
Items123/Picture
$1ormoresymbolsexceptslash(everysymbolafter$willbecutted):Items$UItems123
Items$!UItems123/Pictures

Patternscanbecombined:

Items#/Pictures#/Thumbs*
canintersect
Items1/Pictures125/Thumbs44
or
Items458/Pictures4658/Thumbs1000
etc...

Incrementalbackuprestorerworksonthefilelevelandknowsnothingaboutuserslogical
tablenames.Itwillrestorealltablesinonespecifiedfolder.Later,afterstartingDBreezeand
readingthescheme,itspossiblemanuallytoresidedisktablefilesintocorresponded
physicalplacesduetothestoragelogic.

[20130529]

Speedingupbatchmodifications(updates,randominserts)

ToeconomizediskspaceDBreezetriestoutilizethesameHDDspace,ifitspossible,in
caseofdifferenttypesofupdates.
Thereare3placeswhereupdatesarepossible:
Updateofsearchtrienodes(LianaTrienodes)
UpdateofKey/Values
UpdateofDataBlocks

Tobesurethatoverwritingdatafilewillnotbecorruptedincaseofpowerloss,firstwehave
towritedataintorollbackfile,thenintodatafile.DBreezeinstandardmodeexcludesany
OSintermediatecache(onlyinternalDBreezecache)andmakeswritestothebaremetal.
TodaysHDDsandevenSSDsarequiteslowfortherandomwrite.Thatswhyweusea
techniqueofchangingrandomwritesintosequentialwrites.


WhenweuseDBreeze,forstandarddataaccumulationoftherandomdatafromdifferent
sources,insideofsmalltransactions,thespeeddegradeisnotsovisible.Butwecanseeit
verygoodwhenweneedtoupdateabatchofspecificdata.

WeDONTSEESPEEDDEGRADE,when
weinsertbatchofgrowingupkeysanynewly
insertedkeyisalwaysbiggerthanmaximalexistingkey(SelectForwardwillreturnnewly
insertedkeyasthelastone).Forsuchcaseweshoulddonothing.

WeCANSEESPEEDDEGRADE,when
weupdatebatchofvaluesordatablocksorifwe
insertabatchofkeysinrandomorderand,especially,ifthesekeyshavehighentropy.

Forsuchcaseswehaveintegratednewmethodsfortransactionsandfornestedtables:

tran.
Technical_SetTable_OverwriteIsNotAllowed(t1)

or

vartblABC=tran.InsertTable<byte[]>(masterTable,newbyte[]{1},0)
tblABC.
Technical_SetTable_OverwriteIsNotAllowed()

Thistechniqueisinterestingforthetransactionswithspecificbatchmodifications,
wherespeedreallymatters.Onlydevelopercananswerthisquestionandfinda
balance.
Thistechniqueisnotinterestingforthememorybaseddatastores.
Thesemethodsworkonlyinsideofonetransactionandmustbecalledforevery
tableornestedtableseparately,beforetablemodificationcommand.
Whennewtransactionstarts,overwriteautomaticallywillbeallowedagainforall
tablesandnestedtables.
Overwritingconcernsall:searchtrienodes,valuesanddatablocks.
Rememberalwaystosortbatchascendingbykey,beforeinsertitwilleconomize
HDDspace.

Ofcoursethistechniquemakesdatafilebigger,butitreturnsthedesiredspeed.Alldata
whichcouldbeoverwrittenwillbewrittentotheendofthefile.

Note

When
Technical_SetTable_OverwriteIsNotAllowed
isused,
InsertPart
stilltriestoupdate
values,thatcanbringtospeedloss.Ifweneedthespeedwhileupdate,wecanusesuch

workaround
:
don'tuseInsertPart,onlyInsert
readthewholevalueintomemoryasbyte[]
thenchangeitsmiddlepart(withDBreeze.Utils.BytesProcessingCopyInsideor
CopyInsideArrayCanGrow)
insertthecompletevalue.
Allthetime
Technical_SetTable_OverwriteIsNotAllowed
canbeon.

SourcecodereceivednewfolderDBreeze\bin\Release\NET40wherewestore
DBreeze.dllreadyforMONOand.NET4>usage.ThisfolderDBreeze\bin\Release\will
holdDBreezefor.NET35(Windowsonly).

DBreezeversionfor.NET35canbeusedonlyunderWindows,causeutilizessystem
APIFlushFileBuffersfromkernel32.dll

DBreezeversionfor.NET40doesntuseanysystemAPIfunctionsandcanbeused
underLinuxMONOandunder.NET4>.ForWindows,besuretohavelatests.NET
Frameworkstartingfrom4.5,becausethereMicrosofthasfixedbugwith
FileStream.Flush(true).

[20130608]

Restoringtablefromtheothertable.

StartingfromDBreezeversion01.052wecanrestoretablefromtheothersourcetableon
thefly.

Theexamplecodeofcompaction:

privatevoidTestCompact()
{

using(vartran=engine.GetTransaction())
{
tran.Insert<int,int>("t1",1,1)
tran.Commit()
}


DBreezeEngineengine2=newDBreezeEngine(@D:\temp\DBreezeTest\DBR2)

using(vartran=engine.GetTransaction())
{
tran.SynchronizeTables("t1")

using(vartran2=engine2.GetTransaction())
{

//Copyingfrommainengine(Tablet1)toengine2(tablet1),withchangingallvaluesto2

foreach(varrowintran.SelectForward<int,int>("t1"))
{
tran2.Insert<int,int>(t1,row.Key,2)
}

tran2.Commit()
}

engine2.Dispose()
//engine2isfullyclosed.

//movingtablefromengine2(physicalname)tomainengine(logicalname)

tran.RestoreTableFromTheOtherFile("t1",@"D:\temp\DBreezeTest\DBR2\10000000")
//Point555

//Checking

using(vartran=engine.GetTransaction())
{
foreach(varrowintran.SelectBackward<int,int>("t1"))
{
//GETTINGKEY2
Console.WriteLine("Key:{0}",row.Key)

}
}

Uptopoint555everythingwasok,whilecopyingdatafromoneengineintoanother,parallel
threadscouldreaddatafromtablet1ofthemainengine,parallelwritingthreadsofcourse
wereblockedby
tran.SynchronizeTables("t1")
command.

Startignfrompoint555someparallelthreadswhichwerereadingtablet1couldhavein
memoryreferencetotheoldphysicalfile,readingvaluesfromsuchreferencescanbringto
DBreezeTABLE_WAS_CHANGED_LINKS_ARE_NOT_ACTUALexception.

Discussionlinkis
https://dbreeze.codeplex.com/discussions/446373

Note:DONTUSECOMMITAFTER
RestoreTableFromTheOtherFileCOMMAND,justclose
transaction.

[20130613]

Fulltableslockinginsideoftransaction.

Parallelthreadscanopentransactionsandinparallelreadthesametables,inourstandard
configuration.Forwritingthreadsweusetran.SynchronizeTablescommandtosequentialize
writingthreadsaccesstothetables.

Butwhatifwewanttoblockaccesstothetableseveninparallelreadingthreads,while
modificationcommandsofourcurrenttransactionarenotyetfinished?

Forthiswehavedevelopedspecialtypeoftransaction.

using(vartran=engine.GetTransaction(eTransactionTablesLockTypes.EXCLUSIVE,"t1","p*","c$"))

{
tran.Insert<int,string>("t1",1,"Keshaisagoodparrot")

tran.Commit()

using(vartran=engine.GetTransaction(eTransactionTablesLockTypes.SHARED,"t1"))

{
tran.Insert<int,string>("t1",1,"KeshaisVERYagoodparrot")

tran.Commit()


Insideofsuchtransactionwewanttodefinethelocktypeforthelistedtables.

Note,wemustuseeitherfirsttransactiontype(engine.GetTransaction())ornewtype
(withSHARED/EXCLUSIVE)forthesametablesamongthewholeprogram.

Exampleofusage:

privatevoidExecF_003_1()

using(vartran=engine.GetTransaction(eTransactionTablesLockTypes.EXCLUSIVE,"t1","p*",
"c$"))

Console.WriteLine("T1{0}>{1}{2}",DateTime.Now.Ticks,
System.Threading.Thread.CurrentThread.ManagedThreadId,DateTime.Now.ToString("HH:mm:ss.ms"))

tran.Insert<int,string>("t1",1,"Keshaisagoodparrot")

tran.Commit()

Thread.Sleep(2000)

privatevoidExecF_003_2()

List<string>tbls=newList<string>()

tbls.Add("t1")

tbls.Add("v2")

using(vartran=engine.GetTransaction(eTransactionTablesLockTypes.SHARED,
tbls.ToArray()))

Console.WriteLine("T2{0}>{1}{2}",DateTime.Now.Ticks,
System.Threading.Thread.CurrentThread.ManagedThreadId,DateTime.Now.ToString("HH:mm:ss.ms"))

foreach(varrintran.SelectForward<int,string>("t1"))

Console.WriteLine(r.Value)

privatevoidExecF_003_3()

using(vartran=engine.GetTransaction(eTransactionTablesLockTypes.SHARED,"t1"))

Console.WriteLine("T3{0}>{1}{2}",DateTime.Now.Ticks,
System.Threading.Thread.CurrentThread.ManagedThreadId,DateTime.Now.ToString("HH:mm:ss.ms"))

//Thismustbeusedinanycase,whenSharedthreadscanhaveparallelwrites

tran.SynchronizeTables("t1")


tran.Insert<int,string>("t1",1,"KeshaisaVERYgoodparrot")

tran.Commit()

foreach(varrintran.SelectForward<int,string>("t1"))

Console.WriteLine(r.Value)

usingDBreeze.Utils.Async

privatevoidtestF_003()

Actiont2=()=>

ExecF_003_2()

t2.DoAsync()

Actiont1=()=>

ExecF_003_1()

t1.DoAsync()

Actiont3=()=>

ExecF_003_3()

t3.DoAsync()

TransactionsmarkedasSHAREDwillbeexecutedinparallel.EXCLUSIVEtransactionwill
waittillothertransactions,consumingthesametables,arestoppedandthenblockaccess
forotherthreads(readingorwriting)totheconsumingtables.

Thisapproachisgoodforavoidingtransactionexceptions,incaseofdatacompactionor
removingkeyswithfilerecreation,describedinpreviouschapter.

[20130811]

RemoveKeyValueandgetdeletedvalueandnotificationifvalueexistedinoneround.

ForthiswehaveaddedoverloadinMasterandinNestedtables:
RemoveKey
<TKey>(string
tableName,TKeykey,outboolWasRemoved,outbyte[]deletedValue)

[20130812]

InsertkeyoverloadforMasterandNestedtable,lettingnottooverwritekeyifit
alreadyexists.

ForthiswehaveaddedoverloadinMasterandinNestedtables:

publicvoid
Insert
<TKey,TValue>(stringtableName,TKeykey,TValuevalue,outbyte[]
refToInsertedValue,outbool
WasUpdated
,bool
dontUpdateIfExists
)

WasUpdatedwillbecometrue,ifvalueexists,andfalseifsuchvalueisnotinDB.
dontUpdateIfExists,
equaltotrue,willnotgiveDBtomakeanupdate.

SpeedingupselectoperationsandtraversalswithValuesLazyLoadingIsOn.

DBreezeuseslazyvalueloadingtechnique.Forexample,wecansay
varrow=transaction.Select<int,int>(t1,1)
atthismomentwereceivearow.Weknowthatsuchrowexistsbyrow.Existspropertyand
weknowitskeybyrow.Keyproperty.Atthismomentvalueisstillnottakenintomemory
fromdisk.ItwillbereadoutfromDBonlywhenweinstructrow.Value.

Sometimesitisgood,whenforustheonlykeyisenough.Suchcasescanhappenwhenwe
storesecondaryindexandthelink,totheprimarytable,asapartofthekey.Orifwehave
multiplecolumnsinonerow.Weneedtogetonlyonecolumnanddontneedtoget
complete,probablyhuge,value.

Nevertheless,lazyloadwillworkabitslower,incomparewithgettingkeyandvalueinone
round,duetoextraHDDhits.


Forthiscasewehavedevelopedintransactionaproperty/switch
tran.
ValuesLazyLoadingIsOn
.BydefaultitisON(true),justsetittofalseandall
transactiontraversalcommands,likeSelectForwards,Backwardsetc.,willreturnusrow
alreadywithareadoutValue.ThisswitchwillalsoinfluentNestedTableswhichwegetfrom
tran.InsertTable,SelectTableandrow.GetTable.
Wecansetthisswitchmanytimeswithinonetransactiontotunethespeedofdifferent
queries.

[20140603]

Storingbyte[]serializedobjectsasvalue,nativesupport.

Startingfromnowwecanbindanybyte[]serializer/deserializertoDBreezeinfollowing
manner:

ThisdeclarationmustbedonerightafterDBreezeinstantiation,beforeitsrealusage.

DBreeze.Utils.CustomSerializator.ByteArraySerializator=SerializeProtobuf
DBreeze.Utils.CustomSerializator.ByteArrayDeSerializator=DeserializeProtobuf

where...

WeusemostlyProtobuf.NETserializerinourprojects.Soexamplewillbedonealsowith
Protobuf.GetitviaNugetormakereferencetoit(protobufnet.dll).

HerearecustomwrappingfunctionsforProtobuf:

publicstaticTDeserializeProtobuf<T>(thisbyte[]data)
{
Tret=default(T)

using(System.IO.MemoryStreamms=newSystem.IO.MemoryStream(data))
{

ret=ProtoBuf.Serializer.Deserialize<T>(ms)
ms.Close()
}

returnret
}

publicstaticobjectDeserializeProtobuf(byte[]data,TypeT)
{

objectret=null
using(System.IO.MemoryStreamms=newSystem.IO.MemoryStream(data))
{

ret=ProtoBuf.Serializer.NonGeneric.Deserialize(T,ms)
ms.Close()
}

returnret
}
publicstaticbyte[]SerializeProtobuf(thisobjectdata)
{
byte[]bt=null
using(System.IO.MemoryStreamms=newSystem.IO.MemoryStream())
{
ProtoBuf.Serializer.NonGeneric.Serialize(ms,data)
bt=ms.ToArray()
ms.Close()
}

returnbt
}

NowletsprepareanobjectforstoringinDBreeze,decoratedwithProtobufattributes(extra
documentationaboutprotobufcanbefoundonitswebsite):

[ProtoBuf.ProtoContract]
publicclassXYZ
{
publicXYZ()
{
P1=12
P2="sdfs"
}


[ProtoBuf.ProtoMember(1,IsRequired=true)]
publicintP1{getset}

[ProtoBuf.ProtoMember(2,IsRequired=true)]
publicstringP2{getset}
}

AndnowletsuseDBreezeforstoringobject:

using(vartran=engine.GetTransaction())
{

tran.Insert<int,XYZ>("t1",1,newXYZ(){P1=44,P2="well"})

tran.Commit()
}

Andforretrievingobject:

XYZobj=null

using(vartran=engine.GetTransaction())
{
varrow=tran.Select<int,XYZ>("t1",1)
if(row.Exists)
{
obj=row.Value

//!!!NOTEbettertoassignrow.Valuetoobjandthenuseobjamongtheprogram.
//Callingrow.Valuecausestorereadingdatafromthetableincaseofdefault
//ValueLazyLoadingIsOn
}
}

[20160304]

ExampleofDBreezeinitializationforUWPUniversalWindowsPlatform.

stringdbr_path=System.IO.Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path,
"db")

Task.Run(()=>

//System.Diagnostics.Debug.WriteLine(dbr_path)

if(engine==null)

engine=newDBreezeEngine(dbr_path)

using(vartran=engine.GetTransaction())

tran.Insert<int,int>("t1",1,1)

tran.Commit()

using(vartran=engine.GetTransaction())
{
varre=tran.Select<int,int>("t1",1)
System.Diagnostics.Debug.WriteLine(re.Value)
}
})

[20160320]

Quickstartquide.Customersandorders

Inthisguidewewillcreatecustomers,prototypesofbusinessordersforthesecustomers
anddeterminedifferentsearchfunctions.
Let'screateWinFormapplication,addNuGetreferencetoprotobufnetandDBreeze.Onthe
formcreateabuttonandreplacecodeoftheformwiththisone:
]

usingSystem
usingSystem.Collections.Generic
usingSystem.ComponentModel
usingSystem.Data
usingSystem.Drawing
usingSystem.Linq
usingSystem.Text
usingSystem.Threading.Tasks
usingSystem.Windows.Forms

usingDBreeze
usingDBreeze.Utils

namespaceDBreezeQuickStart
{

publicpartialclassForm1:Form
{
publicForm1()
{
InitializeComponent()
}

publicstaticDBreeze.DBreezeEngineengine=null

protectedoverridevoidOnFormClosing(FormClosingEventArgse)
{
base.OnFormClosing(e)

if(engine!=null)
engine.Dispose()
}

voidInitDb()
{
if(engine==null)
{
engine=newDBreezeEngine(newDBreezeConfiguration{DBreezeDataFolderName=
@"S:\temp\DBreezeTest\DBR1"})
//engine=newDBreezeEngine(newDBreezeConfiguration{DBreezeDataFolderName=
@"C:\temp"})

//SettingdefaultserializerforDBreeze
DBreeze.Utils.CustomSerializator.ByteArraySerializator=ProtobufSerializer.SerializeProtobuf
DBreeze.Utils.CustomSerializator.ByteArrayDeSerializator=
ProtobufSerializer.DeserializeProtobuf
}
}

[ProtoBuf.ProtoContract]
publicclassCustomer
{
[ProtoBuf.ProtoMember(1,IsRequired=true)]
publiclongId{getset}

[ProtoBuf.ProtoMember(2,IsRequired=true)]
publicstringName{getset}
}

[ProtoBuf.ProtoContract]
publicclassOrder
{
publicOrder()
{
udtCreated=DateTime.UtcNow
}

[ProtoBuf.ProtoMember(1,IsRequired=true)]
publiclongId{getset}

[ProtoBuf.ProtoMember(2,IsRequired=true)]
publiclongCustomerId{getset}

///<summary>
///Orderdatetimecreation
///</summary>
[ProtoBuf.ProtoMember(3,IsRequired=true)]
publicDateTimeudtCreated{getset}
}


///<summary>
///STARTINGTESTHERE
///</summary>
///<paramname="sender"></param>
///<paramname="e"></param>
privatevoidbutton1_Click(objectsender,EventArgse)
{

//Onetimedbinit
this.InitDb()

//Simpletest

////Testinsert
//using(vartran=engine.GetTransaction())
//{
//tran.Insert<int,int>("t1",1,1)
//tran.Insert<int,int>("t1",1,2)
//tran.Commit()
//}

////Testselect
//using(vartran=engine.GetTransaction())
//{
//varxrow=tran.Select<int,int>("t1",1)
//if(xrow.Exists)
//{
//Console.WriteLine(xrow.Key.ToString()+xrow.Value.ToString())
//}

////or

//foreach(varrowintran.SelectForward<int,int>("t1"))
//{
//Console.WriteLine(row.Value)
//}
//}

//Morecomplextest

//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!RUNONLYONCE,THENCLEARDB

//InsertingCustomerId1
varcustomer=newCustomer(){Name="TinoZanner"}
Test_InsertCustomer(customer)

//Insertingordersforcustomer1

//for(inti=0i<5i++)
//{

//Test_InsertOrder(newOrder{CustomerId=customer.Id})
//}
//orinsertingbatchoforders
Test_InsertOrders(
Enumerable.Range(1,5)
.Select(r=>newOrder{CustomerId=customer.Id})
)

//InsertingCustomerId2
customer=newCustomer(){Name="MichaelHinze"}
Test_InsertCustomer(customer)

//Insertingordersforcustomer2

//for(inti=0i<8i++)
//{
//Test_InsertOrder(newOrder{CustomerId=customer.Id})
//}
//orinsertingbatchoforders
Test_InsertOrders(
Enumerable.Range(1,8)
.Select(r=>newOrder{CustomerId=customer.Id})
)

//Gettingallorders
Console.WriteLine("Allorders")
Test_GetOrdersByDateTime(DateTime.MinValue,DateTime.MaxValue)

//GettingOrdersofcustomer1
Console.WriteLine("Ordersofcustomer1")
Test_GetOrdersByCustomerIdAndDateTime(1,DateTime.MinValue,DateTime.MaxValue)

//GettingOrdersofcustomer2
Console.WriteLine("Ordersofcustomer2")
Test_GetOrdersByCustomerIdAndDateTime(2,DateTime.MinValue,DateTime.MaxValue)

/*
Result:
InsertedCustomerId:1,Name:TinoZanner
InsertedCustomerId:2,Name:MichaelHinze
Allorders
28.08.201507:15:57.734orderId:1
28.08.201507:15:57.740orderId:2
28.08.201507:15:57.743orderId:3
28.08.201507:15:57.743orderId:4
28.08.201507:15:57.743orderId:5
28.08.201507:15:57.757orderId:6
28.08.201507:15:57.758orderId:7
28.08.201507:15:57.758orderId:8
28.08.201507:15:57.759orderId:9

28.08.201507:15:57.759orderId:10
28.08.201507:15:57.759orderId:11
28.08.201507:15:57.760orderId:12
28.08.201507:15:57.760orderId:13
Ordersofcustomer1
28.08.201507:15:57.734orderId:1
28.08.201507:15:57.740orderId:2
28.08.201507:15:57.743orderId:3
28.08.201507:15:57.743orderId:4
28.08.201507:15:57.743orderId:5
Ordersofcustomer2
28.08.201507:15:57.757orderId:6
28.08.201507:15:57.758orderId:7
28.08.201507:15:57.758orderId:8
28.08.201507:15:57.759orderId:9
28.08.201507:15:57.759orderId:10
28.08.201507:15:57.759orderId:11
28.08.201507:15:57.760orderId:12
28.08.201507:15:57.760orderId:13
*/
return

///<summary>
///
///</summary>
///<paramname="cust"></param>
voidTest_InsertCustomer(Customercust)
{
try
{
using(vartran=engine.GetTransaction())
{
//Wedon'tneedthislinebecausewewriteonlyintooneroottable.
//Addmoretablenamesforsafetransactionoperationsamongmultiple
//roottables(readdocu)
tran.SynchronizeTables("Customers")

//IntableCustomersunderkey1wewillhavenestedtablewithcustomers
vartbl=tran.InsertTable<int>("Customers",1,0)
//Underindex2wewillhavemonotonicallygrownid

if(cust.Id<1)
{
//Insert

//GettingnewIDforthecustomer
cust.Id=tran.Select<int,long>("Customers",2).Value+1
//andinsertingidbackintokey2
tran.Insert<int,long>("Customers",2,cust.Id)
}

//Insertingorupdatingofthecustomer
tbl.Insert<long,Customer>(cust.Id,cust)

//Committingentry
tran.Commit()
}

//Checkingifcustomerissaved
using(vartran=engine.GetTransaction())
{
//usingSelectTableinsteadofInsertTable(readdocu).Inshortifweplantowriteand/orto
read
//fromnestedtableduringonetransactionthenweuseInsertTable,ifonlytoreadthen
SelectTable.

vartbl=tran.SelectTable<int>("Customers",1,0)
varrow=tbl.Select<long,Customer>(cust.Id)
if(row.Exists)
Console.WriteLine("InsertedCustomerId:{0},Name:{1}",row.Value.Id,
row.Value.Name)
else
Console.WriteLine("Insertfailed")
}
}
catch(Exception)
{

throw
}

///<summary>
///
///</summary>
///<paramname="order"></param>
voidTest_InsertOrder(Orderorder)
{
try
{
/*
Inourcase,wewillstoreordersofallcustomersinonetable"Orders".
Ofcoursewecouldcreateforeverycustomerhisowntable,likeOrder1,Order2...etc

Laterweareplanningtosearchorders:
1.byOrder.Id

2.byOrder.udtCreatedFromTo
3.byOrder.CustomerIdandOrder.udtCreatedFromTo

Tofulfill2and3conditionswewillneedtostoreseveralextraindicies.

*/
using(vartran=engine.GetTransaction())
{
//Wedon'tneedthislinebecausewewriteonlyintooneroottable.
//Addmoretablenamesforsafetransactionoperationsamongmultiple
//roottables(readdocu)
tran.SynchronizeTables("Orders")

//Underkey1wewanttostorenestedtablewithorders
vartbl=tran.InsertTable<int>("Orders",1,0)
//Underkey2wewillstoremonotonicallygrownidfororders

//Indextableforthesecondsearchconditionunderkey3
vartblDateIndex=tran.InsertTable<int>("Orders",3,0)
//Indextableforthethirdsearchconditionunderkey4
vartblCustomerAndDateIndex=tran.InsertTable<int>("Orders",4,0)

byte[]key=null

if(order.Id<1)
{
//Insert,gettingnewID
order.Id=tran.Select<int,long>("Orders",2).Value+1
//andinsertingidbackintoindex2
tran.Insert<int,long>("Orders",2,order.Id)

//InsertingsecondaryindexintotblDateIndex.
//IndexwillbecomplexandwillletussearchordersbycreationDateTime
key=
order.udtCreated.To_8_bytes_array().Concat(order.Id.To_8_bytes_array_BigEndian())
//Herewehavecompositekeydate+uniqueOrderIndex(readdocu).ValuewillbeIdof
theorderstoredintbl.
//Asavaluewecouldalsousethesameorderasintbl(redundantstorageforthehigher
speed)orpointertothekey/valueintblforSelectDirect(readdocu)
tblDateIndex.Insert<byte[],long>(key,order.Id)

//InsertingsecondaryindexintotblCustomerAndDateIndex
//KeywillstartfromCustomerId,thencomesdateTimeandthenuniqueidoftheorder
key=
order.CustomerId.To_8_bytes_array_BigEndian().ConcatMany(order.udtCreated.To_8_bytes_array(),
order.Id.To_8_bytes_array_BigEndian())
tblCustomerAndDateIndex.Insert<byte[],long>(key,order.Id)
}

//Insertingorupdatingcustomer

tbl.Insert<long,Order>(order.Id,order)

//Committingentry
tran.Commit()
}
}
catch(Exception)
{

throw
}
}

///<summary>
///
///</summary>
///<paramname="order"></param>
voidTest_InsertOrders(IEnumerable<Order>orders)
{
try
{
/*
Inourcase,wewillstoreordersofallcustomersinonetable"Orders".
Ofcoursewecouldcreateforeverycustomerhisowntable,likeOrder1,Order2...etc

Laterweareplanningtosearchorders:
1.byOrder.Id
2.byOrder.udtCreatedFromTo
3.byOrder.CustomerIdandOrder.udtCreatedFromTo

Tofulfill2and3conditionswewillneedtostoreseveralextraindicies.

*/
using(vartran=engine.GetTransaction())
{

//Wedon'tneedthislinebecausewewriteonlyintooneroottable.
//Addmoretablenamesforsafetransactionoperationsamongmultiple
//roottables(readdocu)
tran.SynchronizeTables("Orders")

//Underkey1wewanttostorenestedtablewithorders
vartbl=tran.InsertTable<int>("Orders",1,0)
//Underkey2wewillstoremonotonicallygrownidfororders
//Indextableforthesecondsearchconditionunderkey3
vartblDateIndex=tran.InsertTable<int>("Orders",3,0)
//Indextableforthethirdsearchconditionunderkey4
vartblCustomerAndDateIndex=tran.InsertTable<int>("Orders",4,0)

byte[]key=null


foreach(varordinorders)
{
if(ord.Id<1)
{
//Insert,gettingnewID
ord.Id=tran.Select<int,long>("Orders",2).Value+1
//andinsertingidbackintoindex2
tran.Insert<int,long>("Orders",2,ord.Id)

//InsertingsecondaryindexintotblDateIndex.
//IndexwillbecomplexandwillletussearchordersbycreationDateTime
key=
ord.udtCreated.To_8_bytes_array().Concat(ord.Id.To_8_bytes_array_BigEndian())
//Herewehavecompositekeydate+uniqueOrderIndex(readdocu).ValuewillbeIdof
theorderstoredintbl.
//Asavaluewecouldalsousethesameorderasintbl(redundantstorageforthe
higherspeed)orpointertothekey/valueintblforSelectDirect(readdocu)
tblDateIndex.Insert<byte[],long>(key,ord.Id)

//InsertingsecondaryindexintotblCustomerAndDateIndex
//KeywillstartfromCustomerId,thencomesdateTimeandthenuniqueidoftheorder
key=
ord.CustomerId.To_8_bytes_array_BigEndian().ConcatMany(ord.udtCreated.To_8_bytes_array(),
ord.Id.To_8_bytes_array_BigEndian())
tblCustomerAndDateIndex.Insert<byte[],long>(key,ord.Id)
}

//Insertingorupdatingcustomer
tbl.Insert<long,Order>(ord.Id,ord)
}

//Committingallchanges
tran.Commit()
}
}
catch(Exception)
{

throw
}
}

///<summary>
///
///</summary>
///<paramname="from"></param>
///<paramname="to"></param>
voidTest_GetOrdersByDateTime(DateTimefrom,DateTimeto)
{
try

{
using(vartran=engine.GetTransaction())
{
vartbl=tran.SelectTable<int>("Orders",1,0)
vartblDateIndex=tran.SelectTable<int>("Orders",3,0)

byte[]keyFrom=
from.To_8_bytes_array().Concat(long.MinValue.To_8_bytes_array_BigEndian())
byte[]keyTo=
to.To_8_bytes_array().Concat(long.MaxValue.To_8_bytes_array_BigEndian())

foreach(varrowintblDateIndex.SelectForwardFromTo<byte[],long>(keyFrom,true,keyTo,
true))
{
varorder=tbl.Select<long,Order>(row.Value)
if(order.Exists)
Console.WriteLine(order.Value.udtCreated.ToString("dd.MM.yyyyHH:mm:ss.fff")+"
orderId:"+order.Value.Id)
}
}
}
catch(Exception)
{

throw
}
}

voidTest_GetOrdersByCustomerIdAndDateTime(longcustomerId,DateTimefrom,DateTimeto)
{
try
{
using(vartran=engine.GetTransaction())
{
vartbl=tran.SelectTable<int>("Orders",1,0)
vartblCustomerAndDateIndex=tran.SelectTable<int>("Orders",4,0)

byte[]keyFrom=
customerId.To_8_bytes_array_BigEndian().ConcatMany(from.To_8_bytes_array(),
long.MinValue.To_8_bytes_array_BigEndian())
byte[]keyTo=
customerId.To_8_bytes_array_BigEndian().ConcatMany(to.To_8_bytes_array(),
long.MaxValue.To_8_bytes_array_BigEndian())

foreach(varrowintblCustomerAndDateIndex.SelectForwardFromTo<byte[],
long>(keyFrom,true,keyTo,true))
{
varorder=tbl.Select<long,Order>(row.Value)
if(order.Exists)
Console.WriteLine(order.Value.udtCreated.ToString("dd.MM.yyyyHH:mm:ss.fff")+"

orderId:"+order.Value.Id)
}
}
}
catch(Exception)
{

throw
}
}

publicstaticclassProtobufSerializer
{
///<summary>
///Deserializesprotobufobjectfrombyte[]
///</summary>
///<typeparamname="T"></typeparam>
///<paramname="data"></param>
///<returns></returns>
publicstaticTDeserializeProtobuf<T>(thisbyte[]data)
{
Tret=default(T)

using(System.IO.MemoryStreamms=newSystem.IO.MemoryStream(data))
{

ret=ProtoBuf.Serializer.Deserialize<T>(ms)
ms.Close()
}

returnret
}

///<summary>
///Deserializesprotobufobjectfrombyte[].Nongenericstyle.
///</summary>
///<paramname="data"></param>
///<paramname="T"></param>
///<returns></returns>
publicstaticobjectDeserializeProtobuf(byte[]data,TypeT)
{
objectret=null
using(System.IO.MemoryStreamms=newSystem.IO.MemoryStream(data))
{

ret=ProtoBuf.Serializer.NonGeneric.Deserialize(T,ms)
ms.Close()
}


returnret
}

///<summary>
///Serializeobjectusingprotobufserializer
///</summary>
///<paramname="data"></param>
///<returns></returns>
publicstaticbyte[]SerializeProtobuf(thisobjectdata)
{
byte[]bt=null
using(System.IO.MemoryStreamms=newSystem.IO.MemoryStream())
{
ProtoBuf.Serializer.NonGeneric.Serialize(ms,data)
bt=ms.ToArray()
ms.Close()
}

returnbt
}
}
}

[20160329]

DBreeze.DataStructures.DataAsTree

DuetothedesireofsomepeopletoimplementintoDBreezeanabilitytostoredataasa
tree,withdependentnodes,outofthebox,wehavecreatednewnamespace
DBreeze.DataStructures.AndinsidethereisaclassDataAsTree.

Howtoworkwiththat:

usingDBreeze
usingDBreeze.DataStructures

DataAsTreerootNode=null

DataAsTreeinsertedNode=null

using(vartran=engine.GetTransaction())

//Inthis"testtree"wewillstoreournewDataStructure,soitshouldbesynchronizedwithother
tables,

//ifwewanttomodifyit

there)

tran.SynchronizeTables("testtree")
//Initializingrootnode.Mustbeinitializedafteranynewtransaction(ifDataAsTreemustbeused
rootNode=newDataAsTree("testtree",tran)
//Addingtotherootnodeasinglechildnode
rootNode.AddNode(newDataAsTree("folder1"))
//Insertingsecondchildnode,gettingreferencetoinsertednode
insertedNode=rootNode.AddNode(newDataAsTree("folder2"))
//Preparinganodebatch
varnodes=newList<DataAsTree>()
nodes.Add(newDataAsTree("xfolder1"))
nodes.Add(newDataAsTree("xfolder2"))
//nodes.Add(newDataAsTree("xfolder2"))
nodes.Add(newDataAsTree("xfile1"))

//Andinsertingitunderthesecondrootchildnode
insertedNode.AddNodes(nodes)
//Insertingnodewiththecontent(itcanbecountedasfile,thouanynodecanhaveContent)
varfileNode=newDataAsTree("file1")
fileNode.NodeContent=newbyte[]{1,2,3,4,5}
//Addingitalsototheroot
rootNode.AddNode(fileNode)
//Committingtransaction,soallourchangesaresavednow
tran.Commit()
}//eousing

Ok,nowletsiteratethroughnodes

using(vartran=engine.GetTransaction())

//Againcreatingrootnode(alwayswhenwestartnewtransactionitmustbeperformed)

rootNode=newDataAsTree("testtree",tran)

//AndrecursivelyreadallourinsertednodesstartingfromRoot(anynodecanbeused)

foreach(vartninrootNode.ReadOutAllChildrenNodesFromCurrentRecursively(tran))

Console.WriteLine(tn.NodeName+"_"+tn.NodeId+"_"+tn.ParentNodeId)

byte[]cnt=tn.GetContent(tran)

if(cnt!=null)

//Showingcontentofthefile

}
}
}//eousing

Now,letsgrabnodesbyspecifiednameandrebindthemtootherparentchangethem:

using(vartran=engine.GetTransaction())

tran.SynchronizeTables("testtree")

rootNode=newDataAsTree("testtree",tran)

//Reconnectingallnodesfrom2parentIdto1parentId

foreach(vartninrootNode.GetNodesByName("xf"))//or
rootNode.GetFirstLevelChildrenNodesByParentId(2)

rootNode.RemoveNode(tn)

tn.ParentNodeId=1

rootNode.AddNode(tn)

tran.Commit()

}//eousing

Now,letsrenamenodesandsupplydifferentcontent

using(vartran=engine.GetTransaction())

tran.SynchronizeTables("testtree")

rootNode=newDataAsTree("testtree",tran)

//Renamingnodesandsettingnewcontent

foreach(vartninrootNode.GetNodesByName("xf"))

{
tn.NodeName=tn.NodeName+"_new_"
tn.NodeContent=newbyte[]{7,7,7}
rootNode.AddNode(tn)

}
tran.Commit()
}//eousing

Ifsomethingisnotworkinglikeitisexpected,please,donthesitatetowritedownanissue
commenton
http://dbreeze.tiesky.com

Copyright2012dbreeze.tiesky.com/AlexeySolovyov/IvarsSudmalis

You might also like