Subversion Repositories Web Services

Rev

Rev 117066 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
4363 fvs 1
<?php
26820 stp 2
//-----------------------------------------------------------------------------
4363 fvs 3
/**
6681 fvs 4
 *
7058 fvs 5
 * This file is part of Open Library System.
96852 fvs 6
 * Copyright (c) 2009, Dansk Bibliotekscenter a/s,
4363 fvs 7
 * Tempovej 7-11, DK-2750 Ballerup, Denmark. CVR: 15149043
8
 *
7058 fvs 9
 * Open Library System is free software: you can redistribute it and/or modify
4363 fvs 10
 * it under the terms of the GNU Affero General Public License as published by
11
 * the Free Software Foundation, either version 3 of the License, or
12
 * (at your option) any later version.
13
 *
7058 fvs 14
 * Open Library System is distributed in the hope that it will be useful,
4363 fvs 15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU Affero General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Affero General Public License
7058 fvs 20
 * along with Open Library System.  If not, see <http://www.gnu.org/licenses/>.
4363 fvs 21
*/
22
 
26820 stp 23
//-----------------------------------------------------------------------------
28313 fvs 24
require_once('OLS_class_lib/webServiceServer_class.php');
25
require_once 'OLS_class_lib/memcache_class.php';
70547 fvs 26
require_once 'OLS_class_lib/solr_query_class.php';
97700 fvs 27
require_once 'OLS_class_lib/open_agency_v2_class.php';
4363 fvs 28
 
26820 stp 29
//-----------------------------------------------------------------------------
6871 fvs 30
class openSearch extends webServiceServer {
97700 fvs 31
  protected $open_agency;
78989 fvs 32
  protected $agency;
41279 fvs 33
  protected $cql2solr;
37396 fvs 34
  protected $curl;
35
  protected $cache;
70547 fvs 36
  protected $search_profile;
37
  protected $repository_name;
108627 fvs 38
  protected $repository; // array containing solr and record_repo uri's
70547 fvs 39
  protected $query_language = 'cqleng';
108627 fvs 40
  protected $number_of_record_repo_calls = 0;
41
  protected $number_of_record_repo_cached = 0;
76551 fvs 42
  protected $agency_catalog_source = '';
79541 fvs 43
  protected $agency_type = '';
70598 fvs 44
  protected $filter_agency = '';
45
  protected $format = '';
46
  protected $which_rec_id = '';
70547 fvs 47
  protected $separate_field_query_style = TRUE; // seach as field:(a OR b) ie FALSE or (field:a OR field:b) ie TRUE
48
  protected $valid_relation = array();
79541 fvs 49
  protected $searchable_source = array();
100210 fvs 50
  protected $searchable_forskningsbibliotek = FALSE;
105426 fvs 51
  protected $search_filter_for_800000 = array();  // set when collection 800000-danbib or 800000-bibdk are searchable
111942 fvs 52
  protected $search_profile_contains_800000 = FALSE;
81538 fvs 53
  protected $collection_contained_in = array();
70547 fvs 54
  protected $rank_frequence_debug;
77742 fvs 55
  protected $collection_alias = array();
78642 fvs 56
  protected $agency_priority_list = array();  // prioritised list af agencies for the actual agency
108627 fvs 57
  protected $unit_fallback = array();     // if record_repo is updated and solr is not, this is used to find some record from the old unit
81830 fvs 58
  protected $feature_sw = array();
104508 fvs 59
  protected $add_collection_with_relation_to_filter = FALSE;
115455 fvs 60
  protected $user_param;
4363 fvs 61
 
70547 fvs 62
 
37396 fvs 63
  public function __construct() {
64
    webServiceServer::__construct('opensearch.ini');
7402 fvs 65
 
37396 fvs 66
    $this->curl = new curl();
108787 fvs 67
    $this->curl->set_option(CURLOPT_TIMEOUT, self::value_or_default($this->config->get_value('curl_timeout', 'setup'), 20));
97700 fvs 68
    $this->open_agency = new OpenAgency($this->config->get_value('agency', 'setup'));
7402 fvs 69
 
108828 fvs 70
    define('FIELD_UNIT_ID', self::value_or_default($this->config->get_value('field_unit_id', 'setup'), 'unit.id'));
71
    define('FIELD_FEDORA_PID', self::value_or_default($this->config->get_value('field_fedora_pid', 'setup'), 'fedoraPid'));
72
    define('FIELD_WORK_ID', self::value_or_default($this->config->get_value('field_work_id', 'setup'), 'rec.workId'));
114817 fvs 73
    define('HOLDINGS_AGENCY_ID_FIELD', self::value_or_default($this->config->get_value('field_holdings_agency_id', 'setup'), 'rec.holdingsAgencyId'));
108828 fvs 74
 
75
    define('DEBUG_ON', $this->debug);
83315 fvs 76
    define('MAX_IDENTICAL_RELATIONS', self::value_or_default($this->config->get_value('max_identical_relation_names', 'setup'), 20));
77
    define('MAX_OBJECTS_IN_WORK', 100);
78620 fvs 78
    define('AND_OP', ' AND ');
79
    define('OR_OP', ' OR ');
89934 fvs 80
    define('RR_MARC_001_A', 'marc.001a');
81
    define('RR_MARC_001_B', 'marc.001b');
37396 fvs 82
  }
7402 fvs 83
 
70547 fvs 84
  /** \brief Entry search: Handles the request and set up the response
85
   *
86
   */
7402 fvs 87
 
37396 fvs 88
  public function search($param) {
115455 fvs 89
    $this->user_param = $param;
37396 fvs 90
    // set some defines
91
    if (!$this->aaa->has_right('opensearch', 500)) {
100089 fvs 92
      Object::set_value($ret_error->searchResponse->_value, 'error', 'authentication_error');
37396 fvs 93
      return $ret_error;
94
    }
95
 
96
    // check for unsupported stuff
97
    $ret_error->searchResponse->_value->error->_value = &$unsupported;
98
    if (empty($param->query->_value)) {
99
      $unsupported = 'Error: No query found in request';
100
    }
70547 fvs 101
    if ($repository_error = self::set_repositories($param->repository->_value)) {
102
      $unsupported = $repository_error;
39676 fvs 103
    }
104
 
33018 fvs 105
// for testing and group all
37396 fvs 106
    if (count($this->aaa->aaa_ip_groups) == 1 && $this->aaa->aaa_ip_groups['all']) {
100089 fvs 107
      Object::set_value($param, 'agency', '100200');
108
      Object::set_value($param, 'profile', 'test');
37396 fvs 109
    }
110
    if (empty($param->agency->_value) && empty($param->profile->_value)) {
100089 fvs 111
      Object::set_value($param, 'agency', $this->config->get_value('agency_fallback', 'setup'));
112
      Object::set_value($param, 'profile', $this->config->get_value('profile_fallback', 'setup'));
37396 fvs 113
    }
114
    if (empty($param->agency->_value)) {
115
      $unsupported = 'Error: No agency in request';
116
    }
116655 fvs 117
    elseif (strtolower($param->sort->_value) == 'random') {
116291 fvs 118
      $unsupported = 'Error: random sort is currently disabled';
119
    }
37396 fvs 120
    elseif (empty($param->profile->_value)) {
121
      $unsupported = 'Error: No profile in request';
122
    }
70547 fvs 123
    elseif (!($this->search_profile = self::fetch_profile_from_agency($param->agency->_value, $param->profile->_value))) {
37396 fvs 124
      $unsupported = 'Error: Cannot fetch profile: ' . $param->profile->_value .
125
                     ' for ' . $param->agency->_value;
126
    }
70547 fvs 127
    if ($unsupported) return $ret_error;
78989 fvs 128
 
129
    $this->agency = $param->agency->_value;
104295 fvs 130
    $this->agency_catalog_source = $this->agency . '-katalog';
70598 fvs 131
    $this->filter_agency = self::set_solr_filter($this->search_profile);
104295 fvs 132
    $this->split_holdings_include = self::split_collections_for_holdingsitem($this->search_profile);
78620 fvs 133
    self::set_valid_relations_and_sources($this->search_profile);
106624 fvs 134
    self::set_search_filters_for_800000_collection($param->forceFilter->_value);
28414 fvs 135
 
81830 fvs 136
    $this->feature_sw = $this->config->get_value('feature_switch', 'setup');
137
 
37396 fvs 138
    $use_work_collection = ($param->collectionType->_value <> 'manifestation');
82884 fvs 139
    if ($use_work_collection) {
83315 fvs 140
      define('MAX_STEP_VALUE', self::value_or_default($this->config->get_value('max_collections', 'setup'), 50));
82884 fvs 141
    }
142
    else {
83315 fvs 143
      define('MAX_STEP_VALUE', self::value_or_default($this->config->get_value('max_manifestations', 'setup'), 200));
82884 fvs 144
    }
25396 pjo 145
 
74010 fvs 146
    $sort = array();
76388 fvs 147
    $rank_types = $this->config->get_value('rank', 'setup');
74010 fvs 148
    if (!self::parse_for_ranking($param, $rank, $rank_types)) {
149
      if ($unsupported = self::parse_for_sorting($param, $sort, $sort_types)) {
150
        return $ret_error;
151
      }
37396 fvs 152
    }
77728 fvs 153
    $boost_q = self::boostUrl($param->userDefinedBoost);
28441 fvs 154
 
70598 fvs 155
    $this->format = self::set_format($param->objectFormat, $this->config->get_value('open_format', 'setup'), $this->config->get_value('solr_format', 'setup'));
74010 fvs 156
 
37396 fvs 157
    if ($unsupported) return $ret_error;
6681 fvs 158
 
71321 fvs 159
    $ret_error->searchResponse->_value->error->_value = &$error;
160
    $start = $param->start->_value;
82884 fvs 161
    $step_value = min($param->stepValue->_value, MAX_STEP_VALUE);
71321 fvs 162
    if (empty($start) && $step_value) {
163
      $start = 1;
164
    }
73590 fvs 165
    if ($param->queryLanguage->_value) {
166
      $this->query_language = $param->queryLanguage->_value;
167
    }
168
    $debug_query = $this->xs_boolean($param->queryDebug->_value);
79541 fvs 169
    $this->agency_type = self::get_agency_type($this->agency);
71321 fvs 170
 
104508 fvs 171
// for future use ...  var_dump($this->open_agency->get_libraries_by_rule('use_holdings_item', 1, 'Folkebibliotek')); die();
71321 fvs 172
    if ($us_settings = $this->repository['universal']) {
173
      require_once 'OLS_class_lib/universal_search_class.php';
174
      $this->watch->start('UniSea');
175
      $universal = new UniversalSearch($this->config->get_section($us_settings), $this->xmlns['mx']);
176
      $collections = $universal->search($param->query->_value, $start, $step_value);
177
      $this->watch->stop('UniSea');
178
      if (is_scalar($collections)) {
179
        $error = $collections;
180
        return $ret_error;
181
      }
182
      if (is_array($collections)) {
183
        self::format_records($collections);
184
      }
185
      $result = &$ret->searchResponse->_value->result->_value;
100089 fvs 186
      Object::set_value($result, 'hitCount', $universal->get_hits());
187
      Object::set_value($result, 'collectionCount', count($collections));
188
      Object::set_value($result, 'more', (($start + $step_value) <= $result->hitCount->_value ? 'true' : 'false'));
71321 fvs 189
      $result->searchResult = &$collections;
100089 fvs 190
      Object::set_value($result->statInfo->_value, 'time', $this->watch->splittime('Total'));
112468 fvs 191
      Object::set_value($result->statInfo->_value, 'trackingId', verbose::$tracking_id);
71321 fvs 192
      return $ret;
193
    }
194
 
89889 fvs 195
    if ($this->repository['postgress'] || $this->repository['rawrepo']) {
94341 fvs 196
      $this->watch->start('rawrepo');
74792 fvs 197
      $this->cql2solr = new SolrQuery($this->repository, $this->config, $this->query_language);
94242 fvs 198
      $this->watch->start('cql');
73590 fvs 199
      $solr_query = $this->cql2solr->parse($param->query->_value);
94242 fvs 200
      $this->watch->stop('cql');
73590 fvs 201
      if ($solr_query['error']) {
202
        $error = self::cql2solr_error_to_string($solr_query['error']);
203
        return $ret_error;
204
      }
205
      verbose::log(TRACE, 'CQL to SOLR: ' . $param->query->_value . ' -> ' . preg_replace('/\s+/', ' ', print_r($solr_query, TRUE)));
78620 fvs 206
      $q = implode(AND_OP, $solr_query['edismax']['q']);
96193 fvs 207
      if (!in_array($this->agency, self::value_or_default($this->config->get_value('all_rawrepo_agency', 'setup'), array()))) {
208
        $filter = rawurlencode(RR_MARC_001_B . ':(870970 OR ' . $this->agency . ')');
209
      }
73590 fvs 210
      foreach ($solr_query['edismax']['fq'] as $fq) {
211
        $filter .= '&fq=' . rawurlencode($fq);
212
      }
213
      $solr_urls[0]['url'] = $this->repository['solr'] .
214
                    '?q=' . urlencode($q) .
215
                    '&fq=' . $filter .
216
                    '&start=' . ($start - 1).  
217
                    '&rows=' . $step_value .  
218
                    '&defType=edismax&wt=phps&fl=' . ($debug_query ? '&debugQuery=on' : '');
81538 fvs 219
      $solr_urls[0]['debug'] = str_replace('wt=phps', 'wt=xml', $solr_urls[0]['url']);
73590 fvs 220
      if ($err = self::do_solr($solr_urls, $solr_arr)) {
221
        $error = $err;
222
        return $ret_error;
223
      }
89889 fvs 224
      $s11_agency = self::value_or_default($this->config->get_value('s11_agency', 'setup'), array());
225
      if ($this->repository['rawrepo']) {
89928 fvs 226
        $collections = self::get_records_from_rawrepo($this->repository['rawrepo'], $solr_arr['response'], in_array($this->agency, $s11_agency));
89889 fvs 227
      }
228
      else {
89928 fvs 229
        $collections = self::get_records_from_postgress($this->repository['postgress'], $solr_arr['response'], in_array($this->agency, $s11_agency));
89889 fvs 230
      }
94341 fvs 231
      $this->watch->stop('rawrepo');
73590 fvs 232
      if (is_scalar($collections)) {
233
        $error = $collections;
234
        return $ret_error;
235
      }
236
      $result = &$ret->searchResponse->_value->result->_value;
100089 fvs 237
      Object::set_value($result, 'hitCount', self::get_num_found($solr_arr));
238
      Object::set_value($result, 'collectionCount', count($collections));
239
      Object::set_value($result, 'more', (($start + $step_value) <= $result->hitCount->_value ? 'true' : 'false'));
73590 fvs 240
      $result->searchResult = &$collections;
100089 fvs 241
      Object::set_value($result->statInfo->_value, 'time', $this->watch->splittime('Total'));
112468 fvs 242
      Object::set_value($result->statInfo->_value, 'trackingId', verbose::$tracking_id);
73590 fvs 243
      if ($debug_query) {
100089 fvs 244
        Object::set_value($result, 'queryDebugResult', self::set_debug_info($solr_arr['debug']));
73590 fvs 245
      }
246
      return $ret;
247
    }
71321 fvs 248
 
37396 fvs 249
    /**
79888 fvs 250
    *  Approach \n
251
    *  a) Do the solr search and fetch enough unit-ids in result \n
70547 fvs 252
    *  b) Fetch a unit-ids work-object unless the record has been found
79888 fvs 253
    *     in an earlier handled work-objects \n
254
    *  c) Collect unit-ids in this work-object \n
255
    *  d) repeat b. and c. until the requested number of objects is found \n
71321 fvs 256
    *  e) if allObject is not set, do a new search combining the users search
70547 fvs 257
    *     with an or'ed list of the unit-ids in the active objects and
79888 fvs 258
    *     remove the unit-ids not found in the result \n
108627 fvs 259
    *  f) Read full records from record_repo for objects in result or fetch display-fields
79888 fvs 260
    *     from solr, depending on the selected format \n
37396 fvs 261
    *
262
    *  if $use_work_collection is FALSE skip b) to e)
263
    */
7402 fvs 264
 
70547 fvs 265
    $use_work_collection |= $sort_types[$sort[0]] == 'random';
108627 fvs 266
    $key_work_struct = md5($param->query->_value . $this->repository_name . $this->filter_agency . self::xs_boolean($param->allObjects->_value) .
77728 fvs 267
                           $use_work_collection .  implode('', $sort) . $rank . $boost_q . $this->config->get_inifile_hash());
37396 fvs 268
 
104295 fvs 269
    $this->cql2solr = new SolrQuery($this->repository, $this->config, $this->query_language, $this->split_holdings_include);
94242 fvs 270
    $this->watch->start('cql');
106624 fvs 271
    if ($param->skipFilter->_value == '1')    // for test
105426 fvs 272
      $solr_query = $this->cql2solr->parse($param->query->_value);
273
    else
274
      $solr_query = $this->cql2solr->parse($param->query->_value, $this->search_filter_for_800000);
114817 fvs 275
    self::modify_query_and_filter_agency($solr_query);
105426 fvs 276
//var_dump($solr_query); var_dump($this->split_holdings_include); var_dump($this->search_filter_for_800000); die();
94242 fvs 277
    $this->watch->stop('cql');
70547 fvs 278
    if ($solr_query['error']) {
73590 fvs 279
      $error = self::cql2solr_error_to_string($solr_query['error']);
70547 fvs 280
      return $ret_error;
281
    }
70598 fvs 282
    if (!count($solr_query['operands'])) {
37396 fvs 283
      $error = 'Error: No query found in request';
284
      return $ret_error;
285
    }
6661 fvs 286
 
70598 fvs 287
    if ($this->filter_agency) {
288
      $filter_q = rawurlencode($this->filter_agency);
37396 fvs 289
    }
290
 
76388 fvs 291
    //var_dump($solr_query); die();
292
    if (is_array($solr_query['edismax']['ranking'])) {
76409 fvs 293
      if (!$rank_cql = $rank_types['rank_cql'][reset($solr_query['edismax']['ranking'])]) {
294
        $rank_cql = $rank_types['rank_cql']['default'];
76388 fvs 295
      }
76409 fvs 296
      if ($rank_cql) {
297
        $rank = $rank_cql;
298
      }
76388 fvs 299
    }
70547 fvs 300
    if ($this->query_language == 'bestMatch') {
301
      $sort_q .= '&mm=1';
70598 fvs 302
      $solr_query['edismax'] = $solr_query['best_match'];
303
      foreach ($solr_query['best_match']['sort'] as $key => $val) {
70547 fvs 304
        $sort_q .= '&' . $key . '=' . urlencode($val);
100110 fvs 305
        Object::set_value($best_match_debug, $key, $val);
70547 fvs 306
      }
307
    }
308
    elseif ($sort) {
309
      foreach ($sort as $s) {
310
        $ss[] = urlencode($sort_types[$s]);
311
      }
312
      $sort_q = '&sort=' . implode(',', $ss);
313
    }
314
    if ($rank == 'rank_frequency') {
77728 fvs 315
      if ($new_rank = self::guess_rank($solr_query, $rank_types, $filter_q)) {
70547 fvs 316
        $rank = $new_rank;
317
      }
318
      else {
319
        $rank = 'rank_none';
320
      }
321
    }
322
    if ($rank_types[$rank]) {
323
      $rank_qf = $this->cql2solr->make_boost($rank_types[$rank]['word_boost']);
324
      $rank_pf = $this->cql2solr->make_boost($rank_types[$rank]['phrase_boost']);
325
      $rank_tie = $rank_types[$rank]['tie'];
326
      $rank_q = '&qf=' . urlencode($rank_qf) .  '&pf=' . urlencode($rank_pf) .  '&tie=' . $rank_tie;
327
    }
37396 fvs 328
 
78868 fvs 329
    $facet_q = self::set_solr_facet_parameters($param->facets->_value);
330
 
108787 fvs 331
  // TODO rows should max to like 5000 and use cursorMark to page forward. cursorMark need a sort paramater to work
108627 fvs 332
    $rows = $step_value ? (($start + $step_value + 100) * 2)  + 100 : 0;
7807 fvs 333
 
70947 fvs 334
    verbose::log(TRACE, 'CQL to SOLR: ' . $param->query->_value . ' -> ' . preg_replace('/\s+/', ' ', print_r($solr_query, TRUE)));
7807 fvs 335
 
37396 fvs 336
    // do the query
108737 fvs 337
    $this->watch->start('Solr_ids');
70547 fvs 338
    if ($sort[0] == 'random') {
339
      if ($err = self::get_solr_array($solr_query['edismax'], 0, 0, '', '', $facet_q, $filter_q, '', $debug_query, $solr_arr))
37396 fvs 340
        $error = $err;
70547 fvs 341
      else {
342
        $numFound = self::get_num_found($solr_arr);
343
      }
37396 fvs 344
    }
345
    else {
108627 fvs 346
      if ($err = self::get_solr_array($solr_query['edismax'], 0, $rows, $sort_q, $rank_q, $facet_q, $filter_q, $boost_q, $debug_query, $solr_arr))
37396 fvs 347
        $error = $err;
348
      else {
70547 fvs 349
        $numFound = self::get_num_found($solr_arr);
108729 fvs 350
        if ($step_value && $numFound) {
351
          self::extract_ids_from_solr($solr_arr, $solr_work_ids);
352
          if (!count($solr_work_ids)) {
353
            $error = 'Internal error: Cannot extract any id\'s from solr';
354
          }
108685 fvs 355
        }
37396 fvs 356
      }
357
    }
108737 fvs 358
    $this->watch->stop('Solr_ids');
4363 fvs 359
 
37396 fvs 360
    if ($error) return $ret_error;
28786 fvs 361
 
37396 fvs 362
    if ($debug_query) {
76388 fvs 363
      $debug_result = self::set_debug_info($solr_arr['debug'], $this->rank_frequence_debug, $best_match_debug);
37396 fvs 364
    }
116088 fvs 365
    if ($solr_arr['debug']) {
366
       $solr_timing = $solr_arr['debug']['timing'];
367
    }
37396 fvs 368
 
108755 fvs 369
    if ($facet_q) {
370
      $facets = self::parse_for_facets($solr_arr);
371
    }
372
 
37396 fvs 373
    $this->watch->start('Build_id');
374
    $work_ids = $used_search_fids = array();
70547 fvs 375
    if ($sort[0] == 'random') {
37396 fvs 376
      $rows = min($step_value, $numFound);
377
      $more = $step_value < $numFound;
378
      for ($w_idx = 0; $w_idx < $rows; $w_idx++) {
379
        do {
380
          $no = rand(0, $numFound-1);
70547 fvs 381
        } while (isset($used_search_fid[$no]));
37396 fvs 382
        $used_search_fid[$no] = TRUE;
70547 fvs 383
        self::get_solr_array($solr_query['edismax'], $no, 1, '', '', '', $filter_q, '', $debug_query, $solr_arr);
108828 fvs 384
        $uid =  self::get_first_solr_element($solr_arr, FIELD_UNIT_ID);
70547 fvs 385
        $work_ids[] = array($uid);
37396 fvs 386
      }
387
    }
388
    else {
389
      $this->cache = new cache($this->config->get_value('cache_host', 'setup'),
390
                               $this->config->get_value('cache_port', 'setup'),
391
                               $this->config->get_value('cache_expire', 'setup'));
108627 fvs 392
      $work_cache_struct = array();
37396 fvs 393
      if (empty($_GET['skipCache'])) {
70547 fvs 394
        if ($work_cache_struct = $this->cache->get($key_work_struct)) {
70947 fvs 395
          verbose::log(TRACE, 'Cache hit, lines: ' . count($work_cache_struct));
37396 fvs 396
        }
26820 stp 397
        else {
108627 fvs 398
          $work_cache_struct = array();
93049 fvs 399
          verbose::log(TRACE, __CLASS__ . '::'. __FUNCTION__ . ' - work_struct cache miss');
37396 fvs 400
        }
401
      }
28786 fvs 402
 
108627 fvs 403
      if (DEBUG_ON) print_r($solr_work_ids);
28786 fvs 404
 
94242 fvs 405
      if (empty($step_value)) {
108627 fvs 406
        $more = ($numFound >= $start);   // no need to find records, only hit count is returned and maybe facets
94242 fvs 407
      }
408
      else {
108627 fvs 409
        if ($err = self::build_work_struct_from_solr($work_cache_struct, $work_ids, $more, $solr_work_ids, $solr_query['edismax'], $start, $step_value, $rows, $sort_q, $rank_q, $filter_q, $boost_q, $use_work_collection, self::xs_boolean($param->allObjects->_value), $numFound, $debug_query)) {
410
          $error = $err;
411
          return $ret_error;
26820 stp 412
        }
37396 fvs 413
      }
414
    }
108627 fvs 415
    $this->watch->stop('Build_id');
7402 fvs 416
 
108627 fvs 417
    if (count($work_ids) < $step_value && count($solr_work_ids) < $numFound) {
418
      verbose::log(WARNING, 'To few search_ids found in solr. Query: ' . implode(AND_OP, $solr_query['edismax']['q']));
26820 stp 419
    }
7402 fvs 420
 
70598 fvs 421
    if (DEBUG_ON) echo 'work_ids: ' . print_r($work_ids, TRUE) . "\n";
422
 
108627 fvs 423
// fetch data to sort_keys and (if needed) sorl display format(s)
424
    $q_unit = array();
425
    foreach ($work_ids as $work) {
426
      foreach ($work as $unit_id) {
427
        $q_unit[] =  '"' . $unit_id . '"';
37396 fvs 428
      }
108627 fvs 429
    }
108828 fvs 430
    $add_queries = array(FIELD_UNIT_ID . ':(' . implode(OR_OP, $q_unit) . ')');
108737 fvs 431
    $this->watch->start('Solr_disp');
111769 fvs 432
// bug 20558: Remove 'unit.isPrimaryObject=true' in order to handle situtations where the manifestation isn't the primary object. The concept of a primary object
433
// is sort of deprecated, since prioritized approach is used to find the "best" manifestaion in a unit
434
    $display_solr_arr = self::do_add_queries_and_fetch_solr_data_fields($add_queries, '*', self::xs_boolean($param->allObjects->_value), '');
108737 fvs 435
    $this->watch->stop('Solr_disp');
108627 fvs 436
        foreach ($display_solr_arr as $d_s_a) {
437
          foreach ($d_s_a['response']['docs'] as $fdoc) {
108828 fvs 438
            $u_id =  self::scalar_or_first_elem($fdoc[FIELD_UNIT_ID]);
108627 fvs 439
            $unit_sort_keys[$u_id] = $fdoc['sort.complexKey'] . '  ' . $u_id;
70547 fvs 440
          }
26820 stp 441
        }
108627 fvs 442
//var_dump($add_queries); var_dump($display_solr_arr); var_dump($unit_sort_keys); die();
443
    define('MAX_QUERY_ELEMENTS', 950);
37396 fvs 444
 
116655 fvs 445
    if ($this->cache && $this->cache->check()) {
108755 fvs 446
      verbose::log(TRACE, 'Cache set, # work: ' . count($work_cache_struct));
70547 fvs 447
      $this->cache->set($key_work_struct, $work_cache_struct);
108627 fvs 448
    }
37396 fvs 449
 
450
    // work_ids now contains the work-records and the fedoraPids they consist of
451
    // now fetch the records for each work/collection
452
    $collections = array();
453
    $rec_no = max(1, $start);
70547 fvs 454
    $HOLDINGS = ' holdings ';
99272 fvs 455
    $use_sort_complex_key = in_array($this->agency, self::value_or_default($this->config->get_value('use_sort_complex_key', 'setup'), array()));
86565 fvs 456
    $this->agency_priority_list = self::fetch_agency_show_priority();
81830 fvs 457
    if ($debug_query) {
458
      $explain_keys = array_keys($solr_arr['debug']['explain']);
459
    }
108627 fvs 460
 
461
    // fetch all addi and hierarchi records for all units in work_ids
462
    foreach ($work_ids as $idx => &$work) {
85870 fvs 463
      if (count($work) >= MAX_OBJECTS_IN_WORK) {
108627 fvs 464
        verbose::log(WARNING, 'record_repo work-record containing: ' . reset($work) . ' contains ' . count($work) . ' units. Cut work to first ' . MAX_OBJECTS_IN_WORK . ' units');
85870 fvs 465
        array_splice($work, MAX_OBJECTS_IN_WORK);
466
      }
70547 fvs 467
      foreach ($work as $unit_id) {
108627 fvs 468
        $repo_urls[$unit_id . '-addi'] = self::record_repo_url('fedora_get_rels_addi', $unit_id);
469
        $repo_urls[$unit_id . '-hierarchy'] = self::record_repo_url('fedora_get_rels_hierarchy', $unit_id);
470
      }
471
    }
472
    $repo_res = self::read_record_repo_all_urls($repo_urls);
473
    //var_dump($repo_urls); var_dump($res_map); var_dump($repo_res); die();
474
 
475
    // find and read root best record in unit's
476
    foreach ($work_ids as &$work) {
477
      foreach ($work as $unit_id) {
478
        $unit_rels_hierarchy = $repo_res[$unit_id . '-hierarchy'];
479
        $unit_info[$unit_id] = self::parse_unit_for_best_agency($unit_rels_hierarchy, $unit_id, FALSE);
480
        list($unit_members, $fpid, $localdata_in_pid, $primary_oid) = $unit_info[$unit_id];
481
        list($pid, $datastream)  = self::create_fedora_pid_and_stream($fpid, $localdata_in_pid);
482
        $raw_urls[$unit_id] = self::record_repo_url('fedora_get_raw', $pid, $datastream);
483
      }
484
    }
485
    $raw_res = self::read_record_repo_all_urls($raw_urls);
486
//var_dump($raw_urls); var_dump($raw_res); die();
487
 
488
    // find number og holding, sort_key and include relations
489
    foreach ($work_ids as &$work) {
490
      $objects = array();
491
      foreach ($work as $unit_id) {
113442 fvs 492
        list($unit_members, $fpid, $localdata_in_pid, $primary_oid, $in_870970_basis) = $unit_info[$unit_id];
70547 fvs 493
        $sort_holdings = ' ';
494
        unset($no_of_holdings);
495
        if (self::xs_boolean($param->includeHoldingsCount->_value)) {
496
          $no_of_holdings = self::get_holdings($fpid);
497
        }
99272 fvs 498
        if ($use_sort_complex_key && (strpos($unit_sort_keys[$unit_id], $HOLDINGS) !== FALSE)) {
70547 fvs 499
          $holds = isset($no_of_holdings) ? $no_of_holdings : self::get_holdings($fpid);
108800 fvs 500
          $sort_holdings = sprintf(' %04d ', 9999 - intval($holds['lend']));
70547 fvs 501
        }
502
        $fpid_sort_keys[$fpid] = str_replace($HOLDINGS, $sort_holdings, $unit_sort_keys[$unit_id]);
503
        if ($debug_query) {
504
          unset($explain);
108627 fvs 505
          foreach ($solr_arr['response']['docs'] as $solr_idx => $solr_rec) {
108828 fvs 506
            if ($unit_id == $solr_rec[FIELD_UNIT_ID]) {
108627 fvs 507
              $explain = $solr_arr['debug']['explain'][$explain_keys[$solr_idx]];
508
              break;
37396 fvs 509
            }
70547 fvs 510
          }
28414 fvs 511
        }
70547 fvs 512
        $sort_key = $fpid_sort_keys[$fpid] . ' ' . sprintf('%04d', count($objects));
513
        $sorted_work[$sort_key] = $unit_id;
100089 fvs 514
        $objects[$sort_key] = new stdClass();
515
        $objects[$sort_key]->_value =
108627 fvs 516
          self::parse_record_repo_object($raw_res[$unit_id],
517
                                    $repo_res[$unit_id . '-addi'],
79644 fvs 518
                                    $unit_members,
113442 fvs 519
                                    $in_870970_basis,
70547 fvs 520
                                    $param->relationData->_value,
521
                                    $fpid,
85002 fvs 522
                                    $primary_oid,
70547 fvs 523
                                    NULL, // no $filter_agency on search - bad performance
524
                                    $no_of_holdings,
525
                                    $explain);
37396 fvs 526
      }
70547 fvs 527
      $work = $sorted_work;
528
      if (DEBUG_ON) print_r($sorted_work);
529
      unset($sorted_work);
100089 fvs 530
      Object::set_value($o->collection->_value, 'resultPosition', $rec_no++);
531
      Object::set_value($o->collection->_value, 'numberOfObjects', count($objects));
108730 fvs 532
      if (count($objects) > 1) {
70547 fvs 533
        ksort($objects);
534
      }
37396 fvs 535
      $o->collection->_value->object = $objects;
100089 fvs 536
      Object::set($collections[], '_value', $o);
37396 fvs 537
      unset($o);
5182 fvs 538
    }
70547 fvs 539
    if (DEBUG_ON) print_r($unit_sort_keys);
540
    if (DEBUG_ON) print_r($fpid_sort_keys);
5182 fvs 541
 
70547 fvs 542
    if ($param->collectionType->_value == 'work-1') {
543
      foreach ($collections as &$c) {
544
        $collection_no = 0;
545
        foreach ($c->_value->collection->_value->object as &$o) {
546
          if ($collection_no++) {
547
            foreach ($o->_value as $tag => $val) {
548
              if (!in_array($tag, array('identifier', 'creationDate', 'formatsAvailable'))) {
549
                unset($o->_value->$tag);
550
              }
551
            }
552
          }
553
        }
554
      }
39165 fvs 555
    }
25335 fvs 556
 
70547 fvs 557
    if ($step_value) {
70598 fvs 558
      if ($this->format['found_open_format']) {
559
        self::format_records($collections);
70547 fvs 560
      }
70598 fvs 561
      if ($this->format['found_solr_format']) {
71090 fvs 562
        self::format_solr($collections, $display_solr_arr, $work_ids, $fpid_sort_keys);
70547 fvs 563
      }
70598 fvs 564
      self::remove_unselected_formats($collections);
70547 fvs 565
    }
566
 
567
//var_dump($solr_2_arr);
568
//var_dump($work_cache_struct);
569
//die();
37396 fvs 570
    if ($_REQUEST['work'] == 'debug') {
571
      echo "returned_work_ids: \n";
572
      print_r($work_ids);
573
      echo "cache: \n";
70547 fvs 574
      print_r($work_cache_struct);
37396 fvs 575
      die();
36057 fvs 576
    }
70547 fvs 577
    //if (DEBUG_ON) { print_r($work_cache_struct); die(); }
37396 fvs 578
    //if (DEBUG_ON) { print_r($collections); die(); }
579
    //if (DEBUG_ON) { print_r($solr_arr); die(); }
36057 fvs 580
 
37396 fvs 581
    $result = &$ret->searchResponse->_value->result->_value;
100089 fvs 582
    Object::set_value($result, 'hitCount', $numFound);
583
    Object::set_value($result, 'collectionCount', count($collections));
584
    Object::set_value($result, 'more', ($more ? 'true' : 'false'));
76725 fvs 585
    self::set_sortUsed($result, $rank, $sort, $sort_types);
37396 fvs 586
    $result->searchResult = $collections;
100089 fvs 587
    Object::set_value($result, 'facetResult', $facets);
75987 fvs 588
    if ($debug_query && $debug_result) {
100089 fvs 589
      Object::set_value($result, 'queryDebugResult', $debug_result);
75987 fvs 590
    }
116088 fvs 591
    if ($solr_timing) {
592
      verbose::log(STAT, 'solrTiming: ' . json_encode($solr_timing));
593
    }
108627 fvs 594
    Object::set_value($result->statInfo->_value, 'fedoraRecordsCached', $this->number_of_record_repo_cached);
595
    Object::set_value($result->statInfo->_value, 'fedoraRecordsRead', $this->number_of_record_repo_calls);
100089 fvs 596
    Object::set_value($result->statInfo->_value, 'time', $this->watch->splittime('Total'));
112468 fvs 597
    Object::set_value($result->statInfo->_value, 'trackingId', verbose::$tracking_id);
37396 fvs 598
 
78508 fvs 599
    verbose::log(STAT, sprintf($this->dump_timer, $this->soap_action) .  
79541 fvs 600
                       ':: agency:' . $this->agency .
78508 fvs 601
                       ' profile:' . $param->profile->_value .
79541 fvs 602
                       ' ip:' . $_SERVER['REMOTE_ADDR'] .
115455 fvs 603
                       ' repoRecs:' . $this->number_of_record_repo_calls .
604
                       ' repoCache:' . $this->number_of_record_repo_cached .
85876 fvs 605
                       ' ' . str_replace(PHP_EOL, '', $this->watch->dump()) .
606
                       ' query:' . $param->query->_value . PHP_EOL);
78508 fvs 607
 
37396 fvs 608
    return $ret;
609
  }
610
 
611
 
70547 fvs 612
  /** \brief Entry getObject: Get an object in a specific format
37396 fvs 613
  *
79888 fvs 614
  * param: agency: \n
615
  *        profile:\n
616
  *        identifier - fedora pid\n
617
  *        objectFormat - one of dkabm, docbook, marcxchange, opensearchobject\n
618
  *        includeHoldingsCount - boolean\n
619
  *        relationData - type, uri og full\n
620
  *        repository\n
78868 fvs 621
  *
622
  * @param object $param - the user request
623
  * @retval object - the answer to the request
37396 fvs 624
  */
625
  public function getObject($param) {
115455 fvs 626
    $this->user_param = $param;
37396 fvs 627
    $ret_error->searchResponse->_value->error->_value = &$error;
628
    if (!$this->aaa->has_right('opensearch', 500)) {
629
      $error = 'authentication_error';
630
      return $ret_error;
36057 fvs 631
    }
70547 fvs 632
    if ($error = self::set_repositories($param->repository->_value)) {
39676 fvs 633
      verbose::log(FATAL, $error);
634
      return $ret_error;
635
    }
37396 fvs 636
    if (empty($param->agency->_value) && empty($param->profile->_value)) {
100089 fvs 637
      Object::set_value($param, 'agency', $this->config->get_value('agency_fallback', 'setup'));
638
      Object::set_value($param, 'profile', $this->config->get_value('profile_fallback', 'setup'));
37396 fvs 639
    }
78989 fvs 640
    if ($this->agency = $param->agency->_value) {
104553 fvs 641
      $this->add_collection_with_relation_to_filter  = TRUE; // Add this to expand getObject to "all" collections
37396 fvs 642
      if ($param->profile->_value) {
78989 fvs 643
        if (!($this->search_profile = self::fetch_profile_from_agency($this->agency, $param->profile->_value))) {
644
          $error = 'Error: Cannot fetch profile: ' . $param->profile->_value . ' for ' . $this->agency;
37396 fvs 645
          return $ret_error;
646
        }
647
      }
108808 fvs 648
      if (!$this->filter_agency = self::set_solr_filter($this->search_profile)) {
78989 fvs 649
        $error = 'Error: Unknown agency: ' . $this->agency;
37396 fvs 650
        return $ret_error;
651
      }
108808 fvs 652
      self::set_valid_relations_and_sources($this->search_profile);
653
      self::set_search_filters_for_800000_collection();
37396 fvs 654
    }
70598 fvs 655
    if ($this->filter_agency) {
656
      $filter_q = rawurlencode($this->filter_agency);
70547 fvs 657
    }
40435 fvs 658
 
81830 fvs 659
    $this->feature_sw = $this->config->get_value('feature_switch', 'setup');
660
 
113331 fvs 661
    $this->agency_catalog_source = $this->agency . '-katalog';
79541 fvs 662
    $this->agency_type = self::get_agency_type($this->agency);
86565 fvs 663
    $this->agency_priority_list = self::fetch_agency_show_priority();
70598 fvs 664
    $this->format = self::set_format($param->objectFormat,
70547 fvs 665
                               $this->config->get_value('open_format', 'setup'),
666
                               $this->config->get_value('solr_format', 'setup'));
37396 fvs 667
    $this->cache = new cache($this->config->get_value('cache_host', 'setup'),
668
                             $this->config->get_value('cache_port', 'setup'),
669
                             $this->config->get_value('cache_expire', 'setup'));
70547 fvs 670
 
96992 fvs 671
    $fpids = self::as_array($param->identifier);
672
    $lpids = self::as_array($param->localIdentifier);
673
    $alpids = self::as_array($param->agencyAndLocalIdentifier);
78620 fvs 674
 
83584 fvs 675
    define('MAX_STEP_VALUE', self::value_or_default($this->config->get_value('max_manifestations', 'setup'), 200));
96992 fvs 676
    if (MAX_STEP_VALUE <= count($fpids) + count($lpids) + count($alpids)) {
83584 fvs 677
      $error = 'getObject can fetch up to ' . MAX_STEP_VALUE . ' records. ';
678
      return $ret_error;
679
    }
680
 
70598 fvs 681
    if ($this->format['found_solr_format']) {
682
      foreach ($this->format as $f) {
70547 fvs 683
        if ($f['is_solr_format']) {
684
          $add_fl .= ',' . $f['format_name'];
685
        }
686
      }
687
    }
96992 fvs 688
    foreach ($lpids as $lid) {
100089 fvs 689
      $fpid = new stdClass();
96992 fvs 690
      $fpid->_value = $this->agency_catalog_source . ':' . str_replace(' ', '', $lid->_value);
691
      $fpids[] = $fpid;
692
      unset($fpid);
693
    }
97079 fvs 694
    if (!empty($alpids)) {
695
      $agency_to_collection = self::value_or_default($this->config->get_value('agency_to_collection', 'setup'), array());
696
      foreach ($alpids as $alid) {
697
        if (!$collection = $agency_to_collection[$alid->_value->agency->_value]) {
698
          $collection = 'katalog';
699
        }
100089 fvs 700
        $fpid = new stdClass();
97079 fvs 701
        $fpid->_value = $alid->_value->agency->_value . '-' . $collection . ':' . str_replace(' ', '', $alid->_value->localIdentifier->_value);
702
        $fpids[] = $fpid;
703
        unset($fpid);
704
      }
96992 fvs 705
    }
89889 fvs 706
    if ($this->repository['postgress'] || $this->repository['rawrepo']) {
78620 fvs 707
      foreach ($fpids as $fpid) {
74849 fvs 708
        list($owner_collection, $id) = explode(':', $fpid->_value);
709
        list($owner, $coll) = explode('-', $owner_collection);
96193 fvs 710
        if (($owner == $this->agency)
711
         || ($owner == '870970')
712
         || in_array($this->agency, self::value_or_default($this->config->get_value('all_rawrepo_agency', 'setup'), array()))) {
95989 fvs 713
          $docs['docs'][] = array(RR_MARC_001_A => $id, RR_MARC_001_B => $owner);
714
        }
74849 fvs 715
      }
89889 fvs 716
      $s11_agency = self::value_or_default($this->config->get_value('s11_agency', 'setup'), array());
717
      if ($this->repository['rawrepo']) {
718
        $collections = self::get_records_from_rawrepo($this->repository['rawrepo'], $docs, in_array($this->agency, $s11_agency));
719
      }
720
      else {
721
        $collections = self::get_records_from_postgress($this->repository['postgress'], $docs, in_array($this->agency, $s11_agency));
722
      }
74849 fvs 723
      if (is_scalar($collections)) {
724
        $error = $collections;
725
        return $ret_error;
726
      }
727
      $result = &$ret->searchResponse->_value->result->_value;
100089 fvs 728
      Object::set_value($result, 'hitCount', count($collections));
729
      Object::set_value($result, 'collectionCount', count($collections));
730
      Object::set_value($result, 'more', 'false');
74849 fvs 731
      $result->searchResult = &$collections;
100089 fvs 732
      Object::set_value($result->statInfo->_value, 'time', $this->watch->splittime('Total'));
112468 fvs 733
      Object::set_value($result->statInfo->_value, 'trackingId', verbose::$tracking_id);
74849 fvs 734
      if ($debug_query) {
100089 fvs 735
        Object::set_value($debug_result, 'rawQueryString', $solr_arr['debug']['rawquerystring']);
736
        Object::set_value($debug_result, 'queryString', $solr_arr['debug']['querystring']);
737
        Object::set_value($debug_result, 'parsedQuery', $solr_arr['debug']['parsedquery']);
738
        Object::set_value($debug_result, 'parsedQueryString', $solr_arr['debug']['parsedquery_toString']);
739
        Object::set_value($result, 'queryDebugResult', $debug_result);
74849 fvs 740
      }
741
      return $ret;
742
 
743
    }
78620 fvs 744
    foreach ($fpids as $fpid) {
70547 fvs 745
      $id_array[] = $fpid->_value;
84341 fvs 746
      list($owner_collection, $id) = explode(':', $fpid->_value);
747
      list($owner, $coll) = explode('-', $owner_collection);
99270 fvs 748
// TODO ??? only if agency-katalog is in actual search profile 
749
//      if (self::agency_rule($owner, 'use_localdata_stream') 
750
//         && $this->searchable_source[$this->agency_catalog_source])  // must be searchable
751
//         && ($owner_collection == $this->agency_catalog_source)      // only for own collection
752
      if (self::agency_rule($owner, 'use_localdata_stream')) {    
86565 fvs 753
        $id_array[] = '870970-basis:' . $id;
84341 fvs 754
        $localdata_object[$fpid->_value] = '870970-basis:' . $id;
755
      }
70547 fvs 756
    }
74792 fvs 757
    $this->cql2solr = new SolrQuery($this->repository, $this->config);
94242 fvs 758
    $this->watch->start('cql');
78620 fvs 759
    $chk_query = $this->cql2solr->parse('rec.id=(' . implode(OR_OP, $id_array) . ')');
94242 fvs 760
    $this->watch->stop('cql');
100306 fvs 761
    $solr_q = 'wt=phps' .
78620 fvs 762
              '&q=' . urlencode(implode(AND_OP, $chk_query['edismax']['q'])) .
70547 fvs 763
              '&fq=' . $filter_q .
764
              '&start=0' .
83315 fvs 765
              '&rows=500' .
70547 fvs 766
              '&defType=edismax' .
112468 fvs 767
              '&fl=rec.collectionIdentifier,' . FIELD_FEDORA_PID . ',rec.id,' . FIELD_UNIT_ID . ',unit.isPrimaryObject' .
768
              $add_fl . '&trackingId=' . verbose::$tracking_id;
113442 fvs 769
    verbose::log(TRACE, __FUNCTION__ . ':: Search for pids in Solr: ' . $this->repository['solr'] . str_replace('wt=phps', '?', $solr_q));
100306 fvs 770
    $this->curl->set_post($solr_q); // use post here because query can be very long. curl has current 8192 as max length get url
771
    $solr_result = $this->curl->get($this->repository['solr']);
772
    $this->curl->set_option(CURLOPT_POST, 0);
70547 fvs 773
    $solr_2_arr[] = unserialize($solr_result);
36057 fvs 774
 
70547 fvs 775
    foreach ($fpids as $fpid_number => $fpid) {
86565 fvs 776
      $no_direct_hit = $unit_id = $fedora_pid = $basis_pid = $basis_unit_id = '';
777
      $in_collection = FALSE;
778
      list($fpid_collection) = explode(':', $fpid->_value);
70547 fvs 779
      foreach ($solr_2_arr as $s_2_a) {
83315 fvs 780
        if ($s_2_a['response']['docs']) {
781
          foreach ($s_2_a['response']['docs'] as $fdoc) {
108828 fvs 782
            $f_id =  self::scalar_or_first_elem($fdoc[FIELD_FEDORA_PID]);
86565 fvs 783
            if ($f_id == $fpid->_value) {
108828 fvs 784
              $unit_id =  self::scalar_or_first_elem($fdoc[FIELD_UNIT_ID]);
86565 fvs 785
              $fedora_pid = $f_id;
786
              $in_collection = $in_collection || in_array($fpid_collection, $fdoc['rec.collectionIdentifier']);
83315 fvs 787
            }
86565 fvs 788
            if ($f_id == $localdata_object[$fpid->_value]) {
108828 fvs 789
              $basis_unit_id =  self::scalar_or_first_elem($fdoc[FIELD_UNIT_ID]);
86565 fvs 790
              $basis_pid = $f_id;
791
              $in_collection = $in_collection || in_array($fpid_collection, $fdoc['rec.collectionIdentifier']);
792
            }
70547 fvs 793
          }
794
        }
795
      }
86565 fvs 796
      if (empty($fedora_pid) && $basis_pid) {
797
        $no_direct_hit = TRUE;
798
        $unit_id =  $basis_unit_id;
799
        $fedora_pid = $basis_pid;
800
      }
80761 fvs 801
 
104295 fvs 802
/* if not searchable, the records is not there
803
      if (!$unit_id) {
79646 fvs 804
        verbose::log(WARNING, 'getObject:: Cannot find unit for ' . $fpid->_value . ' in SOLR');
108627 fvs 805
        self::read_record_repo_rels_hierarchy($fpid->_value, $fedora_rels_hierarchy);
71529 fvs 806
        $unit_id = self::parse_rels_for_unit_id($fedora_rels_hierarchy);
807
      }
104295 fvs 808
*/
80761 fvs 809
      if (!$unit_id) {
810
        $rec_error = 'Error: unknown/missing/inaccessible record: ' . $fpid->_value;
811
      }
812
      else {
108627 fvs 813
        self::read_record_repo_rels_hierarchy($unit_id, $unit_rels_hierarchy);
113442 fvs 814
        list($unit_members, $dummy, $localdata_in_pid, $primary_oid, $in_870970_basis) = self::parse_unit_for_best_agency($unit_rels_hierarchy, $unit_id, FALSE);
86565 fvs 815
        list($fpid_collection, $fpid_local) = explode(':', $fedora_pid);
115033 fvs 816
// collectionIdentifiers are now put on all records with holdings, 
817
// so the $in_collection variable could be used as information about which 870970 records a given library has holding in
818
// but for now, we ignore the information and behave as we did so far
819
        // if (empty($localdata_in_pid) && $in_collection && $fedora_pid) { // include this and only records with holdings can be found in 870970-basis
820
        if (empty($localdata_in_pid) && $fedora_pid) {                   // include this and 870970-basis records will be shown when no local record is found
86579 fvs 821
          $fpid->_value = $fedora_pid;
822
        }
823
        list($fedora_pid, $datastream)  = self::create_fedora_pid_and_stream($fpid->_value, $fedora_pid);
108627 fvs 824
        if (self::deleted_object($fedora_pid)) {
86565 fvs 825
          $rec_error = 'Error: deleted record: ' . $fpid->_value;
84341 fvs 826
        }
108627 fvs 827
        elseif ($error = self::read_record_repo_raw($fedora_pid, $fedora_result, $datastream)) {
86565 fvs 828
          $rec_error = 'Error: unknown/missing record: ' . $fpid->_value;
71529 fvs 829
        }
80761 fvs 830
        elseif ($param->relationData->_value ||
831
            $this->format['found_solr_format'] ||
832
            self::xs_boolean($param->includeHoldingsCount->_value)) {
833
          if (empty($unit_id)) {
108627 fvs 834
            self::read_record_repo_rels_hierarchy($fpid->_value, $fedora_rels_hierarchy);
80761 fvs 835
            $unit_id = self::parse_rels_for_unit_id($fedora_rels_hierarchy);
836
          }
837
          if ($param->relationData->_value) {
108627 fvs 838
            self::read_record_repo_rels_addi($unit_id, $fedora_addi_relation);
80761 fvs 839
          }
840
          if (self::xs_boolean($param->includeHoldingsCount->_value)) {
841
            $this->cql2solr = new SolrQuery($this->repository, $this->config);
842
            $no_of_holdings = self::get_holdings($fpid->_value);
843
          }
70547 fvs 844
        }
845
      }
846
//var_dump($fedora_rels_hierarchy);
847
//var_dump($unit_id);
848
//var_dump($fedora_addi_relation);
849
//die();
100089 fvs 850
      Object::set_value($o->collection->_value, 'resultPosition', $fpid_number + 1);
851
      Object::set_value($o->collection->_value, 'numberOfObjects', 1);
70547 fvs 852
      if ($rec_error) {
100089 fvs 853
        Object::set_value($help->_value, 'error', $rec_error);
854
        Object::set_value($help->_value, 'identifier', $fpid->_value);
80761 fvs 855
        $o->collection->_value->object[] = $help;
856
        unset($help);
70547 fvs 857
        unset($rec_error);
858
      }
859
      else {
100089 fvs 860
        Object::set($o->collection->_value->object[], '_value',
108627 fvs 861
          self::parse_record_repo_object($fedora_result,
70547 fvs 862
                                    $fedora_addi_relation,
79644 fvs 863
                                    $unit_members,
113442 fvs 864
                                    $in_870970_basis,
70547 fvs 865
                                    $param->relationData->_value,
86565 fvs 866
                                    $fpid->_value,
95289 fvs 867
                                    $primary_oid,
70598 fvs 868
                                    $this->filter_agency,
100089 fvs 869
                                    $no_of_holdings));
70547 fvs 870
      }
100089 fvs 871
      Object::set($collections[], '_value', $o);
70547 fvs 872
      unset($o);
873
      $id_array[] = $unit_id;
874
      $work_ids[$fpid_number + 1] = array($unit_id);
875
      unset($unit_id);
876
    }
877
 
70598 fvs 878
    if ($this->format['found_open_format']) {
879
      self::format_records($collections);
39165 fvs 880
    }
70598 fvs 881
    if ($this->format['found_solr_format']) {
882
      self::format_solr($collections, $solr_2_arr, $work_ids);
70547 fvs 883
    }
70598 fvs 884
    self::remove_unselected_formats($collections);
39165 fvs 885
 
37396 fvs 886
    $result = &$ret->searchResponse->_value->result->_value;
100089 fvs 887
    Object::set_value($result, 'hitCount', count($collections));
888
    Object::set_value($result, 'collectionCount', count($collections));
889
    Object::set_value($result, 'more', 'false');
37396 fvs 890
    $result->searchResult = $collections;
100089 fvs 891
    Object::set_value($result, 'facetResult', '');
108627 fvs 892
    Object::set_value($result->statInfo->_value, 'fedoraRecordsCached', $this->number_of_record_repo_cached);
893
    Object::set_value($result->statInfo->_value, 'fedoraRecordsRead', $this->number_of_record_repo_calls);
100089 fvs 894
    Object::set_value($result->statInfo->_value, 'time', $this->watch->splittime('Total'));
112468 fvs 895
    Object::set_value($result->statInfo->_value, 'trackingId', verbose::$tracking_id);
37396 fvs 896
 
78508 fvs 897
    verbose::log(STAT, sprintf($this->dump_timer, $this->soap_action) .  
898
                       ':: agency:' . $param->agency->_value .
115455 fvs 899
                       ' profile:' . $param->profile->_value .
900
                       ' repoRecs:' . $this->number_of_record_repo_calls .
901
                       ' repoCache:' . $this->number_of_record_repo_cached .
902
                       ' ' . $this->watch->dump());
37396 fvs 903
    return $ret;
904
  }
905
 
78038 fvs 906
  /** \brief Entry info: collect info
907
  *
78868 fvs 908
  * @param object $param - the user request
909
  * @retval object - the answer to the request
78038 fvs 910
  */
911
  public function info($param) {
912
    $result = &$ret->infoResponse->_value;
100089 fvs 913
    Object::set_value($result->infoGeneral->_value, 'defaultRepository', $this->config->get_value('default_repository', 'setup'));
78038 fvs 914
    $result->infoRepositories = self::get_repository_info();
915
    $result->infoObjectFormats = self::get_object_format_info();
916
    $result->infoSearchProfile = self::get_search_profile_info($param->agency->_value, $param->profile->_value);
917
    $result->infoSorts = self::get_sort_info();
918
    $result->infoNameSpaces = self::get_namespace_info();
78508 fvs 919
    verbose::log(STAT, sprintf($this->dump_timer, $this->soap_action) .  
920
                       ':: agency:' . $param->agency->_value .
921
                       ' profile:' . $param->profile->_value . ' ' . $this->watch->dump());
78038 fvs 922
    return $ret;
923
  }
37396 fvs 924
 
108777 fvs 925
  /*
926
   ************************************* private ******************************************
927
   */
108761 fvs 928
 
108777 fvs 929
  /*************************************************
930
   *********** input handling functions  ***********
931
   *************************************************/
108761 fvs 932
 
933
  /** \brief parse input for rank parameters
934
   *
935
   * @param object $param -       The request
936
   * @param string $rank -        Name of rank used by request
937
   * @param array $rank_types -   Settings for the given rank
938
   * @retval boolean - TRUE if a ranking is found
939
   */
940
  private function parse_for_ranking($param, &$rank, &$rank_types) {
941
    if ($rr = $param->userDefinedRanking) {
942
      $rank = 'user_rank';
943
      $rank_user['tie'] = $rr->_value->tieValue->_value;
944
      $rfs = (is_array($rr->_value->rankField) ? $rr->_value->rankField : array($rr->_value->rankField));
945
      foreach ($rfs as $rf) {
946
        $boost_type = ($rf->_value->fieldType->_value == 'word' ? 'word_boost' : 'phrase_boost');
947
        $rank_user[$boost_type][$rf->_value->fieldName->_value] = $rf->_value->weight->_value;
948
        $rank .= '_' . $boost_type . '-' . $rf->_value->fieldName->_value . '-' . $rf->_value->weight->_value;
108627 fvs 949
      }
108761 fvs 950
      $rank_types[$rank] = $rank_user;
951
    }
952
    elseif (is_scalar($param->sort->_value)) {
953
      if ($rank_types[$param->sort->_value]) {
954
        $rank = $param->sort->_value;
108627 fvs 955
      }
956
    }
108761 fvs 957
    return !empty($rank);
958
  }
959
 
960
  /** \brief parse input for sort parameters
961
   *
962
   * @param object $param -       The request
963
   * @param string $sort -        Name of sort used by request
964
   * @param array $sort_types -   Settings for the given sort
965
   * @retval mixed - error or NULL
966
   */
967
  private function parse_for_sorting($param, &$sort, &$sort_types) {
968
    if (!is_array($sort)) {
969
      $sort = array();
970
    }
971
    if ($param->sort) {
972
      $random = FALSE;
973
      $sorts = (is_array($param->sort) ? $param->sort : array($param->sort));
974
      $sort_types = $this->config->get_value('sort', 'setup');
975
      foreach ($sorts as $s) {
976
        if (!isset($sort_types[$s->_value])) {
977
          return 'Error: Unknown sort: ' . $s->_value;
108627 fvs 978
        }
108761 fvs 979
        $random = $random || ($s->_value == 'random');
980
        if ($random && count($sort)) {
981
          return 'Error: Random sorting can only be used alone';
108627 fvs 982
        }
108761 fvs 983
        if (is_array($sort_types[$s->_value])) {
984
          foreach ($sort_types[$s->_value] as $item) {
985
            if (!isset($sort_types[$item])) {
986
              return 'Error in service setup: ' . $item . ' specified in ' . $s->_value . ' is not defined';
987
            }
988
            $sort[] = $item;
108627 fvs 989
          }
990
        }
108761 fvs 991
        else {
992
          $sort[] = $s->_value;
993
        }
108627 fvs 994
      }
995
    }
996
  }
997
 
108761 fvs 998
  /** \brief decides which formats to include in result and how the should be build
999
   *
1000
   * @param mixed $objectFormat
1001
   * @param array $open_format
1002
   * @param array $solr_format
1003
   * @retval array
84034 fvs 1004
   */
108761 fvs 1005
  private function set_format($objectFormat, $open_format, $solr_format) {
1006
    if (is_array($objectFormat))
1007
      $help = $objectFormat;
1008
    elseif (empty($objectFormat->_value))
1009
      Object::set($help[], '_value', 'dkabm');
1010
    else
1011
      $help[] = $objectFormat;
1012
    foreach ($help as $of) {
1013
      if ($open_format[$of->_value]) {
113581 fvs 1014
        $ret[$of->_value] = array('user_selected' => TRUE, 'is_open_format' => TRUE, 'format_name' => $open_format[$of->_value]['format'], 'uri' => $open_format[$of->_value]['uri']);
108761 fvs 1015
        $ret['found_open_format'] = TRUE;
1016
      }
1017
      elseif ($solr_format[$of->_value]) {
1018
        $ret[$of->_value] = array('user_selected' => TRUE, 'is_solr_format' => TRUE, 'format_name' => $solr_format[$of->_value]['format']);
1019
        $ret['found_solr_format'] = TRUE;
1020
      }
1021
      else {
1022
        $ret[$of->_value] = array('user_selected' => TRUE, 'is_solr_format' => FALSE);
1023
      }
1024
    }
1025
    if ($ret['found_open_format'] || $ret['found_solr_format']) {
1026
      if (empty($ret['dkabm']))
1027
        $ret['dkabm'] = array('user_selected' => FALSE, 'is_open_format' => FALSE);
1028
      if (empty($ret['marcxchange']))
1029
        $ret['marcxchange'] = array('user_selected' => FALSE, 'is_open_format' => FALSE);
1030
    }
1031
    return $ret;
84034 fvs 1032
  }
1033
 
108761 fvs 1034
  /** \brief Build Solr filter_query parm
1035
   *
1036
   * @param array $profile - the users search profile
1037
   * @param string $index - the index to filter (search) for collection identifiers
1038
   * @retval string - the SOLR filter query that represent the profile
78038 fvs 1039
   */
108761 fvs 1040
  private function set_solr_filter($profile, $index = 'rec.collectionIdentifier') {
1041
    $collection_query = $this->repository['collection_query'];
1042
    $ret = array();
1043
    if (is_array($profile)) {
1044
      $this->collection_alias = self::set_collection_alias($profile);
1045
      foreach ($profile as $p) {
1046
        if (self::xs_boolean($p['sourceSearchable']) || ($this->add_collection_with_relation_to_filter  && count($p['relation']))) {
1047
          if ($filter_query = $collection_query[$p['sourceIdentifier']]) {
1048
            $ret[] = '(' . $index . ':' . $p['sourceIdentifier'] . AND_OP . $filter_query . ')';
78379 fvs 1049
          }
108761 fvs 1050
          else {
1051
            $ret[] = $index . ':' . $p['sourceIdentifier'];
1052
          }
78038 fvs 1053
        }
103879 fvs 1054
      }
78038 fvs 1055
    }
108761 fvs 1056
    return implode(OR_OP, $ret);
78038 fvs 1057
  }
1058
 
108761 fvs 1059
  /** \brief set search_filter_for_800000
1060
   *
1061
   * @param boolean $test_force_filter
78038 fvs 1062
   */
108761 fvs 1063
  private function set_search_filters_for_800000_collection($test_force_filter = false) {
1064
    static $part_of_bib_dk = array();
1065
    static $use_holding = array();
112162 fvs 1066
    $containing_80000 = $this->config->get_value('collections_containing_800000', 'setup');
108761 fvs 1067
    foreach ($this->searchable_source as $source => $searchable) {
1068
      $searchable = $test_force_filter || $searchable;     // for testing purposes
1069
      if (($source == '800000-bibdk') && $searchable) {
111942 fvs 1070
        if (empty($part_of_bib_dk)) $part_of_bib_dk = $this->open_agency->get_libraries_by_rule('part_of_bibliotek_dk', 1, 'Forskningsbibliotek');
1071
        if (empty($use_holding)) $use_holding = $this->open_agency->get_libraries_by_rule('use_holdings_item', 1, 'Forskningsbibliotek');
108761 fvs 1072
 
1073
      }
1074
      if (($source == '800000-danbib') && $searchable) {
111942 fvs 1075
        if (empty($use_holding)) $use_holding = $this->open_agency->get_libraries_by_rule('use_holdings_item', 1, 'Forskningsbibliotek');
108761 fvs 1076
      }
113581 fvs 1077
      if (in_array($source, $containing_80000) && $searchable) {         // clear search filter since "broader" collection is selected
112162 fvs 1078
        $part_of_bib_dk = array();
1079
        $use_holding = array();
1080
        break;
1081
      }
78038 fvs 1082
    }
108761 fvs 1083
    if ($part_of_bib_dk || $use_holding) {
111942 fvs 1084
      $this->search_profile_contains_800000 = TRUE;
108761 fvs 1085
      verbose::log(DEBUG, 'Filter 800000 part_of_bib_dk:: ' . count($part_of_bib_dk) . ' use_holding: ' . count($use_holding));
1086
      // TEST $this->search_filter_for_800000 = 'holdingsitem.agencyId=(' . implode(' OR ', array_slice($part_of_bib_dk + $use_holding, 0, 3)) . ')';
1087
      $this->search_filter_for_800000 = 'holdingsitem.agencyId:(' . implode(' OR ', $part_of_bib_dk + $use_holding) . ')';
1088
    }  
78038 fvs 1089
  }
1090
 
108761 fvs 1091
  /** \brief Build bq (BoostQuery) as field:content^weight
1092
   *
1093
   * @param mixed $boost - boost query
1094
   * @retval string - SOLR boost string
78038 fvs 1095
   */
108761 fvs 1096
  private static function boostUrl($boost) {
1097
    $ret = '';
1098
    if ($boost) {
1099
      $boosts = (is_array($boost) ? $boost : array($boost));
1100
      foreach ($boosts as $bf) {
1101
        if (empty($bf->_value->fieldValue->_value)) {
1102
          if (!$weight = $bf->_value->weight->_value) {
1103
            $weight = 1;
78038 fvs 1104
          }
108761 fvs 1105
          $ret .= '&bf=' .
1106
                  urlencode('product(' . $bf->_value->fieldName->_value . ',' . $weight . ')');
78038 fvs 1107
        }
108761 fvs 1108
        else {
1109
          $ret .= '&bq=' .
1110
                  urlencode($bf->_value->fieldName->_value . ':"' .
1111
                            str_replace('"', '', $bf->_value->fieldValue->_value) . '"^' .
1112
                            $bf->_value->weight->_value);
1113
        }
78038 fvs 1114
      }
1115
    }
1116
    return $ret;
1117
  }
1118
 
108761 fvs 1119
  /** \brief Build search to include collections without holdings
1120
   *
1121
   * @param array $profile - the users search profile
1122
   * @param string $index - the index to match collection identifiers against
1123
   * @retval string - the SOLR filter query that represent the profile
78038 fvs 1124
   */
108761 fvs 1125
  private function split_collections_for_holdingsitem($profile, $index = 'rec.collectionIdentifier') {
1126
    $collection_query = $this->repository['collection_query'];
1127
    $filtered_collections = $normal_collections = array();
1128
    if (is_array($profile)) {
1129
      $this->collection_alias = self::set_collection_alias($profile);
1130
      foreach ($profile as $p) {
1131
        if (self::xs_boolean($p['sourceSearchable']) || ($this->add_collection_with_relation_to_filter  && count($p['relation']))) {
1132
          if ($p['sourceIdentifier'] == $this->agency_catalog_source || $p['sourceIdentifier'] == '870970-basis') {
1133
            $filtered_collections[] = $p['sourceIdentifier'];
1134
          }
1135
          else {
1136
            if ($filter_query = $collection_query[$p['sourceIdentifier']]) {
1137
              $normal_collections[] = '(' . $index . ':' . $p['sourceIdentifier'] . AND_OP . $filter_query . ')';
1138
            }
1139
            else {
1140
              $normal_collections[] = $p['sourceIdentifier'];
1141
            }
1142
          }
1143
        }
1144
      }
78038 fvs 1145
    }
108761 fvs 1146
    return
1147
      ($normal_collections ? $index . ':(' . implode(' OR ', $normal_collections) . ') OR ' : '') .
1148
      ($filtered_collections ? '(' . $index . ':(' . implode(' OR ', $filtered_collections) . ') AND %s)' : '%s');
78038 fvs 1149
  }
1150
 
108761 fvs 1151
  /** \brief Set list of collection alias' depending on the user search profile
1152
   * - in repository: ['collection_alias']['870876-anmeld'] = '870976-allanmeld';
78038 fvs 1153
   *
108761 fvs 1154
   * @param array $profile - the users search profile
1155
   * @retval array - collection alias'
78038 fvs 1156
   */
108761 fvs 1157
  private function set_collection_alias($profile) {
1158
    $collection_alias = array();
1159
    $alias = is_array($this->repository['collection_alias']) ? array_flip($this->repository['collection_alias']) : array();
1160
    foreach ($profile as $p) {
1161
      if (self::xs_boolean($p['sourceSearchable'])) {
1162
        $si = $p['sourceIdentifier'];
1163
        if (empty($alias[$si])) {
1164
          $collection_alias[$si] = $si;
1165
        }
1166
        elseif (empty($collection_alias[$alias[$si]])) {
1167
          $collection_alias[$alias[$si]] = $si;
1168
        }
1169
      }
78038 fvs 1170
    }
108761 fvs 1171
    return $collection_alias;
78038 fvs 1172
  }
1173
 
108761 fvs 1174
 
1175
  /** \brief Sets this->repository from user parameter or defaults to ini-file setup
1176
   *
1177
   * @param string $repository
1178
   * @param boolean $cql_file_mandatory
1179
   * @retval mixed - error or NULL
78038 fvs 1180
   */
108761 fvs 1181
  private function set_repositories($repository, $cql_file_mandatory = TRUE) {
1182
    $repositories = $this->config->get_value('repository', 'setup');
1183
    if (!$this->repository_name = $repository) {
1184
      $this->repository_name = $this->config->get_value('default_repository', 'setup');
1185
    }
1186
    if ($this->repository = $repositories[$this->repository_name]) {
1187
      foreach ($repositories['defaults'] as $key => $url_par) {
1188
        if (empty($this->repository[$key])) {
1189
          $this->repository[$key] = (substr($key, 0, 7) == 'fedora_') ? $this->repository['fedora'] . $url_par : $url_par;
1190
        }
78038 fvs 1191
      }
108761 fvs 1192
      if ($cql_file_mandatory && empty($this->repository['cql_file'])) {
1193
        verbose::log(FATAL, 'cql_file not defined for repository: ' .  $this->repository_name);
1194
        return 'Error: cql_file not defined for repository: '  . $this->repository_name;
78038 fvs 1195
      }
108761 fvs 1196
      if ($this->repository['cql_file']) {
1197
        if (!$this->repository['cql_settings'] = self::get_solr_file($this->repository['cql_file'])) {
1198
          if (!$this->repository['cql_settings'] = @ file_get_contents($this->repository['cql_file'])) {
1199
            verbose::log(FATAL, 'Cannot get cql_file (' . $this->repository['cql_file'] . ') from local directory. Repository: ' .  $this->repository_name);
1200
            return 'Error: Cannot find cql_file for repository: '  . $this->repository_name;
78038 fvs 1201
          }
108761 fvs 1202
          verbose::log(ERROR, 'Cannot get cql_file (' . $this->repository['cql_file'] . ') from SOLR - use local version. Repository: ' .  $this->repository_name);
78038 fvs 1203
        }
1204
      }
108761 fvs 1205
      if (empty($this->repository['filter'])) {
1206
        $this->repository['filter'] = array();
1207
      }
78038 fvs 1208
    }
108761 fvs 1209
    else {
1210
      return 'Error: Unknown repository: ' . $this->repository_name;
1211
    }
78038 fvs 1212
  }
1213
 
108761 fvs 1214
 
108777 fvs 1215
  /**************************************************
1216
   *********** output handling functions  ***********
1217
   **************************************************/
108761 fvs 1218
 
108777 fvs 1219
 
76551 fvs 1220
  /** \brief sets sortUsed if rank or sort is used
74010 fvs 1221
   *
76789 fvs 1222
   * @param object $ret - modified
1223
   * @param string $rank
1224
   * @param string $sort
1225
   * @param array $sort_types
74010 fvs 1226
   */
76725 fvs 1227
  private function set_sortUsed(&$ret, $rank, $sort, $sort_types) {
74010 fvs 1228
    if (isset($rank)) {
78373 fvs 1229
      if (substr($rank, 0, 9) != 'user_rank') {
100089 fvs 1230
        Object::set_value($ret, 'sortUsed', $rank);
78373 fvs 1231
      }
74010 fvs 1232
    }
1233
    elseif (!empty($sort)) {
76725 fvs 1234
      if ($key = array_search($sort, $sort_types)) {
100089 fvs 1235
        Object::set_value($ret, 'sortUsed', $key);
74010 fvs 1236
      }
76725 fvs 1237
      else {
1238
        foreach ($sort as $s) {
100089 fvs 1239
          Object::set_array_value($ret, 'sortUsed', $s);
76725 fvs 1240
        }
1241
      }
74010 fvs 1242
    }
1243
  }
1244
 
108761 fvs 1245
  /** \brief Helper function to set debug info
1246
   *  
1247
   * @param array $solr_debug - debuginfo from SOLR
1248
   * @param object $rank_freq_debug - info about frequencies from ranking
1249
   * @param object $best_match_debug - info about best match parameters
1250
   * @retval object
1251
   */
1252
  private function set_debug_info($solr_debug, $rank_freq_debug = '', $best_match_debug = '') {
1253
    Object::set_value($ret, 'rawQueryString', $solr_debug['rawquerystring']);
1254
    Object::set_value($ret, 'queryString', $solr_debug['querystring']);
1255
    Object::set_value($ret, 'parsedQuery', $solr_debug['parsedquery']);
1256
    Object::set_value($ret, 'parsedQueryString', $solr_debug['parsedquery_toString']);
1257
    if ($best_match_debug) {
1258
      Object::set_value($ret, 'bestMatch', $best_match_debug);
1259
    }
1260
    if ($rank_freq_debug) {
1261
      Object::set_value($ret, 'rankFrequency', $rank_freq_debug);
1262
    }
1263
    return $ret;
1264
  }
1265
 
1266
  /** \brief Pick tags from solr result and create format
1267
   *
1268
   * @param array $collections- the structure is modified
1269
   * @param array $solr
1270
   * @param array $work_ids
1271
   * @param array $pid_sort_keys
1272
   */
113442 fvs 1273
  private function format_solr(&$collections, $solr, &$work_ids, $fpid_sort_keys = array()) {
108761 fvs 1274
    $solr_display_ns = $this->xmlns['ds'];
1275
    $this->watch->start('format_solr');
1276
    foreach ($this->format as $format_name => $format_arr) {
1277
      if ($format_arr['is_solr_format']) {
1278
        $format_tags = explode(',', $format_arr['format_name']);
1279
        foreach ($collections as $idx => &$c) {
1280
          $rec_no = $c->_value->collection->_value->resultPosition->_value;
1281
          foreach ($work_ids[$rec_no] as $mani_no => $unit_no) {
1282
            if (is_array($solr[0]['response']['docs'])) {
1283
              $fpid = $c->_value->collection->_value->object[$mani_no]->_value->identifier->_value;
113161 fvs 1284
              unset($fpid_idx, $basis_idx);
1285
              foreach ($solr[0]['response']['docs'] as $idx_doc => $solr_doc) {
108828 fvs 1286
                $doc_units = is_array($solr_doc[FIELD_UNIT_ID]) ? $solr_doc[FIELD_UNIT_ID] : array($solr_doc[FIELD_UNIT_ID]);
113161 fvs 1287
                if (is_array($doc_units) && in_array($unit_no, $doc_units)) {
1288
                  if ($fpid == $solr_doc[FIELD_FEDORA_PID]) {
113581 fvs 1289
                    $fpid_idx = $idx_doc;
113161 fvs 1290
                  }
1291
                  elseif (self::record_source_from_pid($solr_doc[FIELD_FEDORA_PID]) == '870970-basis') {
113581 fvs 1292
                     $basis_idx = $idx_doc;
113161 fvs 1293
                  }
1294
                }
1295
              }
113581 fvs 1296
              if (!isset($fpid_idx)) {
113161 fvs 1297
                $fpid_idx = isset($basis_idx) ? $basis_idx : 0;
1298
              }
1299
              $solr_doc = $solr[0]['response']['docs'][$fpid_idx];
1300
              foreach ($format_tags as $format_tag) {
1301
                if ($solr_doc[$format_tag] || $format_tag == 'fedora.identifier') {
1302
                  if (strpos($format_tag, '.')) {
1303
                    list($tag_NS, $tag_value) = explode('.', $format_tag);
1304
                  }
1305
                  else {
1306
                    $tag_value = $format_tag;
1307
                  }
1308
                  if ($format_tag == 'fedora.identifier') {
1309
                    Object::set_namespace($mani->_value, $tag_value, $solr_display_ns);
1310
                    Object::set_value($mani->_value, $tag_value, $fpid);
1311
                  }
1312
                  else {
1313
                    if (is_array($solr_doc[$format_tag])) {
1314
                      if ($this->feature_sw[$format_tag] == 'array') {   // more than one for this
1315
                        foreach ($solr_doc[$format_tag] as $solr_tag) {
1316
                          $help = new stdClass();
1317
                          $help->_namespace = $solr_display_ns;
1318
                          $help->_value = self::normalize_chars($solr_tag);
1319
                          $mani->_value->{$tag_value}[] = $help;
1320
                          unset($help);
1321
                        }
108761 fvs 1322
                      }
1323
                      else {
1324
                        Object::set_namespace($mani->_value, $tag_value, $solr_display_ns);
113161 fvs 1325
                        Object::set_value($mani->_value, $tag_value, self::normalize_chars($solr_doc[$format_tag][0]));
108761 fvs 1326
                      }
1327
                    }
113161 fvs 1328
                    else {
1329
                      Object::set_namespace($mani->_value, $tag_value, $solr_display_ns);
1330
                      Object::set_value($mani->_value, $tag_value, self::normalize_chars($solr_doc[$format_tag]));
1331
                    }
108761 fvs 1332
                  }
1333
                }
1334
              }
1335
            }
1336
            if ($mani) {   // should contain data, but for some odd reason it can be empty. Some bug in the solr-indexes?
1337
              $mani->_namespace = $solr_display_ns;
1338
              $sort_key = $fpid_sort_keys[$fpid] . sprintf('%04d', $mani_no);
1339
              $manifestation->manifestation[$sort_key] = $mani;
1340
            }
1341
            unset($mani);
1342
          }
1343
// need to sort objects to put data correct
1344
          if (is_array($manifestation->manifestation) && count($manifestation->manifestation) > 1) {
1345
            ksort($manifestation->manifestation);
1346
          }
1347
          Object::set_namespace($c->_value->formattedCollection->_value, $format_name, $solr_display_ns);
1348
          Object::set_value($c->_value->formattedCollection->_value, $format_name, $manifestation);
1349
          unset($manifestation);
1350
        }
1351
      }
1352
    }
1353
    $this->watch->stop('format_solr');
1354
  }
1355
 
1356
  /** \brief Setup call to OpenFormat and execute the format request
1357
   * If ws_open_format_uri is set, the format request is send to that server otherwise
1358
   * openformat is included using the [format] section from config
1359
   *
1360
   * @param array $collections- the structure is modified
1361
   */
1362
  private function format_records(&$collections) {
1363
    static $formatRecords;
1364
    $this->watch->start('format');
1365
    foreach ($this->format as $format_name => $format_arr) {
1366
      if ($format_arr['is_open_format']) {
1367
        if ($open_format_uri = $this->config->get_value('ws_open_format_uri', 'setup')) {
1368
          Object::set_namespace($f_obj, 'formatRequest', $this->xmlns['of']);
1369
          Object::set($f_obj->formatRequest->_value, 'originalData', $collections);
1370
  // need to set correct namespace
1371
          foreach ($f_obj->formatRequest->_value->originalData as $i => &$oD) {
1372
            $save_ns[$i] = $oD->_namespace;
1373
            $oD->_namespace = $this->xmlns['of'];
1374
          }
1375
          Object::set_namespace($f_obj->formatRequest->_value, 'outputFormat', $this->xmlns['of']);
1376
          Object::set_value($f_obj->formatRequest->_value, 'outputFormat', $format_arr['format_name']);
1377
          Object::set_namespace($f_obj->formatRequest->_value, 'outputType', $this->xmlns['of']);
1378
          Object::set_value($f_obj->formatRequest->_value, 'outputType', 'php');
112468 fvs 1379
          Object::set_value($f_obj->formatRequest->_value, 'trackingId', verbose::$tracking_id);
108761 fvs 1380
          $f_xml = $this->objconvert->obj2soap($f_obj);
113504 fvs 1381
          $this->curl->set_post($f_xml, 0);
1382
          $this->curl->set_option(CURLOPT_HTTPHEADER, array('Content-Type: text/xml; charset=UTF-8'), 0);
113581 fvs 1383
          $f_result = $this->curl->get($format_arr['uri'] ? $format_arr['uri'] : $open_format_uri);
108761 fvs 1384
          $this->curl->set_option(CURLOPT_POST, 0, 0);
1385
          $fr_obj = $this->objconvert->set_obj_namespace(unserialize($f_result), $this->xmlns['of']);
1386
  // need to restore correct namespace
1387
          foreach ($f_obj->formatRequest->_value->originalData as $i => &$oD) {
1388
            $oD->_namespace = $save_ns[$i];
1389
          }
1390
          if (!$fr_obj) {
1391
            $curl_err = $this->curl->get_status();
1392
            verbose::log(FATAL, 'openFormat http-error: ' . $curl_err['http_code'] . ' from: ' . $open_format_uri);
1393
          }
1394
          else {
1395
            $struct = key($fr_obj->formatResponse->_value);
1396
            // if ($struct == 'error') ... 
1397
            foreach ($collections as $idx => &$c) {
1398
              Object::set($c->_value->formattedCollection->_value, $struct,  $fr_obj->formatResponse->_value->{$struct}[$idx]);
1399
            }
1400
          }
1401
        }
1402
        else {
1403
          require_once('OLS_class_lib/format_class.php');
1404
          if (empty($formatRecords)) {
1405
            $formatRecords = new FormatRecords($this->config->get_section('format'), $this->xmlns['of'], $this->objconvert, $this->xmlconvert, $this->watch);
1406
          }
1407
          Object::set_value($param, 'outputFormat', $format_arr['format_name']);
1408
          Object::set_namespace($param, 'outputFormat', $this->xmlns['of']);
1409
          $param->originalData = $collections;
1410
  // need to set correct namespace
1411
          foreach ($param->originalData as $i => &$oD) {
1412
            $save_ns[$i] = $oD->_namespace;
1413
            $oD->_namespace = $this->xmlns['of'];
1414
          }
1415
          $f_result = $formatRecords->format($param->originalData, $param);
1416
          $fr_obj = $this->objconvert->set_obj_namespace($f_result, $this->xmlns['os']);
1417
  // need to restore correct namespace
1418
          foreach ($param->originalData as $i => &$oD) {
1419
            $oD->_namespace = $save_ns[$i];
1420
          }
1421
          if (!$fr_obj) {
1422
            $curl_err = $formatRecords->get_status();
1423
            verbose::log(FATAL, 'openFormat http-error: ' . $curl_err[0]['http_code'] . ' - check [format] settings in ini-file');
1424
          }
1425
          else {
1426
            $struct = key($fr_obj[0]);
1427
            foreach ($collections as $idx => &$c) {
1428
              Object::set($c->_value->formattedCollection->_value, $struct,  $fr_obj[$idx]->{$struct});
1429
            }
1430
          }
1431
        }
1432
      }
1433
    }
1434
    $this->watch->stop('format');
1435
  }
1436
 
1437
  /** \brief Remove not asked for formats from result
1438
   *
1439
   * @param array $collections - the structure is modified
1440
   */
1441
  private function remove_unselected_formats(&$collections) {
1442
    foreach ($collections as $idx => &$c) {
1443
      foreach ($c->_value->collection->_value->object as &$o) {
1444
        if (!$this->format['dkabm']['user_selected'])
1445
          unset($o->_value->record);
1446
        if (!$this->format['marcxchange']['user_selected'])
1447
          unset($o->_value->collection);
1448
      }
1449
    }
1450
  }
1451
 
1452
  /** \brief Remove private/internal subfields from the marcxchange record
1453
   * If all subfields in a field are removed, the field is removed as well
1454
   * Controlled by the repository filter structure set in the services ini-file
1455
   *
1456
   * @param array $record_source- the source of the record, owner or collectionIdentifier
1457
   * @param array $collection- the structure is modified
1458
   * @param array $filter_settings - from the repository
1459
   */
1460
  private function filter_marcxchange($record_source, &$collection, $filter_settings) {
1461
    foreach ($filter_settings as $rs_idx => $filters) {
1462
      if (($marc_filters = $filters['marcxchange']) && preg_match('/' . $rs_idx . '/', $record_source)) {
1463
        @ $mrec = &$collection->record->_value;
1464
        if ($mrec->datafield) {
1465
          foreach ($mrec->datafield as $idf => &$df) {
1466
            foreach ($marc_filters as $tag => $filter) {
1467
              if (preg_match('/' . $tag . '/', $df->_attributes->tag->_value)) {
1468
                if (is_array($df->_value->subfield)) {
1469
                  foreach ($df->_value->subfield as $isf => &$sf) {
1470
                    if (preg_match('/' . $filter . '/', $sf->_attributes->code->_value)) {
1471
                      unset($mrec->datafield[$idf]->_value->subfield[$isf]);
1472
                    }
1473
                  }
1474
                  if (!count($df->_value->subfield)) {  // removed all subfield
1475
                    unset($mrec->datafield[$idf]);
1476
                  }
1477
                }
1478
                elseif (preg_match('/' . $filter . '/', $df->_value->subfield->_attributes->code->_value)) {
1479
                  unset($mrec->datafield[$idf]);
1480
                }
1481
              }
1482
            }
1483
          }
1484
        }
1485
      }
1486
    }
1487
  }
1488
 
1489
  /** \ brief Remove private/internal sections from a docbook record
1490
   *
1491
   * @param array $record_source- the source of the record, owner or collectionIdentifier
1492
   * @param array $article- the structure is modified
1493
   * @param array $filter_settings - from the reposotory
1494
   */
1495
  private function filter_docbook($record_source, &$article, $filter_settings) {
1496
    foreach ($filter_settings as $rs_idx => $filters) {
1497
      if (($docbook_filters = $filters['docbook']) && preg_match('/' . $rs_idx . '/', $record_source)) {
1498
        foreach ($docbook_filters as $section_path => $match) {
1499
          list($part, $item) = explode('/', $section_path);
1500
          foreach ($article->$part as $idx => $section) {
1501
            if ($section->_value->$item->_value == $match) {
1502
              unset($article->{$part}[$idx]);
1503
            }
1504
          }
1505
        }
1506
      }
1507
    }
1508
  }
1509
 
1510
 
108777 fvs 1511
  /**********************************************
1512
   *********** Solr related functions ***********
1513
   **********************************************/
108761 fvs 1514
 
1515
 
114817 fvs 1516
  /** \brief Alter the query and agency filter if HOLDINGS_AGENCY_ID_FIELD is used in query
114961 fvs 1517
   *         - replace 870970-basis with holdings_agency part and (bug: 21233) add holdings_agency part to the agency_catalog source
114817 fvs 1518
   *
1519
   * @param object $solr_query
1520
   */
1521
  private function modify_query_and_filter_agency(&$solr_query) {
1522
    foreach (array('q', 'fq') as $solr_par) {
1523
      foreach ($solr_query['edismax'][$solr_par] as $q_idx => $q) {
1524
        if (strpos($q, HOLDINGS_AGENCY_ID_FIELD . ':') === 0) {
115418 fvs 1525
          if (count($solr_query['edismax'][$solr_par]) == 1) {
1526
            $solr_query['edismax'][$solr_par][$q_idx] = '*';
1527
          }
1528
          else {
1529
            unset($solr_query['edismax'][$solr_par][$q_idx]);
1530
          }
114817 fvs 1531
          $this->filter_agency = str_replace('rec.collectionIdentifier:870970-basis', $q, $this->filter_agency);
114961 fvs 1532
          $collect_agency = 'rec.collectionIdentifier:' . $this->agency_catalog_source;
115132 fvs 1533
          $filtered_collect_agency = '(' . $collect_agency . AND_OP . $q . ')';
1534
          if (strpos($this->filter_agency, $filtered_collect_agency) === FALSE) {
1535
            $this->filter_agency = str_replace($collect_agency, $filtered_collect_agency, $this->filter_agency);
1536
          }
114817 fvs 1537
        }
1538
      }
1539
    }
1540
  }
1541
 
78868 fvs 1542
  /** \brief Set the parameters to solr facets
1543
   *
1544
   * @param object $facets - the facet paramaters from the request
1545
   * @retval string - facet part of solr url
1546
   */
1547
  private function set_solr_facet_parameters($facets) {
86978 fvs 1548
    $max_threads = self::value_or_default($this->config->get_value('max_facet_threads', 'setup'), 50);
78868 fvs 1549
    $ret = '';
1550
    if ($facets->facetName) {
1551
      $facet_min = 1;
1552
      if (isset($facets->facetMinCount->_value)) {
1553
        $facet_min = $facets->facetMinCount->_value;
1554
      }
86978 fvs 1555
      $ret .= '&facet=true&facet.threads=' . min(count($facets->facetName), $max_threads) . '&facet.limit=' . $facets->numberOfTerms->_value .  '&facet.mincount=' . $facet_min;
78868 fvs 1556
      if ($facet_sort = $facets->facetSort->_value) {
1557
        $ret .= '&facet.sort=' . $facet_sort;
1558
      }
83553 fvs 1559
      if ($facet_offset = $facets->facetOffset->_value) {
1560
        $ret .= '&facet.offset=' . $facet_offset;
1561
      }
78868 fvs 1562
      if (is_array($facets->facetName)) {
1563
        foreach ($facets->facetName as $facet_name) {
1564
          $ret .= '&facet.field=' . $facet_name->_value;
1565
        }
1566
      }
1567
      elseif (is_scalar($facets->facetName->_value)) {
1568
        $ret .= '&facet.field=' . $facets->facetName->_value;
1569
      }
1570
    }
1571
    return $ret;
1572
  }
1573
 
73590 fvs 1574
  /** \brief Change cql_error to string
1575
   *
76789 fvs 1576
   * @param array $solr_error
1577
   * @retval string
73590 fvs 1578
   */
1579
  private function cql2solr_error_to_string($solr_error) {
1580
    $str = '';
1581
    foreach (array('no' => '|: ', 'description' => '', 'details' => ' (|)', 'pos' => ' at pos ') as $tag => $txt) {
1582
      list($pre, $post) = explode('|', $txt);
1583
      if ($solr_error[0][$tag]) {
1584
        $str .= $pre . $solr_error[0][$tag]. $post;
1585
      }
1586
    }
1587
    return $str;
1588
  }
1589
 
76789 fvs 1590
  /** \brief Create solr array with records valid for the search-profile and parameters.
1591
   *         If solr_formats is asked for, build list of fields to ask for
1592
   *
1593
   * @param array $add_queries
1594
   * @param string $query
1595
   * @param boolean $all_objects
1596
   * @param string $filter_q
1597
   * @retval mixed - error string or SOLR array
1598
   */
71090 fvs 1599
  private function do_add_queries_and_fetch_solr_data_fields($add_queries, $query, $all_objects, $filter_q) {
1600
    if ($this->format['found_solr_format']) {
1601
      foreach ($this->format as $f) {
1602
        if ($f['is_solr_format']) {
1603
          $add_fl .= ',' . $f['format_name'];
1604
        }
1605
      }
1606
    }
1607
    return self::do_add_queries($add_queries, $query, $all_objects, $filter_q, $add_fl);
1608
  }
76789 fvs 1609
 
70598 fvs 1610
  /** \brief Create solr array with records valid for the search-profile and parameters. If needed fetch data for display as well
1611
   *
76789 fvs 1612
   * @param array $add_queries
1613
   * @param string $query
1614
   * @param boolean $all_objects
1615
   * @param string $filter_q
1616
   * @param string $add_field_list - list of extra fields to return, like display_*
1617
   * @retval mixed - error string or SOLR array
70598 fvs 1618
   */
71090 fvs 1619
  private function do_add_queries($add_queries, $query, $all_objects, $filter_q, $add_field_list='') {
70598 fvs 1620
    foreach ($add_queries as $add_idx => $add_query) {
1621
      if ($this->separate_field_query_style) {
1622
          $add_q =  '(' . $add_query . ')';
1623
      }
1624
      else {
1625
          $add_q =  $this->which_rec_id . ':(' . $add_query . ')';
1626
      }
103879 fvs 1627
      $chk_query = $this->cql2solr->parse($query);
115132 fvs 1628
      self::modify_query_and_filter_agency($chk_query);
70598 fvs 1629
      if ($all_objects) {
103879 fvs 1630
        $chk_query['edismax']['q'] =  array($add_q);
70598 fvs 1631
      }
1632
      else {
1633
        if ($add_query) {
1634
          $chk_query['edismax']['q'][] =  $add_q;
1635
        }
1636
      }
1637
      if ($chk_query['error']) {
1638
        $error = $chk_query['error'];
1639
        return $ret_error;
1640
      }
1641
      $q = $chk_query['edismax'];
1642
      $solr_url = self::create_solr_url($q, 0, 999999, $filter_q);
1643
      list($solr_host, $solr_parm) = explode('?', $solr_url['url'], 2);
108828 fvs 1644
      $solr_parm .= '&fl=rec.collectionIdentifier,unit.isPrimaryObject,' . FIELD_UNIT_ID . ',sort.complexKey' . $add_field_list;
71390 fvs 1645
      verbose::log(DEBUG, 'Re-search: ' . $this->repository['solr'] . '?' . str_replace('&wt=phps', '', $solr_parm) . '&debugQuery=on');
70598 fvs 1646
      if (DEBUG_ON) {
103879 fvs 1647
        echo 'post_array: ' . $solr_url['url'] . PHP_EOL;
70598 fvs 1648
      }
1649
 
1650
      $this->curl->set_post($solr_parm, 0); // use post here because query can be very long
1651
      $this->curl->set_option(CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded; charset=utf-8'), 0);
1652
      $solr_result = $this->curl->get($solr_host, 0);
1653
// remember to clear POST 
1654
      $this->curl->set_option(CURLOPT_POST, 0, 0);
71090 fvs 1655
      if (!($solr_arr[$add_idx] = unserialize($solr_result))) {
70598 fvs 1656
        verbose::log(FATAL, 'Internal problem: Cannot decode Solr re-search');
1657
        return 'Internal problem: Cannot decode Solr re-search';
1658
      }
1659
    }
71090 fvs 1660
    return $solr_arr;
70598 fvs 1661
  }
1662
 
108777 fvs 1663
  /** \brief Check if a record is searchable - currently obsolete
1664
   *
1665
   * @param string $unit_id -
1666
   * @param string $filter_q - the filter query (search profile)
1667
   * @retval boolean - true if at least one record is found
1668
   */
1669
  private function is_searchable($unit_id, $filter_q) {
1670
// do not check for searchability, since the relation is found in the search_profile, it's ok to use it
1671
    return TRUE;
1672
    if (empty($filter_q)) return TRUE;
1673
 
108828 fvs 1674
    self::get_solr_array(FIELD_UNIT_ID . ':' . str_replace(':', '\:', $unit_id), 1, 0, '', '', '', rawurlencode($filter_q), '', '', $solr_arr);
108777 fvs 1675
    return self::get_num_found($solr_arr);
1676
  }
1677
 
108761 fvs 1678
  /** \brief Encapsules how to get the data from the first element
70547 fvs 1679
   *
108761 fvs 1680
   * @param array $solr_arr
1681
   * @param string $element
1682
   * @retval mixed
70547 fvs 1683
   */
108761 fvs 1684
  private function get_first_solr_element($solr_arr, $element) {
1685
    $solr_docs = &$solr_arr['response']['docs'];
1686
    return self::scalar_or_first_elem($solr_docs[0][$element]);
70547 fvs 1687
  }
1688
 
108761 fvs 1689
  /** \brief Encapsules how to get hit count from the solr result
83689 fvs 1690
   *
108761 fvs 1691
   * @param array $solr_arr
1692
   * @retval integer
83689 fvs 1693
   */
108761 fvs 1694
  private function get_num_found($solr_arr) {
1695
    return $solr_arr['response']['numFound'];
1696
  }
1697
 
1698
  /** \brief Encapsules extraction of ids (unit.id or workid) solr result
1699
   *       - stores id and the corresponding fedorePid in unit_fallback
1700
   *
1701
   * @param array $solr_arr
1702
   * @param array $search_ids - contains the result
1703
   */
1704
  private function extract_ids_from_solr($solr_arr, &$search_ids) {
108828 fvs 1705
    $solr_fields = array(FIELD_UNIT_ID, FIELD_WORK_ID);
108761 fvs 1706
    static $u_err = 0;
1707
    $search_ids = array();
1708
    $solr_docs = &$solr_arr['response']['docs'];
1709
    foreach ($solr_docs as &$fdoc) {
1710
      $ids = array();
1711
      foreach ($solr_fields as $fld) {
1712
        if ($id = $fdoc[$fld]) {
1713
          $ids[$fld] = self::scalar_or_first_elem($id);
1714
        }
1715
        else {
1716
          if (++$u_err < 10) {
1717
            verbose::log(FATAL, 'Missing ' . $fld . ' in solr_result. Record no: ' . (count($search_ids) + $u_err));
1718
          }
1719
          break 2;
1720
        }
83689 fvs 1721
      }
108761 fvs 1722
      $search_ids[] = $ids;
83689 fvs 1723
    }
1724
  }
1725
 
108761 fvs 1726
  /** \brief fetch a result from SOLR
86565 fvs 1727
   *
108761 fvs 1728
   * @param array $q - the extended solr query structure
1729
   * @param integer $start - number of first (starting from 1) record
1730
   * @param integer $rows - (maximum) number of records to return
1731
   * @param string $sort - sorting scheme
1732
   * @param string $rank - ranking sceme
1733
   * @param string $facets - facets-string
1734
   * @param string $filter - the users search profile
1735
   * @param string $boost - boost query (so far empty)
1736
   * @param string $debug - include SOLR debug info
1737
   * @param array $solr_arr - result from SOLR
1738
   * @retval string - error if any, NULL otherwise
86565 fvs 1739
   */
108761 fvs 1740
  private function get_solr_array($q, $start, $rows, $sort, $rank, $facets, $filter, $boost, $debug, &$solr_arr) {
1741
    $solr_urls[0] = self::create_solr_url($q, $start, $rows, $filter, $sort, $rank, $facets, $boost, $debug);
1742
    return self::do_solr($solr_urls, $solr_arr);
86565 fvs 1743
  }
1744
 
108761 fvs 1745
  /** \brief fetch hit count for each register in a given list
70547 fvs 1746
   *
108761 fvs 1747
   * @param array $eq - the edismax part of the parsed user query
1748
   * @param array $guess - registers, filters, ... to get frequence for
1749
   *
1750
   * @reval array - hitcount for each register
70547 fvs 1751
   */
108761 fvs 1752
  private function get_register_freqency($eq, $guess) {
1753
    $q = implode(OR_OP, $eq['q']);
1754
    foreach ($eq['fq'] as $fq) {
1755
      $filter .= '&fq=' . rawurlencode($fq);
70547 fvs 1756
    }
108761 fvs 1757
    foreach ($guess as $idx => $g) {
1758
      $filter = implode('&fq=', $g['filter']);
1759
      $solr_urls[]['url'] = $this->repository['solr'] .  
1760
                            '?q=' . $g['register'] . '%3A(' . urlencode($q) .  ')&fq=' . $filter .  '&start=1&rows=0&wt=phps';
1761
      $ret[$idx] = 0;
70547 fvs 1762
    }
108761 fvs 1763
    $err = self::do_solr($solr_urls, $solr_arr);
1764
    $n = 0;
1765
    foreach ($guess as $idx => $g) {
1766
      $ret[$idx] = self::get_num_found($solr_arr[$n++]);
70547 fvs 1767
    }
108761 fvs 1768
    return $ret;
70547 fvs 1769
  }
1770
 
108761 fvs 1771
  /** \brief build a solr url from a variety of parameters (and an url for debugging)
70547 fvs 1772
   *
108761 fvs 1773
   * @param array $eq - the extended solr query structure
1774
   * @param integer $start - number of first (starting from 1) record
1775
   * @param integer $rows - (maximum) number of records to return
1776
   * @param string $filter - the users search profile
1777
   * @param string $sort - sorting scheme
1778
   * @param string $rank - ranking sceme
1779
   * @param string $facets - facets-string
1780
   * @param string $boost - boost query (so far empty)
1781
   * @param string $debug - include SOLR debug info
1782
   * @retval array - then SOLR url and url for debug purposes
70547 fvs 1783
   */
108761 fvs 1784
  private function create_solr_url($eq, $start, $rows, $filter, $sort='', $rank='', $facets='', $boost='', $debug=FALSE) {
111458 fvs 1785
    $q = '(' . implode(')' . AND_OP . '(', $eq['q']) . ')';   // force parenthesis around each AND-node, to fix SOLR problem. BUG: 20957
108761 fvs 1786
    if (is_array($eq['handler_var'])) {
1787
      $handler_var = '&' . implode('&', $eq['handler_var']);
1788
      $filter = '';  // search profile collection filter is done via fq parm created with handler_var
70547 fvs 1789
    }
108761 fvs 1790
    if (is_array($eq['fq'])) {
1791
      foreach ($eq['fq'] as $fq) {
1792
        $filter .= '&fq=' . rawurlencode($fq);
74010 fvs 1793
      }
70547 fvs 1794
    }
108761 fvs 1795
    $url = $this->repository['solr'] .
1796
                    '?q=' . urlencode($q) .
1797
                    '&fq=' . $filter .
1798
                    '&start=' . $start .  $sort . $rank . $boost . $facets .  $handler_var .
1799
                    '&defType=edismax';
108828 fvs 1800
    $debug_url = $url . '&fl=' . FIELD_FEDORA_PID . ',' . FIELD_UNIT_ID . ',' . FIELD_WORK_ID . '&rows=1&debugQuery=on';
1801
    $url .= '&fl=' . FIELD_FEDORA_PID . ',' . FIELD_UNIT_ID . ',' . FIELD_WORK_ID . '&wt=phps&rows=' . $rows . ($debug ? '&debugQuery=on'