Completed
Push — upgrade ( e29d4e...a26676 )
by Simonas
01:42
created

MetadataCollector::getMappings()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 25
rs 9.52
c 0
b 0
f 0
cc 4
nc 5
nop 1
1
<?php
2
3
/*
4
 * This file is part of the ONGR package.
5
 *
6
 * (c) NFQ Technologies UAB <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace ONGR\ElasticsearchBundle\Mapping;
13
14
use Doctrine\Common\Cache\CacheProvider;
15
use ONGR\ElasticsearchBundle\Exception\DocumentParserException;
16
use ONGR\ElasticsearchBundle\Exception\MissingDocumentAnnotationException;
17
18
class MetadataCollector
19
{
20
    private $parser;
21
    private $cache;
22
23
    public function __construct(DocumentParser $parser, CacheProvider $cache = null)
24
    {
25
        $this->parser = $parser;
26
        $this->cache = $cache;
27
    }
28
29
    public function getMapping($namespace) {
30
31
        if ($this->cache && $this->cache->contains($namespace)) {
32
            return $this->cache->fetch($namespace);
33
        }
34
35
36
37
38
39
    }
40
41
    /**
42
     * Searches for documents in the bundle and tries to read them.
43
     *
44
     * @param string $name
45
     * @param array $config Bundle configuration
46
     *
47
     * @return array Empty array on containing zero documents.
48
     */
49
    public function getBundleMapping($name, $config = [])
50
    {
51
        if (!is_string($name)) {
52
            throw new \LogicException('getBundleMapping() in the Metadata collector expects a string argument only!');
53
        }
54
55
        $cacheName =  'ongr.metadata.mapping.' . md5($name.serialize($config));
56
57
        $this->enableCache && $mappings = $this->cache->fetch($cacheName);
0 ignored issues
show
Bug introduced by
The property enableCache does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
58
59
        if (isset($mappings) && false !== $mappings) {
60
            return $mappings;
61
        }
62
63
        $mappings = [];
64
        $documentDir = isset($config['document_dir']) ? $config['document_dir'] : $this->finder->getDocumentDir();
0 ignored issues
show
Bug introduced by
The property finder does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
65
66
        // Handle the case when single document mapping requested
67
        // Usage od ":" in name is deprecated. This if is only for BC.
68
        if (strpos($name, ':') !== false) {
69
            list($bundle, $documentClass) = explode(':', $name);
70
            $documents = $this->finder->getBundleDocumentClasses($bundle);
71
            $documents = in_array($documentClass, $documents) ? [$documentClass] : [];
72
        } else {
73
            $documents = $this->finder->getBundleDocumentClasses($name, $documentDir);
74
            $bundle = $name;
75
        }
76
77
        $bundleNamespace = $this->finder->getBundleClass($bundle);
78
        $bundleNamespace = substr($bundleNamespace, 0, strrpos($bundleNamespace, '\\'));
79
80
        if (!count($documents)) {
81
            return [];
82
        }
83
84
        // Loop through documents found in bundle.
85
        foreach ($documents as $document) {
86
            $documentReflection = new \ReflectionClass(
87
                $bundleNamespace .
88
                '\\' . str_replace('/', '\\', $documentDir) .
89
                '\\' . $document
90
            );
91
92
            try {
93
                $documentMapping = $this->getDocumentReflectionMapping($documentReflection);
94
                if (!$documentMapping) {
95
                    continue;
96
                }
97
            } catch (MissingDocumentAnnotationException $exception) {
98
                // Not a document, just ignore
99
                continue;
100
            }
101
102
            if (!array_key_exists($documentMapping['type'], $mappings)) {
103
                $documentMapping['bundle'] = $bundle;
104
                $mappings = array_merge($mappings, [$documentMapping['type'] => $documentMapping]);
105
            } else {
106
                throw new \LogicException(
107
                    $bundle . ' has 2 same type names defined in the documents. ' .
108
                    'Type names must be unique!'
109
                );
110
            }
111
        }
112
113
        $this->enableCache && $this->cache->save($cacheName, $mappings);
114
115
        return $mappings;
116
    }
117
118
    /**
119
     * Resolves Elasticsearch type by document class.
120
     *
121
     * @param string $className FQCN or string in AppBundle:Document format
122
     *
123
     * @return string
124
     */
125
    public function getDocumentType($className)
126
    {
127
        $mapping = $this->getMapping($className);
128
129
        return $mapping['type'];
130
    }
131
132
    /**
133
     * Retrieves prepared mapping to sent to the elasticsearch client.
134
     *
135
     * @param array $bundles Manager config.
136
     *
137
     * @return array|null
138
     */
139
    public function getClientMapping(array $bundles)
140
    {
141
        /** @var array $typesMapping Array of filtered mappings for the elasticsearch client*/
142
        $typesMapping = null;
143
144
        /** @var array $mappings All mapping info */
145
        $mappings = $this->getMappings($bundles);
0 ignored issues
show
Bug introduced by
The method getMappings() does not exist on ONGR\ElasticsearchBundle\Mapping\MetadataCollector. Did you maybe mean getMapping()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
146
147
        foreach ($mappings as $type => $mapping) {
148
            if (!empty($mapping['properties'])) {
149
                $typesMapping[$type] = array_filter(
150
                    array_merge(
151
                        ['properties' => $mapping['properties']],
152
                        $mapping['fields']
153
                    ),
154
                    function ($value) {
155
                        return (bool)$value || is_bool($value);
156
                    }
157
                );
158
            }
159
        }
160
161
        return $typesMapping;
162
    }
163
164
    /**
165
     * Prepares analysis node for Elasticsearch client.
166
     *
167
     * @param array $bundles
168
     * @param array $analysisConfig
169
     *
170
     * @return array
171
     */
172
    public function getClientAnalysis(array $bundles, $analysisConfig = [])
173
    {
174
        $cacheName = 'ongr.metadata.analysis.'.md5(serialize($bundles));
175
        $this->enableCache && $typesAnalysis = $this->cache->fetch($cacheName);
176
177
        if (isset($typesAnalysis) && false !== $typesAnalysis) {
178
            return $typesAnalysis;
179
        }
180
181
        $typesAnalysis = [
182
            'analyzer' => [],
183
            'normalizers' => [],
184
            'filter' => [],
185
            'tokenizer' => [],
186
            'char_filter' => [],
187
        ];
188
189
        /** @var array $mappings All mapping info */
190
        $mappings = $this->getMappings($bundles);
0 ignored issues
show
Bug introduced by
The method getMappings() does not exist on ONGR\ElasticsearchBundle\Mapping\MetadataCollector. Did you maybe mean getMapping()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
191
192
        foreach ($mappings as $type => $metadata) {
193
            foreach ($metadata['analyzers'] as $analyzerName) {
194
                if (isset($analysisConfig['analyzer'][$analyzerName])) {
195
                    $analyzer = $analysisConfig['analyzer'][$analyzerName];
196
                    $typesAnalysis['analyzer'][$analyzerName] = $analyzer;
197
                    $typesAnalysis['filter'] = $this->getAnalysisNodeConfiguration(
198
                        'filter',
199
                        $analyzer,
200
                        $analysisConfig,
201
                        $typesAnalysis['filter']
202
                    );
203
                    $typesAnalysis['tokenizer'] = $this->getAnalysisNodeConfiguration(
204
                        'tokenizer',
205
                        $analyzer,
206
                        $analysisConfig,
207
                        $typesAnalysis['tokenizer']
208
                    );
209
                    $typesAnalysis['char_filter'] = $this->getAnalysisNodeConfiguration(
210
                        'char_filter',
211
                        $analyzer,
212
                        $analysisConfig,
213
                        $typesAnalysis['char_filter']
214
                    );
215
                }
216
            }
217
        }
218
219
        $this->enableCache && $this->cache->save($cacheName, $typesAnalysis);
220
221
        return $typesAnalysis;
222
    }
223
224
    /**
225
     * Prepares analysis node content for Elasticsearch client.
226
     *
227
     * @param string $type Node type: filter, tokenizer or char_filter
228
     * @param array $analyzer Analyzer from which used helpers will be extracted.
229
     * @param array $analysisConfig Pre configured analyzers container
230
     * @param array $container Current analysis container where prepared helpers will be appended.
231
     *
232
     * @return array
233
     */
234
    private function getAnalysisNodeConfiguration($type, $analyzer, $analysisConfig, $container = [])
235
    {
236
        if (isset($analyzer[$type])) {
237
            if (is_array($analyzer[$type])) {
238
                foreach ($analyzer[$type] as $filter) {
239
                    if (isset($analysisConfig[$type][$filter])) {
240
                        $container[$filter] = $analysisConfig[$type][$filter];
241
                    }
242
                }
243
            } else {
244
                if (isset($analysisConfig[$type][$analyzer[$type]])) {
245
                    $container[$analyzer[$type]] = $analysisConfig[$type][$analyzer[$type]];
246
                }
247
            }
248
        }
249
        return $container;
250
    }
251
252
    /**
253
     * Gathers annotation data from class.
254
     *
255
     * @param \ReflectionClass $reflectionClass Document reflection class to read mapping from.
256
     *
257
     * @return array
258
     * @throws DocumentParserException
259
     */
260
    private function getDocumentReflectionMapping(\ReflectionClass $reflectionClass)
261
    {
262
        return $this->parser->parse($reflectionClass);
263
    }
264
265
    /**
266
     * Returns single document mapping metadata.
267
     *
268
     * @param string $namespace Document namespace
269
     *
270
     * @return array
271
     * @throws DocumentParserException
272
     */
273
    public function getMapping($namespace)
274
    {
275
        $cacheName = 'ongr.metadata.document.'.md5($namespace);
276
277
        $namespace = $this->getClassName($namespace);
278
        $this->enableCache && $mapping = $this->cache->fetch($cacheName);
279
280
        if (isset($mapping) && false !== $mapping) {
281
            return $mapping;
282
        }
283
284
        $mapping = $this->getDocumentReflectionMapping(new \ReflectionClass($namespace));
285
286
        $this->enableCache && $this->cache->save($cacheName, $mapping);
287
288
        return $mapping;
289
    }
290
291
    /**
292
     * Returns fully qualified class name.
293
     *
294
     * @param string $className
295
     * @param string $directory The name of the directory
296
     *
297
     * @return string
298
     */
299
    public function getClassName($className, $directory = null)
300
    {
301
        return $this->finder->getNamespace($className, $directory);
302
    }
303
}
304