/** * This is a private method that performs exactly the same operation as Driver::lockSingleDocument, the reason this is duplicated here * is so that we can simulate the correct locking of documents as part of mocking a workflow that will lock a document correctly but not another * @param $s * @param $transaction_id * @param $contextAlias * @return array */ public function lockSingleDocumentCallback($s, $transaction_id, $contextAlias) { $lCollection = \Tripod\Mongo\Config::getInstance()->getCollectionForLocks($this->tripod->getStoreName()); $countEntriesInLocksCollection = $lCollection->count(array('_id' => array(_ID_RESOURCE => $this->labeller->uri_to_alias($s), _ID_CONTEXT => $contextAlias))); if ($countEntriesInLocksCollection > 0) { //Subject is already locked return false; } else { try { //Add a entry to locks collection for this subject, will throws exception if an entry already there $result = $lCollection->insertOne(array('_id' => array(_ID_RESOURCE => $this->labeller->uri_to_alias($s), _ID_CONTEXT => $contextAlias), _LOCKED_FOR_TRANS => $transaction_id, _LOCKED_FOR_TRANS_TS => \Tripod\Mongo\DateUtil::getMongoDate()), array("w" => 1)); if (!$result->isAcknowledged()) { throw new Exception("Failed to lock document with error message- " . $this->getLastDBError()); } } catch (Exception $e) { //Subject is already locked or unable to lock $this->debugLog(MONGO_LOCK, array('description' => 'Driver::lockSingleDocument - failed with exception', 'transaction_id' => $transaction_id, 'subject' => $s, 'exception-message' => $e->getMessage())); return false; } //Let's get original document for processing. $document = $this->getTripodCollection($this->tripod)->findOne(array('_id' => array(_ID_RESOURCE => $this->labeller->uri_to_alias($s), _ID_CONTEXT => $contextAlias))); if (empty($document)) { //if document is not there, create it try { $result = $this->getTripodCollection($this->tripod)->insertOne(array('_id' => array(_ID_RESOURCE => $this->labeller->uri_to_alias($s), _ID_CONTEXT => $contextAlias)), array("w" => 1)); if (!$result->isAcknowledged()) { throw new Exception("Failed to create new document with error message- " . $this->getLastDBError()); } $document = $this->getTripodCollection($this->tripod)->findOne(array('_id' => array(_ID_RESOURCE => $this->labeller->uri_to_alias($s), _ID_CONTEXT => $contextAlias))); } catch (\Exception $e) { $this->errorLog(MONGO_LOCK, array('description' => 'Driver::lockSingleDocument - failed when creating new document', 'transaction_id' => $transaction_id, 'subject' => $s, 'exception-message' => $e->getMessage())); return false; } } return $document; } }
/** * Adds an _id object (or array of _id objects) to the target document's impact index * * @param array $id * @param array &$target * @throws \InvalidArgumentException */ protected function addIdToImpactIndex(array $id, &$target, $buildImpactIndex = true) { if ($buildImpactIndex) { if (isset($id[_ID_RESOURCE])) { // Ensure that our id is curie'd $id[_ID_RESOURCE] = $this->labeller->uri_to_alias($id[_ID_RESOURCE]); if (!isset($target[_IMPACT_INDEX])) { $target[_IMPACT_INDEX] = array(); } if (!in_array($id, $target[_IMPACT_INDEX])) { $target[_IMPACT_INDEX][] = $id; } } else { foreach ($id as $i) { if (!isset($i[_ID_RESOURCE])) { throw new \InvalidArgumentException("Invalid id format"); } $this->addIdToImpactIndex($i, $target); } } } }
/** * Returns the context alias curie for the supplied context or default context * @param string|null $context * @return string */ protected function getContextAlias($context = null) { $contextAlias = $this->labeller->uri_to_alias(empty($context) ? $this->defaultContext : $context); return empty($contextAlias) ? Config::getInstance()->getDefaultContextAlias() : $contextAlias; }
/** * Returns the ids of all documents that contain and impact index entry * matching the resource and context specified * @param array $resourcesAndPredicates * @param string $context * @return array the ids of search documents that had matching entries in their impact index */ public function findImpactedDocuments(array $resourcesAndPredicates, $context) { $contextAlias = $this->labeller->uri_to_alias($context); $specPredicates = array(); foreach ($this->config->getSearchDocumentSpecifications($this->storeName) as $spec) { if (isset($spec[_ID_KEY])) { $specPredicates[$spec[_ID_KEY]] = $this->config->getDefinedPredicatesInSpec($this->storeName, $spec[_ID_KEY]); } } // build a filter - will be used for impactIndex detection and finding search types to re-gen $searchDocFilters = array(); $resourceFilters = array(); foreach ($resourcesAndPredicates as $resource => $resourcePredicates) { $resourceAlias = $this->labeller->uri_to_alias($resource); $id = array(_ID_RESOURCE => $resourceAlias, _ID_CONTEXT => $contextAlias); // If we don't have a working config or there are no predicates listed, remove all // rows associated with the resource in all search types if (empty($specPredicates) || empty($resourcePredicates)) { // build $filter for queries to impact index $resourceFilters[] = $id; } else { foreach ($specPredicates as $searchDocType => $predicates) { // Only look for search rows if the changed predicates are actually defined in the searchDocspec if (array_intersect($resourcePredicates, $predicates)) { if (!isset($searchDocFilters[$searchDocType])) { $searchDocFilters[$searchDocType] = array(); } // build $filter for queries to impact index $searchDocFilters[$searchDocType][] = $id; } } } } $searchTypes = array(); if (empty($searchDocFilters) && !empty($resourceFilters)) { $query = array(_IMPACT_INDEX => array('$in' => $resourceFilters)); } else { $query = array(); foreach ($searchDocFilters as $searchDocType => $filters) { // first re-gen views where resources appear in the impact index $query[] = array(_IMPACT_INDEX => array('$in' => $filters), '_id.' . _ID_TYPE => $searchDocType); $searchTypes[] = $searchDocType; } if (!empty($resourceFilters)) { $query[] = array(_IMPACT_INDEX => array('$in' => $resourceFilters)); } if (count($query) === 1) { $query = $query[0]; } elseif (count($query) > 1) { $query = array('$or' => $query); } } if (empty($query)) { return array(); } $searchDocs = array(); foreach ($this->config->getCollectionsForSearch($this->storeName, $searchTypes) as $collection) { $cursor = $collection->find($query, array('projection' => array('_id' => true))); foreach ($cursor as $d) { $searchDocs[] = $d; } } return $searchDocs; }
/** * @param string $cbdSubject * @param MongoGraph $cbdGraph * @param Collection $collection * @param string $context * @throws \Exception */ protected function saveCBD($cbdSubject, MongoGraph $cbdGraph, Collection $collection, $context) { $cbdSubject = $this->labeller->uri_to_alias($cbdSubject); if ($cbdGraph == null || $cbdGraph->is_empty()) { throw new \Exception("graph for {$cbdSubject} was null"); } try { $collection->insertOne($cbdGraph->to_tripod_array($cbdSubject, $context), array("w" => 1)); print "."; } catch (\Exception $e) { if (preg_match('/E11000/', $e->getMessage())) { print "M"; // key already exists, merge it $criteria = array("_id" => array("r" => $cbdSubject, "c" => $context)); $existingGraph = new MongoGraph(); $existingGraph->add_tripod_array($collection->findOne($criteria)); $existingGraph->add_graph($cbdGraph); try { $collection->updateOne($criteria, ['$set' => $existingGraph->to_tripod_array($cbdSubject, $context)], array("w" => 1)); } catch (\Exception $e2) { throw new \Exception($e2->getMessage()); // todo: would be good to have typed exception } } else { // retry print "CursorException on update: " . $e->getMessage() . ", retrying\n"; try { $collection->insertOne($cbdGraph->to_tripod_array($cbdSubject, $context), array("w" => 1)); } catch (\Exception $e2) { throw new \Exception($e2->getMessage()); // todo: would be good to have typed exception } } } }