knowrob  2.1.0
A Knowledge Base System for Cognition-enabled Robots
KnowledgeBase.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of KnowRob, please consult
3  * https://github.com/knowrob/knowrob for license details.
4  */
5 
6 #include <thread>
7 #include <boost/property_tree/json_parser.hpp>
8 #include <knowrob/Logger.h>
9 #include <knowrob/KnowledgeBase.h>
10 #include <knowrob/URI.h>
11 #include <boost/python/suite/indexing/vector_indexing_suite.hpp>
12 #include "knowrob/queries/QueryPipeline.h"
13 #include "knowrob/semweb/PrefixRegistry.h"
14 #include "knowrob/semweb/rdf.h"
15 #include "knowrob/semweb/owl.h"
16 #include "knowrob/semweb/rdfs.h"
17 #include "knowrob/semweb/OntologyLanguage.h"
18 #include "knowrob/reasoner/ReasonerManager.h"
19 #include "knowrob/semweb/OntologyFile.h"
20 #include "knowrob/semweb/GraphTransformation.h"
21 #include "knowrob/semweb/TransformedOntology.h"
22 #include "knowrob/integration/python/utils.h"
23 #include "knowrob/integration/python/with.h"
24 
25 #define KB_SETTING_REASONER "reasoner"
26 #define KB_SETTING_DATA_BACKENDS "data-backends"
27 #define KB_SETTING_DATA_SOURCES "data-sources"
28 #define KB_SETTING_DATA_TRANSFORMATION "transformation"
29 #define KB_SETTING_SEMWEB "semantic-web"
30 #define KB_SETTING_PREFIXES "prefixes"
31 #define KB_SETTING_PREFIX_ALIAS "alias"
32 #define KB_SETTING_PREFIX_URI "uri"
33 
34 using namespace knowrob;
35 
37  : isInitialized_(false) {
38  vocabulary_ = std::make_shared<Vocabulary>();
39  // use "system" as default origin until initialization completed
40  vocabulary_->importHierarchy()->setDefaultGraph(ImportHierarchy::ORIGIN_SYSTEM);
41  backendManager_ = std::make_shared<StorageManager>(vocabulary_);
42  reasonerManager_ = std::make_shared<ReasonerManager>(this, backendManager_);
43  edb_ = std::make_shared<StorageInterface>(backendManager_);
44 }
45 
46 KnowledgeBase::KnowledgeBase(const boost::property_tree::ptree &config) : KnowledgeBase() {
47  configure(config);
48  init();
49 }
50 
51 KnowledgeBase::KnowledgeBase(std::string_view configFile) : KnowledgeBase() {
52  boost::property_tree::ptree config;
53  // Test if string is a JSON string or a file path
54  if (configFile.find_first_of('{') == 0) {
55  std::istringstream json_stream(configFile.data());
56  boost::property_tree::read_json(json_stream, config);
57  } else {
58  boost::property_tree::read_json(URI::resolve(configFile), config);
59  }
60  configure(config);
61  init();
62 }
63 
65  stopReasoner();
66  if(observerManager_) {
67  observerManager_->stop();
68  observerManager_ = nullptr;
69  }
70  edb_ = nullptr;
71  backendManager_ = nullptr;
72  reasonerManager_ = nullptr;
73  vocabulary_ = nullptr;
74 }
75 
76 std::shared_ptr<KnowledgeBase> KnowledgeBase::create(const boost::property_tree::ptree &config) {
77  return std::shared_ptr<KnowledgeBase>(new KnowledgeBase(config));
78 }
79 
80 std::shared_ptr<KnowledgeBase> KnowledgeBase::create(std::string_view config) {
81  return std::shared_ptr<KnowledgeBase>(new KnowledgeBase(config));
82 }
83 
84 std::shared_ptr<KnowledgeBase> KnowledgeBase::create() {
85  return std::shared_ptr<KnowledgeBase>(new KnowledgeBase());
86 }
87 
89  isInitialized_ = true;
90  vocabulary_->importHierarchy()->setDefaultGraph(ImportHierarchy::ORIGIN_USER);
91  initBackends();
94 
95  observerManager_ = std::make_shared<ObserverManager>(getBackendForQuery());
96  startReasoner();
97 }
98 
100  for (auto &pair: backendManager_->plugins()) {
101  auto definedBackend = pair.second;
102  definedBackend->value()->setVocabulary(vocabulary_);
103  }
104 }
105 
107  // find all non-persistent backends, which we assume to be empty at this point
108  std::vector<std::shared_ptr<NamedBackend>> nonPersistent;
109  for (auto &it: backendManager_->plugins()) {
110  auto backend = it.second->value();
111  auto queryable = backendManager_->queryable().find(it.first);
112  if (queryable == backendManager_->queryable().end() || !queryable->second->isPersistent()) {
113  nonPersistent.push_back(it.second);
114  }
115  }
116 
117  // synchronize persistent backends with each other
118  if (backendManager_->persistent().size() > 1) {
119  // find versions of persisted origins
120  using BackendOriginVersion = std::pair<std::shared_ptr<QueryableStorage>, VersionedOriginPtr>;
121  std::map<std::string_view, std::vector<BackendOriginVersion>> origins;
122  for (auto &it: backendManager_->persistent()) {
123  auto persistentBackend = it.second;
124  for (auto &origin: persistentBackend->getOrigins()) {
125  origins[origin->value()].emplace_back(it.second, origin);
126  }
127  }
128 
129  // drop all persisted origins with an outdated version
130  for (auto &origin_pair: origins) {
131  auto &v = origin_pair.second;
132  if (v.size() < 2) continue;
133  // find the maximum version
134  std::string_view maxVersion;
135  for (auto &version: v) {
136  if (!maxVersion.empty() && version.second->version() > maxVersion) {
137  maxVersion = version.second->version();
138  }
139  }
140  // drop origin for backends with outdated version, also remove such backends from "origins" array
141  v.erase(std::remove_if(v.begin(), v.end(),
142  [&maxVersion](const BackendOriginVersion &bov) {
143  if (bov.second->version() != maxVersion) {
144  bov.first->removeAllWithOrigin(bov.second->value());
145  return true;
146  } else {
147  return false;
148  }
149  }), v.end());
150  }
151 
152  // At this point, origins contains only the backends with the latest version.
153  // So data can be copied from any of them into all other persistent backends that do
154  // not have the data yet.
155  for (auto &origin_pair: origins) {
156  // First find all persistent backends that do not appear in origin_pair.
157  // These are the ones without the data.
158  std::vector<std::shared_ptr<NamedBackend>> included;
159  for (auto &it: backendManager_->persistent()) {
160  auto &persistentBackend = it.second;
161  if (std::find_if(origin_pair.second.begin(), origin_pair.second.end(),
162  [&persistentBackend](const BackendOriginVersion &bov) {
163  return bov.first == persistentBackend;
164  }) == origin_pair.second.end()) {
165  included.push_back(backendManager_->getPluginWithID(it.first));
166  }
167  }
168  if (included.empty()) continue;
169 
170  // Now copy the data from one of the backends in origin_pair to all backends in included.
171  auto &persistedBackend = origin_pair.second.begin()->first;
172  auto transaction = edb_->createTransaction(
173  persistedBackend,
176  included);
177  persistedBackend->batchOrigin(origin_pair.first, [&](const TripleContainerPtr &triples) {
178  transaction->commit(triples);
179  });
180  }
181  }
182 
183  // insert from first persistent backend into all non-persistent backends.
184  // persistent backends are synchronized before, so we can just take the first one.
185  if (!backendManager_->persistent().empty() && !nonPersistent.empty()) {
186  KB_DEBUG("Synchronizing persistent triples into {} non-persistent backends.", nonPersistent.size());
187  auto &persistedBackend = *backendManager_->persistent().begin();
188  auto transaction = edb_->createTransaction(
189  getBackendForQuery(),
192  nonPersistent);
193  persistedBackend.second->batch([&](const TripleContainerPtr &triples) { transaction->commit(triples); });
194  }
195 }
196 
197 void KnowledgeBase::initVocabulary() {
198  auto v_s = std::make_shared<Variable>("?s");
199  auto v_o = std::make_shared<Variable>("?o");
200 
201  for (auto &it: backendManager_->persistent()) {
202  auto backend = it.second;
203 
204  // initialize the import hierarchy
205  for (auto &origin: backend->getOrigins()) {
206  vocabulary_->importHierarchy()->addDirectImport(vocabulary_->importHierarchy()->ORIGIN_SYSTEM,
207  origin->value());
208  }
209 
210  // iterate over all rdf:type assertions and add them to the vocabulary
211  backend->match(TriplePattern(v_s, rdf::type, v_o),
212  [this](const TriplePtr &triple) {
213  vocabulary_->addResourceType(triple->subject(), triple->valueAsString());
214  vocabulary_->increaseFrequency(rdf::type->stringForm());
215  });
216  // iterate over all rdfs::subClassOf assertions and add them to the vocabulary
217  backend->match(TriplePattern(v_s, rdfs::subClassOf, v_o),
218  [this](const TriplePtr &triple) {
219  vocabulary_->addSubClassOf(triple->subject(), triple->valueAsString(), triple->graph());
220  vocabulary_->increaseFrequency(rdfs::subClassOf->stringForm());
221  });
222  // iterate over all rdfs::subPropertyOf assertions and add them to the vocabulary
223  backend->match(TriplePattern(v_s, rdfs::subPropertyOf, v_o),
224  [this](const TriplePtr &triple) {
225  vocabulary_->addSubPropertyOf(triple->subject(), triple->valueAsString(), triple->graph());
226  vocabulary_->increaseFrequency(rdfs::subPropertyOf->stringForm());
227  });
228  // iterate over all owl::inverseOf assertions and add them to the vocabulary
229  backend->match(TriplePattern(v_s, owl::inverseOf, v_o),
230  [this](const TriplePtr &triple) {
231  vocabulary_->setInverseOf(triple->subject(), triple->valueAsString());
232  vocabulary_->increaseFrequency(owl::inverseOf->stringForm());
233  });
234 
235  // query number of assertions of each property/class.
236  // this is useful information for optimizing the query planner.
237  std::vector<semweb::PropertyPtr> reifiedProperties;
238  backend->count([this, &reifiedProperties](std::string_view resource, uint64_t count) {
239  // special handling for reified relations: they are concepts, but do also increase the relation counter
240  auto reifiedProperty = vocabulary_->getDefinedReification(resource);
241  if (reifiedProperty) reifiedProperties.push_back(reifiedProperty);
242  vocabulary_->setFrequency(resource, count);
243  });
244  for (auto &p: reifiedProperties) {
245  vocabulary_->increaseFrequency(p->iri());
246  }
247  }
248 }
249 
251  auto propertyAtom = IRIAtom::Tabled(triple->predicate());
252  // handle rdf:type assertions
253  if (propertyAtom.get() == rdf::type.get()) {
254  vocabulary_->addResourceType(triple->subject(), triple->valueAsString());
255  vocabulary_->increaseFrequency(rdf::type->stringForm());
256  }
257  // handle rdfs::subClassOf assertions
258  else if (propertyAtom.get() == rdfs::subClassOf.get()) {
259  vocabulary_->addSubClassOf(triple->subject(), triple->valueAsString(), triple->graph());
260  vocabulary_->increaseFrequency(rdfs::subClassOf->stringForm());
261  }
262  // handle rdfs::subPropertyOf assertions
263  else if (propertyAtom.get() == rdfs::subPropertyOf.get()) {
264  vocabulary_->addSubPropertyOf(triple->subject(), triple->valueAsString(), triple->graph());
265  vocabulary_->increaseFrequency(rdfs::subPropertyOf->stringForm());
266  }
267  // handle owl::inverseOf assertions
268  else if (propertyAtom.get() == owl::inverseOf.get()) {
269  vocabulary_->setInverseOf(triple->subject(), triple->valueAsString());
270  vocabulary_->increaseFrequency(owl::inverseOf->stringForm());
271  } else {
272  vocabulary_->defineProperty(propertyAtom);
273  vocabulary_->increaseFrequency(propertyAtom->stringForm());
274  }
275  // TODO: need to add special handling for reified relations here?
276 }
277 
278 void KnowledgeBase::configure(const boost::property_tree::ptree &config) {
279  configurePrefixes(config);
280  // initialize data backends from configuration
281  configureBackends(config);
282  // share vocabulary and import hierarchy with backends
283  initBackends();
284  // load common ontologies
285  loadCommon();
286  // load the "global" data sources.
287  // these are data sources that are loaded into all backends, however
288  // the backends may decide to ignore some of the data sources.
289  configureDataSources(config);
290  // load reasoners from configuration
291  configureReasoner(config);
292 }
293 
294 void KnowledgeBase::configurePrefixes(const boost::property_tree::ptree &config) {
295  auto semwebTree = config.get_child_optional(KB_SETTING_SEMWEB);
296  if (semwebTree) {
297  // load RDF URI aliases
298  auto prefixesList = semwebTree.value().get_child_optional(KB_SETTING_PREFIXES);
299  for (const auto &pair: prefixesList.value()) {
300  auto alias = pair.second.get(KB_SETTING_PREFIX_ALIAS, "");
301  auto uri = pair.second.get(KB_SETTING_PREFIX_URI, "");
302  if (!alias.empty() && !uri.empty()) {
303  PrefixRegistry::registerPrefix(alias, uri);
304  } else {
305  KB_WARN("Invalid entry in semantic-web::prefixes, 'alias' and 'uri' must be defined.");
306  }
307  }
308  }
309 }
310 
311 void KnowledgeBase::configureBackends(const boost::property_tree::ptree &config) {
312  auto backendList = config.get_child_optional(KB_SETTING_DATA_BACKENDS);
313  if (backendList) {
314  for (const auto &pair: backendList.value()) {
315  KB_LOGGED_TRY_CATCH(pair.first, "load", { backendManager_->loadPlugin(pair.second); });
316  }
317  } else {
318  KB_ERROR("configuration has no 'backends' key.");
319  }
320 }
321 
322 void KnowledgeBase::configureReasoner(const boost::property_tree::ptree &config) {
323  auto reasonerList = config.get_child_optional(KB_SETTING_REASONER);
324  if (reasonerList) {
325  for (const auto &pair: reasonerList.value()) {
326  KB_LOGGED_TRY_CATCH(pair.first, "load", {
327  auto definedReasoner = reasonerManager_->loadPlugin(pair.second);
328  // if reasoner implements DataBackend class, add it to the backend manager
329  auto reasonerBackend = std::dynamic_pointer_cast<Storage>(definedReasoner->value());
330  if (reasonerBackend) {
331  backendManager_->addPlugin(definedReasoner->name(), definedReasoner->language(), reasonerBackend);
332  }
333  });
334  }
335  } else {
336  KB_ERROR("configuration has no 'reasoner' key.");
337  }
338 }
339 
341  for (auto &ontoPath: {"owl/rdf-schema.xml", "owl/owl.rdf"}) {
342  loadDataSource(std::make_shared<OntologyFile>(vocabulary_, URI(ontoPath), "rdf-xml"));
343  }
344 }
345 
346 static void do_startReasoner(const std::shared_ptr<DataDrivenReasoner> &reasoner) {
347  if (reasoner->reasonerLanguage() == PluginLanguage::PYTHON) {
348  py::gil_lock lock;
349  reasoner->start();
350  } else {
351  reasoner->start();
352  }
353 }
354 
356  std::vector<std::string_view> failedToStartReasoner;
357  for (auto &pair: reasonerManager_->dataDriven()) {
358  KB_LOGGED_TRY_EXCEPT(pair.first.data(), "start",
359  { do_startReasoner(pair.second); },
360  { failedToStartReasoner.push_back(pair.first); });
361  }
362  // remove reasoner that failed to start
363  for (auto &reasonerName: failedToStartReasoner) {
364  reasonerManager_->removePlugin(reasonerName);
365  }
366 }
367 
369  for (auto &pair: reasonerManager_->dataDriven()) {
370  KB_LOGGED_TRY_CATCH(pair.first.data(), "stop", { pair.second->stop(); });
371  }
372 }
373 
375  auto &queryable = backendManager_->queryable();
376  if (queryable.empty()) {
377  KB_WARN("No queryable backends available.");
378  return nullptr;
379  } else {
380  return queryable.begin()->second;
381  }
382 }
383 
385  return submitQuery(std::make_shared<ConjunctiveQuery>(ConjunctiveQuery({literal}, ctx)));
386 }
387 
389  auto pipeline = std::make_shared<QueryPipeline>(shared_from_this(), conjunctiveQuery);
390  // Wrap output into AnswerBuffer_WithReference object.
391  // Note that the AnswerBuffer_WithReference object is used such that the caller can
392  // destroy the whole pipeline by de-referencing the returned AnswerBufferPtr.
393  auto out = std::make_shared<AnswerBuffer_WithReference>(pipeline);
394  *pipeline >> out;
395  pipeline->stopBuffering();
396  return out;
397 }
398 
400  auto pipeline = std::make_shared<QueryPipeline>(shared_from_this(), phi, ctx);
401  auto out = std::make_shared<AnswerBuffer_WithReference>(pipeline);
402  *pipeline >> out;
403  pipeline->stopBuffering();
404  return out;
405 }
406 
408  return observerManager_->observe(query, callback);
409 }
410 
412  observerManager_->synchronize();
413 }
414 
415 bool KnowledgeBase::insertOne(const Triple &triple) {
416  auto sourceBackend = findSourceBackend(triple);
417  auto transaction = edb_->createTransaction(
421  {sourceBackend});
422  if (transaction->commit(triple)) {
423  auto tripleCopy = new TripleCopy(triple);
424  std::vector<TriplePtr> triples;
425  triples.emplace_back(tripleCopy);
426  auto container = std::make_shared<ProxyTripleContainer>(triples);
427  observerManager_->insert(container);
428  return true;
429  } else {
430  return false;
431  }
432 }
433 
435  auto sourceBackend = findSourceBackend(**triples->begin());
436  auto transaction = edb_->createTransaction(
440  {sourceBackend});
441  if (transaction->commit(triples)) {
442  // update the vocabulary with the new triples
443  for (auto &triple: *triples) {
444  addToVocabulary(triple);
445  }
446  observerManager_->insert(triples);
447  return true;
448  } else {
449  return false;
450  }
451 }
452 
453 bool KnowledgeBase::removeOne(const Triple &triple) {
454  auto sourceBackend = findSourceBackend(triple);
455  auto transaction = edb_->createTransaction(
459  {sourceBackend});
460  if (transaction->commit(triple)) {
461  auto tripleCopy = new TripleCopy(triple);
462  std::vector<TriplePtr> triples;
463  triples.emplace_back(tripleCopy);
464  auto container = std::make_shared<ProxyTripleContainer>(triples);
465  observerManager_->remove(container);
466  return true;
467  } else {
468  return false;
469  }
470 }
471 
473  auto sourceBackend = findSourceBackend(**triples->begin());
474  auto transaction = edb_->createTransaction(
478  {sourceBackend});
479  if (transaction->commit(triples)) {
480  observerManager_->remove(triples);
481  return true;
482  } else {
483  return false;
484  }
485 }
486 
487 bool KnowledgeBase::insertAll(const std::vector<TriplePtr> &triples) {
488  // Note: insertAll blocks until the triples are inserted, so it is safe to use the triples vector as a pointer.
489  return insertAll(std::make_shared<ProxyTripleContainer>(&triples));
490 }
491 
492 bool KnowledgeBase::removeAll(const std::vector<TriplePtr> &triples) {
493  return removeAll(std::make_shared<ProxyTripleContainer>(&triples));
494 }
495 
496 bool KnowledgeBase::removeAllWithOrigin(std::string_view origin) {
497  return edb_->removeAllWithOrigin(origin);
498 }
499 
500 std::shared_ptr<NamedBackend> KnowledgeBase::findSourceBackend(const Triple &triple) {
501  if (!triple.graph()) return {};
502 
503  auto definedBackend_withID = backendManager_->getPluginWithID(triple.graph().value());
504  if (definedBackend_withID) return definedBackend_withID;
505 
506  auto definedReasoner = reasonerManager_->getPluginWithID(triple.graph().value());
507  if (definedReasoner) {
508  auto reasonerBackend = reasonerManager_->getReasonerStorage(definedReasoner);
509  if (reasonerBackend) {
510  for (auto &it: backendManager_->plugins()) {
511  auto &definedBackend_ofReasoner = it.second;
512  if (definedBackend_ofReasoner->value() == reasonerBackend) {
513  return definedBackend_ofReasoner;
514  }
515  }
516  }
517  }
518 
519  return {};
520 }
521 
522 void KnowledgeBase::configureDataSources(const boost::property_tree::ptree &config) {
523  auto dataSourcesList = config.get_child_optional(KB_SETTING_DATA_SOURCES);
524  if (!dataSourcesList) return;
525 
526  for (const auto &pair: dataSourcesList.value()) {
527  auto &subtree = pair.second;
528  auto dataSource = DataSource::create(vocabulary_, subtree);
529  if (!dataSource) {
530  KB_ERROR("Failed to create data source \"{}\".", pair.first);
531  continue;
532  }
533  bool has_error;
534  auto transformationConfig = config.get_child_optional(KB_SETTING_DATA_TRANSFORMATION);
535  if (transformationConfig) {
536  if (dataSource->dataSourceType() != DataSourceType::ONTOLOGY) {
537  KB_ERROR("Transformations can only be applied on ontology data sources.");
538  continue;
539  }
540  auto ontology = std::static_pointer_cast<OntologySource>(dataSource);
541  // apply a transformation to the data source if "transformation" key is present
542  auto transformation = GraphTransformation::create(transformationConfig.value());
543  auto transformed = std::make_shared<TransformedOntology>(URI(ontology->uri()), ontology->format());
544  transformation->apply(*ontology, [&transformed](const TripleContainerPtr &triples) {
545  transformed->storage()->insertAll(triples);
546  });
547  has_error = !loadDataSource(transformed);
548  } else {
549  has_error = !loadDataSource(dataSource);
550  }
551  if (has_error) {
552  KB_ERROR("Failed to load data source from \"{}\".", dataSource->uri());
553  }
554  }
555 }
556 
558  switch (source->dataSourceType()) {
560  return loadOntologySource(std::static_pointer_cast<OntologySource>(source));
562  return loadNonOntologySource(source);
563  }
564  return false;
565 }
566 
567 std::optional<std::string> KnowledgeBase::getVersionOfOrigin(
568  const std::shared_ptr<NamedBackend> &definedBackend, std::string_view origin) const {
569  // check if the origin was loaded before in this session
570  auto runtimeVersion = definedBackend->value()->getVersionOfOrigin(origin);
571  if (runtimeVersion) return runtimeVersion;
572  // otherwise check if the backend is persistent and if so, ask the persistent backend
573  auto persistentBackend = backendManager_->persistent().find(definedBackend->name());
574  if (persistentBackend != backendManager_->persistent().end()) {
575  return persistentBackend->second->getVersionOfOrigin(origin);
576  }
577  return {};
578 }
579 
580 std::vector<std::shared_ptr<NamedBackend>>
581 KnowledgeBase::prepareLoad(std::string_view origin, std::string_view newVersion) const {
582  std::vector<std::shared_ptr<NamedBackend>> backendsToLoad;
583  for (auto &it: backendManager_->plugins()) {
584  // check if the ontology is already loaded by the backend,
585  // and if so whether it has the right version.
586  auto definedBackend = it.second;
587  auto currentVersion = getVersionOfOrigin(definedBackend, origin);
588  if (currentVersion.has_value()) {
589  if (currentVersion.value() != newVersion) {
590  backendsToLoad.emplace_back(it.second);
591  definedBackend->value()->removeAllWithOrigin(origin);
592  }
593  } else {
594  backendsToLoad.emplace_back(it.second);
595  }
596  }
597  return backendsToLoad;
598 }
599 
600 void KnowledgeBase::finishLoad(const std::shared_ptr<OntologySource> &source, std::string_view origin,
601  std::string_view newVersion) {
602  // update the version triple
603  for (auto &it: backendManager_->plugins()) {
604  it.second->value()->setVersionOfOrigin(origin, newVersion);
605  }
606  for (auto &it: backendManager_->persistent()) {
607  auto persistentBackend = it.second;
608  persistentBackend->setVersionOfOrigin(origin, newVersion);
609  }
610 
611  // add direct import
612  if (source->parentOrigin().has_value()) {
613  vocabulary_->importHierarchy()->addDirectImport(source->parentOrigin().value(), origin);
614  } else {
615  vocabulary_->importHierarchy()->addDirectImport(vocabulary_->importHierarchy()->defaultGraph(), origin);
616  }
617 }
618 
619 bool KnowledgeBase::loadOntologySource(const std::shared_ptr<OntologySource> &source) { // NOLINT(misc-no-recursion)
620  auto uri = URI::resolve(source->uri());
621  // Some ontologies may encode version in the URI which we try to extract
622  // below. Otherwise, we just use the current day as version causing a re-load every day.
623  auto newVersion = DataSource::getVersionFromURI(uri);
624 
625  // get all backends that do not have the data loaded yet
626  auto backendsToLoad = prepareLoad(source->origin(), newVersion);
627  if (backendsToLoad.empty()) {
628  // data is already loaded
629  KB_DEBUG("Ontology at \"{}\" already loaded.", uri);
630  return true;
631  }
632 
633  auto result = source->load([this, &backendsToLoad](const TripleContainerPtr &triples) {
634  auto transaction = edb_->createTransaction(
638  backendsToLoad);
639  transaction->commit(triples);
640  });
641  if (!result) {
642  KB_WARN("Failed to load ontology \"{}\".", uri);
643  return false;
644  }
645  finishLoad(source, source->origin(), newVersion);
646 
647  for (auto &imported: source->imports()) {
648  auto importedSource = std::make_shared<OntologyFile>(vocabulary_, URI(imported), source->format());
649  if (!loadOntologySource(importedSource)) {
650  KB_WARN("Failed to load imported ontology \"{}\".", imported);
651  return false;
652  }
653  }
654 
655  return true;
656 }
657 
659  bool hasHandler = false;
660  bool allSucceeded = true;
661 
662  for (auto &kg_pair: backendManager_->plugins()) {
663  auto backend = kg_pair.second->value();
664  if (backend->hasDataHandler(source)) {
665  if (!backend->loadDataSource(source)) {
666  allSucceeded = false;
667  KB_WARN("backend '{}' failed to load data source '{}'", kg_pair.first, source->uri());
668  }
669  hasHandler = true;
670  }
671  }
672 
673  if (!hasHandler) {
674  KB_WARN("no data handler for data source format '{}'", source->format());
675  }
676 
677  return hasHandler && allSucceeded;
678 }
679 
680 void KnowledgeBase::setDefaultGraph(std::string_view origin) {
681  vocabulary_->importHierarchy()->setDefaultGraph(origin);
682 }
683 
684 static std::shared_ptr<KnowledgeBase> makeKB1() {
685  return KnowledgeBase::create();
686 }
687 
688 static std::shared_ptr<KnowledgeBase> makeKB2(std::string_view settingsFile) {
689  return KnowledgeBase::create(settingsFile);
690 }
691 
692 static std::shared_ptr<KnowledgeBase> makeKB3(const boost::property_tree::ptree &settingsTree) {
693  return KnowledgeBase::create(settingsTree);
694 }
695 
696 namespace knowrob::py {
697  template<>
699  using namespace boost::python;
700 
701  // these typedefs are necessary to resolve functions with duplicate names which is
702  // fine in C++ but not in Python, so they need special handling here.
703  // The typedefs are used to explicitly select the mapped method.
704  using QueryPredicate = TokenBufferPtr (KnowledgeBase::*)(const FirstOrderLiteralPtr &, const QueryContextPtr &);
705  using QueryFormula = TokenBufferPtr (KnowledgeBase::*)(const FormulaPtr &, const QueryContextPtr &);
706  using QueryGraph = TokenBufferPtr (KnowledgeBase::*)(const ConjunctiveQueryPtr &);
707  using ContainerAction = bool (KnowledgeBase::*)(const TripleContainerPtr &);
708  using ListAction = bool (KnowledgeBase::*)(const std::vector<TriplePtr> &);
709 
712 
713  class_<KnowledgeBase, std::shared_ptr<KnowledgeBase>, boost::noncopyable>
714  ("KnowledgeBase", no_init)
715  // hide the "create" functions in Python
716  .def("__init__", make_constructor(&makeKB1))
717  .def("__init__", make_constructor(&makeKB2))
718  .def("__init__", make_constructor(&makeKB3))
719  .def("setDefaultGraph", &KnowledgeBase::setDefaultGraph)
720  .def("vocabulary", &KnowledgeBase::vocabulary, return_value_policy<copy_const_reference>())
721  .def("loadCommon", with<no_gil>(&KnowledgeBase::loadCommon))
722  .def("loadDataSource", with<no_gil>(&KnowledgeBase::loadDataSource))
723  .def("submitQuery", with<no_gil>(static_cast<QueryFormula>(&KnowledgeBase::submitQuery)))
724  .def("submitQuery", with<no_gil>(static_cast<QueryPredicate>(&KnowledgeBase::submitQuery)))
725  .def("submitQuery", with<no_gil>(static_cast<QueryGraph>(&KnowledgeBase::submitQuery)))
726  .def("observe", +[](KnowledgeBase &kb, const GraphQueryPtr &query, object &fn) {
727  no_gil unlock;
728  return kb.observe(query, [fn](const BindingsPtr &bindings) {
729  // make sure to lock the GIL before calling the Python function
730  gil_lock lock;
731  fn(bindings);
732  });
733  })
734  .def("insertOne", with<no_gil>(&KnowledgeBase::insertOne))
735  .def("insertAll", with<no_gil>(static_cast<ContainerAction>(&KnowledgeBase::insertAll)))
736  .def("insertAll", with<no_gil>(static_cast<ListAction>(&KnowledgeBase::insertAll)))
737  .def("removeOne", with<no_gil>(&KnowledgeBase::removeOne))
738  .def("removeAll", with<no_gil>(static_cast<ContainerAction>(&KnowledgeBase::removeAll)))
739  .def("removeAll", with<no_gil>(static_cast<ListAction>(&KnowledgeBase::removeAll)))
740  .def("removeAllWithOrigin", with<no_gil>(&KnowledgeBase::removeAllWithOrigin));
741  }
742 }
#define KB_SETTING_DATA_SOURCES
#define KB_SETTING_REASONER
#define KB_SETTING_SEMWEB
#define KB_SETTING_PREFIX_URI
#define KB_SETTING_DATA_TRANSFORMATION
#define KB_SETTING_PREFIX_ALIAS
#define KB_SETTING_PREFIXES
#define KB_SETTING_DATA_BACKENDS
#define KB_LOGGED_TRY_EXCEPT(name, type, goal, except)
Definition: Logger.h:157
#define KB_DEBUG
Definition: Logger.h:25
#define KB_ERROR
Definition: Logger.h:28
#define KB_LOGGED_TRY_CATCH(name, type, goal)
Definition: Logger.h:142
#define KB_WARN
Definition: Logger.h:27
static std::string getVersionFromURI(const std::string &uriString)
Definition: DataSource.cpp:48
static std::shared_ptr< DataSource > create(const VocabularyPtr &vocabulary, const boost::property_tree::ptree &config)
Definition: DataSource.cpp:105
static std::shared_ptr< GraphTransformation > create(const boost::property_tree::ptree &config)
static std::shared_ptr< IRIAtom > Tabled(std::string_view stringForm)
Definition: IRIAtom.cpp:25
static constexpr std::string_view ORIGIN_USER
static constexpr std::string_view ORIGIN_SYSTEM
bool loadNonOntologySource(const DataSourcePtr &source) const
bool removeOne(const Triple &triple)
bool insertAll(const TripleContainerPtr &triples)
std::optional< std::string > getVersionOfOrigin(const std::shared_ptr< NamedBackend > &definedBackend, std::string_view origin) const
void addToVocabulary(const TriplePtr &triple)
std::vector< std::shared_ptr< NamedBackend > > prepareLoad(std::string_view origin, std::string_view newVersion) const
void finishLoad(const std::shared_ptr< OntologySource > &source, std::string_view origin, std::string_view newVersion)
bool removeAllWithOrigin(std::string_view origin)
void configureBackends(const boost::property_tree::ptree &config)
bool insertOne(const Triple &triple)
std::shared_ptr< NamedBackend > findSourceBackend(const Triple &triple)
std::shared_ptr< StorageManager > backendManager_
QueryableBackendPtr getBackendForQuery() const
void setDefaultGraph(std::string_view origin)
std::shared_ptr< ReasonerManager > reasonerManager_
static void configurePrefixes(const boost::property_tree::ptree &config)
void configureReasoner(const boost::property_tree::ptree &config)
bool loadOntologySource(const std::shared_ptr< OntologySource > &source)
auto & vocabulary() const
Definition: KnowledgeBase.h:67
void configure(const boost::property_tree::ptree &config)
bool loadDataSource(const DataSourcePtr &source)
static std::shared_ptr< KnowledgeBase > create()
std::shared_ptr< StorageInterface > edb_
std::shared_ptr< Vocabulary > vocabulary_
bool removeAll(const TripleContainerPtr &triples)
ObserverPtr observe(const GraphQueryPtr &query, const BindingsHandler &callback)
std::shared_ptr< ObserverManager > observerManager_
TokenBufferPtr submitQuery(const ConjunctiveQueryPtr &conjunctiveQuery)
void configureDataSources(const boost::property_tree::ptree &config)
static void registerPrefix(std::string_view prefix, std::string_view uri)
virtual std::string_view valueAsString() const =0
virtual std::optional< std::string_view > graph() const =0
virtual std::string_view subject() const =0
virtual std::string_view predicate() const =0
static std::string resolve(const std::string_view &uriString)
Definition: URI.cpp:79
const IRIAtomPtr inverseOf
Definition: owl.h:17
void createType< Vocabulary >()
Definition: Vocabulary.cpp:271
void createType< KnowledgeBase >()
void createType< GraphQuery >()
Definition: GraphQuery.cpp:96
const IRIAtomPtr type
Definition: rdf.h:15
const IRIAtomPtr subPropertyOf
Definition: rdfs.h:16
const IRIAtomPtr subClassOf
Definition: rdfs.h:15
TripleTemplate< std::string > TripleCopy
Definition: Triple.h:577
std::shared_ptr< TripleContainer > TripleContainerPtr
std::shared_ptr< VersionedOrigin > VersionedOriginPtr
std::shared_ptr< Observer > ObserverPtr
Definition: Observer.h:42
std::shared_ptr< const Bindings > BindingsPtr
Definition: Bindings.h:151
std::shared_ptr< Formula > FormulaPtr
Definition: Formula.h:99
std::shared_ptr< ConjunctiveQuery > ConjunctiveQueryPtr
std::shared_ptr< TokenBuffer > TokenBufferPtr
Definition: TokenBuffer.h:43
std::function< void(const BindingsPtr &)> BindingsHandler
Definition: Bindings.h:152
std::shared_ptr< const QueryContext > QueryContextPtr
Definition: QueryContext.h:41
std::shared_ptr< GraphQuery > GraphQueryPtr
Definition: GraphQuery.h:65
std::shared_ptr< QueryableStorage > QueryableBackendPtr
std::shared_ptr< DataSource > DataSourcePtr
Definition: DataSource.h:107
std::shared_ptr< FirstOrderLiteral > FirstOrderLiteralPtr