Index: trunk/extensions/SemanticMediaWiki/includes/storage/SMW_RAPStore.php |
— | — | @@ -5,36 +5,42 @@ |
6 | 6 | * the store with calls to a RAP store, so it keeps in parallel a second |
7 | 7 | * store with all the semantic data. This allows for a SPARQL endpoint. |
8 | 8 | * |
9 | | - * @author Denny Vrandecic |
| 9 | + * @author Denny Vrandecic (V. 0.1) |
| 10 | + * @author Felix Kratzer (V. 0.2) |
10 | 11 | */ |
11 | 12 | |
12 | 13 | if( !defined( 'MEDIAWIKI' ) ) { |
13 | 14 | die( 'Not an entry point.' ); |
14 | 15 | } |
15 | 16 | |
16 | | -global $smwgIP; |
| 17 | +global $smwgIP,$smwgRAPPath; |
17 | 18 | require_once( "$smwgIP/includes/storage/SMW_Store.php" ); |
18 | 19 | require_once( "$smwgIP/includes/storage/SMW_SQLStore.php" ); |
19 | 20 | require_once( "$smwgIP/includes/SMW_DataValueFactory.php" ); |
20 | 21 | |
| 22 | +define('RDFAPI_INCLUDE_DIR',$smwgRAPPath); |
| 23 | +require_once( "$smwgRAPPath/RdfAPI.php"); |
| 24 | + |
21 | 25 | /** |
22 | 26 | * Storage access class for using RAP as a triple store. |
23 | 27 | * Most of the functions are simply forwarded to the SQL store. |
24 | 28 | */ |
25 | | -class SMWRAPStore extends SMWStore { |
| 29 | +class SMWRAPStore extends SMWSQLStore { |
26 | 30 | protected $sqlstore; |
27 | 31 | protected $rapstore; |
28 | 32 | protected $modeluri; |
29 | 33 | protected $baseuri; |
| 34 | + |
30 | 35 | |
| 36 | + /** |
| 37 | + * TODO: maybe find a better nomenclatur for the model |
| 38 | + **/ |
31 | 39 | public function SMWRAPStore() { |
32 | | - global $smwgRAPPath; |
33 | | - define("RDFAPI_INCLUDE_DIR", $smwgRAPPath); |
34 | | - include(RDFAPI_INCLUDE_DIR . "RDFAPI.php"); |
35 | | - $this->sqlstore = new SMWSQLStore(); |
36 | | - |
37 | | - $this->modeluri = "http://example.org/model/"; // TODO needs some value |
38 | | - $this->baseuri = "http://example.org/id/"; // TODO needs some value |
| 40 | + global $smwgRAPPath,$wgServer; |
| 41 | + |
| 42 | + |
| 43 | + $this->modeluri = SMWExporter::expandURI($wgServer."/model"); |
| 44 | + $this->baseuri = SMWExporter::expandURI($wgServer."/id"); |
39 | 45 | } |
40 | 46 | |
41 | 47 | ///// Writing methods ///// |
— | — | @@ -46,72 +52,111 @@ |
47 | 53 | * data. |
48 | 54 | */ |
49 | 55 | function deleteSubject(Title $subject) { |
| 56 | + |
| 57 | + // Translate SMWSemanticData to a RAP Model |
50 | 58 | $rdfmodel = $this->getRAPModel(); |
51 | | - $this->closeRAP(); |
52 | | - // TODO Implement |
53 | | - return $this->sqlstore->deleteSubject($subject); |
| 59 | + |
| 60 | + $rapsub = new Resource(SMWExporter::expandURI($this->getURI($subject))); |
| 61 | + $this->removeSubjectFromRAP($rdfmodel, $rapsub); |
| 62 | + |
| 63 | + return parent::deleteSubject($subject); |
54 | 64 | } |
55 | | - |
| 65 | + |
| 66 | + |
56 | 67 | /** |
57 | 68 | * Update the semantic data stored for some individual. The data is given |
58 | 69 | * as a SMWSemData object, which contains all semantic data for one particular |
59 | 70 | * subject. The boolean $newpage specifies whether the page is stored for the |
60 | 71 | * first time or not. |
61 | 72 | */ |
62 | | - function updateData(SMWSemanticData $data, $newpage) { |
63 | | - if ($data->hasProperties()) { |
64 | | - // Create a local memmodel |
65 | | - $model = ModelFactory::getDefaultModel(); |
| 73 | + function updateData(SMWSemanticData $data, $newpage){ |
| 74 | + // Create a local memmodel |
| 75 | + $model = ModelFactory::getDefaultModel(); |
| 76 | + |
| 77 | + // Get DB-Model |
| 78 | + $rdfmodel = $this->getRAPModel(); |
| 79 | + |
| 80 | + $ed = SMWExporter::makeExportData($data); //ExpData |
| 81 | + |
| 82 | + // Delete all we know about the subject! |
| 83 | + $rapsub = new Resource(SMWExporter::expandURI($ed->getSubject()->getName())); |
| 84 | + $this->removeSubjectFromRAP($rdfmodel, $rapsub); |
| 85 | + |
| 86 | + $tl = $ed->getTripleList(); // list of tenary arrays |
| 87 | + |
| 88 | + // Temporary List of all Blank Nodes in this dataobject |
| 89 | + $blankNodes = array(); |
| 90 | + |
| 91 | + foreach ($tl as $triple) { |
| 92 | + $s = $triple[0]->getName(); // Subject |
| 93 | + $p = $triple[1]->getName(); // Predicate |
| 94 | + $o = $triple[2]->getName(); // Object |
66 | 95 | |
67 | | - // Translate SMWSemanticData to a RAP Model |
68 | | - $rapsub = new Resource($this->getURI($data->getSubject())); |
69 | | - $properties = $data->getProperties(); |
70 | | - foreach ($properties as $prop) { |
71 | | - if (is_int($prop)) continue; |
72 | | - $rapprop = new Resource($this->getURI($prop)); |
73 | | - $values = $data->getPropertyValues($prop); |
74 | | - foreach ($values as $val) { |
75 | | - if ($val->getTypeID() == "_wpg") { |
76 | | - $rapval = new Resource($this->getURI($val->getTitle())); |
77 | | - $statement = new Statement($rapsub, $rapprop, $rapval); |
78 | | - $model->add($statement); |
79 | | - } else { |
80 | | - // TODO Save as literal |
81 | | - } |
| 96 | + |
| 97 | + // ------------------------------------------------------------------- |
| 98 | + // Subject |
| 99 | + // ------------------------------------------------------------------- |
| 100 | + $rap_subj = new Resource(SMWExporter::expandURI($triple[0]->getName())); |
| 101 | + if($triple[0] instanceof SMWExpLiteral){ } // Should NEVER happen |
| 102 | + elseif($triple[0] instanceof SMWExpResource){ } // Nothing to do |
| 103 | + else{ |
| 104 | + // Is this a blank node?? |
| 105 | + if(substr($triple[0]->getName(),0,1) === "_"){ |
| 106 | + // We need to create our own unique IDs as we cannot load the whole model into mem every time |
| 107 | + // The exporter generates Numbers inside the page so $triple[0]->getName() is unique on the page |
| 108 | + // $ed->getSubject()->getName() is unique for the wiki |
| 109 | + // we use md5 to get a nicer number, but we could use any other hashing method! |
| 110 | + // |
| 111 | + // Denny thinks this might be a bug of RAP... We leave it this way till we know better! |
| 112 | + // |
| 113 | + $bNodeId = '_' . md5($ed->getSubject()->getName() . $triple[0]->getName()); |
| 114 | + $rap_subj = $blankNodes[$bNodeId]; |
82 | 115 | } |
83 | 116 | } |
84 | | - // TODO Save all other data pertaining to a specific subject, |
85 | | - // i.e. n-aries, longstrings, specials, subproperties, ... |
86 | | - // For an example see the SMW_SQLStore::updateData function |
87 | 117 | |
88 | | - // Now add the local model to the DB model |
89 | | - $rdfmodel = $this->getRAPModel(); |
90 | | - // First delete existing statements about subject |
91 | | - $oldmodel = $rdfmodel->find($rapsub, null, null); |
92 | | - $i = $oldmodel->getStatementIterator(); |
93 | | - $i->moveFirst(); |
94 | | - while ($i->current() != null) { |
95 | | - $rdfmodel->remove($i->current()); |
96 | | - $i->next(); |
| 118 | + // ------------------------------------------------------------------- |
| 119 | + // Predicate |
| 120 | + // ------------------------------------------------------------------- |
| 121 | + $rap_pred = new Resource(SMWExporter::expandURI($triple[1]->getName())); |
| 122 | + |
| 123 | + // ------------------------------------------------------------------- |
| 124 | + // Object |
| 125 | + // ------------------------------------------------------------------- |
| 126 | + $rap_obj = new Resource(SMWExporter::expandURI($triple[2]->getName())); |
| 127 | + if($triple[2] instanceof SMWExpLiteral){ |
| 128 | + // This is a literal so get the correct type |
| 129 | + $rap_obj = new Literal($triple[2]->getName()); |
| 130 | + $rap_obj->setDatatype($triple[2]->getDatatype()); |
97 | 131 | } |
98 | | - // TODO Also remove in the other tables (longstring etc.) from above |
99 | | - $rdfmodel->addModel($model); |
100 | | - $rdfmodel->close(); |
101 | | - $model->close(); |
| 132 | + elseif($triple[2] instanceof SMWExpResource){ } // Nothing else to do |
| 133 | + else{ |
| 134 | + // Is this a blank node?? |
| 135 | + if(substr($triple[2]->getName(),0,1) === "_"){ |
| 136 | + // See comment @Subject part about IDs |
| 137 | + $bNodeId = '_' . md5($ed->getSubject()->getName().$triple[2]->getName()); |
| 138 | + $rap_obj = new BlankNode($bNodeId); |
| 139 | + $blankNodes[$bNodeId] = $rap_obj; |
| 140 | + } |
| 141 | + } |
102 | 142 | |
103 | | - $this->closeRAP(); |
| 143 | + // now add the new Statement |
| 144 | + $statement = new Statement($rap_subj, $rap_pred, $rap_obj); |
| 145 | + $model->add($statement); |
104 | 146 | } |
105 | | - return $this->sqlstore->updateData($data, $newpage); |
| 147 | + |
| 148 | + // Add the mem-model to the store |
| 149 | + $rdfmodel->addModel($model); |
| 150 | + |
| 151 | + |
| 152 | + // Close connections |
| 153 | + $model->close(); |
| 154 | + $rdfmodel->close(); |
| 155 | + $this->closeRAP(); |
| 156 | + |
| 157 | + |
| 158 | + return parent::updateData($data, $newpage); |
106 | 159 | } |
107 | 160 | |
108 | | - /** |
109 | | - * Having a title of a page, what is the URI that is described by that page? |
110 | | - */ |
111 | | - protected function getURI(Title $title) { |
112 | | - global $smwgIP; |
113 | | - include_once("$smwgIP/specials/ExportRDF/SMW_SpecialExportRDF.php"); |
114 | | - return ExportRDF::makeURIfromTitle($title); |
115 | | - } |
116 | 161 | |
117 | 162 | /** |
118 | 163 | * Update the store to reflect a renaming of some article. The old and new title objects |
— | — | @@ -120,11 +165,31 @@ |
121 | 166 | * If $keepid is true, the old and new id of the title is the id of $newtitle, and not the |
122 | 167 | * id of $oldtitle. |
123 | 168 | */ |
124 | | - function changeTitle(Title $oldtitle, Title $newtitle, $keepid = true) { |
| 169 | + function changeTitle(Title $oldtitle, Title $newtitle, $pageid, $redirid=0) { |
| 170 | + |
| 171 | + // Save it in parent store now! |
| 172 | + // We need that so we get all information correctly! |
| 173 | + $result = parent::changeTitle($oldtitle, $newtitle, $pageid, $redirid); |
| 174 | + |
| 175 | + // Delete the old stuff |
| 176 | + $nameOld = SMWExporter::expandURI($this->getURI($oldtitle)); |
125 | 177 | $rdfmodel = $this->getRAPModel(); |
126 | | - $this->closeRAP(); |
127 | | - // TODO Implement |
128 | | - return $this->sqlstore->changeTitle($oldtitle, $newtitle, $keepid); |
| 178 | + $rapsubold = new Resource($nameOld); |
| 179 | + $this->removeSubjectFromRAP($rdfmodel, $rapsubold); |
| 180 | + |
| 181 | + |
| 182 | + $newpage = SMWDataValueFactory::newTypeIDValue('_wpg'); |
| 183 | + $newpage->setValues($newtitle->getDBKey(), $newtitle->getNamespace(), $pageid); |
| 184 | + $semdata = $this->getSemanticData($newpage); |
| 185 | + $this->updateData($semdata,false); |
| 186 | + |
| 187 | + // Save the old page |
| 188 | + $oldpage = SMWDataValueFactory::newTypeIDValue('_wpg'); |
| 189 | + $oldpage->setValues($oldtitle->getDBKey(), $oldtitle->getNamespace(), $redirid); |
| 190 | + $semdata = $this->getSemanticData($oldpage); |
| 191 | + $this->updateData($semdata,false); |
| 192 | + |
| 193 | + return $result; |
129 | 194 | } |
130 | 195 | |
131 | 196 | ///// Setup store ///// |
— | — | @@ -161,14 +226,14 @@ |
162 | 227 | } |
163 | 228 | $this->closeRAP(); |
164 | 229 | $this->reportProgress("RAP setup finished. Handing over to SQL store setup.\n\n",$verbose); |
165 | | - return $this->sqlstore->setup($verbose); |
| 230 | + return parent::setup($verbose); |
166 | 231 | } |
167 | 232 | |
168 | 233 | function drop($verbose = true) { |
169 | 234 | /// TODO: undo all DB changes introduced by setup() |
170 | 235 | /// Well, not all, just delete the created model. The database tables must retain, since |
171 | 236 | /// there are only one set of tables for several models. |
172 | | - return $this->sqlstore->drop(); |
| 237 | + return parent::drop(); |
173 | 238 | } |
174 | 239 | |
175 | 240 | /** |
— | — | @@ -179,8 +244,8 @@ |
180 | 245 | // TODO only for MySQL, check for other databases! |
181 | 246 | // Also, RAP ignores prefixes for tables. Bad RAP. Need to check with |
182 | 247 | // the RAP developers to change that. |
183 | | - global $wgDBserver, $wgDBname, $wgDBuser, $wgDBpassword; |
184 | | - $this->rapstore = ModelFactory::getDbStore('MySQL', $wgDBserver, $wgDBname, $wgDBuser, $wgDBpassword); |
| 248 | + global $smwgRapDBserver, $smwgRapDBname, $smwgRapDBuser, $smwgRapDBpassword; |
| 249 | + $this->rapstore = ModelFactory::getDbStore('MySQL', $smwgRapDBserver, $smwgRapDBname, $smwgRapDBuser, $smwgRapDBpassword); |
185 | 250 | return $this->rapstore; |
186 | 251 | } |
187 | 252 | |
— | — | @@ -203,46 +268,53 @@ |
204 | 269 | |
205 | 270 | ///// Reading methods -- All forwarded ///// |
206 | 271 | function getSpecialValues(Title $subject, $specialprop, $requestoptions = NULL) { |
207 | | - return $this->sqlstore->getSpecialValues($subject, $specialprop, $requestoptions); |
| 272 | + return parent::getSpecialValues($subject, $specialprop, $requestoptions); |
208 | 273 | } |
209 | 274 | function getSpecialSubjects($specialprop, $value, $requestoptions = NULL) { |
210 | | - return $this->sqlstore->getSpecialSubjects($specialprop, $value, $requestoptions); |
| 275 | + return parent::getSpecialSubjects($specialprop, $value, $requestoptions); |
211 | 276 | } |
212 | | - function getPropertyValues(Title $subject, Title $property, $requestoptions = NULL, $outputformat = '') { |
213 | | - return $this->sqlstore->getPropertyValues($subject, $property, $requestoptions, $outputformat); |
| 277 | + |
| 278 | + function getPropertyValues($subject, $property, $requestoptions = NULL, $outputformat = '') { |
| 279 | + return parent::getPropertyValues($subject, $property, $requestoptions, $outputformat); |
214 | 280 | } |
215 | 281 | function getPropertySubjects(Title $property, SMWDataValue $value, $requestoptions = NULL) { |
216 | | - return $this->sqlstore->getPropertySubjects($property, $value, $requestoptions); |
| 282 | + return parent::getPropertySubjects($property, $value, $requestoptions); |
217 | 283 | } |
218 | 284 | function getAllPropertySubjects(Title $property, $requestoptions = NULL) { |
219 | | - return $this->sqlstore->getAllPropertySubjects($property, $requestoptions); |
| 285 | + return parent::getAllPropertySubjects($property, $requestoptions); |
220 | 286 | } |
221 | 287 | function getProperties(Title $subject, $requestoptions = NULL) { |
222 | | - return $this->sqlstore->getProperties($subject, $requestoptions); |
| 288 | + return parent::getProperties($subject, $requestoptions); |
223 | 289 | } |
224 | 290 | function getInProperties(SMWDataValue $object, $requestoptions = NULL) { |
225 | | - return $this->sqlstore->getInProperties($object, $requestoptions); |
| 291 | + return parent::getInProperties($object, $requestoptions); |
226 | 292 | } |
| 293 | + /** |
| 294 | + * This one returns semantic data from the parent store (RAP is only made for writing, reading is ALL handles by the SQL store) |
| 295 | + **/ |
| 296 | + function getSemanticData($subject, $filter = false){ |
| 297 | + return parent::getSemanticData($subject, $filter); |
| 298 | + } |
227 | 299 | |
228 | 300 | ///// Query answering -- all forwarded ///// |
229 | 301 | |
230 | 302 | function getQueryResult(SMWQuery $query) { |
231 | | - return $this->sqlstore->getQueryResult($query); |
| 303 | + return parent::getQueryResult($query); |
232 | 304 | } |
233 | 305 | |
234 | 306 | ///// Special page functions -- all forwarded ///// |
235 | 307 | |
236 | 308 | function getPropertiesSpecial($requestoptions = NULL) { |
237 | | - return $this->sqlstore->getPropertiesSpecial($requestoptions); |
| 309 | + return parent::getPropertiesSpecial($requestoptions); |
238 | 310 | } |
239 | 311 | function getUnusedPropertiesSpecial($requestoptions = NULL) { |
240 | | - return $this->sqlstore->getUnusedPropertiesSpecial($requestoptions); |
| 312 | + return parent::getUnusedPropertiesSpecial($requestoptions); |
241 | 313 | } |
242 | 314 | function getWantedPropertiesSpecial($requestoptions = NULL) { |
243 | | - return $this->sqlstore->getWantedPropertiesSpecial($requestoptions); |
| 315 | + return parent::getWantedPropertiesSpecial($requestoptions); |
244 | 316 | } |
245 | 317 | function getStatistics() { |
246 | | - return $this->sqlstore->getStatistics(); |
| 318 | + return parent::getStatistics(); |
247 | 319 | } |
248 | 320 | |
249 | 321 | /** |
— | — | @@ -261,5 +333,50 @@ |
262 | 334 | ob_flush(); |
263 | 335 | flush(); |
264 | 336 | } |
| 337 | + |
| 338 | +///// Additional helpers |
| 339 | + /** |
| 340 | + * Deletes all relations for the given subject from RAP. |
| 341 | + * This especially also handles n-ary relations recursevly as we would lose them |
| 342 | + **/ |
| 343 | + protected function removeSubjectFromRAP($rdfmodel, Resource $subject){ |
| 344 | + $oldmodel = $rdfmodel->find($subject, null, null); |
| 345 | + $i = $oldmodel->getStatementIterator(); |
| 346 | + $i->moveFirst(); |
| 347 | + while ($i->current() != null) { |
| 348 | + $stmt = $i->current(); |
| 349 | + |
| 350 | + $rdfmodel->remove($stmt); |
| 351 | + |
| 352 | + $obj = $stmt->object(); |
| 353 | + if($obj instanceof BlankNode){ |
| 354 | + // It's a blank node in the object, this means a n-ary relation has been saved |
| 355 | + // So delete everything for this blank node as well! |
| 356 | + $this->removeSubjectFromRAP($rdfmodel, $obj); |
| 357 | + } |
| 358 | + |
| 359 | + $i->next(); |
| 360 | + } |
| 361 | + // TODO Delete More Stuff, if we save more stuff |
| 362 | + } |
| 363 | + |
| 364 | + /** |
| 365 | + * Having a title of a page, what is the URI that is described by that page? |
| 366 | + * |
| 367 | + * The result still requires expandURI() |
| 368 | + */ |
| 369 | + protected function getURI($title) { |
| 370 | + $uri = ""; |
| 371 | + if($title instanceof Title){ |
| 372 | + $dv = SMWDataValueFactory::newTypeIDValue('_wpg'); |
| 373 | + $dv->setValues($title->getDBKey(), $title->getNamespace()); |
| 374 | + $exp = $dv->getExportData(); |
| 375 | + $uri = $exp->getSubject()->getName(); |
| 376 | + }else{ |
| 377 | + // There could be other types as well that we do NOT handle here |
| 378 | + } |
| 379 | + |
| 380 | + return $uri; // still requires expandURI() |
| 381 | + } |
265 | 382 | } |
266 | 383 | |