-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathsearch_api_solr.module
384 lines (356 loc) · 12.3 KB
/
search_api_solr.module
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
<?php
/**
* @file
* Provides a Solr-based service class for the Search API.
*/
use Drupal\search_api\Exception\SearchApiException;
use Drupal\search_api\Server\ServerInterface;
use Drupal\search_api\Utility\Utility;
use Symfony\Component\HttpFoundation\Request;
/**
* Implements hook_menu().
*/
function search_api_solr_menu() {
$items['admin/config/search/search_api/server/%search_api_server/files'] = array(
'title' => 'Files',
'description' => 'View Solr configuration files.',
'page callback' => 'drupal_get_form',
'page arguments' => array('search_api_solr_solr_config_form', 5),
'access callback' => 'search_api_solr_access_server_files',
'access arguments' => array(5),
'file' => 'search_api_solr.admin.inc',
'type' => MENU_LOCAL_TASK,
'weight' => -1,
'context' => MENU_CONTEXT_INLINE | MENU_CONTEXT_PAGE,
);
return $items;
}
/**
* Implements hook_help().
*/
function search_api_solr_help($route_name, Request $request) {
if ($route_name == 'search_api.overview') {
// Included because we need the REQUIREMENT_* constants.
include_once(DRUPAL_ROOT . '/core/includes/install.inc');
module_load_include('install', 'search_api_solr');
$reqs = search_api_solr_requirements('runtime');
foreach ($reqs as $req) {
if (isset($req['description'])) {
$type = $req['severity'] == REQUIREMENT_ERROR ? 'error' : ($req['severity'] == REQUIREMENT_WARNING ? 'warning' : 'status');
drupal_set_message($req['description'], $type);
}
}
}
}
/**
* Implements hook_cron().
*
* Used to execute an optimization operation on all enabled Solr servers once a
* day.
*/
function _search_api_solr_cron() {
$action = \Drupal::config('search_api_solr.settings')->get('cron_action');
// We treat all unknown action settings as "none". However, we turn a blind
// eye for Britons and other people who can spell.
if (!in_array($action, array('spellcheck', 'optimize', 'optimise'))) {
return;
}
// 86400 seconds is one day. We use slightly less here to allow for some
// variation in the request time of the cron run, so that the time of day will
// (more or less) stay the same.
if (REQUEST_TIME - \Drupal::config('search_api_solr.settings')->get('last_optimize') > 86340) {
\Drupal::config('search_api_solr.settings')->set('last_optimize', REQUEST_TIME);
$conditions = array('class' => 'search_api_solr_service', 'enabled' => TRUE);
$count = 0;
foreach (search_api_server_load_multiple(FALSE, $conditions) as $server) {
try {
$solr = $server->getSolrConnection();
if ($action != 'spellcheck') {
$solr->optimize(FALSE);
}
else {
$params['rows'] = 0;
$params['spellcheck'] = 'true';
$params['spellcheck.build'] = 'true';
$solr->search(NULL, $params);
}
++$count;
}
catch(SearchApiException $e) {
watchdog_exception('search_api_solr', $e, '%type while optimizing Solr server @server: !message in %function (line %line of %file).', array('@server' => $server->name));
}
}
if ($count) {
$vars['@count'] = $count;
if ($action != 'spellcheck') {
watchdog('search_api_solr', 'Optimized @count Solr server(s).', $vars, WATCHDOG_INFO);
}
else {
watchdog('search_api_solr', 'Rebuilt spellcheck dictionary on @count Solr server(s).', $vars, WATCHDOG_INFO);
}
}
}
}
/**
* Implements hook_search_api_server_update().
*/
function search_api_solr_search_api_server_update(ServerInterface $server) {
// @todo Do we still need to keep static and persistent caches?
// if ($server->getBackendId() == 'search_api_solr') {
// $server->getSolrConnection()->clearCache();
// }
}
/**
* Implements hook_views_api().
*/
function search_api_solr_views_api() {
if (module_exists('search_api_views')) {
return array(
'api' => 3,
);
}
}
/**
* Retrieves Solr-specific data for available data types.
*
* Returns the data type information for both the default Search API data types
* and custom data types defined by hook_search_api_data_type_info(). Names for
* default data types are not included, since they are not relevant to the Solr
* service class.
*
* We're adding some extra Solr field information for the default search api
* data types (as well as on behalf of a couple contrib field types). The
* extra information we're adding is documented in
* search_api_solr_hook_search_api_data_type_info(). You can use the same
* additional keys in hook_search_api_data_type_info() to support custom
* dynamic fields in your indexes with Solr.
*
* @param string|null $type
* (optional) A specific type for which the information should be returned.
* Defaults to returning all information.
*
* @return array|null
* If $type was given, information about that type or NULL if it is unknown.
* Otherwise, an array of all types. The format in both cases is the same as
* for search_api_get_data_type_info().
*
* @see search_api_get_data_type_info()
* @see search_api_solr_hook_search_api_data_type_info()
*/
function search_api_solr_get_data_type_info($type = NULL) {
$types = &drupal_static(__FUNCTION__);
if (!isset($types)) {
// Grab the stock search_api data types.
$types = Utility::getDataTypeInfo();
// Add our extras for the default search api fields.
// @todo We assume that all fields can be multi-valued now, so the
// 'always multiValued' option doesn't make sense anymore.
$types += array(
'text' => array(
'prefix' => 'tm',
'always multiValued' => TRUE,
),
'string' => array(
'prefix' => 's',
),
'integer' => array(
'prefix' => 'i',
),
'decimal' => array(
'prefix' => 'f',
),
'date' => array(
'prefix' => 'd',
),
'duration' => array(
'prefix' => 'i',
),
'boolean' => array(
'prefix' => 'b',
),
'uri' => array(
'prefix' => 's',
),
'tokens' => array(
'prefix' => 'tm',
'always multiValued' => TRUE,
),
);
// Extra data type info.
$extra_types_info = array(
'location' => array(
'prefix' => 'loc',
),
'geohash' => array(
'prefix' => 'geo',
),
);
// For the extra types, only add our extra info if it's already been defined.
foreach ($extra_types_info as $key => $info) {
if (array_key_exists($key, $types)) {
// Merge our extras into the data type info
$types[$key] += $info;
}
}
}
// Return the info.
if (isset($type)) {
return isset($types[$type]) ? $types[$type] : NULL;
}
return $types;
}
/**
* Returns a unique hash for the current site.
*
* This is used to identify Solr documents from different sites within a single
* Solr server.
*
* @return string
* A unique site hash, containing only alphanumeric characters.
*/
function search_api_solr_site_hash() {
// Copied from apachesolr_site_hash().
if (!($hash = \Drupal::config('search_api_solr.settings')->get('site_hash'))) {
global $base_url;
$hash = substr(base_convert(sha1(uniqid($base_url, TRUE)), 16, 36), 0, 6);
\Drupal::config('search_api_solr.settings')->set('site_hash', $hash);
}
return $hash;
}
/**
* Retrieves a list of all config files of a server.
*
* @param SearchApiServer $server
* The Solr server whose files should be retrieved.
* @param string $dir_name
* (optional) The directory that should be searched for files. Defaults to the
* root config directory.
*
* @return array
* An associative array of all config files in the given directory. The keys
* are the file names, values are arrays with information about the file. The
* files are returned in alphabetical order and breadth-first.
*
* @throws SearchApiException
* If a problem occurred while retrieving the files.
*/
function search_api_solr_server_get_files(SearchApiServer $server, $dir_name = NULL) {
$response = $server->getFile($dir_name);
// Search for directories and recursively merge directory files.
$files_data = json_decode($response->data, TRUE);
$files_list = $files_data['files'];
$dir_length = strlen($dir_name) + 1;
$result = array('' => array());
foreach ($files_list as $file_name => $file_info) {
// Annoyingly, Solr 4.7 changed the way the admin/file handler returns
// the file names when listing directory contents: the returned name is now
// only the base name, not the complete path from the config root directory.
// We therefore have to check for this case.
if ($dir_name && substr($file_name, 0, $dir_length) !== "$dir_name/") {
$file_name = "$dir_name/" . $file_name;
}
if (empty($file_info['directory'])) {
$result[''][$file_name] = $file_info;
}
else {
$result[$file_name] = search_api_solr_server_get_files($server, $file_name);
}
}
ksort($result);
ksort($result['']);
return array_reduce($result, 'array_merge', array());
}
/**
* @deprecated
*
* @see search_api_solr_access_server_files()
*/
function search_api_access_server_files(SearchApiServer $server) {
return search_api_solr_access_server_files($server);
}
/**
* Access callback for a server's "Files" tab.
*
* Grants access if the user has the "administer search_api" permission and the
* server is a Solr server.
*
* @param SearchApiServer $server
* The server for which access should be tested.
*
* @return bool
* TRUE if access should be granted, FALSE otherwise.
*/
function search_api_solr_access_server_files(SearchApiServer $server) {
if (!user_access('administer search_api')) {
return FALSE;
}
$service_info = search_api_get_service_info($server->class);
$service_class = $service_info['class'];
if (empty($service_class) || !class_exists($service_class)) {
// Service class not found.
return FALSE;
}
if ($service_class == 'SearchApiSolrService' || in_array('SearchApiSolrService', class_parents($service_class))) {
// It's an SearchApiSolrService based connection class.
return TRUE;
}
return FALSE;
}
/**
* Switches a server to use clean identifiers.
*
* Used as a submit callback in SearchApiSolrService::configurationForm().
*/
function _search_api_solr_switch_to_clean_ids(array $form, array &$form_state) {
$server = $form_state['server'];
$server->options['clean_ids'] = TRUE;
$server->save();
drupal_set_message(t('The Solr server was successfully switched to use clean field identifiers.'));
$count = 0;
$conditions['server'] = $server->machine_name;
$conditions['enabled'] = 1;
foreach (search_api_index_load_multiple(FALSE, $conditions) as $index) {
if (!empty($index->options['fields'])) {
foreach ($index->options['fields'] as $key => $field) {
if (strpos($key, ':') !== FALSE) {
$index->reindex();
++$count;
break;
}
}
}
}
if ($count) {
$msg = format_plural($count, '1 index was scheduled for re-indexing.', '@count indexes were scheduled for re-indexing.');
drupal_set_message($msg);
}
}
/**
* Switches a server to multi-site compatibility mode.
*
* Used as a submit callback in SearchApiSolrService::configurationForm().
*/
function _search_api_solr_switch_to_site_hash(array $form, array &$form_state) {
$server = $form_state['server'];
try {
$conditions['server'] = $server->machine_name;
$indexes = search_api_index_load_multiple(FALSE, $conditions);
if ($indexes) {
foreach ($indexes as $index) {
$index->reindex();
}
$msg = format_plural(count($indexes), '1 index was cleared.', '@count indexes were cleared.');
$server->deleteItems('index_id:(' . implode(' ', array_keys($indexes)) . ')');
drupal_set_message($msg);
}
}
catch (SearchApiException $e) {
$variables = array('@server' => $server->name);
watchdog_exception('search_api_solr', $e, '%type while attempting to enable multi-site compatibility mode for Solr server @server: !message in %function (line %line of %file).', $variables);
drupal_set_message(t('An error occured while attempting to enable multi-site compatibility mode for Solr server @server. Check the logs for details.', $variables), 'error');
return;
}
$server->options['site_hash'] = TRUE;
$server->save();
drupal_set_message(t('The Solr server was successfully switched to multi-site compatibility mode.'));
}