全部博文(230)
分类: 数据库开发技术
2008-09-21 18:45:22
METAKIT C++ TUTORIAL
Riccardo Cohen (rcohen at articque dot com)
articque ()
Date : 19 Septembre 2003
1) Introduction
The metakit database has many interesting and innovative features that will not be described here. Please read Metakit web page : .
Here I'll present my own vision of what one should understand to starting developping with the C++ api.
2) Think different
3) Create database and view
To create a table, just tell its name :
c4_Storage database("metakit_tutorial.db", true);
c4_View maps=database.GetAs("maps[mid:S,mwidth:I,mheight:I,mpath:S]");
This sample code creates a database in file metakit_tutorial.db and adds a view "maps" to store mapping information such as identifier, width, height and file path. But the information is not yet stored, until commit() function is called. Until then it remain in memory.
4) Add rows to the view 'maps'
To add information in the view, just call Add() function with the filled row argument :
c4_StringProp mid("mid"),mpath("mpath");
c4_IntProp mwidth("mwidth"),mheight("mheight");
c4_Row maprow;
mid(maprow)="A";
mwidth(maprow)=10;
mheight(maprow)=10;
mpath(maprow)="/home/MapA.vxf";
maps.Add(maprow);
mid(maprow)="B";
mwidth(maprow)=20;
mheight(maprow)=20;
mpath(maprow)="/home/MapB.vxf";
maps.Add(maprow);
mid(maprow)="C";
mwidth(maprow)=30;
mheight(maprow)=30;
mpath(maprow)="/home/MapC.vxf";
maps.Add(maprow);
Each property is set individually, using the same type as in declaration "GetAs()", then the row is added.
This creates the following View :
mid (string) | mwidth (int) | mheight (int) | mpath (string) |
---|---|---|---|
A | 10 | 10 | /home/MapA.vxf |
B | 20 | 20 | /home/MapB.vxf |
C | 30 | 30 | /home/MapC.vxf |
5) Save file
Just call :
database.Commit();
The Kitviewer provided for MSWindows will show you all information present in the database, to check if work is done all right.
6) Retreive with "SelectRange" (where 20<=width<=30)
To get information from that database, you may try this :
c4_Row selectrow_start,selectrow_end;Which gives the following result :
c4_View tableselect;
mwidth(selectrow_start)=20;
mwidth(selectrow_end)=30;
tableselect=maps.SelectRange(selectrow_start,selectrow_end);
for (idx=0;idxprintf("map %s has width=%d\n",
(const char*)mid(tableselect[idx]),
(long)mwidth(tableselect[idx]));
map B has width=20
map C has width=30
7) Add another view and its data
Here we will add a view with statistical data related to our maps A, B and C:
c4_View datas=database.GetAs("datas[did:S,dmapid:S,dpath:S]");
c4_StringProp did("did"),dmapid("dmapid"),dpath("dpath");
c4_Row datarow;
did(datarow)="1";
dmapid(datarow)="A";
dpath(datarow)="/home/A1.txt";
datas.Add(datarow);
did(datarow)="2";
dmapid(datarow)="A";
dpath(datarow)="/home/A2.txt";
datas.Add(datarow);
did(datarow)="3";
dmapid(datarow)="B";
dpath(datarow)="/home/B1.txt";
datas.Add(datarow);
did(datarow)="4";
dmapid(datarow)="B";
dpath(datarow)="/home/B2.txt";
datas.Add(datarow);
did(datarow)="5";
dmapid(datarow)="C";
dpath(datarow)="/home/C1.txt";
datas.Add(datarow);
database.Commit();
This creates the following View :
did (string) | dmapid (string) | dpath (string) |
---|---|---|
1 | A | /home/A1.txt |
2 | A | /home/A2.txt |
3 | B | /home/B1.txt |
4 | B | /home/B2.txt |
5 | C | /home/C1.txt |
8) Retreive with "Find" (where mapid='B')
long idxsearch=-1;Which gives the following result :
c4_Row findrow;
dmapid(findrow)="B";
while(1)
{
idxsearch=datas.Find(findrow,idxsearch+1);
if (idxsearch>=0)
printf("DATA %s has dmapid=%s\n",
(const char*)dpath(datas[idxsearch]),
(const char*)dmapid(datas[idxsearch]));
else
break;
}
DATA /home/B1.txt has dmapid=B
DATA /home/B2.txt has dmapid=B
Find() is case sensitive
9) Retrieve information using a relation
As I used a lot SQL databases, I like to join tables with a simple relation, nothing more. Here is how to get all
datas for the maps with width between 20 and 30:
(here view datas must rename its property dmapid, since the property name must be the same between the 2 tables)
c4_View relation;Which gives the following result :
relation=(datas.Rename(dmapid,mid)).Join(mid,tableselect);
for (idx=0;idxprintf("DATA %s is applied to map %s (width=%d)\n",
(const char*)dpath(relation[idx]),
(const char*)mpath(relation[idx]),
(long)mwidth(relation[idx]));
DATA /home/B1.txt is applied to map /home/MapB.vxf (width=20)
DATA /home/B2.txt is applied to map /home/MapB.vxf (width=20)
DATA /home/C1.txt is applied to map /home/MapC.vxf (width=30)
10) Searching manually
You may do a specific search with your own function : case insensitive, removing diacritics, partial search etc.
c4_Row strrow;Which gives the following result :
const char*strdata;
const char*expr="ome/B";
for (idx=0;idx{
strdata=(const char*)dpath(datas[idx]);
if (strstr(strdata,expr)!=NULL)
printf("map %s has the string '%s' in the path\n",strdata,expr);
}
map /home/B1.txt has the string 'ome/B' in the path
map /home/B2.txt has the string 'ome/B' in the path
11) Removing rows
Removing change the indexes of course :
c4_Row delrow;Which gives the following result :
dmapid(delrow)="A";
while(1)
{
idxsearch=datas.Find(delrow,0);
if (idxsearch>=0)
{
printf("DATA %s at idx=%d is deleted\n",
(const char*)dpath(datas[idxsearch]),idxsearch);
datas.RemoveAt(idxsearch);
}
else
break;
}
DATA /home/A1.txt at idx=0 is deleted
DATA /home/A2.txt at idx=0 is deleted
Notice that the indexes are all changed in a delete operation. You cannot rely on the index
12) Updating the view content
The function "SetAt()" will change a row :c4_Row updaterow;Which gives the following new content :
mid(updaterow)="E";
mwidth(updaterow)=90;
mheight(updaterow)=90;
mpath(updaterow)="/home/MapE.vxf";
maps.SetAt(2,updaterow);
A | 10 | 10 | /home/MapA.vxf
B | 20 | 20 | /home/MapB.vxf
E | 90 | 90 | /home/MapE.vxf
++) Quick search
Select() and Find() are not optimized searches. Often a simple loop (like in chapter 10) Searching manually) will be much quicker. My performance tests on 60000 rows (each key is present 3 times, which makes 20000 different keys) and on random search have given the following results :c4_View maps=database.GetAs("maps[mid:S,mwidth:I,mheight:I,mpath:S]");All search done with Find() function will be done throught the hash table instead of the original view.
c4_View viewsec=database.GetAs("sec[_H:I,_R:I]");
view=view.Hash(viewsec,1); // 1=number of properties to use for hashing
c4_View maps=database.GetAs("maps[mid:S,subview[mwidth:I,mheight:I,mpath:S]]");in that case, for each mid (your key) you may have any number of rows in the subview. It is a bit more complicated to retreive information, but the result is a very quick access on multiple key hashed view.
c4_View viewsec=database.GetAs("sec[_H:I,_R:I]");
view=view.Hash(viewsec,1);
++) The "Search" function
This function implements a binary tree on sorted views. Look at regression test tbasic2 :{The function works ONLY on sorted views, and returns the position where the row was found, or where it may be inserted (if not found). This position can also be just past the last row. To know if the index found is the row, or a new inserting position, just compare again the row's key with what you search for.
c4_IntProp p1 ("p1");
c4_StringProp p2 ("p2");
c4_View v1;
v1.Add(p1 [111] + p2 ["one"]);
v1.Add(p1 [222] + p2 ["two"]);
v1.Add(p1 [333] + p2 ["three"]);
v1.Add(p1 [345] + p2 ["four"]);
v1.Add(p1 [234] + p2 ["five"]);
v1.Add(p1 [123] + p2 ["six"]);
c4_View v2 = v1.Sort();
A(v2.GetSize() == 6);
A(p1 (v2[0]) == 111);
A(p1 (v2[1]) == 123);
A(p1 (v2[2]) == 222);
A(p1 (v2[3]) == 234);
A(p1 (v2[4]) == 333);
A(p1 (v2[5]) == 345);
A(v2.Search(p1 [123]) == 1); // found at position 1
A(v2.Search(p1 [100]) == 0); // to be inserted at pos 0
A(v2.Search(p1 [200]) == 2); // to be inserted at pos 2
A(v2.Search(p1 [400]) == 6); // to be inserted at the end
}