/** * Converts raw array (as returned by the Elasticsearch client) to document. * * @param array $rawData * @param string $documentClass Document class in short notation (e.g. AppBundle:Product) * * @return DocumentInterface */ public function convertToDocument($rawData, $documentClass) { // Get document metadata $metadata = $this->metadataCollector->getDocumentMetadata($documentClass); switch (true) { case isset($rawData['_source']): $data = $rawData['_source']; break; case isset($rawData['fields']): $data = array_map('reset', $rawData['fields']); /** Check for partial fields as well (@see https://www.elastic.co/guide/en/elasticsearch/reference/1.4/search-request-fields.html) */ // TODO: when partial fields of nested objects are selected, partial objects should be constructed foreach ($data as $key => $field) { if (is_array($field)) { $data = array_merge($data, $field); unset($data[$key]); } } break; default: $data = []; } // Add special fields to data foreach (['_id', '_parent', '_score'] as $specialField) { if (isset($rawData[$specialField])) { $data[$specialField] = $rawData[$specialField]; } } /** @var DocumentInterface $document */ $className = $metadata->getClassName(); $document = $this->assignArrayToObject($data, new $className(), $metadata->getPropertiesMetadata()); return $document; }
/** * Build and return a document from the data source, ready for insertion into ES * * @param int|string $id * @return array */ public function getDocument($id) { $params = ['index' => $this->sourceIndexManager->getLiveIndex(), 'type' => $this->metadataCollector->getDocumentMetadata($this->sourceDocumentClass)->getType(), 'id' => $id]; $doc = $this->sourceIndexManager->getConnection()->getClient()->get($params); $result = $doc['_source']; $result['_id'] = $doc['_id']; return $result; }
/** * Adds a prepared document array to a bulk request for the next commit. * Depending on the connection autocommit mode, the update may be committed right away. * * @param string $documentClass The document class in short notation (i.e. AppBundle:Product) * @param array $documentArray The document to index in ES */ public function persistRaw($documentClass, array $documentArray) { $documentMetadata = $this->metadataCollector->getDocumentMetadata($documentClass); $this->getConnection()->addBulkOperation('index', $this->writeAlias, $documentMetadata->getType(), $documentArray); if ($this->getConnection()->isAutocommit()) { $this->getConnection()->commit(); } }
/** * Returns an array with the Elasticsearch indices and types to be queried, * based on the given document classes in short notation (AppBundle:Product) * * @param string[] $documentClasses * @return array */ public function getTargetIndicesAndTypes(array $documentClasses) { $allDocumentClassToIndexMappings = $this->documentMetadataCollector->getDocumentClassesIndices(); $documentClassToIndexMap = array_intersect_key($allDocumentClassToIndexMappings, array_flip($documentClasses)); $indices = []; $types = []; foreach ($documentClassToIndexMap as $documentClass => $indexManagerName) { $documentMetadata = $this->documentMetadataCollector->getDocumentMetadata($documentClass); $indices[] = $this->indexManagerRegistry->get($indexManagerName)->getReadAlias(); $types[] = $documentMetadata->getType(); } $result = ['index' => array_unique($indices), 'type' => $types]; return $result; }
/** * Converts document or (nested) object to an array. * * @param ObjectInterface $object A document or a (nested) object * @param array $propertiesMetadata * * @return array */ public function convertToArray(ObjectInterface $object, $propertiesMetadata = []) { if (empty($propertiesMetadata)) { $propertiesMetadata = $this->metadataCollector->getDocumentMetadata(get_class($object))->getPropertiesMetadata(); } $array = []; foreach ($propertiesMetadata as $name => $propertyMetadata) { if ($propertyMetadata['propertyAccess'] == DocumentMetadata::PROPERTY_ACCESS_PRIVATE) { $value = $object->{$propertyMetadata['methods']['getter']}(); } else { $value = $object->{$propertyMetadata['propertyName']}; } if (isset($value)) { // If this is a (nested) object or a list of such if (array_key_exists('propertiesMetadata', $propertyMetadata)) { $new = []; if ($propertyMetadata['multiple']) { // Verify value is traversable if (!(is_array($value) || is_object($value) && $value instanceof \Traversable)) { throw new \InvalidArgumentException(sprintf('Value of "%s" is not traversable, although field is set to "multiple"')); } foreach ($value as $item) { $this->checkObjectType($item, $propertyMetadata['className']); $new[] = $this->convertToArray($item, $propertyMetadata['propertiesMetadata']); } } else { $this->checkObjectType($value, $propertyMetadata['className']); $new = $this->convertToArray($value, $propertyMetadata['propertiesMetadata']); } $array[$name] = $new; } elseif ($value instanceof MLProperty) { foreach ($value->getValues() as $language => $langValue) { $array[$name . $this->languageSeparator . $language] = $langValue; } } else { $array[$name] = $value; } } } return $array; }
/** * Test getting metadata for document class */ public function testGetDocumentMetadata() { $this->assertInstanceOf('Sineflow\\ElasticsearchBundle\\Mapping\\DocumentMetadata', $this->metadataCollector->getDocumentMetadata('TestBundle:Foo'), 'Incorrect metadata.'); }