index -> type -> document * * @author Nicolas Ruflin */ class Type implements SearchableInterface { /** * Index. * * @var \Elastica\Index Index object */ protected $_index; /** * Type name. * * @var string Type name */ protected $_name; /** * @var array|string A callable that serializes an object passed to it */ protected $_serializer; /** * Creates a new type object inside the given index. * * @param \Elastica\Index $index Index Object * @param string $name Type name */ public function __construct(Index $index, $name) { $this->_index = $index; $this->_name = $name; } /** * Adds the given document to the search index. * * @param \Elastica\Document $doc Document with data * * @return \Elastica\Response */ public function addDocument(Document $doc) { $endpoint = new \Elasticsearch\Endpoints\Index(); if (null !== $doc->getId() && '' !== $doc->getId()) { $endpoint->setID($doc->getId()); } $options = $doc->getOptions( [ 'version', 'version_type', 'routing', 'percolate', 'parent', 'op_type', 'consistency', 'replication', 'refresh', 'timeout', 'pipeline', ] ); $endpoint->setBody($doc->getData()); $endpoint->setParams($options); $response = $this->requestEndpoint($endpoint); $data = $response->getData(); // set autogenerated id to document if (($doc->isAutoPopulate() || $this->getIndex()->getClient()->getConfigValue(['document', 'autoPopulate'], false)) && $response->isOk() ) { if (!$doc->hasId()) { if (isset($data['_id'])) { $doc->setId($data['_id']); } } if (isset($data['_version'])) { $doc->setVersion($data['_version']); } } return $response; } /** * @param $object * @param Document $doc * * @throws Exception\RuntimeException * * @return Response */ public function addObject($object, Document $doc = null) { if (!isset($this->_serializer)) { throw new RuntimeException('No serializer defined'); } $data = call_user_func($this->_serializer, $object); if (!$doc) { $doc = new Document(); } $doc->setData($data); return $this->addDocument($doc); } /** * Update document, using update script. Requires elasticsearch >= 0.19.0. * * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update.html * * @param \Elastica\Document|\Elastica\Script\AbstractScript $data Document with update data * @param array $options array of query params to use for query. For possible options check es api * * @throws \Elastica\Exception\InvalidException * * @return \Elastica\Response */ public function updateDocument($data, array $options = []) { if (!($data instanceof Document) && !($data instanceof AbstractScript)) { throw new \InvalidArgumentException('Data should be a Document or Script'); } if (!$data->hasId()) { throw new InvalidException('Document or Script id is not set'); } return $this->getIndex()->getClient()->updateDocument( $data->getId(), $data, $this->getIndex()->getName(), $this->getName(), $options ); } /** * Uses _bulk to send documents to the server. * * @param array|\Elastica\Document[] $docs Array of Elastica\Document * @param array $options Array of query params to use for query. For possible options check es api * * @return \Elastica\Bulk\ResponseSet * * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html */ public function updateDocuments(array $docs, array $options = []) { foreach ($docs as $doc) { $doc->setType($this->getName()); } return $this->getIndex()->updateDocuments($docs, $options); } /** * Uses _bulk to send documents to the server. * * @param array|\Elastica\Document[] $docs Array of Elastica\Document * @param array $options Array of query params to use for query. For possible options check es api * * @return \Elastica\Bulk\ResponseSet * * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html */ public function addDocuments(array $docs, array $options = []) { foreach ($docs as $doc) { $doc->setType($this->getName()); } return $this->getIndex()->addDocuments($docs, $options); } /** * Uses _bulk to send documents to the server. * * @param object[] $objects * @param array $options Array of query params to use for query. For possible options check es api * * @return Bulk\ResponseSet * * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html */ public function addObjects(array $objects, array $options = []) { if (!isset($this->_serializer)) { throw new RuntimeException('No serializer defined'); } $docs = []; foreach ($objects as $object) { $data = call_user_func($this->_serializer, $object); $doc = new Document(); $doc->setData($data); $doc->setType($this->getName()); $docs[] = $doc; } return $this->getIndex()->addDocuments($docs, $options); } /** * Get the document from search index. * * @param string $id Document id * @param array $options options for the get request * * @throws \Elastica\Exception\NotFoundException * @throws \Elastica\Exception\ResponseException * * @return \Elastica\Document */ public function getDocument($id, $options = []) { $endpoint = new \Elasticsearch\Endpoints\Get(); $endpoint->setID($id); $endpoint->setParams($options); $response = $this->requestEndpoint($endpoint); $result = $response->getData(); if (!isset($result['found']) || false === $result['found']) { throw new NotFoundException('doc id '.$id.' not found'); } if (isset($result['fields'])) { $data = $result['fields']; } elseif (isset($result['_source'])) { $data = $result['_source']; } else { $data = []; } $document = new Document($id, $data, $this->getName(), $this->getIndex()); $document->setVersion($result['_version']); return $document; } /** * @param string $id * @param array|string $data * * @return Document */ public function createDocument($id = '', $data = []) { $document = new Document($id, $data); $document->setType($this); return $document; } /** * Returns the type name. * * @return string Type name */ public function getName() { return $this->_name; } /** * Sets value type mapping for this type. * * @param \Elastica\Type\Mapping|array $mapping Elastica\Type\MappingType object or property array with all mappings * @param array $query querystring when put mapping (for example update_all_types) * * @return \Elastica\Response */ public function setMapping($mapping, array $query = []) { $mapping = Mapping::create($mapping); $mapping->setType($this); return $mapping->send($query); } /** * Returns current mapping for the given type. * * @return array Current mapping */ public function getMapping() { $response = $this->requestEndpoint(new Get()); $data = $response->getData(); $mapping = array_shift($data); if (isset($mapping['mappings'])) { return $mapping['mappings']; } return []; } /** * Create search object. * * @param string|array|\Elastica\Query $query Array with all query data inside or a Elastica\Query object * @param int|array $options OPTIONAL Limit or associative array of options (option=>value) * @param BuilderInterface $builder * * @return Search */ public function createSearch($query = '', $options = null, BuilderInterface $builder = null) { $search = $this->getIndex()->createSearch($query, $options, $builder); $search->addType($this); return $search; } /** * Do a search on this type. * * @param string|array|\Elastica\Query $query Array with all query data inside or a Elastica\Query object * @param int|array $options OPTIONAL Limit or associative array of options (option=>value) * * @return \Elastica\ResultSet with all results inside * * @see \Elastica\SearchableInterface::search */ public function search($query = '', $options = null) { $search = $this->createSearch($query, $options); return $search->search(); } /** * Count docs by query. * * @param string|array|\Elastica\Query $query Array with all query data inside or a Elastica\Query object * * @return int number of documents matching the query * * @see \Elastica\SearchableInterface::count */ public function count($query = '') { $search = $this->createSearch($query); return $search->count(); } /** * Returns index client. * * @return \Elastica\Index Index object */ public function getIndex() { return $this->_index; } /** * @param \Elastica\Document $document * * @return \Elastica\Response */ public function deleteDocument(Document $document) { $options = $document->getOptions( [ 'version', 'version_type', 'routing', 'parent', 'replication', 'consistency', 'refresh', 'timeout', ] ); return $this->deleteById($document->getId(), $options); } /** * Uses _bulk to delete documents from the server. * * @param array|\Elastica\Document[] $docs Array of Elastica\Document * * @return \Elastica\Bulk\ResponseSet * * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html */ public function deleteDocuments(array $docs) { foreach ($docs as $doc) { $doc->setType($this->getName()); } return $this->getIndex()->deleteDocuments($docs); } /** * Deletes an entry by its unique identifier. * * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete.html * * @param int|string $id Document id * @param array $options * * @throws \InvalidArgumentException * @throws \Elastica\Exception\NotFoundException * * @return \Elastica\Response Response object */ public function deleteById($id, array $options = []) { if (empty($id) || !trim($id)) { throw new \InvalidArgumentException(); } $endpoint = new Delete(); $endpoint->setID($id); $endpoint->setParams($options); $response = $this->requestEndpoint($endpoint); $responseData = $response->getData(); if (isset($responseData['result']) && 'not_found' == $responseData['result']) { throw new NotFoundException('Doc id '.$id.' not found and can not be deleted'); } return $response; } /** * Deletes the given list of ids from this type. * * @param array $ids * @param string|bool $routing Optional routing key for all ids * * @return \Elastica\Response Response object */ public function deleteIds(array $ids, $routing = false) { return $this->getIndex()->getClient()->deleteIds($ids, $this->getIndex(), $this, $routing); } /** * Deletes entries in the db based on a query. * * @param \Elastica\Query|\Elastica\Query\AbstractQuery|string|array $query Query object * @param array $options Optional params * * @return \Elastica\Response * * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete-by-query.html */ public function deleteByQuery($query, array $options = []) { $query = Query::create($query); $endpoint = new DeleteByQuery(); $endpoint->setBody($query->toArray()); $endpoint->setParams($options); return $this->requestEndpoint($endpoint); } /** * Makes calls to the elasticsearch server based on this type. * * @param string $path Path to call * @param string $method Rest method to use (GET, POST, DELETE, PUT) * @param array $data OPTIONAL Arguments as array * @param array $query OPTIONAL Query params * * @return \Elastica\Response Response object */ public function request($path, $method, $data = [], array $query = []) { $path = $this->getName().'/'.$path; return $this->getIndex()->request($path, $method, $data, $query); } /** * Makes calls to the elasticsearch server with usage official client Endpoint based on this type. * * @param AbstractEndpoint $endpoint * * @return Response */ public function requestEndpoint(AbstractEndpoint $endpoint) { $cloned = clone $endpoint; $cloned->setType($this->getName()); return $this->getIndex()->requestEndpoint($cloned); } /** * Sets the serializer callable used in addObject. * * @see \Elastica\Type::addObject * * @param array|string $serializer @see \Elastica\Type::_serializer * * @return $this */ public function setSerializer($serializer) { $this->_serializer = $serializer; return $this; } /** * Checks if the given type exists in Index. * * @return bool True if type exists */ public function exists() { $response = $this->requestEndpoint(new Exists()); return 200 === $response->getStatus(); } }