ElasticaUtil   A
last analyzed

Complexity

Total Complexity 22

Size/Duplication

Total Lines 203
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 2

Test Coverage

Coverage 16.49%

Importance

Changes 2
Bugs 1 Features 0
Metric Value
wmc 22
c 2
b 1
f 0
lcom 2
cbo 2
dl 0
loc 203
ccs 16
cts 97
cp 0.1649
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A message() 0 5 3
A showBooleanHumanReadable() 0 3 2
B getPhraseSuggestion() 0 80 6
B parseSuggestionExplanation() 0 35 6
A addAutocompleteToQueryField() 0 8 1
A getPrinter() 0 8 3
A setPrinterOutput() 0 3 1
1
<?php
2
3
namespace SilverStripe\Elastica;
4
5
/**
6
 * Utility methods to help with searching functions, and also testable without fixtures
7
 */
8
class ElasticaUtil {
9
10
	/**
11
	 * Marker string for pre highlight - can be any string unlikely to appear in a search
12
	 */
13
	private static $pre_marker = " |PREZXCVBNM12345678";
14
15
	/**
16
	 * Marker string for psot highlight - can be any string unlikely to appear in a search
17
	 */
18
	private static $post_marker = "POSTZXCVBNM12345678| ";
19
20
	/**
21
	 * @var boolean true to show CLI output, false to hide
22
	 */
23
	private static $cli_printer_output = true;
24
25
26
	/**
27
	 * Function to display messages only if using the command line
28
	 * @var string $content Text to display when in command line mode
29
	 */
30 10
	public static function message($content) {
31 10
		if (\Director::is_cli() && self::$cli_printer_output === true) {
32
			echo "$content\n";
33
		}
34 10
	}
35
36
37
	/*
38
	Display a human readable yes or no
39
	 */
40
	public static function showBooleanHumanReadable($assertion) {
41
		return $assertion ? 'Yes' : 'No';
42
	}
43
44
45 9
	public static function getPhraseSuggestion($alternativeQuerySuggestions) {
46 9
		$originalQuery = $alternativeQuerySuggestions[0]['text'];
47
48 9
		$highlightsCfg = \Config::inst()->get('Elastica', 'Highlights');
49 9
		$preTags = $highlightsCfg['PreTags'];
50 9
		$postTags = $highlightsCfg['PostTags'];
51
52
		//Use the first suggested phrase
53 9
		$options = $alternativeQuerySuggestions[0]['options'];
54
55 9
		$resultArray = null;
56 9
		if (sizeof($options) > 0) {
57
			//take the first suggestion
58
			$suggestedPhrase = $options[0]['text'];
59
			$suggestedPhraseHighlighted = $options[0]['highlighted'];
60
61
			// now need to fix capitalisation
62
			$originalParts = explode(' ', $originalQuery);
63
			$suggestedParts = explode(' ', $suggestedPhrase);
64
65
			$markedHighlightedParts = ' '.$suggestedPhraseHighlighted.' ';
66
			$markedHighlightedParts = str_replace(' '.$preTags, ' '.self::$pre_marker, $markedHighlightedParts);
67
68
			$markedHighlightedParts = str_replace($postTags.' ', self::$post_marker, $markedHighlightedParts);
69
70
			$markedHighlightedParts = trim($markedHighlightedParts);
71
			$markedHighlightedParts = trim($markedHighlightedParts);
72
73
			$highlightedParts = preg_split('/\s+/', $markedHighlightedParts);
74
75
			//Create a mapping of lowercase to uppercase terms
76
			$lowerToUpper = array();
77
			$lowerToHighlighted = array();
78
			$ctr = 0;
79
			foreach ($suggestedParts as $lowercaseWord) {
80
				$lowerToUpper[$lowercaseWord] = $originalParts[$ctr];
81
				$lowerToHighlighted[$lowercaseWord] = $highlightedParts[$ctr];
82
				$ctr++;
83
			}
84
85
			$plain = array();
86
			$highlighted = array();
87
			foreach ($suggestedParts as $lowercaseWord) {
88
				$possiblyUppercase = $lowerToUpper[$lowercaseWord];
89
				$possiblyUppercaseHighlighted = $lowerToHighlighted[$lowercaseWord];
90
91
				//If the terms are identical other than case, e.g. new => New, then simply swap
92
				if (strtolower($possiblyUppercase) == $lowercaseWord) {
93
					array_push($plain, $possiblyUppercase);
94
					array_push($highlighted, $possiblyUppercase);
95
				} else {
96
					//Need to check capitalisation of terms suggested that are different
97
98
					$chr = mb_substr ($possiblyUppercase, 0, 1, "UTF-8");
99
    				if (mb_strtolower($chr, "UTF-8") != $chr) {
100
    					$upperLowercaseWord = $lowercaseWord;
101
    					$upperLowercaseWord[0] = $chr;
102
    					$withHighlights = str_replace($lowercaseWord, $upperLowercaseWord, $possiblyUppercaseHighlighted);
103
    					$lowercaseWord[0] = $chr;
104
    					array_push($plain, $lowercaseWord);
105
    					array_push($highlighted, $withHighlights);
106
    				} else {
107
    					//No need to capitalise, so add suggested word
108
    					array_push($plain, $lowercaseWord);
109
110
    					//No need to capitalise, so add suggested highlighted word
111
    					array_push($highlighted, $possiblyUppercaseHighlighted);
112
    				}
113
				}
114
			}
115
116
			$highlighted = ' '.implode(' ', $highlighted).' ';
117
			$highlighted = str_replace(self::$pre_marker, ' '.$preTags, $highlighted);
118
			$highlighted = str_replace(self::$post_marker, $postTags.' ', $highlighted);
119
120
			$resultArray['suggestedQuery'] = implode(' ', $plain);
121
			$resultArray['suggestedQueryHighlighted'] = trim($highlighted);
122
		}
123
		return $resultArray;
124
	}
125
126
127
	/**
128
	 * The output format of this function is not documented, so at best this is guess work to an
129 9
	 * extent.  Possible formats are:
130
	 * - ((Title.standard:great Content.standard:ammunition Content.standard:could
131
	 *     Content.standard:bair Content.standard:dancing Content.standard:column
132
	 *     Content.standard:company Content.standard:infantry Content.standard:men
133
	 *     Content.standard:soldier Content.standard:brigade Content.standard:zealand
134
	 *     Content.standard:new)~3)
135
	 *     -ConstantScore(_uid:GutenbergBookExtract#1519)
136
	 *  (Description: bay Description: mannerstram)
137
	 *
138
	 * @param  string $explanation explanation string for more like this terms from Elasticsearch
139
	 * @return array             Array of fieldnames mapped to terms
140
	 */
141
	public static function parseSuggestionExplanation($explanation) {
142
		$explanation = explode('-ConstantScore', $explanation)[0];
143
        $bracketPos = strpos($explanation, ')~');
144
145
        if (substr($explanation, 0,2) == '((') {
146
        	$explanation = substr($explanation, 2, $bracketPos-2);
147
        } elseif (substr($explanation, 0,1) == '(') {
148
        	$explanation = substr($explanation, 1, $bracketPos-2);
149
        }
150
151
       	$terms = array();
152
153
        //Field name(s) => terms
154
        $splits = explode(' ', $explanation);
155
156
        foreach ($splits as $fieldAndTerm) {
157
        	$splits = explode(':', $fieldAndTerm);
158
159
        	// This is the no terms case
160
        	if (sizeof($splits) < 2) {
161
        		break;
162
        	}
163
164
        	$fieldname = $splits[0];
165
        	$term = $splits[1];
166
167
        	if (!isset($terms[$fieldname])) {
168
        		$terms[$fieldname] = array();
169
        	}
170
171
        	array_push($terms[$fieldname], $term);
172
        }
173
174
        return $terms;
175
	}
176
177
	/**
178
	 * Add attributes necessary for jQuery to execute autocomplete
179
	 * @param FormField &$queryField field used to type a search query
180
	 */
181
	public static function addAutocompleteToQueryField(&$queryField, $classesToSearch, $siteTreeOnly, $link, $slug) {
182
		$queryField->setAttribute('data-autocomplete', 'true');
183
		$queryField->setAttribute('data-autocomplete-field', 'Title');
184
		$queryField->setAttribute('data-autocomplete-classes', $classesToSearch);
185
		$queryField->setAttribute('data-autocomplete-sitetree', $siteTreeOnly);
186
		$queryField->setAttribute('data-autocomplete-source', $link);
187
		$queryField->setAttribute('data-autocomplete-function', $slug);
188
	}
189
190
	/**
191
	 * @return function print content to either web browser or command line.  Can be optionally supressed
192
	 */
193
	public static function getPrinter() {
194
		return function ($content) {
195
			if (self::$cli_printer_output === true) {
196
				print(\Director::is_cli() ? "T1 $content\n" : "T2 <p>$content</p>");
197
			}
198
199
		};
200
	}
201 10
202 10
	/**
203 10
	 * Set to true to show output on the command line or browser, false to not
204
	 *
205
	 * @param   $newcli_printer_output true to show output, false to hide it
206
	 */
207 10
	public static function setPrinterOutput($new_cli_printer_output) {
208
		self::$cli_printer_output = $new_cli_printer_output;
209
	}
210
}
211