knowrob  2.1.0
A Knowledge Base System for Cognition-enabled Robots
MongoTriplePattern.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 "knowrob/storage/mongo/MongoTriplePattern.h"
7 #include "knowrob/storage/mongo/MongoTerm.h"
8 #include "knowrob/storage/mongo/Pipeline.h"
9 
10 using namespace knowrob;
11 using namespace knowrob::mongo;
12 
13 #define MONGO_OPERATOR_LTE "$lte"
14 #define MONGO_OPERATOR_GTE "$gte"
15 #define MONGO_OPERATOR_LT "$lt"
16 #define MONGO_OPERATOR_GT "$gt"
17 #define MONGO_OPERATOR_NEQ "$ne"
18 
20  const TriplePattern &tripleExpression,
21  bool b_isTaxonomicProperty,
22  const std::shared_ptr<ImportHierarchy> &importHierarchy)
23  : document_(create(tripleExpression, b_isTaxonomicProperty, importHierarchy)) {
24 }
25 
27  const TriplePattern &tripleExpression,
28  bool b_isTaxonomicProperty,
29  const std::shared_ptr<ImportHierarchy> &importHierarchy) {
30  auto selectorDoc = bson_new();
31  append(selectorDoc, tripleExpression, b_isTaxonomicProperty, importHierarchy);
32  return selectorDoc;
33 }
34 
35 void MongoTriplePattern::append(bson_t *selectorDoc,
36  const TriplePattern &tripleExpression,
37  bool b_isTaxonomicProperty,
38  const std::shared_ptr<ImportHierarchy> &importHierarchy) {
39  // "s"" field
40  MongoTerm::append(selectorDoc,
41  "s", tripleExpression.subjectTerm());
42  // "p" field
43  MongoTerm::append(selectorDoc,
44  (b_isTaxonomicProperty ? "p" : "p*"),
45  tripleExpression.propertyTerm());
46  // "o" field
47  const char *objectOperator = getOperatorString(tripleExpression.objectOperator());
48  MongoTerm::append(selectorDoc,
49  (b_isTaxonomicProperty ? "o*" : "o"),
50  tripleExpression.objectTerm(),
51  objectOperator);
52  // "g" field
53  appendGraphSelector(selectorDoc, tripleExpression, importHierarchy);
54  // epistemic fields
55  appendEpistemicSelector(selectorDoc, tripleExpression);
56  // temporal fields
57  appendTimeSelector(selectorDoc, tripleExpression);
58 }
59 
60 void MongoTriplePattern::appendGraphSelector(bson_t *selectorDoc,
61  const TriplePattern &tripleExpression,
62  const std::shared_ptr<ImportHierarchy> &importHierarchy) {
63  auto &gt = tripleExpression.graphTerm();
64  if (!gt) return;
65 
66  if (gt->termType() == TermType::ATOMIC) {
67  auto graphString = gt.grounded();
68  if (graphString->stringForm() == ImportHierarchy::ORIGIN_ANY || graphString->stringForm() == "*")
69  return;
70 
71  std::vector<TermPtr> childrenNames;
72  for (auto &child: importHierarchy->getImports(graphString->stringForm())) {
73  childrenNames.push_back(Atom::Tabled(child->name()));
74  }
75  if (childrenNames.empty()) {
76  MongoTerm::append(selectorDoc, "graph", graphString);
77  } else {
78  childrenNames.push_back(graphString);
79  MongoTerm::append(selectorDoc, "graph", childrenNames, "$in");
80  }
81  }
82  /*
83  else if (gt->termType() == TermType::FUNCTION) {
84  auto fn = (Function*) gt.get();
85  if (*fn->functor() == *ListTerm::listFunctor()) {
86  aggregation::appendArrayQuery(selectorDoc, "graph", fn->arguments(), "$in");
87  } else {
88  KB_WARN("graph term {} has unexpected function type", *gt);
89  }
90  }
91  */
92  else {
93  KB_WARN("graph term {} has unexpected type", *gt);
94  }
95 }
96 
97 void MongoTriplePattern::appendEpistemicSelector(bson_t *selectorDoc, const TriplePattern &tripleExpression) {
98  auto &ct = tripleExpression.confidenceTerm();
99  auto &at = tripleExpression.perspectiveTerm();
100  auto &u = tripleExpression.isUncertainTerm();
101 
102  if (!((u.has_grounding() && u.grounded()->asBoolean()) || u.has_variable())) {
103  // note: null value of "uncertain" field is seen as value "false"
105  selectorDoc,
106  "uncertain",
108  nullptr,
109  true);
110  }
111 
112  if (at) {
113  if (at->termType() == TermType::ATOMIC) {
115  selectorDoc,
116  "agent",
117  *at,
118  nullptr,
119  false);
120  } else {
121  KB_WARN("agent term {} has unexpected type", *at);
122  }
123  } else {
124  // make sure agent field is undefined: { agent: { $exists: false } }
125  // note: null value of agent field is seen as "self", i.e. the agent running the knowledge base
126  bson_t agentDoc;
127  BSON_APPEND_DOCUMENT_BEGIN(selectorDoc, "agent", &agentDoc);
128  BSON_APPEND_BOOL(&agentDoc, "$exists", false);
129  bson_append_document_end(selectorDoc, &agentDoc);
130  }
131 
132  if (ct) {
133  if (ct->termType() == TermType::ATOMIC) {
134  // note: null value of confidence is seen as larger than the requested threshold
136  selectorDoc,
137  "confidence",
138  *ct,
140  true);
141  } else {
142  KB_WARN("confidence term {} has unexpected type", *ct);
143  }
144  }
145 }
146 
147 void MongoTriplePattern::appendTimeSelector(bson_t *selectorDoc, const TriplePattern &tripleExpression) {
148  static const bool allowNullValues = true;
149  static auto b_occasional = std::make_shared<Integer>(static_cast<int32_t>(true));
150  static auto b_always = std::make_shared<Integer>(static_cast<int32_t>(false));
151  auto bt = &tripleExpression.beginTerm();
152  auto et = &tripleExpression.endTerm();
153  auto &o = tripleExpression.isOccasionalTerm();
154 
155  if (!((o.has_grounding() && o.grounded()->asBoolean()) || o.has_variable())) {
156  // note: null value of "occasional" field is seen as value "false"
158  selectorDoc,
159  "occasional",
161  nullptr,
162  true);
163  } else {
164  auto swap = bt;
165  bt = et;
166  et = swap;
167  }
168 
169  if (bt->has_grounding()) {
171  selectorDoc,
172  "scope.time.since",
173  bt->get(),
175  allowNullValues);
176  }
177  if (et->has_grounding()) {
179  selectorDoc,
180  "scope.time.until",
181  et->get(),
183  allowNullValues);
184  }
185 }
186 
188  switch (operatorType) {
189  case FilterType::EQ:
190  return nullptr;
191  case FilterType::NEQ:
192  return MONGO_OPERATOR_NEQ;
193  case FilterType::LEQ:
194  return MONGO_OPERATOR_LTE;
195  case FilterType::GEQ:
196  return MONGO_OPERATOR_GTE;
197  case FilterType::LT:
198  return MONGO_OPERATOR_LT;
199  case FilterType::GT:
200  return MONGO_OPERATOR_GT;
201  }
202  return nullptr;
203 }
204 
205 static inline void appendSetVariable(bson_t *doc, std::string_view varName, std::string_view newValue) {
206  const auto varKey = MongoTerm::variableKey(varName);
207  const auto varValue = std::string("$") + varKey;
208  bson_t condDoc, condArray, notOperator, notArray;
209  // Only conditionally apply new value in case it has not been grounded before.
210  // varKey: {$cond: [ { $not: [ varValue ] }, newValue, varValue ]}
211  BSON_APPEND_DOCUMENT_BEGIN(doc, varKey.c_str(), &condDoc);
212  BSON_APPEND_ARRAY_BEGIN(&condDoc, "$cond", &condArray);
213  {
214  BSON_APPEND_DOCUMENT_BEGIN(&condArray, "0", &notOperator);
215  BSON_APPEND_ARRAY_BEGIN(&notOperator, "$not", &notArray);
216  BSON_APPEND_UTF8(&notArray, "0", varValue.c_str());
217  bson_append_array_end(&notOperator, &notArray);
218  bson_append_document_end(&condArray, &notOperator);
219 
220  BSON_APPEND_UTF8(&condArray, "1", newValue.data());
221  BSON_APPEND_UTF8(&condArray, "2", varValue.c_str());
222  }
223  bson_append_array_end(&condDoc, &condArray);
224  bson_append_document_end(doc, &condDoc);
225 }
226 
227 static inline void appendMatchVariable(bson_t *doc,
228  std::string_view fieldValue,
229  std::string_view varName,
230  const TripleLookupData &lookupData) {
231  const auto varKey = MongoTerm::variableKey(varName);
232  const auto varLetValue = std::string("$$") + varKey;
233  const auto matchOperator = (fieldValue.back() == '*' ? "$in" : "$eq");
234  bson_t exprDoc, orArr, notDoc, notArr, matchDoc, matchArr;
235 
236  BSON_APPEND_DOCUMENT_BEGIN(doc, "$expr", &exprDoc);
237 
238  if (lookupData.knownGroundedVariables.count(varName) > 0) {
239  // variable is known to have a grounding in the v_VARS field
240  // {$expr: { matchOperator: [varLetValue, fieldValue] } }
241  BSON_APPEND_ARRAY_BEGIN(&exprDoc, matchOperator, &matchArr);
242  BSON_APPEND_UTF8(&matchArr, "0", varLetValue.c_str());
243  BSON_APPEND_UTF8(&matchArr, "1", fieldValue.data());
244  bson_append_array_end(&exprDoc, &matchArr);
245  } else {
246  // {$expr: {$or: [ { $not: [ varLetValue ] }, { matchOperator: [varLetValue, fieldValue] } ]}}
247  BSON_APPEND_ARRAY_BEGIN(&exprDoc, "$or", &orArr);
248 
249  // { $not: [ varLetValue ] }
250  BSON_APPEND_DOCUMENT_BEGIN(&orArr, "0", &notDoc);
251  BSON_APPEND_ARRAY_BEGIN(&notDoc, "$not", &notArr);
252  BSON_APPEND_UTF8(&notArr, "0", varLetValue.c_str());
253  bson_append_array_end(&notDoc, &notArr);
254  bson_append_document_end(&orArr, &notDoc);
255 
256  // { matchOperator: [varLetValue, fieldValue] }
257  BSON_APPEND_DOCUMENT_BEGIN(&orArr, "1", &matchDoc);
258  BSON_APPEND_ARRAY_BEGIN(&matchDoc, matchOperator, &matchArr);
259  BSON_APPEND_UTF8(&matchArr, "0", varLetValue.c_str());
260  BSON_APPEND_UTF8(&matchArr, "1", fieldValue.data());
261  bson_append_array_end(&matchDoc, &matchArr);
262  bson_append_document_end(&orArr, &matchDoc);
263 
264  bson_append_array_end(&exprDoc, &orArr);
265  }
266  bson_append_document_end(doc, &exprDoc);
267 }
268 
270  const TriplePattern &expr,
271  const std::set<std::string_view> &knownGroundedVariables) {
272  std::list<std::pair<const char *, Variable *>> varList;
273 
274  // the object can be specified as a triple (Value, Operator, ActualValue) such that
275  // a value constraint is given plus a variable where the actual value of the triple should be stored.
276  TermPtr objectVar = expr.objectTerm();
277  if (objectVar && !objectVar->isVariable() && expr.objectVariable()) {
278  objectVar = expr.objectVariable();
279  }
280 
281  for (auto &it: {
282  std::make_pair("$next.s", expr.subjectTerm()),
283  std::make_pair("$next.p", expr.propertyTerm()),
284  std::make_pair("$next.o", objectVar),
285  std::make_pair("$next.graph", expr.graphTerm().get()),
286  std::make_pair("$next.confidence", expr.confidenceTerm().get()),
287  std::make_pair("$next.agent", expr.perspectiveTerm().get()),
288  std::make_pair("$next.scope.time.since", expr.beginTerm().get()),
289  std::make_pair("$next.scope.time.until", expr.endTerm().get()),
290  std::make_pair("$next.uncertain", expr.isUncertainTerm().get()),
291  std::make_pair("$next.occasional", expr.isOccasionalTerm().get())
292  }) {
293  if (!it.second || it.second->termType() != TermType::VARIABLE) continue;
294  auto var = (Variable *) it.second.get();
295  // skip variables that were instantiated in previous steps
296  if (knownGroundedVariables.count(var->name()) > 0) continue;
297  varList.emplace_back(it.first, var);
298  }
299 
300  if (!varList.empty()) {
301  auto setVariables = pipeline.appendStageBegin("$set");
302  std::set<std::string_view> setVariablesNames;
303  std::vector<std::pair<const char *, Variable *>> duplicates;
304  for (auto &it: varList) {
305  if (setVariablesNames.count(it.second->name()) > 0) {
306  duplicates.push_back(it);
307  continue;
308  }
309  appendSetVariable(setVariables, it.second->name(), it.first);
310  setVariablesNames.insert(it.second->name());
311  }
312  pipeline.appendStageEnd(setVariables);
313 
314  // handle duplicates: make sure that each variable value is equal to the computed value
315  // of the duplicate variable.
316  for (auto &it: duplicates) {
317  static const std::string varPrefix("$");
318  auto varValue = std::string("$") + MongoTerm::variableKey(it.second->name());
319 
320  // append $match stage for each duplicate variable
321  bson_t exprDoc, matchArr;
322  auto matchStage = pipeline.appendStageBegin("$match"); {
323  BSON_APPEND_DOCUMENT_BEGIN(matchStage, "$expr", &exprDoc);
324  BSON_APPEND_ARRAY_BEGIN(&exprDoc, "$eq", &matchArr);
325  BSON_APPEND_UTF8(&matchArr, "0", varValue.c_str());
326  BSON_APPEND_UTF8(&matchArr, "1", it.first);
327  bson_append_array_end(&exprDoc, &matchArr);
328  bson_append_document_end(matchStage, &exprDoc);
329  }
330  pipeline.appendStageEnd(matchStage);
331  }
332  }
333 }
334 
335 static void nonTransitiveLookup(
336  Pipeline &pipeline,
337  const TripleStore &tripleStore,
338  const TripleLookupData &lookupData,
339  const semweb::PropertyPtr &definedProperty) {
340  bool b_isTaxonomicProperty = (definedProperty &&
341  tripleStore.vocabulary->isTaxonomicProperty(definedProperty->iri()));
342  char arrIndexStr[16];
343  const char *arrIndexKey;
344  uint32_t arrIndex;
345 
346  bool b_skipInputGroundings = false;
347  if (lookupData.expr->numVariables() == 0) {
348  // the triple expression has no variables
349  b_skipInputGroundings = true;
350  } else if (!lookupData.mayHasMoreGroundings) {
351  // all possible previous instantiations are known and stored in lookupData.knownGroundedVariables.
352  b_skipInputGroundings = true;
353  for (auto &exprTerm: {
354  lookupData.expr->subjectTerm(),
355  lookupData.expr->propertyTerm(),
356  lookupData.expr->objectTerm()
357  }) {
358  if (exprTerm->termType() != TermType::VARIABLE) continue;
359  auto var = (Variable *) exprTerm.get();
360  // skip if this variable cannot have a runtime grounding
361  if (lookupData.knownGroundedVariables.count(var->name()) > 0) {
362  // the expression contains a variable that is known to have received a grounding before
363  b_skipInputGroundings = false;
364  break;
365  }
366  }
367  }
368 
369  // filter out documents that do not match the triple pattern. this is done using $match or $lookup operators.
370  // first lookup matching documents and store them in the 'next' field
371  bson_t lookupArray, letDoc;
372  auto lookupStage = pipeline.appendStageBegin("$lookup");
373  BSON_APPEND_UTF8(lookupStage, "from", tripleStore.tripleCollection->name().data());
374  BSON_APPEND_UTF8(lookupStage, "as", "next");
375  if (!b_skipInputGroundings) {
376  // pass "v_VARS" field to lookup pipeline
377  BSON_APPEND_DOCUMENT_BEGIN(lookupStage, "let", &letDoc);
378  if (pipeline.isNested()) {
379  // pass from outer "let" document
380  BSON_APPEND_UTF8(&letDoc, "v_VARS", "$$v_VARS");
381  } else {
382  BSON_APPEND_UTF8(&letDoc, "v_VARS", "$v_VARS");
383  }
384  bson_append_document_end(lookupStage, &letDoc);
385  }
386  BSON_APPEND_ARRAY_BEGIN(lookupStage, "pipeline", &lookupArray);
387  {
388  Pipeline lookupPipeline(&lookupArray);
389 
390  auto matchStage = lookupPipeline.appendStageBegin("$match");
391  if (b_skipInputGroundings) {
392  MongoTriplePattern::append(matchStage, *lookupData.expr, b_isTaxonomicProperty,
393  tripleStore.vocabulary->importHierarchy());
394  } else {
395  // need to match with potential groundings of variables from previous steps these are stored in the "v_VARS" field.
396  bson_t andArray, tripleDoc, variablesDoc;
397  BSON_APPEND_ARRAY_BEGIN(matchStage, "$and", &andArray);
398  {
399  BSON_APPEND_DOCUMENT_BEGIN(&andArray, "0", &tripleDoc);
400  MongoTriplePattern::append(&tripleDoc, *lookupData.expr, b_isTaxonomicProperty,
401  tripleStore.vocabulary->importHierarchy());
402  bson_append_document_end(&andArray, &tripleDoc);
403 
404  // match triple values with previously grounded variables
405  arrIndex = 1;
406  for (auto &it: {
407  std::make_pair("$s", lookupData.expr->subjectTerm()),
408  std::make_pair(b_isTaxonomicProperty ? "$p" : "$p*", lookupData.expr->propertyTerm()),
409  std::make_pair(b_isTaxonomicProperty ? "$o*" : "$o", lookupData.expr->objectTerm())
410  }) {
411  if (it.second->termType() != TermType::VARIABLE) continue;
412  auto var = (Variable *) it.second.get();
413  // skip if this variable cannot have a runtime grounding
414  if (!lookupData.mayHasMoreGroundings &&
415  lookupData.knownGroundedVariables.count(var->name()) == 0)
416  continue;
417 
418  bson_uint32_to_string(arrIndex++,
419  &arrIndexKey, arrIndexStr, sizeof arrIndexStr);
420  BSON_APPEND_DOCUMENT_BEGIN(&andArray, arrIndexKey, &variablesDoc);
421  appendMatchVariable(&variablesDoc, it.first, var->name(), lookupData);
422  bson_append_document_end(&andArray, &variablesDoc);
423  }
424  }
425  bson_append_array_end(matchStage, &andArray);
426  }
427  lookupPipeline.appendStageEnd(matchStage);
428  // { $limit: maxNumOfTriples }
429  if (lookupData.maxNumOfTriples > 0) lookupPipeline.limit(lookupData.maxNumOfTriples);
430  }
431  bson_append_array_end(lookupStage, &lookupArray);
432  pipeline.appendStageEnd(lookupStage);
433 
434  // at this point the 'next' field holds an array of matching documents, if any.
435  if (lookupData.expr->isNegated()) {
436  // following closed-world assumption succeed if no solutions are found
437  pipeline.matchEmptyArray("next");
438  } else {
439  // Unwind, and set preserveEmpty=true if lookupData.expr->isOptional().
440  pipeline.unwind("$next", lookupData.expr->isOptional());
441  // project new variable groundings
442  MongoTriplePattern::setTripleVariables(pipeline, *lookupData.expr, lookupData.knownGroundedVariables);
443  }
444 
445  // finally, remove next field: { $unset: "next" }
446  pipeline.unset("next");
447 }
448 
449 static void transitiveLookup(
450  Pipeline &pipeline,
451  const TripleStore &tripleStore,
452  const TripleLookupData &lookupData,
453  const semweb::PropertyPtr &definedProperty) {
454  bool b_isTaxonomicProperty = (definedProperty &&
455  tripleStore.vocabulary->isTaxonomicProperty(definedProperty->iri()));
456  // start with object in case it is known to be grounded before at runtime
457  bool b_objectIsKnownGrounded = lookupData.expr->objectTerm()->isVariable() &&
458  lookupData.knownGroundedVariables.count(
459  std::static_pointer_cast<Variable>(lookupData.expr->objectTerm())->name()) >
460  0;
461  bool b_startWithSubject = lookupData.expr->subjectTerm()->isGround() || !b_objectIsKnownGrounded;
462 
463  auto startTerm = (b_startWithSubject ?
464  lookupData.expr->subjectTerm() : lookupData.expr->objectTerm());
465  auto endTerm = (b_startWithSubject ?
466  lookupData.expr->objectTerm() : lookupData.expr->subjectTerm());
467 
468  // recursive lookup
469  bson_t restrictSearchDoc;
470  auto lookupStage = pipeline.appendStageBegin("$graphLookup");
471  BSON_APPEND_UTF8(lookupStage, "from", tripleStore.tripleCollection->name().data());
472  if (startTerm->termType() == TermType::ATOMIC) {
473  auto startString = (Atomic *) startTerm.get();
474  BSON_APPEND_UTF8(lookupStage, "startWith", startString->stringForm().data());
475  } else if (startTerm->termType() == TermType::VARIABLE) {
476  auto startVariable = (Variable *) startTerm.get();
477  auto startValue = std::string("$") + MongoTerm::variableKey(startVariable->name());
478  BSON_APPEND_UTF8(lookupStage, "startWith", startValue.c_str());
479  } else {
480  KB_WARN("Ignoring term {} with invalid type for graph lookup.", *startTerm);
481  }
482  BSON_APPEND_UTF8(lookupStage, "connectToField", b_startWithSubject ? "s" : "o");
483  BSON_APPEND_UTF8(lookupStage, "connectFromField", b_startWithSubject ? "o" : "s");
484  BSON_APPEND_UTF8(lookupStage, "as", "t_paths");
485  BSON_APPEND_UTF8(lookupStage, "depthField", "depth");
486  /* { restrictSearchWithMatch: { p*: Query_p, ... }" */
487  BSON_APPEND_DOCUMENT_BEGIN(lookupStage, "restrictSearchWithMatch", &restrictSearchDoc);
488  {
489  MongoTerm::append(&restrictSearchDoc,
490  (b_isTaxonomicProperty ? "p" : "p*"),
491  lookupData.expr->propertyTerm());
492  MongoTriplePattern::appendGraphSelector(&restrictSearchDoc, *lookupData.expr,
493  tripleStore.vocabulary->importHierarchy());
494  MongoTriplePattern::appendEpistemicSelector(&restrictSearchDoc, *lookupData.expr);
495  MongoTriplePattern::appendTimeSelector(&restrictSearchDoc, *lookupData.expr);
496  }
497  bson_append_document_end(lookupStage, &restrictSearchDoc);
498  pipeline.appendStageEnd(lookupStage);
499 
500  // $graphLookup does not ensure order, so we need to order by recursion depth in a separate step
501  // t_sorted = { $sortArray: { input: "$t_paths", sort: { depth: 1 } } }
502  bson_t setSortedDoc, sortArrayDoc, sortByDoc;
503  auto setSortedStage = pipeline.appendStageBegin("$set");
504  BSON_APPEND_DOCUMENT_BEGIN(setSortedStage, "t_sorted", &setSortedDoc);
505  BSON_APPEND_DOCUMENT_BEGIN(&setSortedDoc, "$sortArray", &sortArrayDoc);
506  BSON_APPEND_UTF8(&sortArrayDoc, "input", "$t_paths");
507  BSON_APPEND_DOCUMENT_BEGIN(&sortArrayDoc, "$sortArray", &sortByDoc);
508  BSON_APPEND_INT32(&sortByDoc, "depth", 1);
509  bson_append_document_end(&sortArrayDoc, &sortByDoc);
510  bson_append_document_end(&setSortedDoc, &sortArrayDoc);
511  bson_append_document_end(setSortedStage, &setSortedDoc);
512  pipeline.appendStageEnd(setSortedStage);
513 
514  // { $set: { next: "$t_sorted" } }
515  auto setNext = pipeline.appendStageBegin("$set");
516  BSON_APPEND_UTF8(setNext, "next", "$t_sorted");
517  pipeline.appendStageEnd(setNext);
518 
519  // { $set: { start: { $arrayElemAt: ["$next", 0] } } }
520  bson_t setStartOperation, setStartArr;
521  auto setStart = pipeline.appendStageBegin("$set");
522  BSON_APPEND_DOCUMENT_BEGIN(setStart, "start", &setStartOperation);
523  BSON_APPEND_ARRAY_BEGIN(&setStartOperation, "$arrayElemAt", &setStartArr);
524  BSON_APPEND_UTF8(&setStartArr, "0", "$next");
525  BSON_APPEND_INT32(&setStartArr, "1", 0);
526  bson_append_array_end(&setStartOperation, &setStartArr);
527  bson_append_document_end(setStart, &setStartOperation);
528  pipeline.appendStageEnd(setStart);
529 
530  // { $unset: "t_paths" }, { $unset: "t_sorted" }
531  pipeline.unset("t_paths");
532  pipeline.unset("t_sorted");
533 
534  // iterate over results: { $unwind: "$next" }
535  pipeline.unwind("$next");
536 
537  // graph lookup uses "s" or "o" as start for recursive lookup but ignores the other.
538  // thus a matching must be performed for the results.
539  auto matchEnd = pipeline.appendStageBegin("$match");
540  if (endTerm->termType() == TermType::VARIABLE) {
541  bson_t exprDoc, orArr, ungroundedMatch, groundedMatch, notArray;
542  BSON_APPEND_DOCUMENT_BEGIN(matchEnd, "$expr", &exprDoc);
543  BSON_APPEND_ARRAY_BEGIN(&exprDoc, "$or", &orArr);
544 
545  BSON_APPEND_DOCUMENT_BEGIN(&orArr, "0", &ungroundedMatch);
546  BSON_APPEND_ARRAY_BEGIN(&ungroundedMatch, "$not", &notArray);
547  auto varKey = MongoTerm::variableKey(std::static_pointer_cast<Variable>(endTerm)->name());
548  BSON_APPEND_UTF8(&notArray, "0", (std::string("$") + varKey).c_str());
549  bson_append_array_end(&ungroundedMatch, &notArray);
550  bson_append_document_end(&orArr, &ungroundedMatch);
551 
552  BSON_APPEND_DOCUMENT_BEGIN(&orArr, "1", &groundedMatch);
553  MongoTerm::append(&groundedMatch, (b_startWithSubject ? "next.o" : "next.s"), endTerm);
554  bson_append_document_end(&orArr, &groundedMatch);
555 
556  bson_append_array_end(&exprDoc, &orArr);
557  bson_append_document_end(matchEnd, &exprDoc);
558  } else {
559  MongoTerm::append(matchEnd, (b_startWithSubject ? "next.o" : "next.s"), endTerm);
560  }
561  pipeline.appendStageEnd(matchEnd);
562 
563  // project new variable groundings
564  MongoTriplePattern::setTripleVariables(pipeline, *lookupData.expr, lookupData.knownGroundedVariables);
565  // remove next field again: { $unset: "next" }
566  pipeline.unset("next");
567 }
568 
569 void mongo::lookupTriple(Pipeline &pipeline, const TripleStore &tripleStore, const TripleLookupData &lookupData) {
570  semweb::PropertyPtr definedProperty;
571  if (lookupData.expr->propertyTerm() && lookupData.expr->propertyTerm()->termType() == TermType::ATOMIC) {
572  auto propertyTerm = std::static_pointer_cast<Atomic>(lookupData.expr->propertyTerm());
573  definedProperty = tripleStore.vocabulary->getDefinedProperty(propertyTerm->stringForm());
574  }
575 
576  bool b_isTransitiveProperty = (definedProperty && definedProperty->hasFlag(
578  if (b_isTransitiveProperty)
579  transitiveLookup(pipeline, tripleStore, lookupData, definedProperty);
580  else
581  nonTransitiveLookup(pipeline, tripleStore, lookupData, definedProperty);
582 }
#define MONGO_OPERATOR_GTE
#define MONGO_OPERATOR_LT
#define MONGO_OPERATOR_NEQ
#define MONGO_OPERATOR_LTE
#define MONGO_OPERATOR_GT
#define KB_WARN
Definition: Logger.h:27
static std::shared_ptr< knowrob::Atom > Tabled(std::string_view stringForm)
Definition: Atom.cpp:40
static constexpr std::string_view ORIGIN_ANY
static std::shared_ptr< Numeric > falseAtom()
Definition: Numeric.cpp:20
auto objectOperator() const
auto & propertyTerm() const
Definition: TriplePattern.h:92
auto & subjectTerm() const
Definition: TriplePattern.h:81
auto & objectVariable() const
auto & isOccasionalTerm() const
auto & isUncertainTerm() const
auto & beginTerm() const
auto & objectTerm() const
Definition: TriplePattern.h:97
uint32_t numVariables() const override
auto & endTerm() const
auto & graphTerm() const
auto & perspectiveTerm() const
auto & confidenceTerm() const
static void append(bson_t *doc, const char *key, const TermPtr &term, const char *queryOperator=nullptr, bool matchNullValue=false, bool includeVariables=false)
Definition: MongoTerm.cpp:13
static std::string variableKey(const std::string_view &varName)
Definition: MongoTerm.cpp:127
static bson_t * create(const TriplePattern &tripleExpression, bool b_isTaxonomicProperty, const std::shared_ptr< ImportHierarchy > &importHierarchy)
MongoTriplePattern(const TriplePattern &tripleExpression, bool b_isTaxonomicProperty, const std::shared_ptr< ImportHierarchy > &importHierarchy)
static const char * getOperatorString(knowrob::FilterType operatorType)
static void appendEpistemicSelector(bson_t *selectorDoc, const TriplePattern &tripleExpression)
static void appendTimeSelector(bson_t *selectorDoc, const TriplePattern &tripleExpression)
static void append(bson_t *selectorDoc, const TriplePattern &tripleExpression, bool b_isTaxonomicProperty, const std::shared_ptr< ImportHierarchy > &importHierarchy)
static void setTripleVariables(Pipeline &pipeline, const TriplePattern &expr, const std::set< std::string_view > &knownGroundedVariables)
static void appendGraphSelector(bson_t *selectorDoc, const TriplePattern &tripleExpression, const std::shared_ptr< ImportHierarchy > &importHierarchy)
void appendStageEnd(bson_t *stage)
Definition: Pipeline.cpp:46
bson_t * appendStageBegin()
Definition: Pipeline.cpp:27
auto isNested() const
Definition: Pipeline.h:36
void matchEmptyArray(std::string_view arrayKey)
Definition: Pipeline.cpp:352
void unwind(std::string_view field, bool preserveNullAndEmptyArrays=false)
Definition: Pipeline.cpp:255
void unset(std::string_view field)
Definition: Pipeline.cpp:267
void lookupTriple(Pipeline &pipeline, const TripleStore &tripleStore, const TripleLookupData &lookupData)
VariableRule & var()
Definition: terms.cpp:91
TermRule & string()
Definition: terms.cpp:63
@ TRANSITIVE_PROPERTY
Definition: Property.h:24
std::shared_ptr< Property > PropertyPtr
Definition: Property.h:175
std::shared_ptr< Term > TermPtr
Definition: Term.h:117
std::set< std::string_view > knownGroundedVariables
std::shared_ptr< knowrob::mongo::Collection > tripleCollection
Definition: TripleStore.h:27
std::shared_ptr< knowrob::Vocabulary > vocabulary
Definition: TripleStore.h:29