Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
82.22% covered (warning)
82.22%
37 / 45
55.56% covered (warning)
55.56%
5 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
CirrusIndexField
82.22% covered (warning)
82.22%
37 / 45
55.56% covered (warning)
55.56%
5 / 9
27.24
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setMappingFlags
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getMapping
87.50% covered (warning)
87.50%
7 / 8
0.00% covered (danger)
0.00%
0 / 1
3.02
 addIndexingHints
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
12
 addNoopHandler
50.00% covered (danger)
50.00%
3 / 6
0.00% covered (danger)
0.00%
0 / 1
2.50
 getHint
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 setHint
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 resetHints
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 resetMultiList
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
6
1<?php
2
3namespace CirrusSearch\Search;
4
5use CirrusSearch\CirrusSearch;
6use CirrusSearch\SearchConfig;
7use Elastica\Document;
8use Elastica\Param;
9use SearchEngine;
10use SearchIndexField;
11use SearchIndexFieldDefinition;
12
13/**
14 * Basic ElasticSearch index field
15 * @since 1.28
16 */
17abstract class CirrusIndexField extends SearchIndexFieldDefinition {
18    /**
19     * Name of the param on \Elastica\Document that contains
20     * hints about the noop_script handlers.
21     */
22    public const DOC_HINT_PARAM = '_cirrus_hints';
23
24    /**
25     * name of the noop handler for multilist
26     */
27    public const MULTILIST_HANDLER = 'multilist';
28
29    /**
30     * magic word to instruct the noop plugin to cleanup a particular multilist group
31     */
32    public const MULTILIST_DELETE_GROUPING = '__DELETE_GROUPING__';
33
34    /**
35     * Name of the hint as returned by SearchIndexField::getEngineHints()
36     */
37    public const NOOP_HINT = 'noop';
38
39    /**
40     * Name of the type in Elastic
41     * @var string
42     */
43    protected $typeName = 'unknown';
44
45    /**
46     * @var SearchConfig
47     */
48    protected $config;
49
50    /**
51     * Specific mapping flags
52     * @var int
53     */
54    protected $mappingFlags;
55
56    /**
57     * @param string $name
58     * @param string $type
59     * @param SearchConfig $config
60     */
61    public function __construct( $name, $type, SearchConfig $config ) {
62        parent::__construct( $name, $type );
63        $this->config = $config;
64    }
65
66    /**
67     * Set flags for specific mapping
68     * @param int $flags
69     * @return self
70     */
71    public function setMappingFlags( $flags ) {
72        $this->mappingFlags = $flags;
73        return $this;
74    }
75
76    /**
77     * Get mapping for specific search engine
78     * @param SearchEngine $engine
79     * @return array
80     */
81    public function getMapping( SearchEngine $engine ) {
82        if ( !( $engine instanceof CirrusSearch ) ) {
83            throw new \LogicException( "Cannot map CirrusSearch fields for another engine." );
84        }
85
86        $config = [
87            'type' => $this->typeName,
88        ];
89        if ( $this->checkFlag( SearchIndexField::FLAG_NO_INDEX ) ) {
90            $config['index'] = false;
91        }
92        return $config;
93    }
94
95    /**
96     * Inspect SearchIndexField::getEngineHints() for indexing hints
97     * and forward them to special metadata in the document.
98     *
99     * @param Document $doc
100     * @param string $fieldName
101     * @param array $hints
102     */
103    public static function addIndexingHints( Document $doc, $fieldName, array $hints ) {
104        if ( $hints && isset( $hints[self::NOOP_HINT] ) ) {
105            self::addNoopHandler( $doc, $fieldName, $hints[self::NOOP_HINT] );
106        }
107    }
108
109    /**
110     * Add a special metadata to $doc to control the noop_script
111     * @param \Elastica\Param $doc
112     * @param string $field
113     * @param string|array $handler the handler as understood by the super_noop_script
114     */
115    public static function addNoopHandler( \Elastica\Param $doc, $field, $handler ) {
116        if ( !$doc->hasParam( self::DOC_HINT_PARAM ) ) {
117            $doc->setParam( self::DOC_HINT_PARAM,
118                [ self::NOOP_HINT => [ $field => $handler ] ] );
119        } else {
120            $params = $doc->getParam( self::DOC_HINT_PARAM );
121            $params[self::NOOP_HINT][$field] = $handler;
122            $doc->setParam( self::DOC_HINT_PARAM, $params );
123        }
124    }
125
126    /**
127     * Get the hint named $hint
128     *
129     * @param \Elastica\Param $doc
130     * @param string $hint name of the hint
131     * @return mixed|null the hint value or null if inexistent
132     */
133    public static function getHint( \Elastica\Param $doc, $hint ) {
134        if ( $doc->hasParam( self::DOC_HINT_PARAM ) ) {
135            $params = $doc->getParam( self::DOC_HINT_PARAM );
136            if ( isset( $params[$hint] ) ) {
137                return $params[$hint];
138            }
139        }
140        return null;
141    }
142
143    /**
144     * Set the hint named $hint
145     *
146     * @param \Elastica\Param $doc
147     * @param string $hint name of the hint
148     * @param mixed $value the hint value
149     */
150    public static function setHint( \Elastica\Param $doc, $hint, $value ) {
151        $params = [];
152        if ( $doc->hasParam( self::DOC_HINT_PARAM ) ) {
153            $params = $doc->getParam( self::DOC_HINT_PARAM );
154        }
155        $params[$hint] = $value;
156        $doc->setParam( self::DOC_HINT_PARAM, $params );
157    }
158
159    /**
160     * Clear all hints
161     *
162     * @param Param $doc
163     */
164    public static function resetHints( Param $doc ) {
165        if ( $doc->hasParam( self::DOC_HINT_PARAM ) ) {
166            if ( $doc instanceof Document ) {
167                self::resetMultiList( $doc );
168            }
169            $doc->setParam( self::DOC_HINT_PARAM, null );
170        }
171    }
172
173    /**
174     * Remove multilist __DELETE_GROUPING__ records
175     * @param Document $doc
176     */
177    private static function resetMultiList( Document $doc ) {
178        $noopHandlers = self::getHint( $doc, self::NOOP_HINT ) ?: [];
179        foreach ( $noopHandlers as $field => $handler ) {
180            if ( $handler === self::MULTILIST_HANDLER && $doc->has( $field ) ) {
181                $data = $doc->get( $field );
182                $data = is_array( $data ) ? $data : [ $data ];
183                $doc->set(
184                    $field,
185                    array_values( array_filter( $data, static function ( string $x ) {
186                            return !str_ends_with( $x, self::MULTILIST_DELETE_GROUPING );
187                    } ) )
188                );
189            }
190        }
191    }
192}