knowrob  2.1.0
A Knowledge Base System for Cognition-enabled Robots
MongoTaxonomy.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 <mongoc.h>
7 #include <set>
8 #include "knowrob/storage/mongo/MongoTaxonomy.h"
9 #include "knowrob/storage/mongo/Pipeline.h"
10 #include "knowrob/semweb/rdfs.h"
11 
12 using namespace knowrob;
13 using namespace knowrob::mongo;
14 using namespace knowrob::semweb;
15 
17  const std::shared_ptr<mongo::Collection> &tripleCollection,
18  const std::shared_ptr<mongo::Collection> &oneCollection,
19  const VocabularyPtr &vocabulary)
20  : tripleCollection_(tripleCollection), oneCollection_(oneCollection), vocabulary_(vocabulary) {
21 }
22 
23 template<typename ResourceType>
24 static void bulkUpdateTaxonomy(
25  std::shared_ptr<mongo::BulkOperation> &bulk,
26  std::shared_ptr<Vocabulary> &vocabulary,
27  std::string_view taxonomyRelation,
28  const std::vector<MongoTaxonomy::StringPair> &assertions) {
29  if (assertions.empty()) { return; }
30 
31  std::set<MongoTaxonomy::StringPair> invalidAssertions;
32  for (auto &assertion: assertions) {
33  // Note: the assertion itself must not be included as the Vocabulary class is
34  // used to build the o* field.
35  auto resource = vocabulary->define<ResourceType>(assertion.first);
36  resource->forallChildren([&invalidAssertions](const ResourceType &child, const ResourceType &directParent) {
37  invalidAssertions.insert({child.iri(), directParent.iri()});
38  });
39  }
40 
41  for (auto &assertion: invalidAssertions) {
42  auto query = bson_new();
43  BSON_APPEND_UTF8(query, "s", assertion.first.data());
44  BSON_APPEND_UTF8(query, "p", taxonomyRelation.data());
45  BSON_APPEND_UTF8(query, "o", assertion.second.data());
46 
47  auto update = bson_new();
48  bson_t setDoc, setArray;
49  BSON_APPEND_DOCUMENT_BEGIN(update, "$set", &setDoc);
50  BSON_APPEND_ARRAY_BEGIN(&setDoc, "o*", &setArray);
51 
52  auto cls = vocabulary->define<ResourceType>(assertion.second);
53  uint32_t numParents = 0;
54  cls->forallParents([&setArray, &numParents](const ResourceType &parent) {
55  auto arrayKey = std::to_string(numParents++);
56  BSON_APPEND_UTF8(&setArray, arrayKey.c_str(), parent.iri().data());
57  }, true);
58 
59  bson_append_array_end(&setDoc, &setArray);
60  bson_append_document_end(update, &setDoc);
61 
62  bulk->pushUpdate(query, update);
63 
64  bson_destroy(query);
65  bson_destroy(update);
66  }
67 }
68 
69 static void bulkUpdateTriples_insert(
70  std::shared_ptr<mongo::BulkOperation> &bulk,
71  std::shared_ptr<Vocabulary> &vocabulary,
72  const std::set<std::string_view> &invalidPropertyAssertions) {
73  for (auto &invalidProperty: invalidPropertyAssertions) {
74  // match all assertions where the property appears in the p* field
75  auto query = bson_new();
76  BSON_APPEND_UTF8(query, "p*", invalidProperty.data());
77 
78  // update the p* field by using $addToSet to add parents of the invalidated property to the p* field
79  // { $addToSet: { "p*": { $each: [ .... ] } } }
80  auto update = bson_new();
81  bson_t addToSetDoc, addToSetEach, addToSetArray;
82  BSON_APPEND_DOCUMENT_BEGIN(update, "$addToSet", &addToSetDoc);
83  BSON_APPEND_DOCUMENT_BEGIN(&addToSetDoc, "p*", &addToSetEach);
84  BSON_APPEND_ARRAY_BEGIN(&addToSetEach, "$each", &addToSetArray);
85 
86  auto resource = vocabulary->define<Property>(invalidProperty);
87  uint32_t numParents = 0;
88  resource->forallParents([&addToSetArray, &numParents](const Property &parent) {
89  auto arrayKey = std::to_string(numParents++);
90  BSON_APPEND_UTF8(&addToSetArray, arrayKey.c_str(), parent.iri().data());
91  }, false);
92 
93  bson_append_array_end(&addToSetEach, &addToSetArray);
94  bson_append_document_end(&addToSetDoc, &addToSetEach);
95  bson_append_document_end(update, &addToSetDoc);
96  bulk->pushUpdate(query, update);
97 
98  bson_destroy(query);
99  bson_destroy(update);
100  }
101 }
102 
103 static void lookupParents(
104  Pipeline &pipeline,
105  const std::string_view &collection,
106  const std::string_view &entity,
107  const std::string_view &relation) {
108  // lookup parent hierarchy.
109  // e.g. for subClassOf these are all o* values of subClassOf documents of entity
110  bson_t lookupArray;
111  auto lookupStage = pipeline.appendStageBegin("$lookup");
112  BSON_APPEND_UTF8(lookupStage, "from", collection.data());
113  BSON_APPEND_UTF8(lookupStage, "as", "parents");
114  BSON_APPEND_ARRAY_BEGIN(lookupStage, "pipeline", &lookupArray);
115  {
116  Pipeline lookupPipeline(&lookupArray);
117  // { $match: { s: $entity, p: $relation } }
118  auto matchStage = lookupPipeline.appendStageBegin("$match");
119  BSON_APPEND_UTF8(matchStage, "s", entity.data());
120  BSON_APPEND_UTF8(matchStage, "p", relation.data());
121  lookupPipeline.appendStageEnd(matchStage);
122  // { $project: { "o*": 1 } }
123  lookupPipeline.project("o*");
124  // { $unwind: "$o*" }
125  lookupPipeline.unwind("$o*");
126  }
127  bson_append_array_end(lookupStage, &lookupArray);
128  pipeline.appendStageEnd(lookupStage);
129 
130  // convert "parents" field from list of documents to list of strings:
131  // { $set { parents: { $map: { input: "$parents", "in": "$$this.o*" } } } }
132  bson_t parentsDoc, mapDoc;
133  auto setStage = pipeline.appendStageBegin("$set");
134  BSON_APPEND_DOCUMENT_BEGIN(setStage, "parents", &parentsDoc);
135  {
136  BSON_APPEND_DOCUMENT_BEGIN(&parentsDoc, "$map", &mapDoc);
137  {
138  BSON_APPEND_UTF8(&mapDoc, "input", "$parents");
139  BSON_APPEND_UTF8(&mapDoc, "in", "$$this.o*");
140  }
141  bson_append_document_end(&parentsDoc, &mapDoc);
142  }
143  bson_append_document_end(setStage, &parentsDoc);
144  pipeline.appendStageEnd(setStage);
145 }
146 
147 static void bulkUpdateTriples_remove(
148  std::shared_ptr<mongo::BulkOperation> &bulk,
149  std::shared_ptr<Vocabulary>& /*vocabulary*/,
150  std::string_view tripleCollectionName,
151  const std::set<std::string_view> &invalidPropertyAssertions) {
152  for (auto &invalidProperty: invalidPropertyAssertions) {
153  // match all assertions where the property appears in the p* field
154  auto query = bson_new();
155  BSON_APPEND_UTF8(query, "p*", invalidProperty.data());
156 
157  // update the p* field
158  auto update = bson_new();
159  bson_t pipelineArray;
160  BSON_APPEND_ARRAY_BEGIN(update, "pipeline", &pipelineArray);
161  Pipeline pipeline(&pipelineArray);
162  {
163  // first lookup hierarchy into array field "parents"
164  lookupParents(pipeline, tripleCollectionName, invalidProperty, rdfs::subPropertyOf->stringForm());
165  // { $set: { "p*": "$parents" } }
166  auto setDoc = pipeline.appendStageBegin("$set");
167  BSON_APPEND_UTF8(setDoc, "p*", "$parents");
168  pipeline.appendStageEnd(setDoc);
169  // { $project: { "p*": 1 } }
170  pipeline.project("p*");
171  }
172  bson_append_array_end(update, &pipelineArray);
173 
174  bulk->pushUpdate(query, update);
175 
176  bson_destroy(query);
177  bson_destroy(update);
178  }
179 }
180 
182  const std::vector<StringPair> &subClassAssertions,
183  const std::vector<StringPair> &subPropertyAssertions) {
184  update(subClassAssertions, subPropertyAssertions, true);
185 }
186 
188  const std::vector<StringPair> &subClassAssertions,
189  const std::vector<StringPair> &subPropertyAssertions) {
190  update(subClassAssertions, subPropertyAssertions, false);
191 }
192 
194  const std::vector<StringPair> &subClassAssertions,
195  const std::vector<StringPair> &subPropertyAssertions,
196  bool isInsert) {
197  // below performs the server-side data transformation for updating hierarchy relations
198  // such as rdf::type.
199 
200  if (subClassAssertions.empty() && subPropertyAssertions.empty()) return;
201 
202  // create a bulk operation for updating subClassOf and subPropertyOf relations
203  // using the taxonomic assertions in the Vocabulary class
204  auto bulk = tripleCollection_->createBulkOperation();
205 
206  // add bulk operations to update subClassOf and subPropertyOf relations
207  if (!subClassAssertions.empty()) {
208  bulkUpdateTaxonomy<Class>(bulk, vocabulary_, rdfs::subClassOf->stringForm(), subClassAssertions);
209  }
210  if (!subPropertyAssertions.empty()) {
211  bulkUpdateTaxonomy<Property>(bulk, vocabulary_, rdfs::subPropertyOf->stringForm(), subPropertyAssertions);
212  }
213 
214  // also update the p* field of all documents that contain an invalidated property in this field
215  if (!subPropertyAssertions.empty()) {
216  std::set<std::string_view> invalidPropertyAssertions;
217  for (auto &assertion: subPropertyAssertions) {
218  invalidPropertyAssertions.insert(assertion.first);
219  }
220  if (isInsert) {
221  bulkUpdateTriples_insert(bulk, vocabulary_, invalidPropertyAssertions);
222  } else {
223  bulkUpdateTriples_remove(bulk, vocabulary_, tripleCollection_->name(), invalidPropertyAssertions);
224  }
225  }
226 
227  if (!bulk->empty()) {
228  bulk->execute();
229  }
230 }
void update(const std::vector< StringPair > &subClassAssertions, const std::vector< StringPair > &subPropertyAssertions, bool isInsert)
void updateRemove(const std::vector< StringPair > &subClassAssertions, const std::vector< StringPair > &subPropertyAssertions)
std::shared_ptr< mongo::Collection > tripleCollection_
Definition: MongoTaxonomy.h:36
void updateInsert(const std::vector< StringPair > &subClassAssertions, const std::vector< StringPair > &subPropertyAssertions)
MongoTaxonomy(const std::shared_ptr< mongo::Collection > &tripleCollection, const std::shared_ptr< mongo::Collection > &oneCollection, const VocabularyPtr &vocabulary)
void appendStageEnd(bson_t *stage)
Definition: Pipeline.cpp:46
bson_t * appendStageBegin()
Definition: Pipeline.cpp:27
void project(std::string_view field)
Definition: Pipeline.cpp:299
void forallParents(const PropertyVisitor &visitor, bool includeSelf=true, bool skipDuplicates=true)
Definition: Property.cpp:110
const IRIAtomPtr subPropertyOf
Definition: rdfs.h:16
const IRIAtomPtr subClassOf
Definition: rdfs.h:15
std::shared_ptr< Vocabulary > VocabularyPtr
Definition: Vocabulary.h:233