Completed
Push — dev2 ( 48fafe...84774d )
by Gordon
03:07
created

Searchable::getAllSearchableFields()   C

Complexity

Conditions 10
Paths 6

Size

Total Lines 54
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 3 Features 0
Metric Value
c 4
b 3
f 0
dl 0
loc 54
rs 6.8372
cc 10
eloc 31
nc 6
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace SilverStripe\Elastica;
4
5
use Elastica\Document;
6
use Elastica\Type\Mapping;
7
use ShortcodeParser;
8
9
/**
10
 * Adds elastic search integration to a data object.
11
 */
12
class Searchable extends \DataExtension {
13
14
	/**
15
	 * Counter used to display progress of indexing
16
	 * @var integer
17
	 */
18
	public static $index_ctr = 0;
19
20
	/**
21
	 * Everytime progressInterval divides $index_ctr exactly display progress
22
	 * @var integer
23
	 */
24
	private static $progressInterval = 0;
25
26
	public static $mappings = array(
27
		'Boolean'     => 'boolean',
28
		'Decimal'     => 'double',
29
		'Currency'    => 'double',
30
		'Double'      => 'double',
31
		'Enum'        => 'string',
32
		'Float'       => 'float',
33
		'HTMLText'    => 'string',
34
		'HTMLVarchar' => 'string',
35
		'Int'         => 'integer',
36
		'Text'        => 'string',
37
		'VarChar'     => 'string',
38
		'Varchar'     => 'string',
39
		'Year'        => 'integer',
40
		'Percentage'  => 'double',
41
		'Time'  => 'date',
42
43
		// The 2 different date types will be stored with different formats
44
		'Date'        => 'date',
45
		'SS_Datetime' => 'date',
46
		'Datetime' => 'date',
47
		'DBLocale'    => 'string'
48
	);
49
50
51
	/**
52
	 * @var ElasticaService associated elastica search service
53
	 */
54
	protected $service;
55
56
57
	/**
58
	 * Array of fields that need HTML parsed
59
	 * @var array
60
	 */
61
	protected $html_fields = array();
62
63
	/**
64
	 * Store a mapping of relationship name to result type
65
	 */
66
	protected $relationship_methods = array();
67
68
69
	/**
70
	 * If importing a large number of items from a fixtures file, or indeed some other source, then
71
	 * it is quicker to set a flag of value IndexingOff => false.  This has the effect of ensuring
72
	 * no indexing happens, a request is normally made per fixture when loading.  One can then run
73
	 * the reindexing teask to bulk index in one HTTP POST request to Elasticsearch
74
	 *
75
	 * @var boolean
76
	 */
77
	private static $IndexingOff = false;
78
79
80
	/**
81
	 * @see getElasticaResult
82
	 * @var \Elastica\Result
83
	 */
84
	protected $elastica_result;
85
86
	public function __construct(ElasticaService $service) {
87
		$this->service = $service;
88
		parent::__construct();
89
	}
90
91
92
	/**
93
	 * Get the elasticsearch type name
94
	 *
95
	 * @return string
96
	 */
97
	public function getElasticaType() {
98
		return get_class($this->owner);
99
	}
100
101
102
	/**
103
	 * If the owner is part of a search result
104
	 * the raw Elastica search result is returned
105
	 * if set via setElasticaResult
106
	 *
107
	 * @return \Elastica\Result
108
	 */
109
	public function getElasticaResult() {
110
		return $this->elastica_result;
111
	}
112
113
114
	/**
115
	 * Set the raw Elastica search result
116
	 *
117
	 * @param \Elastica\Result
118
	 */
119
	public function setElasticaResult(\Elastica\Result $result) {
120
		$this->elastica_result = $result;
121
	}
122
123
124
	/**
125
	 * Gets an array of elastic field definitions.
126
	 *
127
	 * @return array
128
	 */
129
	public function getElasticaFields($storeMethodName = false, $recurse = true) {
130
		$db = $this->owner->db();
131
		$fields = $this->getAllSearchableFields();
132
		$result = array();
133
134
		foreach($fields as $name => $params) {
135
			$spec = array();
136
			$name = str_replace('()', '', $name);
137
138
			if(array_key_exists($name, $db)) {
139
				$class = $db[$name];
140
				$this->assignSpecForStandardFieldType($class, $spec);
141
			} else {
142
				// field name is not in the db, it could be a method
143
				$has_lists = $this->getListRelationshipMethods();
144
				$has_ones = $this->owner->has_one();
145
146
				// check has_many and many_many relations
147
				if(isset($has_lists[$name])) {
148
					// the classes returned by the list method
149
					$resultType = $has_lists[$name];
150
					$this->assignSpecForManyRelationship($resultType, $spec);
151
				} else if(isset($has_ones[$name])) {
152
					$resultType = $has_ones[$name];
153
					HERE
154
				}
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected '}'
Loading history...
155
				// otherwise fall back to string - Enum is one such category
156
				else {
157
					$spec["type"] = "string";
158
				}
159
			}
160
161
			// in the case of a relationship type will not be set
162
			if(isset($spec['type'])) {
163
				if($spec['type'] == 'string') {
164
					$unstemmed = array();
165
					$unstemmed['type'] = "string";
166
					$unstemmed['analyzer'] = "unstemmed";
167
					$unstemmed['term_vector'] = "yes";
168
					$extraFields = array('standard' => $unstemmed);
169
170
					$shingles = array();
171
					$shingles['type'] = "string";
172
					$shingles['analyzer'] = "shingles";
173
					$shingles['term_vector'] = "yes";
174
					$extraFields['shingles'] = $shingles;
175
176
					//Add autocomplete field if so required
177
					$autocomplete = \Config::inst()->get($this->owner->ClassName, 'searchable_autocomplete');
178
179
					if(isset($autocomplete) && in_array($name, $autocomplete)) {
180
						$autocompleteField = array();
181
						$autocompleteField['type'] = "string";
182
						$autocompleteField['index_analyzer'] = "autocomplete_index_analyzer";
183
						$autocompleteField['search_analyzer'] = "autocomplete_search_analyzer";
184
						$autocompleteField['term_vector'] = "yes";
185
						$extraFields['autocomplete'] = $autocompleteField;
186
					}
187
188
					$spec['fields'] = $extraFields;
189
					// FIXME - make index/locale specific, get from settings
190
					$spec['analyzer'] = 'stemmed';
191
					$spec['term_vector'] = "yes";
192
				}
193
			}
194
195
			$result[$name] = $spec;
196
		}
197
198
		if($this->owner->hasMethod('updateElasticHTMLFields')) {
199
			$this->html_fields = $this->owner->updateElasticHTMLFields($this->html_fields);
200
		}
201
202
		return $result;
203
	}
204
205
206
	private function assignSpecForHasOne($resultType, &$spec) {
207
		$resultTypeInstance = \Injector::inst()->create($resultType);
208
209
		$resultTypeMapping = array();
210
211
		// get the fields for the result type, but do not recurse
212
		if($recurse) {
213
			$resultTypeMapping = $resultTypeInstance->getElasticaFields($storeMethodName, false);
214
		}
215
216
		$resultTypeMapping['ID'] = array('type' => 'integer');
217
218
		if($storeMethodName) {
219
			$resultTypeMapping['__method'] = $name;
220
		}
221
		$spec = array('properties' => $resultTypeMapping);
222
		// we now change the name to the result type, not the method name
223
		$name = $resultType;
224
	}
225
226
	private function assignSpecForManyRelationship($resultType, &$spec) {
227
		$resultTypeInstance = \Injector::inst()->create($resultType);
228
		$resultTypeMapping = array();
229
230
		// get the fields for the result type, but do not recurse
231
		if($recurse) {
232
			$resultTypeMapping = $resultTypeInstance->getElasticaFields($storeMethodName, false);
233
		}
234
235
		$resultTypeMapping['ID'] = array('type' => 'integer');
236
237
		if($storeMethodName) {
238
			$resultTypeMapping['__method'] = $name;
239
		}
240
241
		$spec = array('properties' => $resultTypeMapping);
242
243
244
		// we now change the name to the result type, not the method name
245
		$name = $resultType;
246
	}
247
248
249
	private function assignSpecForStandardFieldType($class, &$spec) {
250
		if(($pos = strpos($class, '('))) {
251
			// Valid in the case of Varchar(255)
252
			$class = substr($class, 0, $pos);
253
		}
254
255
		if(array_key_exists($class, self::$mappings)) {
256
			$spec['type'] = self::$mappings[$class];
257
			if($spec['type'] === 'date') {
258
				if($class == 'Date') {
259
					$spec['format'] = 'y-M-d';
260
				} elseif($class == 'SS_Datetime') {
261
					$spec['format'] = 'y-M-d H:m:s';
262
				} elseif($class == 'Datetime') {
263
					$spec['format'] = 'y-M-d H:m:s';
264
				} elseif($class == 'Time') {
265
					$spec['format'] = 'H:m:s';
266
				}
267
			}
268
			if($class === 'HTMLText' || $class === 'HTMLVarchar') {
269
				array_push($this->html_fields, $name);
270
			}
271
		}
272
		// no need for an extra case here as all SS types checked in tests
273
	}
274
275
276
	/**
277
	 * Get the elasticsearch mapping for the current document/type
278
	 *
279
	 * @return \Elastica\Type\Mapping
280
	 */
281
	public function getElasticaMapping() {
282
		$mapping = new Mapping();
283
284
		$fields = $this->getElasticaFields(false);
285
286
		$localeMapping = array();
287
288
		if($this->owner->hasField('Locale')) {
289
			$localeMapping['type'] = 'string';
290
			// we wish the locale to be stored as is
291
			$localeMapping['index'] = 'not_analyzed';
292
			$fields['Locale'] = $localeMapping;
293
		}
294
295
		// ADD CUSTOM FIELDS HERE THAT ARE INDEXED BY DEFAULT
296
		// add a mapping to flag whether or not class is in SiteTree
297
		$fields['IsInSiteTree'] = array('type'=>'boolean');
298
		$fields['Link'] = array('type' => 'string', 'index' => 'not_analyzed');
299
300
		$mapping->setProperties($fields);
301
302
		//This concatenates all the fields together into a single field.
303
		//Initially added for suggestions compatibility, in that searching
304
		//_all field picks up all possible suggestions
305
		$mapping->enableAllField();
306
307
		if($this->owner->hasMethod('updateElasticsearchMapping')) {
308
			$mapping = $this->owner->updateElasticsearchMapping($mapping);
309
		}
310
		return $mapping;
311
	}
312
313
314
	/**
315
	 * Get an elasticsearch document
316
	 *
317
	 * @return \Elastica\Document
318
	 */
319
	public function getElasticaDocument() {
320
		self::$index_ctr++;
321
		$fields = $this->getFieldValuesAsArray();
322
		$progress = Controller::curr()->getVar('progress');
323
		if(!empty($progress)) {
324
			self::$progressInterval = (int)$progress;
325
		}
326
327
		if(self::$progressInterval > 0) {
328
			if(self::$index_ctr % self::$progressInterval === 0) {
329
				ElasticaUtil::message("\t" . $this->owner->ClassName . " - Prepared " . self::$index_ctr . " for indexing...");
330
			}
331
		}
332
333
		// Optionally update the document
334
		$document = new Document($this->owner->ID, $fields);
335
		if($this->owner->hasMethod('updateElasticsearchDocument')) {
336
			$document = $this->owner->updateElasticsearchDocument($document);
337
		}
338
339
		// Check if the current classname is part of the site tree or not
340
		// Results are cached to save reprocessing the same
341
		$classname = $this->owner->ClassName;
342
		$inSiteTree = $this->isInSiteTree($classname);
343
344
		$document->set('IsInSiteTree', $inSiteTree);
345
346
		if($inSiteTree) {
347
			$document->set('Link', $this->owner->AbsoluteLink());
348
		}
349
350
		if(isset($this->owner->Locale)) {
351
			$document->set('Locale', $this->owner->Locale);
352
		}
353
354
		return $document;
355
	}
356
357
358
	public function getFieldValuesAsArray($recurse = true) {
359
		$fields = array();
360
		$has_ones = $this->owner->has_one();
361
362
		foreach($this->getElasticaFields($recurse) as $field => $config) {
363
			if(null === $this->owner->$field && is_callable(get_class($this->owner) . "::" . $field)) {
364
				// call a method to get a field value
365
				if(in_array($field, $this->html_fields)) {
366
					// Parse short codes in HTML, and then convert to text
367
					$fields[$field] = $this->owner->$field;
368
					$html = ShortcodeParser::get_active()->parse($this->owner->$field());
369
					$txt = \Convert::html2raw($html);
370
					$fields[$field] = $txt;
371
				} else {
372
					// Plain text
373
					$fields[$field] = $this->owner->$field();
374
				}
375
376
			} else {
377
				if(in_array($field, $this->html_fields)) {
378
					$fields[$field] = $this->owner->$field;
379
					if(gettype($this->owner->$field) !== 'NULL') {
380
						$html = ShortcodeParser::get_active()->parse($this->owner->$field);
381
						$txt = \Convert::html2raw($html);
382
						$fields[$field] = $txt;
383
					}
384
				} else {
385
					if(isset($config['properties']['__method'])) {
386
						$methodName = $config['properties']['__method'];
387
						$data = $this->owner->$methodName();
388
						$relArray = array();
389
390
						// get the fields of a has_one relational object
391
						if(isset($has_ones[$methodName])) {
392
							if($data->ID > 0) {
393
								$item = $data->getFieldValuesAsArray(false);
394
								$relArray = $item;
395
							}
396
397
						// get the fields for a has_many or many_many relational list
398
						} else {
399
							foreach($data->getIterator() as $item) {
400
								if($recurse) {
401
									// populate the subitem but do not recurse any further if more relationships
402
									$itemDoc = $item->getFieldValuesAsArray(false);
403
									array_push($relArray, $itemDoc);
404
								}
405
							}
406
						}
407
						// save the relation as an array (for now)
408
						$fields[$methodName] = $relArray;
409
					} else {
410
						$fields[$field] = $this->owner->$field;
411
					}
412
413
				}
414
415
			}
416
		}
417
418
		return $fields;
419
	}
420
421
422
	/**
423
	 * Returns whether to include the document into the search index.
424
	 * All documents are added unless they have a field "ShowInSearch" which is set to false
425
	 *
426
	 * @return boolean
427
	 */
428
	public function showRecordInSearch() {
429
		return !($this->owner->hasField('ShowInSearch') && false == $this->owner->ShowInSearch);
430
	}
431
432
433
	/**
434
	 * Delete the record from the search index if ShowInSearch is deactivated (non-SiteTree).
435
	 */
436
	public function onBeforeWrite() {
437
		if(($this->owner instanceof \SiteTree)) {
438
			if($this->owner->hasField('ShowInSearch') &&
439
				$this->owner->isChanged('ShowInSearch', 2) && false == $this->owner->ShowInSearch) {
440
				$this->doDeleteDocument();
441
			}
442
		}
443
	}
444
445
446
	/**
447
	 * Delete the record from the search index if ShowInSearch is deactivated (SiteTree).
448
	 */
449
	public function onBeforePublish() {
450
		if(false == $this->owner->ShowInSearch) {
451
			if($this->owner->isPublished()) {
452
				$liveRecord = \Versioned::get_by_stage(get_class($this->owner), 'Live')->
453
					byID($this->owner->ID);
454
				if($liveRecord->ShowInSearch != $this->owner->ShowInSearch) {
455
					$this->doDeleteDocument();
456
				}
457
			}
458
		}
459
	}
460
461
462
	/**
463
	 * Updates the record in the search index (non-SiteTree).
464
	 */
465
	public function onAfterWrite() {
466
		$this->doIndexDocument();
467
	}
468
469
470
	/**
471
	 * Updates the record in the search index (SiteTree).
472
	 */
473
	public function onAfterPublish() {
474
		$this->doIndexDocument();
475
	}
476
477
478
	/**
479
	 * Updates the record in the search index.
480
	 */
481
	protected function doIndexDocument() {
482
		if($this->showRecordInSearch()) {
483
			if(!$this->owner->IndexingOff) {
484
				$this->service->index($this->owner);
485
			}
486
		}
487
	}
488
489
490
	/**
491
	 * Removes the record from the search index (non-SiteTree).
492
	 */
493
	public function onAfterDelete() {
494
		$this->doDeleteDocumentIfInSearch();
495
	}
496
497
498
	/**
499
	 * Removes the record from the search index (non-SiteTree).
500
	 */
501
	public function onAfterUnpublish() {
502
		$this->doDeleteDocumentIfInSearch();
503
	}
504
505
506
	/**
507
	 * Removes the record from the search index if the "ShowInSearch" attribute is set to true.
508
	 */
509
	protected function doDeleteDocumentIfInSearch() {
510
		if($this->showRecordInSearch()) {
511
			$this->doDeleteDocument();
512
		}
513
	}
514
515
516
	/**
517
	 * Removes the record from the search index.
518
	 */
519
	protected function doDeleteDocument() {
520
		try {
521
			if(!$this->owner->IndexingOff) {
522
				// this goes to elastica service
523
				$this->service->remove($this->owner);
524
			}
525
		} catch (\Elastica\Exception\NotFoundException $e) {
526
			trigger_error("Deleted document " . $this->owner->ClassName . " (" . $this->owner->ID .
527
				") not found in search index.", E_USER_NOTICE);
528
		}
529
530
	}
531
532
533
	/**
534
	 * Return all of the searchable fields defined in $this->owner::$searchable_fields and all the parent classes.
535
	 *
536
	 * @param  $recuse Whether or not to traverse relationships. First time round yes, subsequently no
537
	 * @return array searchable fields
538
	 */
539
	public function getAllSearchableFields($recurse = true) {
540
		$fields = \Config::inst()->get(get_class($this->owner), 'searchable_fields');
541
542
		// fallback to default method
543
		if(!$fields) {
544
			user_error('The field $searchable_fields must be set for the class ' . $this->owner->ClassName);
545
		}
546
547
		// get the values of these fields
548
		$elasticaMapping = $this->fieldsToElasticaConfig($fields);
549
550
		if($recurse) {
551
			// now for the associated methods and their results
552
			$methodDescs = \Config::inst()->get(get_class($this->owner), 'searchable_relationships');
553
			$has_ones = $this->owner->has_one();
554
			$has_lists = $this->getListRelationshipMethods();
555
556
			if(isset($methodDescs) && is_array($methodDescs)) {
557
				foreach($methodDescs as $methodDesc) {
558
					// split before the brackets which can optionally list which fields to index
559
					$splits = explode('(', $methodDesc);
560
					$methodName = $splits[0];
561
562
					if(isset($has_lists[$methodName])) {
563
564
						$relClass = $has_lists[$methodName];
565
						$fields = \Config::inst()->get($relClass, 'searchable_fields');
566
						if(!$fields) {
567
							user_error('The field $searchable_fields must be set for the class ' . $relClass);
568
						}
569
						$rewrite = $this->fieldsToElasticaConfig($fields);
570
571
						// mark as a method, the resultant fields are correct
572
						$elasticaMapping[$methodName . '()'] = $rewrite;
573
					} else if(isset($has_ones[$methodName])) {
574
						$relClass = $has_ones[$methodName];
575
						$fields = \Config::inst()->get($relClass, 'searchable_fields');
576
						if(!$fields) {
577
							user_error('The field $searchable_fields must be set for the class ' . $relClass);
578
						}
579
						$rewrite = $this->fieldsToElasticaConfig($fields);
580
581
						// mark as a method, the resultant fields are correct
582
						$elasticaMapping[$methodName . '()'] = $rewrite;
583
					} else {
584
						user_error('The method ' . $methodName . ' not found in class ' . $this->owner->ClassName .
585
								', please check configuration');
586
					}
587
				}
588
			}
589
		}
590
591
		return $elasticaMapping;
592
	}
593
594
595
	/*
596
	Evaluate each field, e.g. 'Title', 'Member.Name'
597
	 */
598
	private function fieldsToElasticaConfig($fields) {
599
		// Copied from DataObject::searchableFields() as there is no separate accessible method
600
		$rewrite = array();
601
		foreach($fields as $name => $specOrName) {
602
			$identifer = (is_int($name)) ? $specOrName : $name;
603
			$rewrite[$identifer] = array();
604
			if(!isset($rewrite[$identifer]['title'])) {
605
				$rewrite[$identifer]['title'] = (isset($labels[$identifer]))
606
					? $labels[$identifer] : \FormField::name_to_label($identifer);
607
			}
608
			if(!isset($rewrite[$identifer]['filter'])) {
609
				$rewrite[$identifer]['filter'] = 'PartialMatchFilter';
610
			}
611
		}
612
613
		return $rewrite;
614
	}
615
616
617
	public function requireDefaultRecords() {
618
		parent::requireDefaultRecords();
619
620
		$searchableFields = $this->getElasticaFields(true, true);
621
622
623
		$doSC = \SearchableClass::get()->filter(array('Name' => $this->owner->ClassName))->first();
624
		if(!$doSC) {
625
			$doSC = new \SearchableClass();
626
			$doSC->Name = $this->owner->ClassName;
627
628
			$inSiteTree = $this->isInSiteTree($this->owner->ClassName);
629
			$doSC->InSiteTree = $inSiteTree;
630
631
			$doSC->write();
632
		}
633
634
		foreach($searchableFields as $name => $searchableField) {
635
			// check for existence of methods and if they exist use that as the name
636
			if(!isset($searchableField['type'])) {
637
				$name = $searchableField['properties']['__method'];
638
			}
639
640
			$filter = array('ClazzName' => $this->owner->ClassName, 'Name' => $name);
641
			$doSF = \SearchableField::get()->filter($filter)->first();
642
643
644
			if(!$doSF) {
645
				$doSF = new \SearchableField();
646
				$doSF->ClazzName = $this->owner->ClassName;
647
				$doSF->Name = $name;
648
649
				if(isset($searchableField['type'])) {
650
					$doSF->Type = $searchableField['type'];
651
				} else {
652
					$doSF->Name = $searchableField['properties']['__method'];
653
					$doSF->Type = 'relationship';
654
				}
655
				$doSF->SearchableClassID = $doSC->ID;
656
657
				if(isset($searchableField['fields']['autocomplete'])) {
658
					$doSF->Autocomplete = true;
659
				}
660
661
				$doSF->write();
662
				\DB::alteration_message("Created new searchable editable field " . $name, "changed");
663
			}
664
665
			// FIXME deal with deletions
666
		}
667
	}
668
669
670
	private function getListRelationshipMethods() {
671
		$has_manys = $this->owner->has_many();
672
		$many_manys = $this->owner->many_many();
673
674
		// array of method name to retuned object ClassName for relationships returning lists
675
		$has_lists = $has_manys;
676
		foreach(array_keys($many_manys) as $key) {
677
			$has_lists[$key] = $many_manys[$key];
678
		}
679
680
		return $has_lists;
681
	}
682
683
684
	private function isInSiteTree($classname) {
685
		$inSiteTree = ($classname === 'SiteTree' ? true : false);
686
		if(!$inSiteTree) {
687
			$class = new \ReflectionClass($this->owner->ClassName);
688
			while($class = $class->getParentClass()) {
689
				$parentClass = $class->getName();
690
				if($parentClass == 'SiteTree') {
691
					$inSiteTree = true;
692
					break;
693
				}
694
			}
695
		}
696
		return $inSiteTree;
697
	}
698
699
700
	/*
701
	Allow the option of overriding the default template with one of <ClassName>ElasticSearchResult
702
	 */
703
	public function RenderResult($linkToContainer = '') {
704
		$vars = new \ArrayData(array('SearchResult' => $this->owner, 'ContainerLink' => $linkToContainer));
705
		$possibleTemplates = array($this->owner->ClassName . 'ElasticSearchResult', 'ElasticSearchResult');
706
		return $this->owner->customise($vars)->renderWith($possibleTemplates);
707
	}
708
709
710
711
	public function getTermVectors() {
712
		return $this->service->getTermVectors($this->owner);
713
	}
714
715
716
	public function updateCMSFields(\FieldList $fields) {
717
		$isIndexed = false;
718
		// SIteTree object must have a live record, ShowInSearch = true
719
		if($this->isInSiteTree($this->owner->ClassName)) {
720
			$liveRecord = \Versioned::get_by_stage(get_class($this->owner), 'Live')->
721
				byID($this->owner->ID);
722
			if($liveRecord->ShowInSearch) {
723
				$isIndexed = true;
724
			} else {
725
				$isIndexed = false;
726
			}
727
		} else {
728
			// In the case of a DataObject we use the ShowInSearchFlag
729
			$isIndexed = true;
730
		}
731
732
		if($isIndexed) {
733
			$termVectors = $this->getTermVectors();
734
			$termFields = array_keys($termVectors);
735
			sort($termFields);
736
737
			foreach($termFields as $field) {
738
				$terms = new \ArrayList();
739
740
				foreach(array_keys($termVectors[$field]['terms']) as $term) {
741
					$do = new \DataObject();
742
					$do->Term = $term;
743
					$stats = $termVectors[$field]['terms'][$term];
744
					if(isset($stats['ttf'])) {
745
						$do->TTF = $stats['ttf'];
746
					}
747
748
					if(isset($stats['doc_freq'])) {
749
						$do->DocFreq = $stats['doc_freq'];
750
					}
751
752
					if(isset($stats['term_freq'])) {
753
						$do->TermFreq = $stats['term_freq'];
754
					}
755
					$terms->push($do);
756
				}
757
758
				$config = \GridFieldConfig_RecordViewer::create(100);
759
				$config->getComponentByType('GridFieldDataColumns')->setDisplayFields(array(
760
					'Term' => 'Term',
761
					'TTF' => 'Total term frequency (how often a term occurs in all documents)',
762
					'DocFreq' => 'n documents with this term',
763
					'TermFreq'=> 'n times this term appears in this field'
764
				));
765
766
			   $underscored = str_replace('.', '_', $field);
767
768
				$gridField = new \GridField(
769
					'TermsFor' . $underscored, // Field name
770
					$field . 'TITLE' . $field, // Field title
771
					$terms,
772
					$config
773
				);
774
			   $fields->addFieldToTab('Root.ElasticaTerms.' . $underscored, $gridField);
775
			}
776
777
		}
778
779
		return $fields;
780
	}
781
782
783
}
784