1<?php 2 3namespace Elastica; 4 5use Elastica\Bulk\Action; 6use Elastica\Exception\InvalidException; 7 8/** 9 * Single document stored in elastic search. 10 * 11 * @author Nicolas Ruflin <spam@ruflin.com> 12 */ 13class Document extends AbstractUpdateAction 14{ 15 public const OP_TYPE_CREATE = Action::OP_TYPE_CREATE; 16 17 /** 18 * Document data. 19 * 20 * @var array Document data 21 */ 22 protected $_data = []; 23 24 /** 25 * Whether to use this document to upsert if the document does not exist. 26 * 27 * @var bool 28 */ 29 protected $_docAsUpsert = false; 30 31 /** 32 * @var bool 33 */ 34 protected $_autoPopulate = false; 35 36 /** 37 * Creates a new document. 38 * 39 * @param string|null $id The document ID, if null it will be created 40 * @param array|string $data Data array 41 * @param Index|string $index Index name 42 */ 43 public function __construct(?string $id = null, $data = [], $index = '') 44 { 45 $this->setId($id); 46 $this->setData($data); 47 $this->setIndex($index); 48 } 49 50 /** 51 * @return mixed 52 */ 53 public function __get(string $key) 54 { 55 return $this->get($key); 56 } 57 58 /** 59 * @param mixed $value 60 */ 61 public function __set(string $key, $value): void 62 { 63 $this->set($key, $value); 64 } 65 66 public function __isset(string $key): bool 67 { 68 return $this->has($key) && null !== $this->get($key); 69 } 70 71 public function __unset(string $key): void 72 { 73 $this->remove($key); 74 } 75 76 /** 77 * Get the value of the given field. 78 * 79 * @param mixed $key 80 * 81 * @throws InvalidException If the given field does not exist 82 * 83 * @return mixed 84 */ 85 public function get($key) 86 { 87 if (!$this->has($key)) { 88 throw new InvalidException("Field {$key} does not exist"); 89 } 90 91 return $this->_data[$key]; 92 } 93 94 /** 95 * Set the value of the given field. 96 * 97 * @param mixed $value 98 * 99 * @throws InvalidException if the current document is a serialized data 100 */ 101 public function set(string $key, $value): self 102 { 103 if (!\is_array($this->_data)) { 104 throw new InvalidException('Document data is serialized data. Data creation is forbidden.'); 105 } 106 $this->_data[$key] = $value; 107 108 return $this; 109 } 110 111 /** 112 * Returns if the current document has the given field. 113 */ 114 public function has(string $key): bool 115 { 116 return \is_array($this->_data) && \array_key_exists($key, $this->_data); 117 } 118 119 /** 120 * Removes a field from the document, by the given key. 121 * 122 * @throws InvalidException if the given field does not exist 123 */ 124 public function remove(string $key): self 125 { 126 if (!$this->has($key)) { 127 throw new InvalidException("Field {$key} does not exist"); 128 } 129 unset($this->_data[$key]); 130 131 return $this; 132 } 133 134 /** 135 * Adds a file to the index. 136 * 137 * To use this feature you have to call the following command in the 138 * elasticsearch directory: 139 * <code> 140 * ./bin/plugin -install elasticsearch/elasticsearch-mapper-attachments/1.6.0 141 * </code> 142 * This installs the tika file analysis plugin. More infos about supported formats 143 * can be found here: {@link http://tika.apache.org/0.7/formats.html} 144 * 145 * @param string $key Key to add the file to 146 * @param string $filepath Path to add the file 147 * @param string $mimeType Header mime type 148 */ 149 public function addFile(string $key, string $filepath, string $mimeType = ''): self 150 { 151 $value = \base64_encode(\file_get_contents($filepath)); 152 153 if ('' !== $mimeType) { 154 $value = ['_content_type' => $mimeType, '_name' => $filepath, '_content' => $value]; 155 } 156 157 $this->set($key, $value); 158 159 return $this; 160 } 161 162 /** 163 * Add file content. 164 */ 165 public function addFileContent(string $key, string $content): self 166 { 167 return $this->set($key, \base64_encode($content)); 168 } 169 170 /** 171 * Adds a geopoint field to the document. 172 * 173 * @param string $key Field key 174 * 175 * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/geo-point.html 176 */ 177 public function addGeoPoint(string $key, float $latitude, float $longitude): self 178 { 179 $value = ['lat' => $latitude, 'lon' => $longitude]; 180 181 $this->set($key, $value); 182 183 return $this; 184 } 185 186 /** 187 * Overwrites the current document data with the given data. 188 * 189 * @param array|string $data Data array 190 */ 191 public function setData($data): self 192 { 193 $this->_data = $data; 194 195 return $this; 196 } 197 198 /** 199 * Returns the document data. 200 * 201 * @return array|string Document data 202 */ 203 public function getData() 204 { 205 return $this->_data; 206 } 207 208 public function setDocAsUpsert(bool $value): self 209 { 210 $this->_docAsUpsert = $value; 211 212 return $this; 213 } 214 215 public function getDocAsUpsert(): bool 216 { 217 return $this->_docAsUpsert; 218 } 219 220 public function setAutoPopulate(bool $autoPopulate = true): self 221 { 222 $this->_autoPopulate = $autoPopulate; 223 224 return $this; 225 } 226 227 public function isAutoPopulate(): bool 228 { 229 return $this->_autoPopulate; 230 } 231 232 public function setPipeline(string $pipeline): self 233 { 234 return $this->setParam('pipeline', $pipeline); 235 } 236 237 public function getPipeline(): string 238 { 239 return $this->getParam('pipeline'); 240 } 241 242 public function hasPipeline(): bool 243 { 244 return $this->hasParam('pipeline'); 245 } 246 247 /** 248 * Returns the document as an array. 249 */ 250 public function toArray(): array 251 { 252 $doc = $this->getParams(); 253 $doc['_source'] = $this->getData(); 254 255 return $doc; 256 } 257 258 /** 259 * @param array|Document $data 260 * 261 * @throws InvalidException If invalid data has been provided 262 */ 263 public static function create($data): self 264 { 265 if ($data instanceof self) { 266 return $data; 267 } 268 269 if (\is_array($data)) { 270 return new static('', $data); 271 } 272 273 throw new InvalidException('Failed to create document. Invalid data passed.'); 274 } 275} 276