PageRenderTime 39ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/src/QueryType/Update/Query/Document.php

https://github.com/Gasol/solarium
PHP | 502 lines | 168 code | 56 blank | 278 comment | 22 complexity | 0baa3993e82bf30713526cf25c6d23a2 MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of the Solarium package.
  4. *
  5. * For the full copyright and license information, please view the COPYING
  6. * file that was distributed with this source code.
  7. */
  8. namespace Solarium\QueryType\Update\Query;
  9. use Solarium\Core\Query\AbstractDocument;
  10. use Solarium\Core\Query\Helper;
  11. use Solarium\Exception\RuntimeException;
  12. /**
  13. * Read/Write Solr document.
  14. *
  15. * This document type is used for update queries. It has all of the features of
  16. * the readonly document and it also allows for updating or adding fields and
  17. * boosts.
  18. *
  19. * While it is possible to use this document type for a select, alter it and use
  20. * it in an update query (effectively the 'edit' that Solr doesn't have) this
  21. * is not recommended. Most Solr indexes have fields that are indexed and not
  22. * stored. You will lose that data because it is impossible to retrieve it from
  23. * Solr. Always update from the original data source.
  24. *
  25. * Atomic updates are also support, using the field modifiers.
  26. */
  27. class Document extends AbstractDocument
  28. {
  29. /**
  30. * Directive to set or replace the field value(s) with the specified value(s), or remove the values if 'null' or
  31. * empty list is specified as the new value. May be specified as a single value, or as a list for multiValued
  32. * fields.
  33. *
  34. * @var string
  35. */
  36. const MODIFIER_SET = 'set';
  37. /**
  38. * Directive to increment a numeric value by a specific amount. Must be specified as a single numeric value.
  39. *
  40. * @var string
  41. */
  42. const MODIFIER_INC = 'inc';
  43. /**
  44. * Directive to add the specified values to a multiValued field. May be specified as a single value, or as a list.
  45. *
  46. * @var string
  47. */
  48. const MODIFIER_ADD = 'add';
  49. /**
  50. * Directive to add the specified values to a multiValued field, only if not already present. May be specified as a
  51. * single value, or as a list.
  52. *
  53. * @var string
  54. */
  55. const MODIFIER_ADD_DISTINCT = 'add-distinct';
  56. /**
  57. * Directive to remove (all occurrences of) the specified values from a multiValued field. May be specified as a
  58. * single value, or as a list.
  59. *
  60. * @var string
  61. */
  62. const MODIFIER_REMOVE = 'remove';
  63. /**
  64. * Directive to remove all occurrences of the specified regex from a multiValued field. May be specified as a single
  65. * value, or as a list.
  66. *
  67. * @var string
  68. */
  69. const MODIFIER_REMOVEREGEX = 'removeregex';
  70. /**
  71. * This value has the same effect as not setting a version.
  72. *
  73. * @var int
  74. */
  75. const VERSION_DONT_CARE = 0;
  76. /**
  77. * This value requires an existing document with the same key, but no specific version.
  78. *
  79. * @var int
  80. */
  81. const VERSION_MUST_EXIST = 1;
  82. /**
  83. * This value requires that no document with the same key exists (so no automatic overwrite like default).
  84. *
  85. * @var int
  86. */
  87. const VERSION_MUST_NOT_EXIST = -1;
  88. /**
  89. * Document boost value.
  90. *
  91. * Null menas no boost which is something different than a boost by '0.0'.
  92. *
  93. * @var float|null
  94. */
  95. protected $boost;
  96. /**
  97. * Allows us to determine what kind of atomic update we want to set.
  98. *
  99. * @var array
  100. */
  101. protected $modifiers = [];
  102. /**
  103. * This field needs to be explicitly set to observe the rules of atomic updates.
  104. *
  105. * @var string
  106. */
  107. protected $key;
  108. /**
  109. * Field boosts.
  110. *
  111. * Using fieldname as the key and the boost as the value
  112. *
  113. * @var array
  114. */
  115. protected $fieldBoosts;
  116. /**
  117. * Version value.
  118. *
  119. * Can be used for updating using Solr's optimistic concurrency control
  120. *
  121. * @var int
  122. */
  123. protected $version;
  124. /**
  125. * Helper instance.
  126. *
  127. * @var Helper
  128. */
  129. protected $helper;
  130. protected $filterControlCharacters = true;
  131. /**
  132. * Constructor.
  133. *
  134. * @param array $fields
  135. * @param array $boosts
  136. * @param array $modifiers
  137. */
  138. public function __construct(array $fields = [], array $boosts = [], array $modifiers = [])
  139. {
  140. $this->fields = $fields;
  141. $this->fieldBoosts = $boosts;
  142. $this->modifiers = $modifiers;
  143. }
  144. /**
  145. * Set field value.
  146. *
  147. * Magic method for setting fields as properties of this document
  148. * object, by field name.
  149. *
  150. * If you supply NULL as the value the field will be removed
  151. * If you supply an array a multivalue field will be created.
  152. * In all cases any existing (multi)value will be overwritten.
  153. *
  154. * @param string $name
  155. * @param mixed $value
  156. */
  157. public function __set($name, $value): void
  158. {
  159. $this->setField($name, $value);
  160. }
  161. /**
  162. * Unset field value.
  163. *
  164. * Magic method for removing fields by un-setting object properties
  165. *
  166. * @param string $name
  167. */
  168. public function __unset($name): void
  169. {
  170. $this->removeField($name);
  171. }
  172. /**
  173. * Add a field value.
  174. *
  175. * If a field already has a value it will be converted
  176. * to a multivalue field.
  177. *
  178. * @param string $key
  179. * @param mixed $value
  180. * @param float|null $boost
  181. * @param string|null $modifier
  182. *
  183. * @return self Provides fluent interface
  184. */
  185. public function addField(string $key, $value, ?float $boost = null, ?string $modifier = null): self
  186. {
  187. if (!isset($this->fields[$key])) {
  188. $this->setField($key, $value, $boost, $modifier);
  189. } else {
  190. // convert single value to array if needed
  191. if (!\is_array($this->fields[$key])) {
  192. $this->fields[$key] = [$this->fields[$key]];
  193. }
  194. if ($this->filterControlCharacters && \is_string($value)) {
  195. $value = $this->getHelper()->filterControlCharacters($value);
  196. }
  197. $this->fields[$key][] = $value;
  198. if (null !== $boost) {
  199. $this->setFieldBoost($key, $boost);
  200. }
  201. if (null !== $modifier) {
  202. $this->setFieldModifier($key, $modifier);
  203. }
  204. }
  205. return $this;
  206. }
  207. /**
  208. * Set a field value.
  209. *
  210. * If a field already has a value it will be overwritten. You cannot use
  211. * this method for a multivalue field.
  212. * If you supply NULL as the value the field will be removed
  213. *
  214. * @param string $key
  215. * @param mixed $value
  216. * @param float|null $boost
  217. * @param string|null $modifier
  218. *
  219. * @return self Provides fluent interface
  220. */
  221. public function setField(string $key, $value, ?float $boost = null, ?string $modifier = null): self
  222. {
  223. if (null === $value && null === $modifier) {
  224. $this->removeField($key);
  225. } else {
  226. if ($this->filterControlCharacters && \is_string($value)) {
  227. $value = $this->getHelper()->filterControlCharacters($value);
  228. }
  229. $this->fields[$key] = $value;
  230. if (null !== $boost) {
  231. $this->setFieldBoost($key, $boost);
  232. }
  233. if (null !== $modifier) {
  234. $this->setFieldModifier($key, $modifier);
  235. }
  236. }
  237. return $this;
  238. }
  239. /**
  240. * Remove a field.
  241. *
  242. * @param string $key
  243. *
  244. * @return self Provides fluent interface
  245. */
  246. public function removeField(string $key): self
  247. {
  248. if (isset($this->fields[$key])) {
  249. unset($this->fields[$key]);
  250. }
  251. if (isset($this->fieldBoosts[$key])) {
  252. unset($this->fieldBoosts[$key]);
  253. }
  254. return $this;
  255. }
  256. /**
  257. * Get the boost value for a field.
  258. *
  259. * @param string $key
  260. *
  261. * @return float
  262. */
  263. public function getFieldBoost(string $key): ?float
  264. {
  265. return $this->fieldBoosts[$key] ?? null;
  266. }
  267. /**
  268. * Set the boost value for a field.
  269. *
  270. * @param string $key
  271. * @param float $boost
  272. *
  273. * @return self Provides fluent interface
  274. */
  275. public function setFieldBoost(string $key, float $boost): self
  276. {
  277. $this->fieldBoosts[$key] = $boost;
  278. return $this;
  279. }
  280. /**
  281. * Get boost values for all fields.
  282. *
  283. * @return array
  284. */
  285. public function getFieldBoosts(): array
  286. {
  287. return $this->fieldBoosts;
  288. }
  289. /**
  290. * Set the document boost value.
  291. *
  292. * @param float $boost
  293. *
  294. * @return self Provides fluent interface
  295. *
  296. * @deprecated No longer supported since Solr 7
  297. */
  298. public function setBoost(float $boost): self
  299. {
  300. $this->boost = $boost;
  301. return $this;
  302. }
  303. /**
  304. * Get the document boost value.
  305. *
  306. * @return float|null
  307. *
  308. * @deprecated No longer supported since Solr 7
  309. */
  310. public function getBoost(): ?float
  311. {
  312. return $this->boost;
  313. }
  314. /**
  315. * Clear all fields.
  316. *
  317. * @return self Provides fluent interface
  318. **/
  319. public function clear(): self
  320. {
  321. $this->fields = [];
  322. $this->fieldBoosts = [];
  323. $this->modifiers = [];
  324. return $this;
  325. }
  326. /**
  327. * Sets the uniquely identifying key for use in atomic updating.
  328. *
  329. * You can set an existing field as key by supplying that field name as key, or add a new field by also supplying a
  330. * value.
  331. *
  332. * @param string $key
  333. * @param mixed $value
  334. *
  335. * @return self Provides fluent interface
  336. */
  337. public function setKey(string $key, $value = null): self
  338. {
  339. $this->key = $key;
  340. if (null !== $value) {
  341. $this->addField($key, $value);
  342. }
  343. return $this;
  344. }
  345. /**
  346. * Sets the modifier type for the provided field.
  347. *
  348. * @param string $key
  349. * @param string $modifier
  350. *
  351. * @throws RuntimeException
  352. *
  353. * @return self
  354. */
  355. public function setFieldModifier(string $key, string $modifier = null): self
  356. {
  357. if (!\in_array($modifier, [self::MODIFIER_ADD, self::MODIFIER_ADD_DISTINCT, self::MODIFIER_REMOVE, self::MODIFIER_REMOVEREGEX, self::MODIFIER_INC, self::MODIFIER_SET], true)) {
  358. throw new RuntimeException('Attempt to set an atomic update modifier that is not supported');
  359. }
  360. $this->modifiers[$key] = $modifier;
  361. return $this;
  362. }
  363. /**
  364. * Returns the appropriate modifier for atomic updates.
  365. *
  366. * @param string $key
  367. *
  368. * @return string|null
  369. */
  370. public function getFieldModifier(string $key): ?string
  371. {
  372. return $this->modifiers[$key] ?? null;
  373. }
  374. /**
  375. * Get fields.
  376. *
  377. * Adds validation for atomicUpdates
  378. *
  379. * @throws RuntimeException
  380. *
  381. * @return array
  382. */
  383. public function getFields(): array
  384. {
  385. if ((null === $this->key || !isset($this->fields[$this->key])) && \count($this->modifiers) > 0) {
  386. throw new RuntimeException('A document that uses modifiers (atomic updates) must have a key defined before it is used');
  387. }
  388. return parent::getFields();
  389. }
  390. /**
  391. * Set version.
  392. *
  393. * @param int $version
  394. *
  395. * @return self
  396. */
  397. public function setVersion(int $version): self
  398. {
  399. $this->version = $version;
  400. return $this;
  401. }
  402. /**
  403. * Get version.
  404. *
  405. * @return int|null
  406. */
  407. public function getVersion(): ?int
  408. {
  409. return $this->version;
  410. }
  411. /**
  412. * Get a helper instance.
  413. *
  414. * Uses lazy loading: the helper is instantiated on first use
  415. *
  416. * @return Helper
  417. */
  418. public function getHelper(): Helper
  419. {
  420. if (null === $this->helper) {
  421. $this->helper = new Helper();
  422. }
  423. return $this->helper;
  424. }
  425. /**
  426. * Whether values should be filtered for control characters automatically.
  427. *
  428. * @param bool $filterControlCharacters
  429. *
  430. * @return self
  431. */
  432. public function setFilterControlCharacters(bool $filterControlCharacters): self
  433. {
  434. $this->filterControlCharacters = $filterControlCharacters;
  435. return $this;
  436. }
  437. /**
  438. * Returns whether values should be filtered automatically or control characters.
  439. *
  440. * @return bool
  441. */
  442. public function getFilterControlCharacters(): bool
  443. {
  444. return $this->filterControlCharacters;
  445. }
  446. }