/**
 * Returns array of name for bundles in the specified form. The array is keyed on bundle name (in <table>.<name> format) with values set to arrays of bundle-specific form options (no such options are currently supported however)
 *
 * @param string $ps_code The code for the form
 * @param string $ps_target The type of search the form targets. This should be a table name (ca_objects, ca_entities, etc.). If omitted defaults to ca_objects.
 * @return array An array of bundle names with associated bundle-level form option arrays
 */
function caGetAdvancedSearchFormElementNames($ps_code, $ps_target = 'ca_objects', $pa_options = null)
{
    $o_config = Configuration::load(Configuration::load()->get('advanced_search_config'));
    $va_forms = $o_config->getAssoc($ps_target);
    $va_form_elements = array();
    if (isset($va_forms[$ps_code]) && is_array($va_forms[$ps_code])) {
        $t_element = new ca_metadata_elements();
        foreach ($va_forms[$ps_code]['bundles'] as $vs_bundle => $va_info) {
            $va_tmp = explode('.', $vs_bundle);
            if ($t_element->load(array('element_code' => $va_tmp[1]))) {
                if ($t_element->get('datatype') > 0) {
                    $va_form_elements[$vs_bundle] = $va_info;
                    continue;
                }
                if (isset($pa_options['includeSubElements']) && $pa_options['includeSubElements']) {
                    if (sizeof($va_sub_elements = $t_element->getElementsInSet()) > 1) {
                        foreach ($va_sub_elements as $vn_element_id => $va_element_info) {
                            if ($va_tmp[1] == $va_element_info['element_code']) {
                                continue;
                            }
                            if ($va_element_info['datatype'] == 0) {
                                continue;
                            }
                            $va_form_elements[$va_tmp[0] . '.' . $va_element_info['element_code']] = array();
                        }
                    }
                }
            }
            $va_form_elements[$vs_bundle] = $va_info;
        }
        return $va_form_elements;
    }
    return null;
}
 public function Get($pa_additional_query_params = null, $pa_options = null)
 {
     $ps_query = $this->request->getParameter('term', pString);
     $ps_bundle = $this->request->getParameter('bundle', pString);
     $va_tmp = explode('.', $ps_bundle);
     $o_dm = Datamodel::load();
     if (!($t_table = $o_dm->getInstanceByTableName($va_tmp[0], true))) {
         // bad table name
         print _t("Invalid table name");
         return null;
     }
     $t_element = new ca_metadata_elements();
     if (!$t_element->load(array('element_code' => $va_tmp[1]))) {
         print _t("Invalid element code");
         return null;
     }
     if ((int) $t_element->getSetting('suggestExistingValues') !== 1) {
         print _t("Value suggestion is not supported for this metadata element");
         return null;
     }
     if ($this->request->user->getBundleAccessLevel($va_tmp[0], $va_tmp[1]) == __CA_BUNDLE_ACCESS_NONE__) {
         print _t("You do not have access to this bundle");
         return null;
     }
     $va_type_restrictions = $t_element->getTypeRestrictions($t_table->tableNum());
     if (!$va_type_restrictions || !is_array($va_type_restrictions) || !sizeof($va_type_restrictions)) {
         print _t("Element code is not bound to the specified table");
         return null;
     }
     $o_db = new Db();
     switch ($t_element->getSetting('suggestExistingValueSort')) {
         case 'recent':
             // date/time entered
             $vs_sort_field = 'value_id DESC';
             $vn_max_returned_values = 10;
             break;
         default:
             // alphabetically
             $vs_sort_field = 'value_longtext1 ASC';
             $vn_max_returned_values = 50;
             break;
     }
     $qr_res = $o_db->query("\n\t\t\t\tSELECT DISTINCT value_longtext1\n\t\t\t\tFROM ca_attribute_values\n\t\t\t\tWHERE\n\t\t\t\t\telement_id = ?\n\t\t\t\t\tAND\n\t\t\t\t\t(value_longtext1 LIKE ?)\n\t\t\t\tORDER BY\n\t\t\t\t\t{$vs_sort_field}\n\t\t\t\tLIMIT {$vn_max_returned_values}\n\t\t\t", (int) $t_element->getPrimaryKey(), (string) $ps_query . '%');
     $this->view->setVar('attribute_value_list', $qr_res->getAllFieldValues('value_longtext1'));
     return $this->render('ajax_attribute_value_list_html.php');
 }
 private function getElementAsDOM($pn_parent_id)
 {
     $t_element = new ca_metadata_elements();
     $t_list = new ca_lists();
     $qr_elements = $this->opo_db->query("SELECT * FROM ca_metadata_elements WHERE parent_id = ? ORDER BY element_id", $pn_parent_id);
     if (!$qr_elements->numRows()) {
         return null;
     }
     $vo_elements = $this->opo_dom->createElement("elements");
     while ($qr_elements->nextRow()) {
         $vo_element = $this->opo_dom->createElement("metadataElement");
         $vo_element->setAttribute("code", $this->makeIDNO($qr_elements->get("element_code")));
         $vo_element->setAttribute("datatype", ca_metadata_elements::getAttributeNameForTypeCode($qr_elements->get("datatype")));
         if ($qr_elements->get("list_id")) {
             $t_list->load($qr_elements->get("list_id"));
             $vo_element->setAttribute("list", $t_list->get("list_code"));
         }
         $vo_labels = $this->opo_dom->createElement("labels");
         $qr_element_labels = $this->opo_db->query("SELECT * FROM ca_metadata_element_labels WHERE element_id=?", $qr_elements->get("element_id"));
         while ($qr_element_labels->nextRow()) {
             $vo_label = $this->opo_dom->createElement("label");
             $vo_label->setAttribute("locale", $this->opt_locale->localeIDToCode($qr_element_labels->get("locale_id")));
             $vo_label->appendChild($this->opo_dom->createElement("name", caEscapeForXML($qr_element_labels->get("name"))));
             if (strlen(trim($qr_element_labels->get("description"))) > 0) {
                 $vo_label->appendChild($this->opo_dom->createElement("description", caEscapeForXML($qr_element_labels->get("description"))));
             }
             $vo_labels->appendChild($vo_label);
         }
         $vo_element->appendChild($vo_labels);
         $t_element->load($qr_elements->get("element_id"));
         $vo_settings = $this->opo_dom->createElement("settings");
         if (is_array($va_settings = $t_element->getSettings())) {
             foreach ($va_settings as $vs_setting => $va_values) {
                 if (is_null($va_values)) {
                     continue;
                 }
                 if (!is_array($va_values)) {
                     $va_values = array($va_values);
                 }
                 foreach ($va_values as $vs_value) {
                     $vo_setting = $this->opo_dom->createElement("setting", $vs_value);
                     $vo_setting->setAttribute("name", $vs_setting);
                     $vo_settings->appendChild($vo_setting);
                 }
             }
         }
         $vo_element->appendChild($vo_settings);
         $vo_sub_elements = $this->getElementAsDOM($qr_elements->get("element_id"));
         if ($vo_sub_elements) {
             $vo_element->appendChild($vo_sub_elements);
         }
         $vo_elements->appendChild($vo_element);
     }
     return $vo_elements;
 }
 private function stabilizeElementRanks($pn_parent_id, $pa_ranks)
 {
     $vo_db = new Db();
     $t_element = new ca_metadata_elements();
     do {
         $va_ranks = array_keys($pa_ranks);
         $vn_rank = $va_ranks[0];
         $qr_res = $vo_db->query("\n\t\t\t\tSELECT * FROM\n\t\t\t\t\tca_metadata_elements\n\t\t\t\t\tWHERE\n\t\t\t\t\t\t(parent_id=?)\n\t\t\t\t\t\tAND\n\t\t\t\t\t\t(rank>?)\n\t\t\t\t\tORDER BY\n\t\t\t\t\t\trank\n\t\t\t", $pn_parent_id, $vn_rank);
         while ($qr_res->nextRow()) {
             $t_element->load($qr_res->get('element_id'));
             $t_element->set('rank', intval($t_element->get('rank')) + $pa_ranks[0]);
             $t_element->setMode(ACCESS_WRITE);
             $t_element->update();
         }
         $qr_res = $vo_db->query("\n\t\t\t\tSELECT * FROM\n\t\t\t\t\tca_metadata_elements\n\t\t\t\t\tWHERE\n\t\t\t\t\t\t(parent_id=?)\n\t\t\t\t\t\tAND\n\t\t\t\t\t\t(rank=?)\n\t\t\t\t\tORDER BY\n\t\t\t\t\t\trank\n\t\t\t", $pn_parent_id, $vn_rank);
         $i = 0;
         while ($qr_res->nextRow()) {
             $i++;
             $t_element->load($qr_res->get('element_id'));
             $t_element->set('rank', intval($t_element->get('rank')) + $i);
             $t_element->setMode(ACCESS_WRITE);
             $t_element->update();
         }
         $pa_ranks = $this->elementRankStabilizationNeeded($pn_parent_id);
     } while (is_array($pa_ranks));
 }
 protected function search($pa_bundles = null)
 {
     $va_return = parent::search($pa_bundles);
     if ($this->getTableName() == 'ca_entities' && is_array($va_return['results']) && sizeof($va_return['results']) > 0) {
         $o_db = new Db();
         $t_element = new ca_metadata_elements();
         $t_element->load(array('element_code' => 'is_index_display'));
         $vn_element_id = $t_element->getPrimaryKey();
         if (!$vn_element_id) {
             return $va_return;
         }
         $vn_yes_id = caGetListItemID('yn', 'yes');
         foreach ($va_return['results'] as &$va_result) {
             $vn_entity_id = $va_result['entity_id'];
             $qr_objects = $o_db->query("\n\t\t\t\t\tSELECT DISTINCT ca_objects.object_id FROM ca_entities, ca_objects_x_entities, ca_objects, ca_attributes, ca_attribute_values\n\t\t\t\t\tWHERE ca_entities.entity_id = ca_objects_x_entities.entity_id\n\t\t\t\t\tAND ca_attributes.row_id = ca_objects.object_id\n\t\t\t\t\tAND ca_attribute_values.attribute_id = ca_attributes.attribute_id\n\t\t\t\t\tAND ca_objects_x_entities.object_id = ca_objects.object_id\n\t\t\t\t\tAND ca_attribute_values.element_id = ?\n\t\t\t\t\tAND ca_entities.entity_id = ?\n\t\t\t\t\tAND ca_attribute_values.item_id = ?\n\t\t\t\t\tAND ca_objects.deleted = 0\n\t\t\t\t", $vn_element_id, $vn_entity_id, $vn_yes_id);
             while ($qr_objects->nextRow()) {
                 $va_object_info = array();
                 $vn_object_id = $qr_objects->get('ca_objects.object_id');
                 if (MemoryCache::contains($vn_object_id, 'AboutDrawingServices')) {
                     $va_object_info = MemoryCache::fetch($vn_object_id, 'AboutDrawingServices');
                 } else {
                     $va_object_info['object_id'] = $vn_object_id;
                     $qr_labels = $o_db->query("\n\t\t\t\t\t\t\tSELECT ca_object_labels.* FROM ca_object_labels, ca_objects\n\t\t\t\t\t\t\tWHERE ca_object_labels.object_id = ca_objects.object_id\n\t\t\t\t\t\t\tAND ca_objects.object_id = ?\n\t\t\t\t\t\t", $vn_object_id);
                     $vs_label = '';
                     while ($qr_labels->nextRow()) {
                         $va_object_info['labels'][] = $qr_labels->getRow();
                         $vs_label = $qr_labels->get('name');
                         // keep the last label around
                     }
                     $va_object_info['label'] = $vs_label;
                     $qr_reps = $o_db->query("\n\t\t\t\t\t\t\tSELECT ca_object_representations.* FROM ca_object_representations, ca_objects_x_object_representations, ca_objects\n\t\t\t\t\t\t\tWHERE ca_object_representations.representation_id = ca_objects_x_object_representations.representation_id\n\t\t\t\t\t\t\tAND ca_objects_x_object_representations.object_id = ca_objects.object_id\n\t\t\t\t\t\t\tAND ca_objects.object_id = ?\n\t\t\t\t\t\t\tAND ca_objects.deleted = 0 AND ca_object_representations.deleted = 0\n\t\t\t\t\t\t", $vn_object_id);
                     while ($qr_reps->nextRow()) {
                         $va_object_info['representations'][] = array('representation_id' => $qr_reps->get('representation_id'), 'icon' => $qr_reps->getMediaUrl('media', 'icon'), 'preview170' => $qr_reps->getMediaUrl('media', 'preview170'), 'medium' => $qr_reps->getMediaUrl('media', 'medium'), 'original' => $qr_reps->getMediaUrl('media', 'original'));
                     }
                     MemoryCache::save($vn_object_id, $va_object_info, 'AboutDrawingServices');
                 }
                 $va_result['ca_objects'][] = $va_object_info;
             }
             /* the old&simple, but very slow code
             				$t_entity = new ca_entities($va_result['entity_id']);
             				$va_objects = $t_entity->getRelatedItems('ca_objects');
             
             				foreach($va_objects as $va_object) {
             					$t_object = new ca_objects($va_object['object_id']);
             					$va_reps = $t_object->getRepresentations(array('icon', 'preview170', 'medium', 'original'));
             					$va_representation_info = array();
             					foreach($va_reps as $va_rep) {
             						$va_representation_info[] = array(
             							'representation_id' => $va_rep['representation_id'],
             							'urls' => $va_rep['urls'],
             						);
             					}
             
             					$va_object_info = array();
             					$va_object_info['object_id'] = $va_object['object_id'];
             					$va_object_info['labels'] = $va_object['labels'];
             					$va_object_info['label'] = $va_object['label'];
             					$va_object_info['representations'] = $va_representation_info;
             
             					$va_result['ca_objects'][] = $va_object_info;
             				}*/
         }
     }
     return $va_return;
 }
 /**
  * Given a item_id (request parameter 'id') returns a list of direct children for use in the hierarchy browser
  * Returned data is JSON format
  */
 public function getFacetHierarchyLevel()
 {
     $va_access_values = caGetUserAccessValues($this->request);
     $ps_facet_name = $this->request->getParameter('facet', pString);
     $this->opo_browse->setTypeRestrictions(array($this->opn_type_restriction_id));
     if (!is_array($va_facet_info = $this->opo_browse->getInfoForFacet($ps_facet_name))) {
         return null;
     }
     $va_facet = $this->opo_browse->getFacet($ps_facet_name, array('sort' => 'name', 'checkAccess' => $va_access_values));
     $pa_ids = explode(";", $ps_ids = $this->request->getParameter('id', pString));
     if (!sizeof($pa_ids)) {
         $pa_ids = array(null);
     }
     $va_level_data = array();
     if (($vn_max_items_per_page = $this->request->getParameter('max', pInteger)) < 1 || $vn_max_items_per_page > 1000) {
         $vn_max_items_per_page = null;
     }
     $t_model = $this->opo_datamodel->getInstanceByTableName($this->ops_tablename, true);
     $o_config = Configuration::load();
     if (!is_array($va_sorts = $o_config->getList($this->ops_tablename . '_hierarchy_browser_sort_values')) || !sizeof($va_sorts)) {
         $va_sorts = array();
     }
     foreach ($va_sorts as $vn_i => $vs_sort_fld) {
         $va_tmp = explode(".", $vs_sort_fld);
         if ($va_tmp[1] == 'preferred_labels') {
             $va_tmp[0] = $vs_label_table_name;
             if (!($va_tmp[1] = $va_tmp[2])) {
                 $va_tmp[1] = $vs_label_display_field_name;
             }
             unset($va_tmp[2]);
             $va_sorts[$vn_i] = join(".", $va_tmp);
         }
     }
     if (!in_array($vs_sort_dir = strtolower($o_config->get($this->ops_tablename . '_hierarchy_browser_sort_direction')), array('asc', 'desc'))) {
         $vs_sort_dir = 'asc';
     }
     $va_expanded_facet = array();
     $t_item = new ca_list_items();
     foreach ($va_facet as $vn_id => $va_facet_item) {
         $va_expanded_facet[$vn_id] = true;
         $va_ancestors = $t_item->getHierarchyAncestors($vn_id, array('idsOnly' => true));
         if (is_array($va_ancestors)) {
             foreach ($va_ancestors as $vn_ancestor_id) {
                 $va_expanded_facet[$vn_ancestor_id] = true;
             }
         }
     }
     foreach ($pa_ids as $pn_id) {
         $va_json_data = array('_primaryKey' => 'item_id');
         $va_tmp = explode(":", $pn_id);
         $vn_id = $va_tmp[0];
         $vn_start = (int) $va_tmp[1];
         if ($vn_start < 0) {
             $vn_start = 0;
         }
         switch ($va_facet_info['type']) {
             case 'attribute':
                 // is it a list attribute?
                 $t_element = new ca_metadata_elements();
                 if ($t_element->load(array('element_code' => $va_facet_info['element_code']))) {
                     if ($t_element->get('datatype') == 3) {
                         // 3=list
                         $t_list = new ca_lists();
                         if (!$vn_id) {
                             $vn_id = $t_list->getRootListItemID($t_element->get('list_id'));
                         }
                         $t_item = new ca_list_items($vn_id);
                         $va_children = $t_item->getHierarchyChildren(null, array('idsOnly' => true));
                         $va_child_counts = $t_item->getHierarchyChildCountsForIDs($va_children);
                         $qr_res = caMakeSearchResult('ca_list_items', $va_children);
                         $vs_pk = $t_model->primaryKey();
                         if ($qr_res) {
                             while ($qr_res->nextHit()) {
                                 $vn_parent_id = $qr_res->get('ca_list_items.parent_id');
                                 $vn_item_id = $qr_res->get('ca_list_items.item_id');
                                 if (!isset($va_expanded_facet[$vn_item_id])) {
                                     continue;
                                 }
                                 $va_item = array();
                                 $va_item['item_id'] = $vn_item_id;
                                 $va_item['name'] = $qr_res->get('ca_list_items.preferred_labels');
                                 $va_item['children'] = isset($va_child_counts[$vn_item_id]) && $va_child_counts[$vn_item_id] ? $va_child_counts[$vn_item_id] : 0;
                                 $va_json_data[$vn_item_id] = $va_item;
                             }
                         }
                     }
                 }
                 break;
             case 'label':
                 // label facet
                 $va_facet_info['table'] = $this->ops_tablename;
                 // fall through to default case
             // fall through to default case
             default:
                 if (!$vn_id) {
                     $va_hier_ids = $this->opo_browse->getHierarchyIDsForFacet($ps_facet_name, array('checkAccess' => $va_access_values));
                     $t_item = $this->opo_datamodel->getInstanceByTableName($va_facet_info['table']);
                     $t_item->load($vn_id);
                     $vn_id = $vn_root = $t_item->getHierarchyRootID();
                     $va_hierarchy_list = $t_item->getHierarchyList(true);
                     $vn_last_id = null;
                     $vn_c = 0;
                     foreach ($va_hierarchy_list as $vn_i => $va_item) {
                         if (!in_array($vn_i, $va_hier_ids)) {
                             continue;
                         }
                         // only show hierarchies that have items in browse result
                         if ($vn_start <= $vn_c) {
                             $va_item['item_id'] = $va_item[$t_item->primaryKey()];
                             if (!isset($va_facet[$va_item['item_id']]) && $vn_root == $va_item['item_id']) {
                                 continue;
                             }
                             unset($va_item['parent_id']);
                             unset($va_item['label']);
                             $va_json_data[$va_item['item_id']] = $va_item;
                             $vn_last_id = $va_item['item_id'];
                         }
                         $vn_c++;
                         if (!is_null($vn_max_items_per_page) && $vn_c >= $vn_max_items_per_page + $vn_start) {
                             break;
                         }
                     }
                     if (sizeof($va_json_data) == 2) {
                         // if only one hierarchy root (root +  _primaryKey in array) then don't bother showing it
                         $vn_id = $vn_last_id;
                         unset($va_json_data[$vn_last_id]);
                     }
                 }
                 if ($vn_id) {
                     $vn_c = 0;
                     foreach ($va_facet as $vn_i => $va_item) {
                         if ($va_item['parent_id'] == $vn_id) {
                             if ($vn_start <= $vn_c) {
                                 $va_item['item_id'] = $va_item['id'];
                                 $va_item['name'] = $va_item['label'];
                                 $va_item['children'] = $va_item['child_count'];
                                 unset($va_item['label']);
                                 unset($va_item['child_count']);
                                 unset($va_item['id']);
                                 $va_json_data[$va_item['item_id']] = $va_item;
                             }
                             $vn_c++;
                             if (!is_null($vn_max_items_per_page) && $vn_c >= $vn_max_items_per_page + $vn_start) {
                                 break;
                             }
                         }
                     }
                 }
                 break;
         }
         $vs_rank_fld = $t_item->getProperty('RANK');
         $va_sorted_items = array();
         foreach ($va_json_data as $vn_id => $va_node) {
             if (!is_array($va_node)) {
                 continue;
             }
             $vs_key = preg_replace('![^A-Za-z0-9]!', '_', $va_node['name']);
             if (isset($va_node['sort']) && $va_node['sort']) {
                 $va_sorted_items[$va_node['sort']][$vs_key] = $va_node;
             } else {
                 if ($vs_rank_fld && ($vs_rank = (int) sprintf("%08d", $va_node[$vs_rank_fld]))) {
                     $va_sorted_items[$vs_rank][$vs_key] = $va_node;
                 } else {
                     $va_sorted_items[$vs_key][$vs_key] = $va_node;
                 }
             }
         }
         ksort($va_sorted_items);
         if ($vs_sort_dir == 'desc') {
             $va_sorted_items = array_reverse($va_sorted_items);
         }
         $va_json_data = array();
         $va_sorted_items = array_slice($va_sorted_items, $vn_start, $vn_max_items_per_page);
         foreach ($va_sorted_items as $vs_k => $va_v) {
             ksort($va_v);
             if ($vs_sort_dir == 'desc') {
                 $va_v = array_reverse($va_v);
             }
             $va_json_data = array_merge($va_json_data, $va_v);
         }
         $va_json_data['_itemCount'] = sizeof($va_json_data);
         $va_json_data['_sortOrder'] = array_keys($va_json_data);
         $va_json_data['_primaryKey'] = $t_model->primaryKey();
         // pass the name of the primary key so the hierbrowser knows where to look for item_id's
         $va_level_data[$pn_id] = $va_json_data;
     }
     if (!trim($this->request->getParameter('init', pString))) {
         $this->opo_result_context->setParameter($ps_facet_name . '_browse_last_id', $pn_id);
         $this->opo_result_context->saveContext();
     }
     $this->view->setVar('facet_list', $va_level_data);
     return $this->render('Browse/facet_hierarchy_level_json.php');
 }
Exemple #7
0
 private function _doQueriesForSqlSearch($po_rewritten_query, $pn_subject_tablenum, $ps_dest_table, $pn_level = 0)
 {
     // query is always of type Zend_Search_Lucene_Search_Query_Boolean
     $vn_i = 0;
     $va_old_signs = $po_rewritten_query->getSigns();
     foreach ($po_rewritten_query->getSubqueries() as $o_lucene_query_element) {
         $vb_is_blank_search = false;
         if (is_null($va_old_signs)) {
             // if array is null then according to Zend Lucene all subqueries should be "are required"... so we AND them
             $vs_op = "AND";
         } else {
             if (is_null($va_old_signs[$vn_i])) {
                 // is the sign for a particular query is null then OR is (it is "neither required nor prohibited")
                 $vs_op = 'OR';
             } else {
                 $vs_op = $va_old_signs[$vn_i] === false ? 'NOT' : 'AND';
                 // true sign indicated "required" (AND) operation, false indicated "prohibited" (NOT) operation
             }
         }
         if ($vn_i == 0) {
             $vs_op = 'OR';
         }
         $va_direct_query_temp_tables = array();
         // List of temporary tables created by direct search queries; tables listed here are dropped at the end of processing for the query element
         switch (get_class($o_lucene_query_element)) {
             case 'Zend_Search_Lucene_Search_Query_Boolean':
                 $this->_createTempTable('ca_sql_search_temp_' . $pn_level);
                 $this->_doQueriesForSqlSearch($o_lucene_query_element, $pn_subject_tablenum, 'ca_sql_search_temp_' . $pn_level, $pn_level + 1);
                 // merge with current destination
                 switch ($vs_op) {
                     case 'AND':
                         // and
                         $this->_createTempTable($ps_dest_table . '_acc');
                         if ($vn_i == 0) {
                             $vs_sql = "\n\t\t\t\t\t\t\t\t\tINSERT IGNORE INTO {$ps_dest_table}\n\t\t\t\t\t\t\t\t\tSELECT DISTINCT row_id, boost\n\t\t\t\t\t\t\t\t\tFROM ca_sql_search_temp_{$pn_level}\n\t\t\t\t\t\t\t\t";
                             //print "$vs_sql<hr>";
                             $qr_res = $this->opo_db->query($vs_sql);
                         } else {
                             $vs_sql = "\n\t\t\t\t\t\t\t\t\tINSERT IGNORE INTO {$ps_dest_table}_acc\n\t\t\t\t\t\t\t\t\tSELECT mfs.row_id, SUM(mfs.boost)\n\t\t\t\t\t\t\t\t\tFROM {$ps_dest_table} mfs\n\t\t\t\t\t\t\t\t\tINNER JOIN ca_sql_search_temp_{$pn_level} AS ftmp1 ON ftmp1.row_id = mfs.row_id\n\t\t\t\t\t\t\t\t\tGROUP BY mfs.row_id\n\t\t\t\t\t\t\t\t";
                             //print "$vs_sql<hr>";
                             $qr_res = $this->opo_db->query($vs_sql);
                             $qr_res = $this->opo_db->query("TRUNCATE TABLE {$ps_dest_table}");
                             $qr_res = $this->opo_db->query("INSERT INTO {$ps_dest_table} SELECT row_id, boost FROM {$ps_dest_table}_acc");
                         }
                         $this->_dropTempTable($ps_dest_table . '_acc');
                         break;
                     case 'NOT':
                         $vs_sql = "\n\t\t\t\t\t\t\t\tDELETE FROM {$ps_dest_table} WHERE row_id IN\n\t\t\t\t\t\t\t\t(SELECT row_id FROM ca_sql_search_temp_{$pn_level})\n\t\t\t\t\t\t\t";
                         //print "$vs_sql<hr>";
                         $qr_res = $this->opo_db->query($vs_sql);
                         break;
                     default:
                     case 'OR':
                         // or
                         $vs_sql = "\n\t\t\t\t\t\t\t\tINSERT IGNORE INTO {$ps_dest_table}\n\t\t\t\t\t\t\t\tSELECT row_id, SUM(boost)\n\t\t\t\t\t\t\t\tFROM ca_sql_search_temp_{$pn_level}\n\t\t\t\t\t\t\t\tGROUP BY row_id\n\t\t\t\t\t\t\t";
                         //print "$vs_sql<hr>";
                         $qr_res = $this->opo_db->query($vs_sql);
                         break;
                 }
                 $this->_dropTempTable('ca_sql_search_temp_' . $pn_level);
                 break;
             case 'Zend_Search_Lucene_Search_Query_Term':
             case 'Zend_Search_Lucene_Search_Query_MultiTerm':
             case 'Zend_Search_Lucene_Search_Query_Phrase':
             case 'Zend_Search_Lucene_Search_Query_Range':
                 $va_ft_terms = array();
                 $va_ft_like_terms = array();
                 $va_ft_stem_terms = array();
                 $vs_direct_sql_query = null;
                 $pa_direct_sql_query_params = null;
                 // set to array with values to use with direct SQL query placeholders or null to pass single standard table_num value as param (most queries just need this single value)
                 $va_tmp = array();
                 $vs_access_point = '';
                 $va_raw_terms = array();
                 switch (get_class($o_lucene_query_element)) {
                     case 'Zend_Search_Lucene_Search_Query_Range':
                         $va_lower_term = $o_lucene_query_element->getLowerTerm();
                         $va_upper_term = $o_lucene_query_element->getUpperTerm();
                         $va_element = $this->_getElementIDForAccessPoint($va_lower_term->field);
                         switch ($va_element['datatype']) {
                             case 4:
                                 // geocode
                                 $t_geocode = new GeocodeAttributeValue();
                                 $va_parsed_value = $t_geocode->parseValue($va_lower_term->text, $va_element['element_info']);
                                 $vs_lower_lat = $va_parsed_value['value_decimal1'];
                                 $vs_lower_long = $va_parsed_value['value_decimal2'];
                                 $va_parsed_value = $t_geocode->parseValue($va_upper_term->text, $va_element['element_info']);
                                 $vs_upper_lat = $va_parsed_value['value_decimal1'];
                                 $vs_upper_long = $va_parsed_value['value_decimal2'];
                                 $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = " . intval($va_element['element_id']) . ") AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 BETWEEN " . floatval($vs_lower_lat) . " AND " . floatval($vs_upper_lat) . ")\n\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal2 BETWEEN " . floatval($vs_lower_long) . " AND " . floatval($vs_upper_long) . ")\t\n\t\t\t\t\t\t\t\t\t";
                                 break;
                             case 6:
                                 // currency
                                 $t_cur = new CurrencyAttributeValue();
                                 $va_parsed_value = $t_cur->parseValue($va_lower_term->text, $va_element['element_info']);
                                 $vs_currency = preg_replace('![^A-Z0-9]+!', '', $va_parsed_value['value_longtext1']);
                                 $vn_lower_val = $va_parsed_value['value_decimal1'];
                                 $va_parsed_value = $t_cur->parseValue($va_upper_term->text, $va_element['element_info']);
                                 $vn_upper_val = $va_parsed_value['value_decimal1'];
                                 $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = " . intval($va_element['element_id']) . ") AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 BETWEEN " . floatval($vn_lower_val) . " AND " . floatval($vn_upper_val) . ")\n\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t(cav.value_longtext1 = '" . $this->opo_db->escape($vs_currency) . "')\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t";
                                 break;
                             case 10:
                                 // timecode
                                 $t_timecode = new TimecodeAttributeValue();
                                 $va_parsed_value = $t_timecode->parseValue($va_lower_term->text, $va_element['element_info']);
                                 $vn_lower_val = $va_parsed_value['value_decimal1'];
                                 $va_parsed_value = $t_timecode->parseValue($va_upper_term->text, $va_element['element_info']);
                                 $vn_upper_val = $va_parsed_value['value_decimal1'];
                                 break;
                             case 8:
                                 // length
                                 $t_len = new LengthAttributeValue();
                                 $va_parsed_value = $t_len->parseValue($va_lower_term->text, $va_element['element_info']);
                                 $vn_lower_val = $va_parsed_value['value_decimal1'];
                                 $va_parsed_value = $t_len->parseValue($va_upper_term->text, $va_element['element_info']);
                                 $vn_upper_val = $va_parsed_value['value_decimal1'];
                                 break;
                             case 9:
                                 // weight
                                 $t_weight = new WeightAttributeValue();
                                 $va_parsed_value = $t_weight->parseValue($va_lower_term->text, $va_element['element_info']);
                                 $vn_lower_val = $va_parsed_value['value_decimal1'];
                                 $va_parsed_value = $t_weight->parseValue($va_upper_term->text, $va_element['element_info']);
                                 $vn_upper_val = $va_parsed_value['value_decimal1'];
                                 break;
                             case 11:
                                 // integer
                                 $vn_lower_val = intval($va_lower_term->text);
                                 $vn_upper_val = intval($va_upper_term->text);
                                 $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = " . intval($va_element['element_id']) . ") AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t(cav.value_integer1 BETWEEN " . floatval($vn_lower_val) . " AND " . floatval($vn_upper_val) . ")\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t";
                                 break;
                             case 12:
                                 // decimal
                                 $vn_lower_val = floatval($va_lower_term->text);
                                 $vn_upper_val = floatval($va_upper_term->text);
                                 break;
                         }
                         if (!$vs_direct_sql_query) {
                             $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t(cav.element_id = " . intval($va_element['element_id']) . ") AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 BETWEEN " . floatval($vn_lower_val) . " AND " . floatval($vn_upper_val) . ")\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t";
                         }
                         break;
                     case 'Zend_Search_Lucene_Search_Query_Phrase':
                         $va_words = array();
                         foreach ($o_lucene_query_element->getQueryTerms() as $o_term) {
                             if (!$vs_access_point && ($vs_field = $o_term->field)) {
                                 $vs_access_point = $vs_field;
                             }
                             $va_raw_terms[] = $vs_text = (string) $o_term->text;
                             if (strlen($vs_escaped_text = $this->opo_db->escape($vs_text))) {
                                 $va_words[] = $vs_escaped_text;
                             }
                         }
                         if (!sizeof($va_words)) {
                             continue 3;
                         }
                         $va_ap_tmp = explode(".", $vs_access_point);
                         $vn_fld_table = $vn_fld_num = null;
                         if (sizeof($va_ap_tmp) == 2) {
                             $va_element = $this->_getElementIDForAccessPoint($vs_access_point);
                             if ($va_element) {
                                 $vs_fld_num = $va_element['field_num'];
                                 $vs_fld_table_num = $va_element['table_num'];
                                 $vs_fld_limit_sql = " AND (swi.field_table_num = {$vs_fld_table_num} AND swi.field_num = '{$vs_fld_num}')";
                             }
                         }
                         $va_temp_tables = array();
                         $vn_w = 0;
                         foreach ($va_words as $vs_word) {
                             $vn_w++;
                             $vs_temp_table = 'ca_sql_search_phrase_' . md5($pn_subject_tablenum . "/" . $vs_word . "/" . $vn_w);
                             $this->_createTempTable($vs_temp_table);
                             $vs_sql = "\n\t\t\t\t\t\t\t\t\tINSERT INTO {$vs_temp_table}\n\t\t\t\t\t\t\t\t\tSELECT swi.index_id + 1, 1\n\t\t\t\t\t\t\t\t\tFROM ca_sql_search_words sw \n\t\t\t\t\t\t\t\t\tINNER JOIN ca_sql_search_word_index AS swi ON sw.word_id = swi.word_id \n\t\t\t\t\t\t\t\t\t" . (sizeof($va_temp_tables) ? " INNER JOIN " . $va_temp_tables[sizeof($va_temp_tables) - 1] . " AS tt ON swi.index_id = tt.row_id" : "") . "\n\t\t\t\t\t\t\t\t\tWHERE \n\t\t\t\t\t\t\t\t\t\tsw.word = ? AND swi.table_num = ? {$vs_fld_limit_sql}\n \t\t\t\t\t\t\t\t\t\t" . ($this->getOption('omitPrivateIndexing') ? " AND swi.access = 0" : '') . "\n\t\t\t\t\t\t\t\t";
                             $qr_res = $this->opo_db->query($vs_sql, $vs_word, (int) $pn_subject_tablenum);
                             $qr_count = $this->opo_db->query("SELECT count(*) c FROM {$vs_temp_table}");
                             if (!$qr_count->nextRow() || !(int) $qr_count->get('c')) {
                                 foreach ($va_temp_tables as $vs_temp_table) {
                                     $this->_dropTempTable($vs_temp_table);
                                 }
                                 break 2;
                             }
                             $va_temp_tables[] = $vs_temp_table;
                         }
                         $vs_results_temp_table = array_pop($va_temp_tables);
                         $this->opo_db->query("UPDATE {$vs_results_temp_table} SET row_id = row_id - 1");
                         $va_direct_query_temp_tables[$vs_results_temp_table] = true;
                         $vs_direct_sql_query = "SELECT swi.row_id, ca.boost \n\t\t\t\t\t\t\t\t\t\t\t\t\tFROM {$vs_results_temp_table} ca\n\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_sql_search_word_index AS swi ON swi.index_id = ca.row_id \n\t\t\t\t\t\t\t";
                         $pa_direct_sql_query_params = array();
                         // don't pass any params
                         foreach ($va_temp_tables as $vs_temp_table) {
                             $this->_dropTempTable($vs_temp_table);
                         }
                         break;
                     case 'Zend_Search_Lucene_Search_Query_MultiTerm':
                         $va_ft_like_term_list = array();
                         foreach ($o_lucene_query_element->getTerms() as $o_term) {
                             $va_raw_terms[] = $vs_term = (string) (method_exists($o_term, "getTerm") ? $o_term->getTerm()->text : $o_term->text);
                             if (!$vs_access_point && ($vs_field = method_exists($o_term, "getTerm") ? $o_term->getTerm()->field : $o_term->field)) {
                                 $vs_access_point = $vs_field;
                             }
                             $vs_stripped_term = preg_replace('!\\*+$!u', '', $vs_term);
                             $va_ft_like_terms[] = $vs_stripped_term . ($vb_had_wildcard ? '%' : '');
                         }
                         break;
                     default:
                         $vs_access_point = $o_lucene_query_element->getTerm()->field;
                         $vs_term = $o_lucene_query_element->getTerm()->text;
                         if ($vs_access_point && mb_strtoupper($vs_term) == _t('[BLANK]')) {
                             $vb_is_blank_search = true;
                             break;
                         }
                         $va_terms = $this->_tokenize($vs_term, true, $vn_i);
                         $vb_output_term = false;
                         foreach ($va_terms as $vs_term) {
                             if (in_array(trim(mb_strtolower($vs_term, 'UTF-8')), WLPlugSearchEngineSqlSearch::$s_stop_words)) {
                                 continue;
                             }
                             if (get_class($o_lucene_query_element) != 'Zend_Search_Lucene_Search_Query_MultiTerm') {
                                 $vs_stripped_term = preg_replace('!\\*+$!u', '', $vs_term);
                                 // do stemming
                                 if ($this->opb_do_stemming) {
                                     $vs_to_stem = preg_replace('!\\*$!u', '', $vs_term);
                                     if (!preg_match('!y$!u', $vs_to_stem) && !preg_match('![0-9]+!', $vs_to_stem)) {
                                         // don't stem things ending in 'y' as that can cause problems (eg "Bowery" becomes "Boweri")
                                         if (!($vs_stem = trim($this->opo_stemmer->stem($vs_to_stem)))) {
                                             $vs_stem = (string) $vs_term;
                                         }
                                         $va_ft_stem_terms[] = "'" . $this->opo_db->escape($vs_stem) . "'";
                                     } else {
                                         $va_ft_terms[] = '"' . $this->opo_db->escape($vs_term) . '"';
                                     }
                                 } else {
                                     $va_ft_terms[] = '"' . $this->opo_db->escape($vs_term) . '"';
                                 }
                                 $vb_output_term = true;
                             }
                         }
                         if ($vb_output_term) {
                             $va_raw_terms[] = $vs_term;
                         } else {
                             $vn_i--;
                         }
                         break;
                 }
                 $vs_fld_num = $vs_table_num = $t_table = null;
                 $vb_ft_bit_optimization = false;
                 if ($vs_access_point) {
                     list($vs_table, $vs_field, $vs_sub_field) = explode('.', $vs_access_point);
                     if (in_array($vs_table, array('created', 'modified'))) {
                         $o_tep = new TimeExpressionParser();
                         $vs_date = join(' ', $va_raw_terms);
                         if (!$o_tep->parse($vs_date)) {
                             break;
                         }
                         $va_range = $o_tep->getUnixTimestamps();
                         $vn_user_id = null;
                         if ($vs_field = trim($vs_field)) {
                             if (!is_int($vs_field)) {
                                 $t_user = new ca_users();
                                 if ($t_user->load(array("user_name" => $vs_field))) {
                                     $vn_user_id = (int) $t_user->getPrimaryKey();
                                 }
                             } else {
                                 $vn_user_id = (int) $vs_field;
                             }
                         }
                         $vs_user_sql = $vn_user_id ? " AND (ccl.user_id = " . (int) $vn_user_id . ")" : "";
                         switch ($vs_table) {
                             case 'created':
                                 $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\tSELECT ccl.logged_row_id, 1\n\t\t\t\t\t\t\t\t\t\t\tFROM ca_change_log ccl\n\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t(ccl.log_datetime BETWEEN " . (int) $va_range['start'] . " AND " . (int) $va_range['end'] . ")\n\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t(ccl.logged_table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t(ccl.changetype = 'I')\n\t\t\t\t\t\t\t\t\t\t\t\t{$vs_user_sql}\n\t\t\t\t\t\t\t\t\t\t";
                                 break;
                             case 'modified':
                                 $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\tSELECT ccl.logged_row_id, 1\n\t\t\t\t\t\t\t\t\t\t\tFROM ca_change_log ccl\n\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t(ccl.log_datetime BETWEEN " . (int) $va_range['start'] . " AND " . (int) $va_range['end'] . ")\n\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t(ccl.logged_table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t(ccl.changetype = 'U')\n\t\t\t\t\t\t\t\t\t\t\t\t{$vs_user_sql}\n\t\t\t\t\t\t\t\t\t\tUNION\n\t\t\t\t\t\t\t\t\t\t\tSELECT ccls.subject_row_id, 1\n\t\t\t\t\t\t\t\t\t\t\tFROM ca_change_log ccl\n\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_change_log_subjects AS ccls ON ccls.log_id = ccl.log_id\n\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t(ccl.log_datetime BETWEEN " . (int) $va_range['start'] . " AND " . (int) $va_range['end'] . ")\n\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t(ccls.subject_table_num = {$pn_subject_tablenum})\n\t\t\t\t\t\t\t\t\t\t\t\t{$vs_user_sql}\n\t\t\t\t\t\t\t\t\t\t";
                                 break;
                         }
                     } else {
                         if ($vs_table && $vs_field) {
                             $t_table = $this->opo_datamodel->getInstanceByTableName($vs_table, true);
                             if ($t_table) {
                                 $vs_table_num = $t_table->tableNum();
                                 if (is_numeric($vs_field)) {
                                     $vs_fld_num = 'I' . $vs_field;
                                     $vn_fld_num = (int) $vs_field;
                                 } else {
                                     $vn_fld_num = $this->getFieldNum($vs_table, $vs_field);
                                     $vs_fld_num = 'I' . $vn_fld_num;
                                     if (!strlen($vn_fld_num)) {
                                         $t_element = new ca_metadata_elements();
                                         if ($t_element->load(array('element_code' => $vs_sub_field ? $vs_sub_field : $vs_field))) {
                                             $vn_fld_num = $t_element->getPrimaryKey();
                                             $vs_fld_num = 'A' . $vn_fld_num;
                                             if (!$vb_is_blank_search) {
                                                 //
                                                 // For certain types of attributes we can directly query the
                                                 // attributes in the database rather than using the full text index
                                                 // This allows us to do "intelligent" querying... for example on date ranges
                                                 // parsed from natural language input and for length dimensions using unit conversion
                                                 //
                                                 switch ($t_element->get('datatype')) {
                                                     case 2:
                                                         // dates
                                                         $vb_all_numbers = true;
                                                         foreach ($va_raw_terms as $vs_term) {
                                                             if (!is_numeric($vs_term)) {
                                                                 $vb_all_numbers = false;
                                                                 break;
                                                             }
                                                         }
                                                         $vs_raw_term = join(' ', $va_raw_terms);
                                                         $vb_exact = $vs_raw_term[0] == "#" ? true : false;
                                                         // dates prepended by "#" are considered "exact" or "contained - the matched dates must be wholly contained by the search term
                                                         if ($vb_exact) {
                                                             $vs_raw_term = substr($vs_raw_term, 1);
                                                             if ($this->opo_tep->parse($vs_raw_term)) {
                                                                 $va_dates = $this->opo_tep->getHistoricTimestamps();
                                                                 $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 BETWEEN " . floatval($va_dates['start']) . " AND " . floatval($va_dates['end']) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal2 BETWEEN " . floatval($va_dates['start']) . " AND " . floatval($va_dates['end']) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                             }
                                                         } else {
                                                             if ($this->opo_tep->parse($vs_raw_term)) {
                                                                 $va_dates = $this->opo_tep->getHistoricTimestamps();
                                                                 $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 BETWEEN " . floatval($va_dates['start']) . " AND " . floatval($va_dates['end']) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal2 BETWEEN " . floatval($va_dates['start']) . " AND " . floatval($va_dates['end']) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 <= " . floatval($va_dates['start']) . " AND cav.value_decimal2 >= " . floatval($va_dates['end']) . ")\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                             }
                                                         }
                                                         break;
                                                     case 4:
                                                         // geocode
                                                         $t_geocode = new GeocodeAttributeValue();
                                                         // If it looks like a lat/long pair that has been tokenized by Lucene
                                                         // into oblivion rehydrate it here.
                                                         if ($va_coords = caParseGISSearch(join(' ', $va_raw_terms))) {
                                                             $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 BETWEEN {$va_coords['min_latitude']} AND {$va_coords['max_latitude']})\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal2 BETWEEN {$va_coords['min_longitude']} AND {$va_coords['max_longitude']})\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                         }
                                                         break;
                                                     case 6:
                                                         // currency
                                                         $t_cur = new CurrencyAttributeValue();
                                                         $va_parsed_value = $t_cur->parseValue(join(' ', $va_raw_terms), $t_element->getFieldValuesArray());
                                                         $vn_amount = $va_parsed_value['value_decimal1'];
                                                         $vs_currency = preg_replace('![^A-Z0-9]+!', '', $va_parsed_value['value_longtext1']);
                                                         $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 = " . floatval($vn_amount) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_longtext1 = '" . $this->opo_db->escape($vs_currency) . "')\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                         break;
                                                     case 8:
                                                         // length
                                                         $t_len = new LengthAttributeValue();
                                                         $va_parsed_value = $t_len->parseValue(array_shift($va_raw_terms), $t_element->getFieldValuesArray());
                                                         $vn_len = $va_parsed_value['value_decimal1'];
                                                         // this is always in meters so we can compare this value to the one in the database
                                                         $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 = " . floatval($vn_len) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                         break;
                                                     case 9:
                                                         // weight
                                                         $t_weight = new WeightAttributeValue();
                                                         $va_parsed_value = $t_weight->parseValue(array_shift($va_raw_terms), $t_element->getFieldValuesArray());
                                                         $vn_weight = $va_parsed_value['value_decimal1'];
                                                         // this is always in kilograms so we can compare this value to the one in the database
                                                         $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 = " . floatval($vn_weight) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                         break;
                                                     case 10:
                                                         // timecode
                                                         $t_timecode = new TimecodeAttributeValue();
                                                         $va_parsed_value = $t_timecode->parseValue(join(' ', $va_raw_terms), $t_element->getFieldValuesArray());
                                                         $vn_timecode = $va_parsed_value['value_decimal1'];
                                                         $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 = " . floatval($vn_timecode) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                         break;
                                                     case 11:
                                                         // integer
                                                         $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_integer1 = " . intval(array_shift($va_raw_terms)) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                         break;
                                                     case 12:
                                                         // decimal
                                                         $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 = " . floatval(array_shift($va_raw_terms)) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                         break;
                                                 }
                                             }
                                         } else {
                                             // neither table fields nor elements, i.e. 'virtual' fields like _count should
                                             $vn_fld_num = false;
                                             $vs_fld_num = $vs_field;
                                         }
                                     }
                                 }
                                 if ($t_table->getFieldInfo($t_table->fieldName($vn_fld_num), 'FIELD_TYPE') == FT_BIT) {
                                     $vb_ft_bit_optimization = true;
                                 }
                             }
                         }
                     }
                 }
                 //
                 // If we're querying on the fulltext index then we need to construct
                 // the query here... if we already have a direct SQL query to run then we can skip this
                 //
                 if ($vb_is_blank_search) {
                     $va_sql_where[] = "((swi.field_table_num = " . intval($vs_table_num) . ") AND (swi.field_num = '{$vs_fld_num}') AND (swi.word_id = 0))";
                     if (!sizeof($va_sql_where)) {
                         continue;
                     }
                     $vs_sql_where = join(' OR ', $va_sql_where);
                 } elseif (!$vs_direct_sql_query) {
                     $va_sql_where = array();
                     if (sizeof($va_ft_terms)) {
                         if ($t_table && strlen($vs_fld_num) > 1) {
                             $va_sql_where[] = "((swi.field_table_num = " . intval($vs_table_num) . ") AND (swi.field_num = '{$vs_fld_num}') AND (sw.word IN (" . join(',', $va_ft_terms) . ")))";
                         } else {
                             if (sizeof($va_ft_terms) == 1) {
                                 $va_sql_where[] = "(sw.word = " . $va_ft_terms[0] . ")";
                             } else {
                                 $va_sql_where[] = "(sw.word IN (" . join(',', $va_ft_terms) . "))";
                             }
                         }
                     }
                     if (sizeof($va_ft_like_terms)) {
                         $va_tmp = array();
                         foreach ($va_ft_like_terms as $vs_term) {
                             if ($vb_ft_bit_optimization) {
                                 $va_tmp[] = '(sw.word = \' ' . $this->opo_db->escape(trim($vs_term)) . ' \')';
                             } else {
                                 $va_tmp[] = '(sw.word LIKE \'' . $this->opo_db->escape(trim($vs_term)) . '%\')';
                             }
                         }
                         if ($t_table && strlen($vs_fld_num) > 1) {
                             $va_sql_where[] = "((swi.field_table_num = " . intval($vs_table_num) . ") AND (swi.field_num = '{$vs_fld_num}') AND (" . join(' AND ', $va_tmp) . "))";
                         } else {
                             $va_sql_where[] = "(" . join(' AND ', $va_tmp) . ")";
                         }
                     }
                     if (sizeof($va_ft_stem_terms)) {
                         if ($t_table && strlen($vs_fld_num) > 1) {
                             $va_sql_where[] = "((swi.field_table_num = " . intval($vs_table_num) . ") AND (swi.field_num = '{$vs_fld_num}') AND (sw.stem IN (" . join(',', $va_ft_stem_terms) . ")))";
                         } else {
                             $va_sql_where[] = "(sw.stem IN (" . join(',', $va_ft_stem_terms) . "))";
                         }
                     }
                     if (!sizeof($va_sql_where)) {
                         continue;
                     }
                     $vs_sql_where = join(' OR ', $va_sql_where);
                 } else {
                     $va_ft_terms = $va_ft_like_terms = $va_ft_like_terms = array();
                 }
                 //print "OP=$vs_op<br>";
                 if ($vn_i == 0) {
                     if ($vs_direct_sql_query) {
                         $vs_direct_sql_query = str_replace('^JOIN', "", $vs_direct_sql_query);
                     }
                     $vs_sql = $vs_direct_sql_query ? "INSERT IGNORE INTO {$ps_dest_table} {$vs_direct_sql_query}" : "\n\t\t\t\t\t\t\tINSERT IGNORE INTO {$ps_dest_table}\n\t\t\t\t\t\t\tSELECT swi.row_id, SUM(swi.boost)\n\t\t\t\t\t\t\tFROM ca_sql_search_word_index swi\n\t\t\t\t\t\t\t" . (!$vb_is_blank_search ? "INNER JOIN ca_sql_search_words AS sw ON sw.word_id = swi.word_id" : '') . "\n\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t{$vs_sql_where}\n\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\tswi.table_num = ?\n\t\t\t\t\t\t\t\t" . ($this->getOption('omitPrivateIndexing') ? " AND swi.access = 0" : '') . "\n\t\t\t\t\t\t\tGROUP BY swi.row_id \n\t\t\t\t\t\t";
                     if (($vn_num_terms = sizeof($va_ft_terms) + sizeof($va_ft_like_terms) + sizeof($va_ft_stem_terms)) > 1 && !$vs_direct_sql_query) {
                         $vs_sql .= " HAVING count(distinct sw.word_id) = {$vn_num_terms}";
                     }
                     if ($this->debug) {
                         print 'FIRST: ' . $vs_sql . " [{$pn_subject_tablenum}]<hr>\n";
                     }
                     //print $vs_sql;
                     $qr_res = $this->opo_db->query($vs_sql, is_array($pa_direct_sql_query_params) ? $pa_direct_sql_query_params : array((int) $pn_subject_tablenum));
                 } else {
                     switch ($vs_op) {
                         case 'AND':
                             if ($vs_direct_sql_query) {
                                 $vs_direct_sql_query = str_replace('^JOIN', "INNER JOIN {$ps_dest_table} AS ftmp1 ON ftmp1.row_id = ca.row_id", $vs_direct_sql_query);
                             }
                             $this->_createTempTable($ps_dest_table . '_acc');
                             $vs_sql = $vs_direct_sql_query ? "INSERT IGNORE INTO {$ps_dest_table}_acc {$vs_direct_sql_query}" : "\n\t\t\t\t\t\t\t\t\tINSERT IGNORE INTO {$ps_dest_table}_acc\n\t\t\t\t\t\t\t\t\tSELECT swi.row_id, SUM(swi.boost)\n\t\t\t\t\t\t\t\t\tFROM ca_sql_search_word_index swi\n\t\t\t\t\t\t\t\t\tINNER JOIN ca_sql_search_words AS sw ON sw.word_id = swi.word_id\n\t\t\t\t\t\t\t\t\tINNER JOIN {$ps_dest_table} AS ftmp1 ON ftmp1.row_id = swi.row_id\n\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t{$vs_sql_where}\n\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\tswi.table_num = ?\n\t\t\t\t\t\t\t\t\t\t" . ($this->getOption('omitPrivateIndexing') ? " AND swi.access = 0" : '') . "\n\t\t\t\t\t\t\t\t\tGROUP BY\n\t\t\t\t\t\t\t\t\t\tswi.row_id\n\t\t\t\t\t\t\t\t";
                             if (($vn_num_terms = sizeof($va_ft_terms) + sizeof($va_ft_like_terms) + sizeof($va_ft_stem_terms)) > 1) {
                                 $vs_sql .= " HAVING count(distinct sw.word_id) = {$vn_num_terms}";
                             }
                             if ($this->debug) {
                                 print 'AND:' . $vs_sql . "<hr>\n";
                             }
                             $qr_res = $this->opo_db->query($vs_sql, is_array($pa_direct_sql_query_params) ? $pa_direct_sql_query_params : array((int) $pn_subject_tablenum));
                             $qr_res = $this->opo_db->query("TRUNCATE TABLE {$ps_dest_table}");
                             $qr_res = $this->opo_db->query("INSERT INTO {$ps_dest_table} SELECT row_id, boost FROM {$ps_dest_table}_acc");
                             //$qr_res = $this->opo_db->query("TRUNCATE TABLE ca_sql_search_temp_2");
                             $this->_dropTempTable($ps_dest_table . '_acc');
                             break;
                         case 'NOT':
                             if ($vs_direct_sql_query) {
                                 $vs_direct_sql_query = str_replace('^JOIN', "", $vs_direct_sql_query);
                             }
                             $vs_sql = "\n\t\t\t\t\t\t\t\t\tSELECT row_id\n\t\t\t\t\t\t\t\t\tFROM ca_sql_search_words sw\n\t\t\t\t\t\t\t\t\tINNER JOIN ca_sql_search_word_index AS swi ON sw.word_id = swi.word_id\n\t\t\t\t\t\t\t\t\tWHERE \n\t\t\t\t\t\t\t\t\t\t" . ($vs_sql_where ? "{$vs_sql_where} AND " : "") . " swi.table_num = ? \n\t\t\t\t\t\t\t\t\t\t" . ($this->getOption('omitPrivateIndexing') ? " AND swi.access = 0" : '');
                             //print "$vs_sql<hr>";
                             $qr_res = $this->opo_db->query($vs_sql, is_array($pa_direct_sql_query_params) ? $pa_direct_sql_query_params : array((int) $pn_subject_tablenum));
                             $va_ids = $qr_res->getAllFieldValues("row_id");
                             $vs_sql = "\n\t\t\t\t\t\t\t\t\tDELETE FROM {$ps_dest_table} \n\t\t\t\t\t\t\t\t\tWHERE \n\t\t\t\t\t\t\t\t\t\trow_id IN (?)\n\t\t\t\t\t\t\t\t";
                             $qr_res = $this->opo_db->query($vs_sql, array($va_ids));
                             //print "$vs_sql<hr>";
                             break;
                         default:
                         case 'OR':
                             if ($vs_direct_sql_query) {
                                 $vs_direct_sql_query = str_replace('^JOIN', "", $vs_direct_sql_query);
                             }
                             $vs_sql = $vs_direct_sql_query ? "INSERT IGNORE INTO {$ps_dest_table} {$vs_direct_sql_query}" : "\n\t\t\t\t\t\t\t\t\tINSERT IGNORE INTO {$ps_dest_table}\n\t\t\t\t\t\t\t\t\tSELECT swi.row_id, SUM(swi.boost)\n\t\t\t\t\t\t\t\t\tFROM ca_sql_search_word_index swi\n\t\t\t\t\t\t\t\t\tINNER JOIN ca_sql_search_words AS sw ON sw.word_id = swi.word_id\n\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t{$vs_sql_where}\n\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\tswi.table_num = ?\n\t\t\t\t\t\t\t\t\t\t" . ($this->getOption('omitPrivateIndexing') ? " AND swi.access = 0" : '') . "\n\t\t\t\t\t\t\t\t\tGROUP BY\n\t\t\t\t\t\t\t\t\t\tswi.row_id\n\t\t\t\t\t\t\t\t";
                             if ($this->debug) {
                                 print 'OR' . $vs_sql . "<hr>\n";
                             }
                             $qr_res = $this->opo_db->query($vs_sql, is_array($pa_direct_sql_query_params) ? $pa_direct_sql_query_params : array((int) $pn_subject_tablenum));
                             break;
                     }
                 }
                 // Drop any temporary tables created by direct search queries
                 foreach (array_keys($va_direct_query_temp_tables) as $vs_temp_table_to_drop) {
                     $this->_dropTempTable($vs_temp_table_to_drop);
                 }
                 break;
             default:
                 //print get_class($o_lucene_query_element);
                 break;
         }
         $vn_i++;
     }
 }
Exemple #8
0
 /**
  * Return list of items from the specified table that are related to the current browse set. This is the method that actually
  * pulls the facet content, regardless of whether the facet is cached yet or not. If you want to use the facet cache, call
  * BrowseEngine::getFacet()
  *
  * @see BrowseEngine::getFacet()
  * Options:
  *		checkAccess = array of access values to filter facets that have an 'access' field by
  *		checkAvailabilityOnly = if true then content is not actually fetch - only the availablility of content is verified
  *		user_id = If set item level access control is performed relative to specified user_id, otherwise defaults to logged in user
  */
 public function getFacetContent($ps_facet_name, $pa_options = null)
 {
     global $AUTH_CURRENT_USER_ID;
     $vs_browse_table_name = $this->ops_browse_table_name;
     $vs_browse_table_num = $this->opn_browse_table_num;
     $vn_user_id = isset($pa_options['user_id']) && (int) $pa_options['user_id'] ? (int) $pa_options['user_id'] : (int) $AUTH_CURRENT_USER_ID;
     $vb_show_if_no_acl = (bool) ($this->opo_config->get('default_item_access_level') > __CA_ACL_NO_ACCESS__);
     $t_user = new ca_users($vn_user_id);
     if (is_array($va_groups = $t_user->getUserGroups()) && sizeof($va_groups)) {
         $va_group_ids = array_keys($va_groups);
     } else {
         $va_group_ids = array();
     }
     if (!is_array($this->opa_browse_settings)) {
         return null;
     }
     if (!isset($this->opa_browse_settings['facets'][$ps_facet_name])) {
         return null;
     }
     if (!is_array($pa_options)) {
         $pa_options = array();
     }
     $vb_check_availability_only = isset($pa_options['checkAvailabilityOnly']) ? (bool) $pa_options['checkAvailabilityOnly'] : false;
     $va_all_criteria = $this->getCriteria();
     $va_criteria = $this->getCriteria($ps_facet_name);
     $va_facet_info = $this->opa_browse_settings['facets'][$ps_facet_name];
     $t_subject = $this->getSubjectInstance();
     if ($va_facet_info['relative_to']) {
         $vs_browse_table_name = $va_facet_info['relative_to'];
         $vs_browse_table_num = $this->opo_datamodel->getTableNum($vs_browse_table_name);
     }
     $vs_browse_type_limit_sql = '';
     if (($va_browse_type_ids = $this->getTypeRestrictionList()) && sizeof($va_browse_type_ids)) {
         // type restrictions
         $vs_browse_type_limit_sql = '(' . $t_subject->tableName() . '.' . $t_subject->getTypeFieldName() . ' IN (' . join(', ', $va_browse_type_ids) . ')' . ($t_subject->getFieldInfo('type_id', 'IS_NULL') ? " OR (" . $this->ops_browse_table_name . '.' . $t_subject->getTypeFieldName() . " IS NULL)" : '') . ')';
         if (is_array($va_facet_info['type_restrictions'])) {
             // facet type restrictions bind a facet to specific types; we check them here
             $va_restrict_to_types = $this->_convertTypeCodesToIDs($va_facet_info['type_restrictions']);
             $vb_is_ok_to_browse = false;
             foreach ($va_browse_type_ids as $vn_type_id) {
                 if (in_array($vn_type_id, $va_restrict_to_types)) {
                     $vb_is_ok_to_browse = true;
                     break;
                 }
             }
             if (!$vb_is_ok_to_browse) {
                 return array();
             }
         }
     }
     // Values to exclude from list attributes and authorities; can be idnos or ids
     $va_exclude_values = caGetOption('exclude_values', $va_facet_info, array(), array('castTo' => 'array'));
     $va_results = $this->opo_ca_browse_cache->getResults();
     $vb_single_value_is_present = false;
     $vs_single_value = isset($va_facet_info['single_value']) ? $va_facet_info['single_value'] : null;
     $va_wheres = array();
     switch ($va_facet_info['type']) {
         # -----------------------------------------------------
         case 'has':
             $vn_state = null;
             if (isset($va_all_criteria[$ps_facet_name])) {
                 break;
             }
             // only one instance of this facet allowed per browse
             if (!($t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true))) {
                 break;
             }
             $vs_yes_text = isset($va_facet_info['label_yes']) && $va_facet_info['label_yes'] ? $va_facet_info['label_yes'] : _t('Yes');
             $vs_no_text = isset($va_facet_info['label_no']) && $va_facet_info['label_no'] ? $va_facet_info['label_no'] : _t('No');
             $va_facet_values = array('yes' => array('id' => 1, 'label' => $vs_yes_text), 'no' => array('id' => 0, 'label' => $vs_no_text));
             // Actually check that both yes and no values will result in something
             if ($va_facet_info['element_code']) {
                 $t_element = new ca_metadata_elements();
                 if (!$t_element->load(array('element_code' => $va_facet_info['element_code']))) {
                     break;
                 }
                 $vs_element_code = $va_facet_info['element_code'];
                 $va_facet = array();
                 $va_counts = array();
                 foreach ($va_facet_values as $vs_state_name => $va_state_info) {
                     $va_wheres = array();
                     $va_joins = array();
                     if (!(bool) $va_state_info['id']) {
                         // no option
                         $va_wheres[] = $this->ops_browse_table_name . '.' . $t_item->primaryKey() . " NOT IN (select row_id from ca_attributes where table_num = " . $t_item->tableNum() . " AND element_id = " . $t_element->getPrimaryKey() . ")";
                     } else {
                         // yes option
                         $va_joins[] = "LEFT JOIN ca_attributes AS caa ON  " . $this->ops_browse_table_name . '.' . $t_item->primaryKey() . " = caa.row_id AND " . $t_item->tableNum() . " = caa.table_num";
                         $va_wheres[] = "caa.element_id = " . $t_element->getPrimaryKey();
                     }
                     if ($t_item->hasField('deleted')) {
                         $va_wheres[] = "(" . $t_item->tableName() . ".deleted = 0)";
                     }
                     if (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_item->hasField('access')) {
                         $va_wheres[] = "(" . $vs_browse_table_name . ".access IN (" . join(',', $pa_options['checkAccess']) . "))";
                     }
                     if (sizeof($va_results)) {
                         $va_wheres[] = $vs_browse_table_name . "." . $t_item->primaryKey() . " IN (" . join(",", $va_results) . ")";
                     }
                     if ($va_facet_info['relative_to']) {
                         if ($t_subject->hasField('deleted')) {
                             $va_wheres[] = "(" . $t_subject->tableName() . ".deleted = 0)";
                         }
                         if ($va_relative_sql_data = $this->_getRelativeFacetSQLData($va_facet_info['relative_to'], $pa_options)) {
                             $va_joins = array_merge($va_joins, $va_relative_sql_data['joins']);
                             $va_wheres = array_merge($va_wheres, $va_relative_sql_data['wheres']);
                         }
                     }
                     if ($this->opo_config->get('perform_item_level_access_checking')) {
                         if ($t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true)) {
                             // Join to limit what browse table items are used to generate facet
                             $va_joins[] = 'LEFT JOIN ca_acl ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_acl.row_id AND ca_acl.table_num = ' . $t_item->tableNum() . "\n";
                             $va_wheres[] = "(\n\t\t\t\t\t\t\t\t\t\t((\n\t\t\t\t\t\t\t\t\t\t\t(ca_acl.user_id = " . (int) $vn_user_id . ")\n\t\t\t\t\t\t\t\t\t\t\t" . (sizeof($va_group_ids) > 0 ? "OR\n\t\t\t\t\t\t\t\t\t\t\t(ca_acl.group_id IN (" . join(",", $va_group_ids) . "))" : "") . "\n\t\t\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t\t\t(ca_acl.user_id IS NULL and ca_acl.group_id IS NULL)\n\t\t\t\t\t\t\t\t\t\t) AND ca_acl.access >= " . __CA_ACL_READONLY_ACCESS__ . ")\n\t\t\t\t\t\t\t\t\t\t" . ($vb_show_if_no_acl ? "OR ca_acl.acl_id IS NULL" : "") . "\n\t\t\t\t\t\t\t\t\t)";
                         }
                     }
                     $vs_join_sql = join("\n", $va_joins);
                     $vs_where_sql = '';
                     if (sizeof($va_wheres) > 0) {
                         $vs_where_sql = ' WHERE ' . join(' AND ', $va_wheres);
                     }
                     if ($vb_check_availability_only) {
                         $vs_sql = "\n\t\t\t\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\t\t\t\tFROM " . $vs_browse_table_name . "\n\t\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t\t\t\tLIMIT 2\n\t\t\t\t\t\t\t\t";
                         //print "$vs_sql<hr>";
                         $qr_res = $this->opo_db->query($vs_sql);
                         if ($qr_res->nextRow()) {
                             $va_counts[$vs_state_name] = (int) $qr_res->numRows();
                         }
                     } else {
                         $vs_sql = "\n\t\t\t\t\t\t\t\t\tSELECT " . $vs_browse_table_name . '.' . $t_item->primaryKey() . "\n\t\t\t\t\t\t\t\t\tFROM " . $vs_browse_table_name . "\n\t\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t\t\t";
                         //print "$vs_sql<hr>";
                         $qr_res = $this->opo_db->query($vs_sql);
                         if ($qr_res->numRows() > 0) {
                             $va_facet[$vs_state_name] = $va_state_info;
                         } else {
                             return array();
                             // if either option in a "has" facet fails then don't show the facet
                         }
                     }
                 }
             } else {
                 $vs_rel_table_name = $va_facet_info['table'];
                 if (!is_array($va_restrict_to_relationship_types = $va_facet_info['restrict_to_relationship_types'])) {
                     $va_restrict_to_relationship_types = array();
                 }
                 $va_restrict_to_relationship_types = $this->_getRelationshipTypeIDs($va_restrict_to_relationship_types, $va_facet_info['relationship_table']);
                 if (!is_array($va_exclude_relationship_types = $va_facet_info['exclude_relationship_types'])) {
                     $va_exclude_relationship_types = array();
                 }
                 $va_exclude_relationship_types = $this->_getRelationshipTypeIDs($va_exclude_relationship_types, $va_facet_info['relationship_table']);
                 $vn_table_num = $this->opo_datamodel->getTableNum($vs_rel_table_name);
                 $vs_rel_table_pk = $this->opo_datamodel->getTablePrimaryKeyName($vn_table_num);
                 switch (sizeof($va_path = array_keys($this->opo_datamodel->getPath($vs_browse_table_name, $vs_rel_table_name)))) {
                     case 3:
                         $t_item_rel = $this->opo_datamodel->getInstanceByTableName($va_path[1], true);
                         $t_rel_item = $this->opo_datamodel->getInstanceByTableName($va_path[2], true);
                         $vs_key = 'relation_id';
                         break;
                     case 2:
                         $t_item_rel = null;
                         $t_rel_item = $this->opo_datamodel->getInstanceByTableName($va_path[1], true);
                         $vs_key = $t_rel_item->primaryKey();
                         break;
                     default:
                         // bad related table
                         return null;
                         break;
                 }
                 $vs_cur_table = array_shift($va_path);
                 $va_joins_init = array();
                 foreach ($va_path as $vs_join_table) {
                     $va_rel_info = $this->opo_datamodel->getRelationships($vs_cur_table, $vs_join_table);
                     $va_joins_init[] = ($vn_state ? 'INNER' : 'LEFT') . ' JOIN ' . $vs_join_table . ' ON ' . $vs_cur_table . '.' . $va_rel_info[$vs_cur_table][$vs_join_table][0][0] . ' = ' . $vs_join_table . '.' . $va_rel_info[$vs_cur_table][$vs_join_table][0][1] . "\n";
                     $vs_cur_table = $vs_join_table;
                 }
                 $va_facet = array();
                 $va_counts = array();
                 foreach ($va_facet_values as $vs_state_name => $va_state_info) {
                     $va_wheres = array();
                     $va_joins = $va_joins_init;
                     if (!(bool) $va_state_info['id']) {
                         // no option
                         $va_wheres[] = "(" . $t_rel_item->tableName() . "." . $t_rel_item->primaryKey() . " IS NULL)";
                         if ($t_rel_item->hasField('deleted')) {
                             $va_wheres[] = "((" . $t_rel_item->tableName() . ".deleted = 0) OR (" . $t_rel_item->tableName() . ".deleted IS NULL))";
                         }
                         if (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_rel_item->hasField('access')) {
                             $va_wheres[] = "((" . $t_rel_item->tableName() . ".access NOT IN (" . join(',', $pa_options['checkAccess']) . ")) OR (" . $t_rel_item->tableName() . ".access IS NULL))";
                         }
                         if (sizeof($va_restrict_to_relationship_types) > 0 && is_object($t_item_rel)) {
                             $va_wheres[] = "((" . $t_item_rel->tableName() . ".type_id NOT IN (" . join(',', $va_restrict_to_relationship_types) . ")) OR (" . $t_item_rel->tableName() . ".type_id IS NULL))";
                         }
                         if (sizeof($va_exclude_relationship_types) > 0 && is_object($t_item_rel)) {
                             $va_wheres[] = "(" . $t_item_rel->tableName() . ".type_id IN (" . join(',', $va_exclude_relationship_types) . "))";
                         }
                     } else {
                         // yes option
                         $va_wheres[] = "(" . $t_rel_item->tableName() . "." . $t_rel_item->primaryKey() . " IS NOT NULL)";
                         if ($t_rel_item->hasField('deleted')) {
                             $va_wheres[] = "(" . $t_rel_item->tableName() . ".deleted = 0)";
                         }
                         if (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_rel_item->hasField('access')) {
                             $va_wheres[] = "(" . $t_rel_item->tableName() . ".access IN (" . join(',', $pa_options['checkAccess']) . "))";
                         }
                         if (sizeof($va_restrict_to_relationship_types) > 0 && is_object($t_item_rel)) {
                             $va_wheres[] = "(" . $t_item_rel->tableName() . ".type_id IN (" . join(',', $va_restrict_to_relationship_types) . "))";
                         }
                         if (sizeof($va_exclude_relationship_types) > 0 && is_object($t_item_rel)) {
                             $va_wheres[] = "(" . $t_item_rel->tableName() . ".type_id NOT IN (" . join(',', $va_exclude_relationship_types) . "))";
                         }
                     }
                     if ($t_item->hasField('deleted')) {
                         $va_wheres[] = "(" . $t_item->tableName() . ".deleted = 0)";
                     }
                     if (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_item->hasField('access')) {
                         $va_wheres[] = "(" . $vs_browse_table_name . ".access IN (" . join(',', $pa_options['checkAccess']) . "))";
                     }
                     if (sizeof($va_results)) {
                         $va_wheres[] = $vs_browse_table_name . "." . $t_item->primaryKey() . " IN (" . join(",", $va_results) . ")";
                     }
                     if ($va_facet_info['relative_to']) {
                         if ($t_subject->hasField('deleted')) {
                             $va_wheres[] = "(" . $t_subject->tableName() . ".deleted = 0)";
                         }
                         if ($va_relative_sql_data = $this->_getRelativeFacetSQLData($va_facet_info['relative_to'], $pa_options)) {
                             $va_joins = array_merge($va_joins, $va_relative_sql_data['joins']);
                             $va_wheres = array_merge($va_wheres, $va_relative_sql_data['wheres']);
                         }
                     }
                     if ($this->opo_config->get('perform_item_level_access_checking')) {
                         if ($t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true)) {
                             // Join to limit what browse table items are used to generate facet
                             $va_joins[] = 'LEFT JOIN ca_acl ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_acl.row_id AND ca_acl.table_num = ' . $t_item->tableNum() . "\n";
                             $va_wheres[] = "(\n\t\t\t\t\t\t\t\t\t\t((\n\t\t\t\t\t\t\t\t\t\t\t(ca_acl.user_id = " . (int) $vn_user_id . ")\n\t\t\t\t\t\t\t\t\t\t\t" . (sizeof($va_group_ids) > 0 ? "OR\n\t\t\t\t\t\t\t\t\t\t\t(ca_acl.group_id IN (" . join(",", $va_group_ids) . "))" : "") . "\n\t\t\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t\t\t(ca_acl.user_id IS NULL and ca_acl.group_id IS NULL)\n\t\t\t\t\t\t\t\t\t\t) AND ca_acl.access >= " . __CA_ACL_READONLY_ACCESS__ . ")\n\t\t\t\t\t\t\t\t\t\t" . ($vb_show_if_no_acl ? "OR ca_acl.acl_id IS NULL" : "") . "\n\t\t\t\t\t\t\t\t\t)";
                         }
                     }
                     $vs_join_sql = join("\n", $va_joins);
                     $vs_where_sql = '';
                     if (sizeof($va_wheres) > 0) {
                         $vs_where_sql = ' WHERE ' . join(' AND ', $va_wheres);
                     }
                     if ($vb_check_availability_only) {
                         $vs_sql = "\n\t\t\t\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\t\t\t\tFROM " . $vs_browse_table_name . "\n\t\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t\t\t\tLIMIT 2\n\t\t\t\t\t\t\t\t";
                         //print "$vs_sql<hr>";
                         $qr_res = $this->opo_db->query($vs_sql);
                         if ($qr_res->nextRow()) {
                             $va_counts[$vs_state_name] = (int) $qr_res->numRows();
                         }
                     } else {
                         $vs_sql = "\n\t\t\t\t\t\t\t\t\tSELECT " . $vs_browse_table_name . '.' . $t_item->primaryKey() . "\n\t\t\t\t\t\t\t\t\tFROM " . $vs_browse_table_name . "\n\t\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t\t\t";
                         //print "$vs_sql<hr>";
                         $qr_res = $this->opo_db->query($vs_sql);
                         if ($qr_res->numRows() > 0) {
                             $va_facet[$vs_state_name] = $va_state_info;
                         } else {
                             return array();
                             // if either option in a "has" facet fails then don't show the facet
                         }
                     }
                 }
             }
             if ($vb_check_availability_only) {
                 return sizeof($va_counts) > 1 ? true : false;
             }
             return $va_facet;
             break;
             # -----------------------------------------------------
         # -----------------------------------------------------
         case 'label':
             if (!($t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true))) {
                 break;
             }
             if (!($t_label = $t_item->getLabelTableInstance())) {
                 break;
             }
             if (!is_array($va_restrict_to_types = $va_facet_info['restrict_to_types'])) {
                 $va_restrict_to_types = array();
             }
             $vs_item_pk = $t_item->primaryKey();
             $vs_label_table_name = $t_label->tableName();
             $vs_label_pk = $t_label->primaryKey();
             $vs_label_display_field = $t_item->getLabelDisplayField();
             $vs_label_sort_field = $t_item->getLabelSortField();
             $vs_where_sql = $vs_join_sql = '';
             $vb_needs_join = false;
             $va_where_sql = array();
             $va_joins = array();
             if ($vs_browse_type_limit_sql) {
                 $va_where_sql[] = $vs_browse_type_limit_sql;
             }
             if (isset($va_facet_info['preferred_labels_only']) && $va_facet_info['preferred_labels_only'] && $t_label->hasField('is_preferred')) {
                 $va_where_sql[] = "l.is_preferred = 1";
             }
             if (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_item->hasField('access')) {
                 $va_where_sql[] = "(" . $vs_browse_table_name . ".access IN (" . join(',', $pa_options['checkAccess']) . "))";
             }
             if ($t_item->hasField('deleted')) {
                 $va_where_sql[] = "(" . $vs_browse_table_name . ".deleted = 0)";
                 $vb_needs_join = true;
             }
             if (sizeof($va_restrict_to_types)) {
                 $va_restrict_to_type_ids = caMakeTypeIDList($vs_browse_table_name, $va_restrict_to_types, array('dont_include_subtypes_in_type_restriction' => true));
                 if (sizeof($va_restrict_to_type_ids)) {
                     $va_where_sql[] = "(" . $vs_browse_table_name . "." . $t_item->getTypeFieldName() . " IN (" . join(", ", $va_restrict_to_type_ids) . ")" . ($t_item->getFieldInfo('type_id', 'IS_NULL') ? " OR (" . $vs_browse_table_name . '.' . $t_item->getTypeFieldName() . " IS NULL)" : '') . ")";
                     $vb_needs_join = true;
                 }
             }
             if (sizeof($va_exclude_types)) {
                 $va_exclude_type_ids = caMakeTypeIDList($vs_browse_table_name, $va_exclude_types, array('dont_include_subtypes_in_type_restriction' => true));
                 if (sizeof($va_exclude_type_ids)) {
                     $va_where_sql[] = "(" . $vs_browse_table_name . "." . $t_item->getTypeFieldName() . " NOT IN (" . join(", ", $va_exclude_type_ids) . ")" . ($t_item->getFieldInfo('type_id', 'IS_NULL') ? " OR (" . $vs_browse_table_name . '.' . $t_item->getTypeFieldName() . " IS NULL)" : '') . ")";
                     $vb_needs_join = true;
                 }
             }
             if ($vb_needs_join) {
                 $va_joins[] = "INNER JOIN " . $vs_browse_table_name . " ON " . $vs_browse_table_name . "." . $t_item->primaryKey() . " = l." . $t_item->primaryKey();
             }
             if ($va_facet_info['relative_to']) {
                 if ($t_subject->hasField('deleted')) {
                     $va_where_sql[] = "(" . $t_subject->tableName() . ".deleted = 0)";
                 }
                 if ($va_relative_sql_data = $this->_getRelativeFacetSQLData($va_facet_info['relative_to'], $pa_options)) {
                     $va_joins = array_merge($va_joins, $va_relative_sql_data['joins']);
                     $va_where_sql = array_merge($va_where_sql, $va_relative_sql_data['wheres']);
                 }
             }
             if (sizeof($va_results)) {
                 if ($va_facet_info['relative_to']) {
                     $va_where_sql[] = $this->ops_browse_table_name . "." . $t_subject->primaryKey() . " IN (" . join(",", $va_results) . ")";
                 } else {
                     $va_where_sql[] = "l.{$vs_item_pk} IN (" . join(",", $va_results) . ")";
                 }
             }
             if ($this->opo_config->get('perform_item_level_access_checking')) {
                 if ($t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true)) {
                     // Join to limit what browse table items are used to generate facet
                     $va_joins[] = 'LEFT JOIN ca_acl ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_acl.row_id AND ca_acl.table_num = ' . $t_item->tableNum() . "\n";
                     $va_where_sql[] = "(\n\t\t\t\t\t\t\t\t((\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id = " . (int) $vn_user_id . ")\n\t\t\t\t\t\t\t\t\t" . (sizeof($va_group_ids) > 0 ? "OR\n\t\t\t\t\t\t\t\t\t(ca_acl.group_id IN (" . join(",", $va_group_ids) . "))" : "") . "\n\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id IS NULL and ca_acl.group_id IS NULL)\n\t\t\t\t\t\t\t\t) AND ca_acl.access >= " . __CA_ACL_READONLY_ACCESS__ . ")\n\t\t\t\t\t\t\t\t" . ($vb_show_if_no_acl ? "OR ca_acl.acl_id IS NULL" : "") . "\n\t\t\t\t\t\t\t)";
                 }
             }
             $vs_join_sql = join("\n", $va_joins);
             if (sizeof($va_where_sql)) {
                 $vs_where_sql = "WHERE " . join(" AND ", $va_where_sql);
             }
             if ($vb_check_availability_only) {
                 $vs_sql = "\n\t\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\t\tFROM {$vs_label_table_name} l\n\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t\tLIMIT 1\n\t\t\t\t\t\t";
                 $qr_res = $this->opo_db->query($vs_sql);
                 return (int) $qr_res->numRows() > 0 ? true : false;
             } else {
                 $vs_parent_fld = $t_item->getProperty('HIERARCHY_PARENT_ID_FLD');
                 $vs_sql = "\n\t\t\t\t\t\t\tSELECT  l.* " . ($vs_parent_fld ? ", " . $vs_browse_table_name . "." . $vs_parent_fld : '') . " \n\t\t\t\t\t\t\tFROM {$vs_label_table_name} l\n\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t";
                 $qr_res = $this->opo_db->query($vs_sql);
                 $va_values = array();
                 $va_child_counts = array();
                 $vn_parent_id = null;
                 while ($qr_res->nextRow()) {
                     $vn_id = $qr_res->get($t_item->primaryKey());
                     if ($vs_parent_fld) {
                         $vn_parent_id = $qr_res->get($vs_parent_fld);
                         if ($vn_parent_id) {
                             $va_child_counts[$vn_parent_id]++;
                         }
                     }
                     $va_values[$vn_id][$qr_res->get('locale_id')] = array_merge($qr_res->getRow(), array('id' => $vn_id, 'parent_id' => $vn_parent_id, 'label' => $qr_res->get($vs_label_display_field)));
                     if (!is_null($vs_single_value) && $vn_id == $vs_single_value) {
                         $vb_single_value_is_present = true;
                     }
                 }
                 if ($vs_parent_fld) {
                     foreach ($va_values as $vn_id => $va_values_by_locale) {
                         foreach ($va_values_by_locale as $vn_locale_id => $va_value) {
                             $va_values[$vn_id][$vn_locale_id]['child_count'] = (int) $va_child_counts[$vn_id];
                         }
                     }
                 }
                 if (!is_null($vs_single_value) && !$vb_single_value_is_present) {
                     return array();
                 }
                 $va_values = caExtractValuesByUserLocale($va_values);
                 return $va_values;
             }
             break;
             # -----------------------------------------------------
         # -----------------------------------------------------
         case 'attribute':
             $t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true);
             $t_element = new ca_metadata_elements();
             if (!$t_element->load(array('element_code' => $va_facet_info['element_code']))) {
                 return array();
             }
             $vn_element_type = $t_element->get('datatype');
             $vn_element_id = $t_element->getPrimaryKey();
             $va_joins = array('INNER JOIN ca_attribute_values ON ca_attributes.attribute_id = ca_attribute_values.attribute_id', 'INNER JOIN ' . $vs_browse_table_name . ' ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_attributes.row_id AND ca_attributes.table_num = ' . intval($vs_browse_table_num));
             $va_wheres = array();
             if (sizeof($va_results) && $this->numCriteria() > 0) {
                 $va_wheres[] = "(" . $t_subject->tableName() . '.' . $t_subject->primaryKey() . " IN (" . join(',', $va_results) . "))";
             }
             if (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_item->hasField('access')) {
                 $va_wheres[] = "(" . $vs_browse_table_name . ".access IN (" . join(',', $pa_options['checkAccess']) . "))";
             }
             if ($vs_browse_type_limit_sql) {
                 $va_wheres[] = $vs_browse_type_limit_sql;
             }
             if ($t_item->hasField('deleted')) {
                 $va_wheres[] = "(" . $vs_browse_table_name . ".deleted = 0)";
             }
             if ($va_facet_info['relative_to']) {
                 if ($t_subject->hasField('deleted')) {
                     $va_wheres[] = "(" . $t_subject->tableName() . ".deleted = 0)";
                 }
                 if ($va_relative_sql_data = $this->_getRelativeFacetSQLData($va_facet_info['relative_to'], $pa_options)) {
                     $va_joins = array_merge($va_joins, $va_relative_sql_data['joins']);
                     $va_wheres = array_merge($va_wheres, $va_relative_sql_data['wheres']);
                 }
             }
             if ($this->opo_config->get('perform_item_level_access_checking')) {
                 if ($t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true)) {
                     // Join to limit what browse table items are used to generate facet
                     $va_joins[] = 'LEFT JOIN ca_acl ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_acl.row_id AND ca_acl.table_num = ' . $t_item->tableNum() . "\n";
                     $va_wheres[] = "(\n\t\t\t\t\t\t\t\t((\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id = " . (int) $vn_user_id . ")\n\t\t\t\t\t\t\t\t\t" . (sizeof($va_group_ids) > 0 ? "OR\n\t\t\t\t\t\t\t\t\t(ca_acl.group_id IN (" . join(",", $va_group_ids) . "))" : "") . "\n\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id IS NULL and ca_acl.group_id IS NULL)\n\t\t\t\t\t\t\t\t) AND ca_acl.access >= " . __CA_ACL_READONLY_ACCESS__ . ")\n\t\t\t\t\t\t\t\t" . ($vb_show_if_no_acl ? "OR ca_acl.acl_id IS NULL" : "") . "\n\t\t\t\t\t\t\t)";
                 }
             }
             $vs_join_sql = join("\n", $va_joins);
             if (is_array($va_wheres) && sizeof($va_wheres) && ($vs_where_sql = join(' AND ', $va_wheres))) {
                 $vs_where_sql = ' AND (' . $vs_where_sql . ')';
             }
             if ($vb_check_availability_only) {
                 // exclude criteria values
                 $vs_criteria_exclude_sql = '';
                 if (is_array($va_criteria) && sizeof($va_criteria)) {
                     $vs_criteria_exclude_sql = ' AND (ca_attribute_values.value_longtext1 NOT IN (' . join(", ", caQuoteList(array_keys($va_criteria))) . ')) ';
                 }
                 $vs_sql = "\n\t\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\t\tFROM ca_attributes\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t(ca_attribute_values.element_id = ?) {$vs_criteria_exclude_sql} {$vs_where_sql}\n\t\t\t\t\t\t\tLIMIT 2";
                 //print $vs_sql;
                 $qr_res = $this->opo_db->query($vs_sql, $vn_element_id);
                 return (int) $qr_res->numRows() > 1 ? true : false;
             } else {
                 $vs_sql = "\n\t\t\t\t\t\t\tSELECT DISTINCT value_longtext1, value_decimal1, value_longtext2, value_integer1\n\t\t\t\t\t\t\tFROM ca_attributes\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\tca_attribute_values.element_id = ? {$vs_where_sql}";
                 $qr_res = $this->opo_db->query($vs_sql, $vn_element_id);
                 $va_values = array();
                 $va_list_items = null;
                 $va_suppress_values = null;
                 if ($va_facet_info['suppress'] && !is_array($va_facet_info['suppress'])) {
                     $va_facet_info['suppress'] = array($va_facet_info['suppress']);
                 }
                 if (!is_array($va_suppress_values = caGetOption('suppress', $va_facet_info, null))) {
                     $va_suppress_values = caGetOption('exclude_values', $va_facet_info, null);
                 }
                 switch ($vn_element_type) {
                     case __CA_ATTRIBUTE_VALUE_LIST__:
                         $va_values = $qr_res->getAllFieldValues('value_longtext1');
                         $qr_res->seek(0);
                         $t_list_item = new ca_list_items();
                         $va_list_item_cache = $t_list_item->getFieldValuesForIDs($va_values, array('idno', 'item_value', 'parent_id', 'access'));
                         $va_list_child_count_cache = array();
                         if (is_array($va_list_item_cache)) {
                             foreach ($va_list_item_cache as $vn_id => $va_item) {
                                 if (!($vn_parent_id = $va_item['parent_id'])) {
                                     continue;
                                 }
                                 if (is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && !in_array($va_item['access'], $pa_options['checkAccess'])) {
                                     continue;
                                 }
                                 $va_list_child_count_cache[$vn_parent_id]++;
                             }
                         }
                         $va_list_label_cache = $t_list_item->getPreferredDisplayLabelsForIDs($va_values);
                         // Translate value idnos to ids
                         if (is_array($va_suppress_values)) {
                             $va_suppress_values = ca_lists::getItemIDsFromList($t_element->get('list_id'), $va_suppress_values);
                         }
                         $va_facet_list = array();
                         foreach ($va_values as $vn_val) {
                             if (!$vn_val) {
                                 continue;
                             }
                             if (is_array($va_suppress_values) && in_array($vn_val, $va_suppress_values)) {
                                 continue;
                             }
                             if (is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && !in_array($va_item['access'], $pa_options['checkAccess'])) {
                                 continue;
                             }
                             if ($va_criteria[$vn_val]) {
                                 continue;
                             }
                             // skip items that are used as browse critera - don't want to browse on something you're already browsing on
                             $vn_child_count = isset($va_list_child_count_cache[$vn_val]) ? $va_list_child_count_cache[$vn_val] : 0;
                             $va_facet_list[$vn_val] = array('id' => $vn_val, 'label' => html_entity_decode($va_list_label_cache[$vn_val]), 'parent_id' => isset($va_list_item_cache[$vn_val]['parent_id']) ? $va_list_item_cache[$vn_val]['parent_id'] : null, 'child_count' => $vn_child_count);
                         }
                         // preserve order of list
                         $va_values_sorted_by_list_order = array();
                         if (is_array($va_list_item_cache)) {
                             foreach ($va_list_item_cache as $vn_item_id => $va_item) {
                                 if (isset($va_facet_list[$vn_item_id])) {
                                     $va_values_sorted_by_list_order[$vn_item_id] = $va_facet_list[$vn_item_id];
                                 }
                             }
                         }
                         return caSortArrayByKeyInValue($va_values_sorted_by_list_order, array('label'));
                         break;
                     case __CA_ATTRIBUTE_VALUE_OBJECTS__:
                     case __CA_ATTRIBUTE_VALUE_ENTITIES__:
                     case __CA_ATTRIBUTE_VALUE_PLACES__:
                     case __CA_ATTRIBUTE_VALUE_OCCURRENCES__:
                     case __CA_ATTRIBUTE_VALUE_COLLECTIONS__:
                     case __CA_ATTRIBUTE_VALUE_LOANS__:
                     case __CA_ATTRIBUTE_VALUE_MOVEMENTS__:
                     case __CA_ATTRIBUTE_VALUE_STORAGELOCATIONS__:
                     case __CA_ATTRIBUTE_VALUE_OBJECTLOTS__:
                         if ($t_rel_item = AuthorityAttributeValue::elementTypeToInstance($vn_element_type)) {
                             $va_ids = $qr_res->getAllFieldValues('value_integer1');
                             $va_auth_items = $t_rel_item->getPreferredDisplayLabelsForIDs($va_ids);
                             $qr_res->seek(0);
                         }
                         break;
                     default:
                         if (isset($va_facet_info['suppress']) && is_array($va_facet_info['suppress'])) {
                             $va_suppress_values = $va_facet_info['suppress'];
                         }
                         break;
                 }
                 while ($qr_res->nextRow()) {
                     $o_attr = Attribute::getValueInstance($vn_element_type, $qr_res->getRow(), true);
                     if (!($vs_val = trim($o_attr->getDisplayValue()))) {
                         continue;
                     }
                     if (is_array($va_suppress_values) && in_array($vs_val, $va_suppress_values)) {
                         continue;
                     }
                     if ($va_criteria[$vs_val]) {
                         continue;
                     }
                     // skip items that are used as browse critera - don't want to browse on something you're already browsing on
                     switch ($vn_element_type) {
                         case __CA_ATTRIBUTE_VALUE_LIST__:
                             $vn_child_count = 0;
                             if ($va_list_parent_ids[$vs_val]) {
                                 $vn_child_count++;
                             }
                             $va_values[$vs_val] = array('id' => str_replace('/', '&#47;', $vs_val), 'label' => html_entity_decode($va_list_items[$vs_val]['name_plural'] ? $va_list_items[$vs_val]['name_plural'] : $va_list_items[$vs_val]['item_value']), 'parent_id' => $va_list_items[$vs_val]['parent_id'], 'child_count' => $vn_child_count);
                             break;
                         case __CA_ATTRIBUTE_VALUE_OBJECTS__:
                         case __CA_ATTRIBUTE_VALUE_ENTITIES__:
                         case __CA_ATTRIBUTE_VALUE_PLACES__:
                         case __CA_ATTRIBUTE_VALUE_OCCURRENCES__:
                         case __CA_ATTRIBUTE_VALUE_COLLECTIONS__:
                         case __CA_ATTRIBUTE_VALUE_LOANS__:
                         case __CA_ATTRIBUTE_VALUE_MOVEMENTS__:
                         case __CA_ATTRIBUTE_VALUE_STORAGELOCATIONS__:
                         case __CA_ATTRIBUTE_VALUE_OBJECTLOTS__:
                             $va_values[$vs_val] = array('id' => $vn_id, 'label' => html_entity_decode($va_auth_items[$vn_id] ? $va_auth_items[$vn_id] : $vs_val));
                             break;
                         case __CA_ATTRIBUTE_VALUE_CURRENCY__:
                             $va_values[sprintf("%014.2f", preg_replace("![\\D]+!", "", $vs_val))] = array('id' => str_replace('/', '&#47;', $vs_val), 'label' => $vs_val);
                             break;
                         default:
                             $va_values[$vs_val] = array('id' => str_replace('/', '&#47;', $vs_val), 'label' => $vs_val);
                             break;
                     }
                     if (!is_null($vs_single_value) && $vs_val == $vs_single_value) {
                         $vb_single_value_is_present = true;
                     }
                 }
                 if (!is_null($vs_single_value) && !$vb_single_value_is_present) {
                     return array();
                 }
                 ksort($va_values);
                 return $va_values;
             }
             break;
             # -----------------------------------------------------
         # -----------------------------------------------------
         case 'location':
             $t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true);
             $vs_sort_field = null;
             if ($t_item->getProperty('ID_NUMBERING_ID_FIELD') == $vs_field_name) {
                 $vs_sort_field = $t_item->getProperty('ID_NUMBERING_SORT_FIELD');
             }
             $va_joins = array();
             $va_wheres = array();
             $vs_where_sql = '';
             $va_wheres[] = "({$vs_browse_table_name}.current_loc_class IS NOT NULL)";
             if (sizeof($va_results) && $this->numCriteria() > 0) {
                 $va_wheres[] = "(" . $t_subject->tableName() . '.' . $t_subject->primaryKey() . " IN (" . join(',', $va_results) . "))";
             }
             if (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_item->hasField('access')) {
                 $va_wheres[] = "(" . $vs_browse_table_name . ".access IN (" . join(',', $pa_options['checkAccess']) . "))";
             }
             if ($vs_browse_type_limit_sql) {
                 $va_wheres[] = $vs_browse_type_limit_sql;
             }
             if ($t_item->hasField('deleted')) {
                 $va_wheres[] = "(" . $vs_browse_table_name . ".deleted = 0)";
             }
             if ($this->opo_config->get('perform_item_level_access_checking')) {
                 if ($t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true)) {
                     // Join to limit what browse table items are used to generate facet
                     $va_joins[] = 'LEFT JOIN ca_acl ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_acl.row_id AND ca_acl.table_num = ' . $t_item->tableNum() . "\n";
                     $va_wheres[] = "(\n\t\t\t\t\t\t\t\t((\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id = " . (int) $vn_user_id . ")\n\t\t\t\t\t\t\t\t\t" . (sizeof($va_group_ids) > 0 ? "OR\n\t\t\t\t\t\t\t\t\t(ca_acl.group_id IN (" . join(",", $va_group_ids) . "))" : "") . "\n\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id IS NULL and ca_acl.group_id IS NULL)\n\t\t\t\t\t\t\t\t) AND ca_acl.access >= " . __CA_ACL_READONLY_ACCESS__ . ")\n\t\t\t\t\t\t\t\t" . ($vb_show_if_no_acl ? "OR ca_acl.acl_id IS NULL" : "") . "\n\t\t\t\t\t\t\t)";
                 }
             }
             $vs_join_sql = join("\n", $va_joins);
             if (is_array($va_wheres) && sizeof($va_wheres) && ($vs_where_sql = join(' AND ', $va_wheres))) {
                 $vs_where_sql = '(' . $vs_where_sql . ')';
             }
             if ($vb_check_availability_only) {
                 if (sizeof($va_criteria) > 0) {
                     return false;
                 }
                 // only one current location criteria allowed
                 $vs_sql = "\n\t\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\t\tFROM {$vs_browse_table_name}\n\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t\tLIMIT 2";
                 $qr_res = $this->opo_db->query($vs_sql);
                 if ($qr_res->nextRow()) {
                     return (int) $qr_res->numRows() > 0 ? true : false;
                 }
                 return false;
             } else {
                 if (sizeof($va_criteria) > 0) {
                     return array();
                 }
                 // only one current location criteria allowed
                 $vs_pk = $t_item->primaryKey();
                 $vs_sql = "\n\t\t\t\t\t\t\tSELECT DISTINCT {$vs_browse_table_name}.current_loc_class, {$vs_browse_table_name}.current_loc_subclass, {$vs_browse_table_name}.current_loc_id\n\t\t\t\t\t\t\tFROM {$vs_browse_table_name}\n\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t{$vs_where_sql}";
                 if ($vs_sort_field) {
                     $vs_sql .= " ORDER BY {$vs_sort_field}";
                 }
                 $qr_res = $this->opo_db->query($vs_sql);
                 $va_collapse_map = $this->getCollapseMapForLocationFacet($va_facet_info);
                 $va_values = $va_values_by_table = array();
                 while ($qr_res->nextRow()) {
                     if (!($vs_loc_class = trim($qr_res->get('current_loc_class')))) {
                         continue;
                     }
                     if (!($vs_loc_subclass = trim($qr_res->get('current_loc_subclass')))) {
                         continue;
                     }
                     if (!($vs_loc_id = trim($qr_res->get('current_loc_id')))) {
                         continue;
                     }
                     $vs_val = "{$vs_loc_class}:{$vs_loc_subclass}:{$vs_loc_id}";
                     if ($va_criteria[$vs_val]) {
                         continue;
                     }
                     // skip items that are used as browse critera - don't want to browse on something you're already browsing on
                     $va_values_by_table[$vs_loc_class][$vs_loc_subclass][$vs_loc_id] = true;
                 }
                 foreach ($va_values_by_table as $vs_loc_class => $va_loc_id_by_subclass) {
                     foreach ($va_loc_id_by_subclass as $vs_loc_subclass => $va_loc_ids) {
                         if (sizeof($va_tmp = array_keys($va_loc_ids))) {
                             $vs_loc_table_name = $this->opo_datamodel->getTableName($vs_loc_class);
                             if (($vs_table_name = $vs_loc_table_name) == 'ca_objects_x_storage_locations') {
                                 $vs_table_name = 'ca_storage_locations';
                             }
                             $qr_res = caMakeSearchResult($vs_table_name, $va_tmp);
                             if (isset($va_collapse_map[$vs_table_name]) && isset($va_collapse_map[$vs_table_name]['*']) && $va_collapse_map[$vs_table_name]['*']) {
                                 $va_values[$vs_id = "{$vs_loc_class}"] = array('id' => $vs_id, 'label' => $va_collapse_map[$vs_table_name]['*']);
                                 continue;
                             }
                             while ($qr_res->nextHit()) {
                                 $vn_id = $qr_res->getPrimaryKey();
                                 $va_config = ca_objects::getConfigurationForCurrentLocationType($vs_table_name, $vs_loc_subclass, array('facet' => isset($va_facet_info['display']) ? $va_facet_info['display'] : null));
                                 $vs_template = isset($va_config['template']) ? $va_config['template'] : "^{$vs_table_name}.preferred_labels";
                                 if (isset($va_collapse_map[$vs_table_name]) && isset($va_collapse_map[$vs_table_name][$vs_loc_subclass]) && $va_collapse_map[$vs_table_name][$vs_loc_subclass]) {
                                     $va_values[$vs_id = "{$vs_loc_class}:{$vs_loc_subclass}"] = array('id' => $vs_id, 'label' => $va_collapse_map[$vs_table_name][$vs_loc_subclass]);
                                     continue;
                                 }
                                 $va_values[$vs_id = "{$vs_loc_class}:{$vs_loc_subclass}:{$vn_id}"] = array('id' => $vs_id, 'label' => $qr_res->getWithTemplate($vs_template));
                             }
                         }
                     }
                 }
                 if (!is_null($vs_single_value) && !$vb_single_value_is_present) {
                     return array();
                 }
                 return caSortArrayByKeyInValue($va_values, array('label'));
             }
             return array();
             break;
             # -----------------------------------------------------
         # -----------------------------------------------------
         case 'fieldList':
             $t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true);
             $vs_field_name = $va_facet_info['field'];
             $va_field_info = $t_item->getFieldInfo($vs_field_name);
             $t_list = new ca_lists();
             $t_list_item = new ca_list_items();
             $va_joins = array();
             $va_wheres = array();
             $vs_where_sql = '';
             if (isset($va_field_info['LIST_CODE']) && ($vs_list_name = $va_field_info['LIST_CODE'])) {
                 // Handle fields containing ca_list_item.item_id's
                 $va_joins = array('INNER JOIN ' . $vs_browse_table_name . ' ON ' . $vs_browse_table_name . '.' . $vs_field_name . ' = li.item_id', 'INNER JOIN ca_lists ON ca_lists.list_id = li.list_id');
                 if (sizeof($va_results) && $this->numCriteria() > 0) {
                     $va_wheres[] = "(" . $t_subject->tableName() . '.' . $t_subject->primaryKey() . " IN (" . join(',', $va_results) . "))";
                 }
                 if (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_item->hasField('access')) {
                     $va_wheres[] = "(" . $vs_browse_table_name . ".access IN (" . join(',', $pa_options['checkAccess']) . "))";
                     $va_wheres[] = "(li.access IN (" . join(',', $pa_options['checkAccess']) . "))";
                 }
                 if ($vs_browse_type_limit_sql) {
                     $va_wheres[] = $vs_browse_type_limit_sql;
                 }
                 if ($t_subject->hasField('deleted')) {
                     $va_wheres[] = "(" . $t_subject->tableName() . ".deleted = 0)";
                 }
                 if ($va_facet_info['relative_to']) {
                     if ($va_relative_sql_data = $this->_getRelativeFacetSQLData($va_facet_info['relative_to'], $pa_options)) {
                         $va_joins = array_merge($va_joins, $va_relative_sql_data['joins']);
                         $va_wheres = array_merge($va_wheres, $va_relative_sql_data['wheres']);
                     }
                 }
                 if (is_array($va_criteria) && sizeof($va_criteria)) {
                     $va_wheres[] = "(li.item_id NOT IN (" . join(",", array_keys($va_criteria)) . "))";
                 }
                 if ($this->opo_config->get('perform_item_level_access_checking')) {
                     if ($t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true)) {
                         // Join to limit what browse table items are used to generate facet
                         $va_joins[] = 'LEFT JOIN ca_acl ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_acl.row_id AND ca_acl.table_num = ' . $t_item->tableNum() . "\n";
                         $va_wheres[] = "(\n\t\t\t\t\t\t\t\t\t((\n\t\t\t\t\t\t\t\t\t\t(ca_acl.user_id = " . (int) $vn_user_id . ")\n\t\t\t\t\t\t\t\t\t\t" . (sizeof($va_group_ids) > 0 ? "OR\n\t\t\t\t\t\t\t\t\t\t(ca_acl.group_id IN (" . join(",", $va_group_ids) . "))" : "") . "\n\t\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t\t(ca_acl.user_id IS NULL and ca_acl.group_id IS NULL)\n\t\t\t\t\t\t\t\t\t) AND ca_acl.access >= " . __CA_ACL_READONLY_ACCESS__ . ")\n\t\t\t\t\t\t\t\t\t" . ($vb_show_if_no_acl ? "OR ca_acl.acl_id IS NULL" : "") . "\n\t\t\t\t\t\t\t\t)";
                     }
                 }
                 $vs_join_sql = join("\n", $va_joins);
                 if (is_array($va_wheres) && sizeof($va_wheres) && ($vs_where_sql = join(' AND ', $va_wheres))) {
                     $vs_where_sql = ' AND (' . $vs_where_sql . ')';
                 }
                 if ($vb_check_availability_only) {
                     $vs_sql = "\n\t\t\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\t\t\tFROM ca_list_items li\n\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\tca_lists.list_code = ? {$vs_where_sql}\n\t\t\t\t\t\t\t\tLIMIT 2";
                     $qr_res = $this->opo_db->query($vs_sql, $vs_list_name);
                     return (int) $qr_res->numRows() > 1 ? true : false;
                 } else {
                     // Get label ordering fields
                     $va_ordering_fields_to_fetch = isset($va_facet_info['order_by_label_fields']) && is_array($va_facet_info['order_by_label_fields']) ? $va_facet_info['order_by_label_fields'] : array();
                     $va_orderbys = array();
                     $t_rel_item_label = new ca_list_item_labels();
                     foreach ($va_ordering_fields_to_fetch as $vs_sort_by_field) {
                         if (!$t_rel_item_label->hasField($vs_sort_by_field)) {
                             continue;
                         }
                         $va_orderbys[] = $va_label_selects[] = 'lil.' . $vs_sort_by_field;
                     }
                     $vs_order_by = sizeof($va_orderbys) ? "ORDER BY " . join(', ', $va_orderbys) : '';
                     $vs_sql = "\n\t\t\t\t\t\t\t\tSELECT DISTINCT lil.item_id, lil.name_singular, lil.name_plural, lil.locale_id\n\t\t\t\t\t\t\t\tFROM ca_list_items li\n\t\t\t\t\t\t\t\tINNER JOIN ca_list_item_labels AS lil ON lil.item_id = li.item_id\n\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\tca_lists.list_code = ?  AND lil.is_preferred = 1 {$vs_where_sql} {$vs_order_by}";
                     //print $vs_sql." [$vs_list_name]";
                     $qr_res = $this->opo_db->query($vs_sql, $vs_list_name);
                     $va_values = array();
                     while ($qr_res->nextRow()) {
                         $vn_id = $qr_res->get('item_id');
                         if ($va_criteria[$vn_id]) {
                             continue;
                         }
                         // skip items that are used as browse critera - don't want to browse on something you're already browsing on
                         $va_values[$vn_id][$qr_res->get('locale_id')] = array('id' => $vn_id, 'label' => $qr_res->get('name_plural'));
                         if (!is_null($vs_single_value) && $vn_id == $vs_single_value) {
                             $vb_single_value_is_present = true;
                         }
                     }
                     if (!is_null($vs_single_value) && !$vb_single_value_is_present) {
                         return array();
                     }
                     return caExtractValuesByUserLocale($va_values);
                 }
             } else {
                 if ($vs_list_name = $va_field_info['LIST']) {
                     $va_list_items_by_value = array();
                     // fields with values set according to ca_list_items (not a foreign key ref)
                     if ($va_list_items = caExtractValuesByUserLocale($t_list->getItemsForList($vs_list_name))) {
                         foreach ($va_list_items as $vn_id => $va_list_item) {
                             $va_list_items_by_value[$va_list_item['item_value']] = $va_list_item['name_plural'];
                         }
                     } else {
                         foreach ($va_field_info['BOUNDS_CHOICE_LIST'] as $vs_val => $vn_id) {
                             $va_list_items_by_value[$vn_id] = $vs_val;
                         }
                     }
                     if (sizeof($va_results) && $this->numCriteria() > 0) {
                         $va_wheres[] = "(" . $t_subject->tableName() . '.' . $t_subject->primaryKey() . " IN (" . join(',', $va_results) . "))";
                     }
                     if ($vs_browse_type_limit_sql) {
                         $va_wheres[] = $vs_browse_type_limit_sql;
                     }
                     if ($t_subject->hasField('deleted')) {
                         $va_wheres[] = "(" . $t_subject->tableName() . ".deleted = 0)";
                     }
                     if ($va_facet_info['relative_to']) {
                         if ($va_relative_sql_data = $this->_getRelativeFacetSQLData($va_facet_info['relative_to'], $pa_options)) {
                             $va_joins = array_merge($va_joins, $va_relative_sql_data['joins']);
                             $va_wheres = array_merge($va_wheres, $va_relative_sql_data['wheres']);
                         }
                     }
                     if ($this->opo_config->get('perform_item_level_access_checking')) {
                         if ($t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true)) {
                             // Join to limit what browse table items are used to generate facet
                             $va_joins[] = 'LEFT JOIN ca_acl ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_acl.row_id AND ca_acl.table_num = ' . $t_item->tableNum() . "\n";
                             $va_wheres[] = "(\n\t\t\t\t\t\t\t\t\t\t((\n\t\t\t\t\t\t\t\t\t\t\t(ca_acl.user_id = " . (int) $vn_user_id . ")\n\t\t\t\t\t\t\t\t\t\t\t" . (sizeof($va_group_ids) > 0 ? "OR\n\t\t\t\t\t\t\t\t\t\t\t(ca_acl.group_id IN (" . join(",", $va_group_ids) . "))" : "") . "\n\t\t\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t\t\t(ca_acl.user_id IS NULL and ca_acl.group_id IS NULL)\n\t\t\t\t\t\t\t\t\t\t) AND ca_acl.access >= " . __CA_ACL_READONLY_ACCESS__ . ")\n\t\t\t\t\t\t\t\t\t\t" . ($vb_show_if_no_acl ? "OR ca_acl.acl_id IS NULL" : "") . "\n\t\t\t\t\t\t\t\t\t)";
                         }
                     }
                     if (is_array($va_wheres) && sizeof($va_wheres) && ($vs_where_sql = join(' AND ', $va_wheres))) {
                         $vs_where_sql = '(' . $vs_where_sql . ')';
                     }
                     $vs_join_sql = join("\n", $va_joins);
                     if ($vb_check_availability_only) {
                         $vs_sql = "\n\t\t\t\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\t\t\t\tFROM " . $vs_browse_table_name . "\n\t\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t\t" . ($vs_where_sql ? 'WHERE' : '') . "\n\t\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t\t\t\tLIMIT 2";
                         $qr_res = $this->opo_db->query($vs_sql);
                         return (int) $qr_res->numRows() > 1 ? true : false;
                     } else {
                         $vs_sql = "\n\t\t\t\t\t\t\t\t\tSELECT DISTINCT " . $vs_browse_table_name . '.' . $vs_field_name . "\n\t\t\t\t\t\t\t\t\tFROM " . $vs_browse_table_name . "\n\t\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t\t" . ($vs_where_sql ? 'WHERE' : '') . "\n\t\t\t\t\t\t\t\t\t\t{$vs_where_sql}";
                         //print $vs_sql." [$vs_list_name]";
                         $qr_res = $this->opo_db->query($vs_sql);
                         $va_values = array();
                         while ($qr_res->nextRow()) {
                             $vn_id = $qr_res->get($vs_field_name);
                             if ($va_criteria[$vn_id]) {
                                 continue;
                             }
                             // skip items that are used as browse critera - don't want to browse on something you're already browsing on
                             if (isset($va_list_items_by_value[$vn_id])) {
                                 $va_values[$vn_id] = array('id' => $vn_id, 'label' => $va_list_items_by_value[$vn_id]);
                                 if (!is_null($vs_single_value) && $vn_id == $vs_single_value) {
                                     $vb_single_value_is_present = true;
                                 }
                             }
                         }
                         if (!is_null($vs_single_value) && !$vb_single_value_is_present) {
                             return array();
                         }
                         return $va_values;
                     }
                 } else {
                     if ($t_browse_table = $this->opo_datamodel->getInstanceByTableName($vs_facet_table = $va_facet_info['table'], true)) {
                         // Handle fields containing ca_list_item.item_id's
                         $va_joins = array('INNER JOIN ' . $vs_browse_table_name . ' ON ' . $vs_browse_table_name . '.' . $vs_field_name . ' = ' . $vs_facet_table . '.' . $t_browse_table->primaryKey());
                         $vs_display_field_name = null;
                         if (method_exists($t_browse_table, 'getLabelTableInstance')) {
                             $t_label_instance = $t_browse_table->getLabelTableInstance();
                             $vs_display_field_name = isset($va_facet_info['display']) && $va_facet_info['display'] ? $va_facet_info['display'] : $t_label_instance->getDisplayField();
                             $va_joins[] = 'INNER JOIN ' . $t_label_instance->tableName() . " AS lab ON lab." . $t_browse_table->primaryKey() . ' = ' . $t_browse_table->tableName() . '.' . $t_browse_table->primaryKey();
                         }
                         if (sizeof($va_results) && $this->numCriteria() > 0) {
                             $va_wheres[] = "(" . $t_subject->tableName() . '.' . $t_subject->primaryKey() . " IN (" . join(',', $va_results) . "))";
                         }
                         if (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_item->hasField('access')) {
                             $va_wheres[] = "(" . $vs_browse_table_name . ".access IN (" . join(',', $pa_options['checkAccess']) . "))";
                         }
                         if ($vs_browse_type_limit_sql) {
                             $va_wheres[] = $vs_browse_type_limit_sql;
                         }
                         if ($va_facet_info['relative_to']) {
                             if ($t_subject->hasField('deleted')) {
                                 $va_wheres[] = "(" . $t_subject->tableName() . ".deleted = 0)";
                             }
                             if ($va_relative_sql_data = $this->_getRelativeFacetSQLData($va_facet_info['relative_to'], $pa_options)) {
                                 $va_joins = array_merge($va_joins, $va_relative_sql_data['joins']);
                                 $va_wheres = array_merge($va_wheres, $va_relative_sql_data['wheres']);
                             }
                         }
                         if ($this->opo_config->get('perform_item_level_access_checking')) {
                             if ($t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true)) {
                                 // Join to limit what browse table items are used to generate facet
                                 $va_joins[] = 'LEFT JOIN ca_acl ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_acl.row_id AND ca_acl.table_num = ' . $t_item->tableNum() . "\n";
                                 $va_wheres[] = "(\n\t\t\t\t\t\t\t\t\t\t\t((\n\t\t\t\t\t\t\t\t\t\t\t\t(ca_acl.user_id = " . (int) $vn_user_id . ")\n\t\t\t\t\t\t\t\t\t\t\t\t" . (sizeof($va_group_ids) > 0 ? "OR\n\t\t\t\t\t\t\t\t\t\t\t\t(ca_acl.group_id IN (" . join(",", $va_group_ids) . "))" : "") . "\n\t\t\t\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t\t\t\t(ca_acl.user_id IS NULL and ca_acl.group_id IS NULL)\n\t\t\t\t\t\t\t\t\t\t\t) AND ca_acl.access >= " . __CA_ACL_READONLY_ACCESS__ . ")\n\t\t\t\t\t\t\t\t\t\t\t" . ($vb_show_if_no_acl ? "OR ca_acl.acl_id IS NULL" : "") . "\n\t\t\t\t\t\t\t\t\t\t)";
                             }
                         }
                         $vs_join_sql = join("\n", $va_joins);
                         if (is_array($va_wheres) && sizeof($va_wheres) && ($vs_where_sql = join(' AND ', $va_wheres))) {
                             $vs_where_sql = 'WHERE (' . $vs_where_sql . ')';
                         }
                         if ($vb_check_availability_only) {
                             $vs_sql = "\n\t\t\t\t\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\t\t\t\t\tFROM {$vs_facet_table}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t\t\t\t\tLIMIT 1";
                             $qr_res = $this->opo_db->query($vs_sql);
                             return (int) $qr_res->numRows() > 0 ? true : false;
                         } else {
                             $vs_sql = "\n\t\t\t\t\t\t\t\t\t\tSELECT DISTINCT *\n\t\t\t\t\t\t\t\t\t\tFROM {$vs_facet_table}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t\t\t{$vs_where_sql}";
                             //print $vs_sql;
                             $qr_res = $this->opo_db->query($vs_sql);
                             $va_values = array();
                             $vs_pk = $t_browse_table->primaryKey();
                             while ($qr_res->nextRow()) {
                                 $vn_id = $qr_res->get($vs_pk);
                                 if ($va_criteria[$vn_id]) {
                                     continue;
                                 }
                                 // skip items that are used as browse critera - don't want to browse on something you're already browsing on
                                 $va_values[$vn_id][$qr_res->get('locale_id')] = array('id' => $vn_id, 'label' => $qr_res->get($vs_display_field_name));
                                 if (!is_null($vs_single_value) && $vn_id == $vs_single_value) {
                                     $vb_single_value_is_present = true;
                                 }
                             }
                             if (!is_null($vs_single_value) && !$vb_single_value_is_present) {
                                 return array();
                             }
                             return caExtractValuesByUserLocale($va_values);
                         }
                     }
                 }
             }
             return array();
             break;
             # -----------------------------------------------------
         # -----------------------------------------------------
         case 'field':
             $t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true);
             if (!is_array($va_restrict_to_types = $va_facet_info['restrict_to_types'])) {
                 $va_restrict_to_types = array();
             }
             if (!is_array($va_restrict_to_types = $this->_convertTypeCodesToIDs($va_restrict_to_types, array('instance' => $t_item, 'dontExpandHierarchically' => true)))) {
                 $va_restrict_to_types = array();
             }
             $va_restrict_to_types_expanded = $this->_convertTypeCodesToIDs($va_restrict_to_types, array('instance' => $t_item));
             $vs_field_name = $va_facet_info['field'];
             $va_field_info = $t_item->getFieldInfo($vs_field_name);
             $vs_sort_field = null;
             if ($t_item->getProperty('ID_NUMBERING_ID_FIELD') == $vs_field_name) {
                 $vs_sort_field = $vs_browse_table_name . '.' . $t_item->getProperty('ID_NUMBERING_SORT_FIELD');
             }
             $t_list = new ca_lists();
             $t_list_item = new ca_list_items();
             $va_joins = array();
             $va_wheres = array();
             $vs_where_sql = '';
             $va_facet_values = null;
             if ($vb_is_bit = $va_field_info['FIELD_TYPE'] == FT_BIT) {
                 $vs_yes_text = caGetOption('label_yes', $va_facet_info, _t('Yes'));
                 $vs_no_text = caGetOption('label_no', $va_facet_info, _t('No'));
                 $va_facet_values = array(1 => array('id' => 1, 'label' => $vs_yes_text), 0 => array('id' => 0, 'label' => $vs_no_text));
             }
             if (is_array($va_restrict_to_types) && sizeof($va_restrict_to_types) > 0 && method_exists($t_rel_item, "getTypeList")) {
                 $va_wheres[] = "{$va_restrict_to_types_expanded}.type_id IN (" . join(',', caGetOption('dont_include_subtypes', $va_facet_info, false) ? $va_restrict_to_types : $va_restrict_to_types_expanded) . ")";
                 $va_selects[] = "{$va_restrict_to_types_expanded}.type_id";
             }
             if (sizeof($va_results) && $this->numCriteria() > 0) {
                 $va_wheres[] = "(" . $t_subject->tableName() . '.' . $t_subject->primaryKey() . " IN (" . join(',', $va_results) . "))";
             }
             if (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_item->hasField('access')) {
                 $va_wheres[] = "(" . $vs_browse_table_name . ".access IN (" . join(',', $pa_options['checkAccess']) . "))";
             }
             if ($vs_browse_type_limit_sql) {
                 $va_wheres[] = $vs_browse_type_limit_sql;
             }
             if ($t_item->hasField('deleted')) {
                 $va_wheres[] = "(" . $vs_browse_table_name . ".deleted = 0)";
             }
             if ($va_facet_info['relative_to']) {
                 if ($t_subject->hasField('deleted')) {
                     $va_wheres[] = "(" . $t_subject->tableName() . ".deleted = 0)";
                 }
                 if ($va_relative_sql_data = $this->_getRelativeFacetSQLData($va_facet_info['relative_to'], $pa_options)) {
                     $va_joins = array_merge($va_joins, $va_relative_sql_data['joins']);
                     $va_wheres = array_merge($va_wheres, $va_relative_sql_data['wheres']);
                 }
             }
             if ($this->opo_config->get('perform_item_level_access_checking')) {
                 if ($t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true)) {
                     // Join to limit what browse table items are used to generate facet
                     $va_joins[] = 'LEFT JOIN ca_acl ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_acl.row_id AND ca_acl.table_num = ' . $t_item->tableNum() . "\n";
                     $va_wheres[] = "(\n\t\t\t\t\t\t\t\t((\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id = " . (int) $vn_user_id . ")\n\t\t\t\t\t\t\t\t\t" . (sizeof($va_group_ids) > 0 ? "OR\n\t\t\t\t\t\t\t\t\t(ca_acl.group_id IN (" . join(",", $va_group_ids) . "))" : "") . "\n\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id IS NULL and ca_acl.group_id IS NULL)\n\t\t\t\t\t\t\t\t) AND ca_acl.access >= " . __CA_ACL_READONLY_ACCESS__ . ")\n\t\t\t\t\t\t\t\t" . ($vb_show_if_no_acl ? "OR ca_acl.acl_id IS NULL" : "") . "\n\t\t\t\t\t\t\t)";
                 }
             }
             $vs_join_sql = join("\n", $va_joins);
             if (is_array($va_wheres) && sizeof($va_wheres) && ($vs_where_sql = join(' AND ', $va_wheres))) {
                 $vs_where_sql = '(' . $vs_where_sql . ')';
             }
             if ($vb_check_availability_only) {
                 $vs_sql = "\n\t\t\t\t\t\t\tSELECT DISTINCT {$vs_browse_table_name}.{$vs_field_name}\n\t\t\t\t\t\t\tFROM {$vs_browse_table_name}\n\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t\tLIMIT 2";
                 $qr_res = $this->opo_db->query($vs_sql);
                 if ($qr_res->numRows() > 1) {
                     return true;
                 }
                 return false;
             } else {
                 $vs_pk = $t_item->primaryKey();
                 $vs_sql = "\n\t\t\t\t\t\t\tSELECT DISTINCT {$vs_browse_table_name}.{$vs_field_name}\n\t\t\t\t\t\t\tFROM {$vs_browse_table_name}\n\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t{$vs_where_sql}";
                 if ($vs_sort_field) {
                     $vs_sql .= " ORDER BY {$vs_sort_field}";
                 }
                 $qr_res = $this->opo_db->query($vs_sql);
                 $va_values = array();
                 while ($qr_res->nextRow()) {
                     if (!($vs_val = trim($qr_res->get($vs_field_name))) && !$vb_is_bit) {
                         continue;
                     }
                     if ($va_criteria[$vs_val]) {
                         continue;
                     }
                     // skip items that are used as browse critera - don't want to browse on something you're already browsing on
                     if ($vb_is_bit && isset($va_facet_values[$vs_val])) {
                         $va_values[$vs_val] = $va_facet_values[$vs_val];
                     } else {
                         $va_values[$vs_val] = array('id' => str_replace('/', '&#47;', $vs_val), 'label' => $vs_val);
                     }
                     if (!is_null($vs_single_value) && $vs_val == $vs_single_value) {
                         $vb_single_value_is_present = true;
                     }
                 }
                 if (!is_null($vs_single_value) && !$vb_single_value_is_present) {
                     return array();
                 }
                 return $va_values;
             }
             return array();
             break;
             # -----------------------------------------------------
         # -----------------------------------------------------
         case 'violations':
             $t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true);
             $vs_field_name = $va_facet_info['field'];
             $va_field_info = $t_item->getFieldInfo($vs_field_name);
             $va_joins = array();
             $va_wheres = array();
             $vs_where_sql = '';
             $va_facet_values = null;
             if (sizeof($va_results) && $this->numCriteria() > 0) {
                 $va_wheres[] = "(" . $t_subject->tableName() . '.' . $t_subject->primaryKey() . " IN (" . join(',', $va_results) . "))";
             }
             if (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_item->hasField('access')) {
                 $va_wheres[] = "(" . $vs_browse_table_name . ".access IN (" . join(',', $pa_options['checkAccess']) . "))";
             }
             if ($vs_browse_type_limit_sql) {
                 $va_wheres[] = $vs_browse_type_limit_sql;
             }
             if ($t_item->hasField('deleted')) {
                 $va_wheres[] = "(" . $vs_browse_table_name . ".deleted = 0)";
             }
             if ($va_facet_info['relative_to']) {
                 if ($t_subject->hasField('deleted')) {
                     $va_wheres[] = "(" . $t_subject->tableName() . ".deleted = 0)";
                 }
                 if ($va_relative_sql_data = $this->_getRelativeFacetSQLData($va_facet_info['relative_to'], $pa_options)) {
                     $va_joins = array_merge($va_joins, $va_relative_sql_data['joins']);
                     $va_wheres = array_merge($va_wheres, $va_relative_sql_data['wheres']);
                 }
             }
             if ($this->opo_config->get('perform_item_level_access_checking')) {
                 if ($t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true)) {
                     // Join to limit what browse table items are used to generate facet
                     $va_joins[] = 'LEFT JOIN ca_acl ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_acl.row_id AND ca_acl.table_num = ' . $t_item->tableNum() . "\n";
                     $va_wheres[] = "(\n\t\t\t\t\t\t\t\t((\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id = " . (int) $vn_user_id . ")\n\t\t\t\t\t\t\t\t\t" . (sizeof($va_group_ids) > 0 ? "OR\n\t\t\t\t\t\t\t\t\t(ca_acl.group_id IN (" . join(",", $va_group_ids) . "))" : "") . "\n\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id IS NULL and ca_acl.group_id IS NULL)\n\t\t\t\t\t\t\t\t) AND ca_acl.access >= " . __CA_ACL_READONLY_ACCESS__ . ")\n\t\t\t\t\t\t\t\t" . ($vb_show_if_no_acl ? "OR ca_acl.acl_id IS NULL" : "") . "\n\t\t\t\t\t\t\t)";
                 }
             }
             $vs_join_sql = join("\n", $va_joins);
             if (is_array($va_wheres) && sizeof($va_wheres) && ($vs_where_sql = join(' AND ', $va_wheres))) {
                 $vs_where_sql = '(' . $vs_where_sql . ')';
             }
             if ($vb_check_availability_only) {
                 $vs_sql = "\n\t\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\t\tFROM {$vs_browse_table_name}\n\t\t\t\t\t\t\tINNER JOIN ca_metadata_dictionary_rule_violations ON ca_metadata_dictionary_rule_violations.row_id = {$vs_browse_table_name}." . $t_item->primaryKey() . " AND ca_metadata_dictionary_rule_violations.table_num = {$vs_browse_table_num}\n\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t\tLIMIT 2";
                 $qr_res = $this->opo_db->query($vs_sql);
                 if ($qr_res->nextRow()) {
                     return (int) $qr_res->numRows() > 0 ? true : false;
                 }
                 return false;
             } else {
                 $vs_pk = $t_item->primaryKey();
                 $vs_sql = "\n\t\t\t\t\t\t\tSELECT DISTINCT ca_metadata_dictionary_rules.rule_id\n\t\t\t\t\t\t\tFROM {$vs_browse_table_name}\n\t\t\t\t\t\t\tINNER JOIN ca_metadata_dictionary_rule_violations ON ca_metadata_dictionary_rule_violations.row_id = {$vs_browse_table_name}." . $t_item->primaryKey() . " AND ca_metadata_dictionary_rule_violations.table_num = {$vs_browse_table_num}\n\t\t\t\t\t\t\tINNER JOIN ca_metadata_dictionary_rules ON ca_metadata_dictionary_rules.rule_id = ca_metadata_dictionary_rule_violations.rule_id\n\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t{$vs_where_sql}";
                 $qr_res = $this->opo_db->query($vs_sql);
                 $va_values = array();
                 $t_rule = new ca_metadata_dictionary_rules();
                 while ($qr_res->nextRow()) {
                     if ($t_rule->load($qr_res->get('rule_id'))) {
                         if (!($vs_val = trim($t_rule->getSetting('label')))) {
                             continue;
                         }
                         $vs_code = $t_rule->get('rule_code');
                         if ($va_criteria[$vs_val]) {
                             continue;
                         }
                         // skip items that are used as browse critera - don't want to browse on something you're already browsing on
                         if (isset($va_facet_values[$vs_code])) {
                             $va_values[$vs_code] = $va_facet_values[$vs_code];
                         } else {
                             $va_values[$vs_code] = array('id' => $vs_code, 'label' => $vs_val);
                         }
                         if (!is_null($vs_single_value) && $vs_code == $vs_single_value) {
                             $vb_single_value_is_present = true;
                         }
                     }
                 }
                 if (!is_null($vs_single_value) && !$vb_single_value_is_present) {
                     return array();
                 }
                 return $va_values;
             }
             return array();
             break;
             # -----------------------------------------------------
         # -----------------------------------------------------
         case 'checkouts':
             if ($vs_browse_table_name != 'ca_objects') {
                 return array();
             }
             $t_item = new ca_objects();
             $va_joins = array();
             $va_wheres = array();
             $vs_where_sql = '';
             $va_facet_values = null;
             if (sizeof($va_results) && $this->numCriteria() > 0) {
                 $va_wheres[] = "(" . $t_subject->tableName() . '.' . $t_subject->primaryKey() . " IN (" . join(',', $va_results) . "))";
             }
             if (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_item->hasField('access')) {
                 $va_wheres[] = "(" . $vs_browse_table_name . ".access IN (" . join(',', $pa_options['checkAccess']) . "))";
             }
             if ($vs_browse_type_limit_sql) {
                 $va_wheres[] = $vs_browse_type_limit_sql;
             }
             if ($t_item->hasField('deleted')) {
                 $va_wheres[] = "(" . $vs_browse_table_name . ".deleted = 0)";
             }
             if ($va_facet_info['relative_to']) {
                 if ($t_subject->hasField('deleted')) {
                     $va_wheres[] = "(" . $t_subject->tableName() . ".deleted = 0)";
                 }
                 if ($va_relative_sql_data = $this->_getRelativeFacetSQLData($va_facet_info['relative_to'], $pa_options)) {
                     $va_joins = array_merge($va_joins, $va_relative_sql_data['joins']);
                     $va_wheres = array_merge($va_wheres, $va_relative_sql_data['wheres']);
                 }
             }
             $vs_checkout_join_sql = "INNER JOIN ca_object_checkouts ON ca_object_checkouts.object_id = ca_objects.object_id";
             $vn_current_time = time();
             switch ($va_facet_info['status']) {
                 case 'overdue':
                     $va_wheres[] = "((ca_object_checkouts.checkout_date <= {$vn_current_time}) AND (ca_object_checkouts.return_date IS NULL) AND (ca_object_checkouts.due_date <= {$vn_current_time}))";
                     break;
                 case 'reserved':
                     $va_wheres[] = "((ca_object_checkouts.checkout_date IS NULL) AND (ca_object_checkouts.return_date IS NULL))";
                     break;
                 case 'available':
                     $vs_checkout_join_sql = '';
                     $va_wheres[] = "(ca_objects.object_id NOT IN (SELECT object_id FROM ca_object_checkouts WHERE (ca_object_checkouts.checkout_date <= {$vn_current_time}) AND (ca_object_checkouts.return_date IS NULL)))";
                     break;
                 default:
                 case 'out':
                     $va_wheres[] = "((ca_object_checkouts.checkout_date <= {$vn_current_time}) AND (ca_object_checkouts.return_date IS NULL))";
                     break;
             }
             if ($vs_checkout_join_sql) {
                 $va_joins[] = $vs_checkout_join_sql;
                 $va_joins[] = "INNER JOIN ca_users ON ca_object_checkouts.user_id = ca_users.user_id";
             }
             if ($this->opo_config->get('perform_item_level_access_checking')) {
                 if ($t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true)) {
                     // Join to limit what browse table items are used to generate facet
                     $va_joins[] = 'LEFT JOIN ca_acl ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_acl.row_id AND ca_acl.table_num = ' . $t_item->tableNum() . "\n";
                     $va_wheres[] = "(\n\t\t\t\t\t\t\t\t((\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id = " . (int) $vn_user_id . ")\n\t\t\t\t\t\t\t\t\t" . (sizeof($va_group_ids) > 0 ? "OR\n\t\t\t\t\t\t\t\t\t(ca_acl.group_id IN (" . join(",", $va_group_ids) . "))" : "") . "\n\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id IS NULL and ca_acl.group_id IS NULL)\n\t\t\t\t\t\t\t\t) AND ca_acl.access >= " . __CA_ACL_READONLY_ACCESS__ . ")\n\t\t\t\t\t\t\t\t" . ($vb_show_if_no_acl ? "OR ca_acl.acl_id IS NULL" : "") . "\n\t\t\t\t\t\t\t)";
                 }
             }
             $vs_join_sql = join("\n", $va_joins);
             if (is_array($va_wheres) && sizeof($va_wheres) && ($vs_where_sql = join(' AND ', $va_wheres))) {
                 $vs_where_sql = '(' . $vs_where_sql . ')';
             }
             if ($vb_check_availability_only) {
                 switch ($va_facet_info['mode']) {
                     case 'user':
                         $vs_sql = "\n\t\t\t\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\t\t\t\tFROM ca_objects\n\t\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t{$vs_where_sql} AND ca_objects.deleted = 0\n\t\t\t\t\t\t\t\t\tLIMIT 2";
                         break;
                     default:
                     case 'all':
                         $vs_sql = "\n\t\t\t\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\t\t\t\tFROM ca_objects\n\t\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\tca_objects.deleted = 0 " . (sizeof($va_results) ? "AND (" . $t_subject->tableName() . '.' . $t_subject->primaryKey() . " IN (" . join(',', $va_results) . "))" : "") . "\n\t\t\t\t\t\t\t\t\tLIMIT 2";
                         break;
                 }
                 $qr_res = $this->opo_db->query($vs_sql);
                 if ($qr_res->nextRow()) {
                     return (int) $qr_res->numRows() > 0 ? true : false;
                 }
                 return false;
             } else {
                 $va_values = array();
                 $vs_pk = $t_item->primaryKey();
                 switch ($va_facet_info['mode']) {
                     case 'user':
                         $vs_sql = "\n\t\t\t\t\t\t\t\t\tSELECT DISTINCT ca_object_checkouts.user_id, ca_users.fname, ca_users.lname, ca_users.email\n\t\t\t\t\t\t\t\t\tFROM ca_objects\n\t\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t{$vs_where_sql} " . (sizeof($va_results) ? " AND (" . $t_subject->tableName() . '.' . $t_subject->primaryKey() . " IN (" . join(',', $va_results) . "))" : "");
                         $qr_res = $this->opo_db->query($vs_sql);
                         while ($qr_res->nextRow()) {
                             $vn_user_id = $qr_res->get('user_id');
                             $vs_val = $qr_res->get('fname') . ' ' . $qr_res->get('lname') . (($vs_email = $qr_res->get('email')) ? "({$vs_email})" : '');
                             if ($va_criteria[$vs_val]) {
                                 continue;
                             }
                             // skip items that are used as browse critera - don't want to browse on something you're already browsing on
                             if (isset($va_facet_values[$vn_user_id])) {
                                 $va_values[$vn_user_id] = $va_facet_values[$vn_user_id];
                             } else {
                                 $va_values[$vn_user_id] = array('id' => $vn_user_id, 'label' => $vs_val);
                             }
                             if (!is_null($vs_single_value) && $vn_user_id == $vs_single_value) {
                                 $vb_single_value_is_present = true;
                             }
                         }
                         break;
                     case 'all':
                     default:
                         foreach (array(_t('Available') => 'available', _t('Out') => 'out', _t('Reserved') => 'reserved', _t('Overdue') => 'overdue') as $vs_status_text => $vs_status) {
                             $vs_join_sql = "INNER JOIN ca_object_checkouts ON ca_object_checkouts.object_id = ca_objects.object_id";
                             switch ($vs_status) {
                                 case 'overdue':
                                     $vs_where = "((ca_object_checkouts.checkout_date <= {$vn_current_time}) AND (ca_object_checkouts.return_date IS NULL) AND (ca_object_checkouts.due_date <= {$vn_current_time}))";
                                     break;
                                 case 'reserved':
                                     $vs_where = "((ca_object_checkouts.checkout_date IS NULL) AND (ca_object_checkouts.return_date IS NULL))";
                                     break;
                                 case 'available':
                                     $vs_join_sql = '';
                                     $vs_where = "(ca_objects.object_id NOT IN (SELECT object_id FROM ca_object_checkouts WHERE (ca_object_checkouts.checkout_date <= {$vn_current_time}) AND (ca_object_checkouts.return_date IS NULL)))";
                                     break;
                                 default:
                                 case 'out':
                                     $vs_where = "((ca_object_checkouts.checkout_date <= {$vn_current_time}) AND (ca_object_checkouts.return_date IS NULL))";
                                     break;
                             }
                             if (sizeof($va_results) && $this->numCriteria() > 0) {
                                 $vs_where .= " AND (" . $t_subject->tableName() . '.' . $t_subject->primaryKey() . " IN (" . join(',', $va_results) . "))";
                             }
                             $vs_sql = "\n\t\t\t\t\t\t\t\t\t\tSELECT count(*) c\n\t\t\t\t\t\t\t\t\t\tFROM ca_objects\n\t\t\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t{$vs_where}\n\t\t\t\t\t\t\t\t\t";
                             $qr_res = $this->opo_db->query($vs_sql);
                             $qr_res->nextRow();
                             if (!$qr_res->get('c')) {
                                 continue;
                             }
                             $va_values[$vs_status] = array('id' => $vs_status, 'label' => $vs_status_text);
                             if (!is_null($vs_single_value) && $vs_status == $vs_single_value) {
                                 $vb_single_value_is_present = true;
                             }
                         }
                         break;
                 }
                 if (!is_null($vs_single_value) && !$vb_single_value_is_present) {
                     return array();
                 }
                 return $va_values;
             }
             return array();
             break;
             # -----------------------------------------------------
         # -----------------------------------------------------
         case 'normalizedDates':
             $t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true);
             $t_element = new ca_metadata_elements();
             $vb_is_element = $vb_is_field = false;
             if (!($vb_is_element = $t_element->load(array('element_code' => $va_facet_info['element_code']))) && !($vb_is_field = $t_item->hasField($va_facet_info['element_code']) && $t_item->getFieldInfo($va_facet_info['element_code'], 'FIELD_TYPE') === FT_HISTORIC_DATERANGE)) {
                 return array();
             }
             if ($vb_is_element) {
                 $va_joins = array('INNER JOIN ca_attribute_values ON ca_attributes.attribute_id = ca_attribute_values.attribute_id', 'INNER JOIN ' . $vs_browse_table_name . ' ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_attributes.row_id AND ca_attributes.table_num = ' . intval($vs_browse_table_num));
             } else {
                 $va_joins = array();
             }
             $va_wheres = array();
             $vs_normalization = $va_facet_info['normalization'];
             // how do we construct the date ranges presented to uses. In other words - how do we want to allow users to browse dates? By year, decade, century?
             if (sizeof($va_results) && $this->numCriteria() > 0) {
                 $va_wheres[] = "(" . $t_subject->tableName() . '.' . $t_subject->primaryKey() . " IN (" . join(',', $va_results) . "))";
             }
             if (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_item->hasField('access')) {
                 $va_wheres[] = "(" . $vs_browse_table_name . ".access IN (" . join(',', $pa_options['checkAccess']) . "))";
             }
             if ($vs_browse_type_limit_sql) {
                 $va_wheres[] = $vs_browse_type_limit_sql;
             }
             if ($t_item->hasField('deleted')) {
                 $va_wheres[] = "(" . $vs_browse_table_name . ".deleted = 0)";
             }
             if ($va_facet_info['relative_to']) {
                 if ($t_subject->hasField('deleted')) {
                     $va_wheres[] = "(" . $t_subject->tableName() . ".deleted = 0)";
                 }
                 if ($va_relative_sql_data = $this->_getRelativeFacetSQLData($va_facet_info['relative_to'], $pa_options)) {
                     $va_joins = array_merge($va_joins, $va_relative_sql_data['joins']);
                     $va_wheres = array_merge($va_wheres, $va_relative_sql_data['wheres']);
                 }
             }
             if ($this->opo_config->get('perform_item_level_access_checking')) {
                 if ($t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true)) {
                     // Join to limit what browse table items are used to generate facet
                     $va_joins[] = 'LEFT JOIN ca_acl ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_acl.row_id AND ca_acl.table_num = ' . $t_item->tableNum() . "\n";
                     $va_wheres[] = "(\n\t\t\t\t\t\t\t\t((\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id = " . (int) $vn_user_id . ")\n\t\t\t\t\t\t\t\t\t" . (sizeof($va_group_ids) > 0 ? "OR\n\t\t\t\t\t\t\t\t\t(ca_acl.group_id IN (" . join(",", $va_group_ids) . "))" : "") . "\n\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id IS NULL and ca_acl.group_id IS NULL)\n\t\t\t\t\t\t\t\t) AND ca_acl.access >= " . __CA_ACL_READONLY_ACCESS__ . ")\n\t\t\t\t\t\t\t\t" . ($vb_show_if_no_acl ? "OR ca_acl.acl_id IS NULL" : "") . "\n\t\t\t\t\t\t\t)";
                 }
             }
             $vs_where_sql = '';
             if (is_array($va_wheres) && sizeof($va_wheres) && ($vs_where_sql = join(' AND ', $va_wheres))) {
                 $vs_where_sql = ' AND (' . $vs_where_sql . ')';
             }
             $vs_join_sql = join("\n", $va_joins);
             if ($vb_is_element) {
                 $vn_element_id = $t_element->getPrimaryKey();
                 $vs_dir = strtoupper($va_facet_info['sort']) === 'DESC' ? "DESC" : "ASC";
                 $o_tep = new TimeExpressionParser();
                 $vn_min_date = $vn_max_date = null;
                 $vs_min_sql = $vs_max_sql = '';
                 if (isset($va_facet_info['minimum_date'])) {
                     if ($o_tep->parse($va_facet_info['minimum_date'])) {
                         $va_tmp = $o_tep->getHistoricTimestamps();
                         $vn_min_date = (double) $va_tmp['start'];
                         $vs_min_sql = " AND (ca_attribute_values.value_decimal1 >= {$vn_min_date})";
                     }
                 }
                 if (isset($va_facet_info['maximum_date'])) {
                     if ($o_tep->parse($va_facet_info['maximum_date'])) {
                         $va_tmp = $o_tep->getHistoricTimestamps();
                         $vn_max_date = (double) $va_tmp['end'];
                         $vs_max_sql = " AND (ca_attribute_values.value_decimal2 <= {$vn_max_date})";
                     }
                 }
                 if ($vb_check_availability_only) {
                     $vs_sql = "\n\t\t\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\t\t\tFROM ca_attributes\n\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\tca_attribute_values.element_id = ? \n\t\t\t\t\t\t\t\t\t{$vs_min_sql}\n\t\t\t\t\t\t\t\t\t{$vs_max_sql}\n\t\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t\t\t\tLIMIT 1";
                     //print $vs_sql;
                     $qr_res = $this->opo_db->query($vs_sql, $vn_element_id);
                     return (int) $qr_res->numRows() > 0 ? true : false;
                 } else {
                     $vs_sql = "\n\t\t\t\t\t\t\t\tSELECT DISTINCT ca_attribute_values.value_decimal1, ca_attribute_values.value_decimal2\n\t\t\t\t\t\t\t\tFROM ca_attributes\n\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\tca_attribute_values.element_id = ? \n\t\t\t\t\t\t\t\t\t{$vs_min_sql}\n\t\t\t\t\t\t\t\t\t{$vs_max_sql}\n\t\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t\t";
                     //print $vs_sql;
                     $qr_res = $this->opo_db->query($vs_sql, $vn_element_id);
                     $vn_current_year = (int) date("Y");
                     $va_values = array();
                     $vb_include_unknown = (bool) caGetOption('include_unknown', $va_facet_info, false);
                     $vb_unknown_is_set = false;
                     while ($qr_res->nextRow()) {
                         $vn_start = $qr_res->get('value_decimal1');
                         $vn_end = $qr_res->get('value_decimal2');
                         if (!($vn_start && $vn_end)) {
                             if ($vb_include_unknown) {
                                 $vb_unknown_is_set = true;
                             }
                             continue;
                         }
                         if ($vn_end > $vn_current_year + 50) {
                             continue;
                         }
                         // bad years can make for large facets that cause timeouts so cut it off 50 years into the future
                         $va_normalized_values = $o_tep->normalizeDateRange($vn_start, $vn_end, $vs_normalization);
                         foreach ($va_normalized_values as $vn_sort_value => $vs_normalized_value) {
                             if ($va_criteria[$vs_normalized_value]) {
                                 continue;
                             }
                             // skip items that are used as browse critera - don't want to browse on something you're already browsing on
                             if (is_numeric($vs_normalized_value) && (int) $vs_normalized_value === 0) {
                                 continue;
                             }
                             // don't include year=0
                             $va_values[$vn_sort_value][$vs_normalized_value] = array('id' => $vs_normalized_value, 'label' => $vs_normalized_value);
                             if (!is_null($vs_single_value) && $vs_normalized_value == $vs_single_value) {
                                 $vb_single_value_is_present = true;
                             }
                         }
                     }
                     if ($vb_include_unknown && !$vb_unknown_is_set) {
                         // Check for rows where no data is set at all as opposed to null dates
                         $vs_sql = "\n\t\t\t\t\t\t\t\t\tSELECT DISTINCT ca_attributes.row_id\n\t\t\t\t\t\t\t\t\tFROM ca_attributes\n\t\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\tca_attribute_values.element_id = ? \n\t\t\t\t\t\t\t\t\t\t{$vs_min_sql}\n\t\t\t\t\t\t\t\t\t\t{$vs_max_sql}\n\t\t\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t\t\t";
                         //print $vs_sql;
                         $qr_res = $this->opo_db->query($vs_sql, $vn_element_id);
                         if ($qr_res->numRows() < sizeof($va_results)) {
                             $vb_unknown_is_set = true;
                         }
                     }
                     if ($vb_unknown_is_set && sizeof($va_values) > 0) {
                         $va_values['999999999'][_t('Date unknown')] = array('id' => 'null', 'label' => _t('Date unknown'));
                     }
                     if (!is_null($vs_single_value) && !$vb_single_value_is_present) {
                         return array();
                     }
                     ksort($va_values);
                     if ($vs_dir == 'DESC') {
                         $va_values = array_reverse($va_values);
                     }
                     $va_sorted_values = array();
                     foreach ($va_values as $vn_sort_value => $va_values_for_sort_value) {
                         $va_sorted_values = array_merge($va_sorted_values, $va_values_for_sort_value);
                     }
                     return $va_sorted_values;
                 }
             } else {
                 // is intrinsic
                 $vs_dir = strtoupper($va_facet_info['sort']) === 'DESC' ? "DESC" : "ASC";
                 $vs_browse_start_fld = $t_item->getFieldInfo($va_facet_info['element_code'], 'START');
                 $vs_browse_end_fld = $t_item->getFieldInfo($va_facet_info['element_code'], 'END');
                 $o_tep = new TimeExpressionParser();
                 $vn_min_date = $vn_max_date = null;
                 $vs_min_sql = $vs_max_sql = '';
                 if (isset($va_facet_info['minimum_date'])) {
                     if ($o_tep->parse($va_facet_info['minimum_date'])) {
                         $va_tmp = $o_tep->getHistoricTimestamps();
                         $vn_min_date = (double) $va_tmp['start'];
                         $vs_min_sql = " AND ({$vs_browse_table_name}.{$vs_browse_start_fld} >= {$vn_min_date})";
                     }
                 }
                 if (isset($va_facet_info['maximum_date'])) {
                     if ($o_tep->parse($va_facet_info['maximum_date'])) {
                         $va_tmp = $o_tep->getHistoricTimestamps();
                         $vn_max_date = (double) $va_tmp['end'];
                         $vs_max_sql = " AND ({$vs_browse_table_name}.{$vs_browse_end_fld} <= {$vn_max_date})";
                     }
                 }
                 if ($vb_check_availability_only) {
                     $vs_sql = "\n\t\t\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\t\t\tFROM {$vs_browse_table_name}\n\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t1 = 1\n\t\t\t\t\t\t\t\t\t{$vs_min_sql}\n\t\t\t\t\t\t\t\t\t{$vs_max_sql}\n\t\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t\t\t\tLIMIT 1";
                     //print $vs_sql;
                     $qr_res = $this->opo_db->query($vs_sql);
                     return (int) $qr_res->numRows() > 0 ? true : false;
                 } else {
                     $vs_sql = "\n\t\t\t\t\t\t\t\tSELECT DISTINCT {$vs_browse_table_name}.{$vs_browse_start_fld}, {$vs_browse_table_name}.{$vs_browse_end_fld}\n\t\t\t\t\t\t\t\tFROM {$vs_browse_table_name}\n\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t1 = 1\n\t\t\t\t\t\t\t\t\t{$vs_min_sql}\n\t\t\t\t\t\t\t\t\t{$vs_max_sql}\n\t\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t\t";
                     //print $vs_sql;
                     $qr_res = $this->opo_db->query($vs_sql);
                     $va_values = array();
                     while ($qr_res->nextRow()) {
                         $vn_start = $qr_res->get($vs_browse_start_fld);
                         $vn_end = $qr_res->get($vs_browse_end_fld);
                         if (!($vn_start && $vn_end)) {
                             continue;
                         }
                         $va_normalized_values = $o_tep->normalizeDateRange($vn_start, $vn_end, $vs_normalization);
                         foreach ($va_normalized_values as $vn_sort_value => $vs_normalized_value) {
                             if ($va_criteria[$vs_normalized_value]) {
                                 continue;
                             }
                             // skip items that are used as browse critera - don't want to browse on something you're already browsing on
                             if (is_numeric($vs_normalized_value) && (int) $vs_normalized_value === 0) {
                                 continue;
                             }
                             // don't include year=0
                             $va_values[$vn_sort_value][$vs_normalized_value] = array('id' => $vs_normalized_value, 'label' => $vs_normalized_value);
                             if (!is_null($vs_single_value) && $vs_normalized_value == $vs_single_value) {
                                 $vb_single_value_is_present = true;
                             }
                         }
                     }
                     if (!is_null($vs_single_value) && !$vb_single_value_is_present) {
                         return array();
                     }
                     ksort($va_values);
                     if ($vs_dir == 'DESC') {
                         $va_values = array_reverse($va_values);
                     }
                     $va_sorted_values = array();
                     foreach ($va_values as $vn_sort_value => $va_values_for_sort_value) {
                         $va_sorted_values = array_merge($va_sorted_values, $va_values_for_sort_value);
                     }
                     return $va_sorted_values;
                 }
             }
             break;
             # -----------------------------------------------------
         # -----------------------------------------------------
         case 'normalizedLength':
             $t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true);
             $t_element = new ca_metadata_elements();
             $vb_is_element = $vb_is_field = false;
             if (!($vb_is_element = $t_element->load(array('element_code' => $va_facet_info['element_code']))) && !($vb_is_field = $t_item->hasField($va_facet_info['element_code']) && $t_item->getFieldInfo($va_facet_info['element_code'], 'FIELD_TYPE') === FT_HISTORIC_DATERANGE)) {
                 return array();
             }
             if ($vb_is_element) {
                 $va_joins = array('INNER JOIN ca_attribute_values ON ca_attributes.attribute_id = ca_attribute_values.attribute_id', 'INNER JOIN ' . $vs_browse_table_name . ' ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_attributes.row_id AND ca_attributes.table_num = ' . intval($vs_browse_table_num));
             } else {
                 $va_joins = array();
             }
             $va_wheres = array();
             $vs_normalization = $va_facet_info['normalization'];
             // how do we construct the dimensions ranges presented to users. In other words - what increments do we can to use to  browse measurments?
             if (sizeof($va_results) && $this->numCriteria() > 0) {
                 $va_wheres[] = "(" . $t_subject->tableName() . '.' . $t_subject->primaryKey() . " IN (" . join(',', $va_results) . "))";
             }
             if (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_item->hasField('access')) {
                 $va_wheres[] = "(" . $vs_browse_table_name . ".access IN (" . join(',', $pa_options['checkAccess']) . "))";
             }
             if ($vs_browse_type_limit_sql) {
                 $va_wheres[] = $vs_browse_type_limit_sql;
             }
             if ($t_item->hasField('deleted')) {
                 $va_wheres[] = "(" . $vs_browse_table_name . ".deleted = 0)";
             }
             if ($va_facet_info['relative_to']) {
                 if ($t_subject->hasField('deleted')) {
                     $va_wheres[] = "(" . $t_subject->tableName() . ".deleted = 0)";
                 }
                 if ($va_relative_sql_data = $this->_getRelativeFacetSQLData($va_facet_info['relative_to'], $pa_options)) {
                     $va_joins = array_merge($va_joins, $va_relative_sql_data['joins']);
                     $va_wheres = array_merge($va_wheres, $va_relative_sql_data['wheres']);
                 }
             }
             if ($this->opo_config->get('perform_item_level_access_checking')) {
                 if ($t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true)) {
                     // Join to limit what browse table items are used to generate facet
                     $va_joins[] = 'LEFT JOIN ca_acl ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_acl.row_id AND ca_acl.table_num = ' . $t_item->tableNum() . "\n";
                     $va_wheres[] = "(\n\t\t\t\t\t\t\t\t((\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id = " . (int) $vn_user_id . ")\n\t\t\t\t\t\t\t\t\t" . (sizeof($va_group_ids) > 0 ? "OR\n\t\t\t\t\t\t\t\t\t(ca_acl.group_id IN (" . join(",", $va_group_ids) . "))" : "") . "\n\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id IS NULL and ca_acl.group_id IS NULL)\n\t\t\t\t\t\t\t\t) AND ca_acl.access >= " . __CA_ACL_READONLY_ACCESS__ . ")\n\t\t\t\t\t\t\t\t" . ($vb_show_if_no_acl ? "OR ca_acl.acl_id IS NULL" : "") . "\n\t\t\t\t\t\t\t)";
                 }
             }
             $vs_where_sql = '';
             if (is_array($va_wheres) && sizeof($va_wheres) && ($vs_where_sql = join(' AND ', $va_wheres))) {
                 $vs_where_sql = ' AND (' . $vs_where_sql . ')';
             }
             $vs_join_sql = join("\n", $va_joins);
             $vn_element_id = $t_element->getPrimaryKey();
             $vs_dir = strtoupper($va_facet_info['sort']) === 'DESC' ? "DESC" : "ASC";
             $vs_min_sql = $vs_max_sql = '';
             $vo_minimum_dimension = caParseLengthDimension(caGetOption('minimum_dimension', $va_facet_info, "0 in"));
             $vo_maximum_dimension = caParseLengthDimension(caGetOption('maximum_dimension', $va_facet_info, "0 in"));
             if ($vo_minimum_dimension) {
                 $vn_tmp = (double) $vo_minimum_dimension->convertTo('METER', 6, 'en_US');
                 $vs_min_sql = " AND (ca_attribute_values.value_decimal1 >= {$vn_tmp})";
             }
             if (caGetOption('maximum_dimension', $va_facet_info, null) && $vo_maximum_dimension) {
                 $vn_tmp = (double) $vo_maximum_dimension->convertTo('METER', 6, 'en_US');
                 $vs_max_sql = " AND (ca_attribute_values.value_decimal1 <= {$vn_tmp})";
             }
             if ($vb_check_availability_only) {
                 $vs_sql = "\n\t\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\t\tFROM ca_attributes\n\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\tca_attribute_values.element_id = ? \n\t\t\t\t\t\t\t\t{$vs_min_sql}\n\t\t\t\t\t\t\t\t{$vs_max_sql}\n\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t\t\tLIMIT 1";
                 //print $vs_sql;
                 $qr_res = $this->opo_db->query($vs_sql, $vn_element_id);
                 return (int) $qr_res->numRows() > 0 ? true : false;
             } else {
                 $vs_sql = "\n\t\t\t\t\t\t\tSELECT DISTINCT ca_attribute_values.value_decimal1, ca_attribute_values.value_decimal2, ca_attribute_values.value_longtext1, ca_attribute_values.value_longtext2\n\t\t\t\t\t\t\tFROM ca_attributes\n\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\tca_attribute_values.element_id = ? \n\t\t\t\t\t\t\t\t{$vs_min_sql}\n\t\t\t\t\t\t\t\t{$vs_max_sql}\n\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t";
                 //print $vs_sql;
                 $qr_res = $this->opo_db->query($vs_sql, $vn_element_id);
                 $va_values = array();
                 if (!($vs_output_units = caGetLengthUnitType($vs_units = caGetOption('units', $va_facet_info, 'm')))) {
                     $vs_output_units = Zend_Measure_Length::METER;
                 }
                 $vs_increment = caGetOption('increment', $va_facet_info, '1 m');
                 $vo_increment = caParseLengthDimension($vs_increment);
                 $vn_increment_in_current_units = (double) $vo_increment->convertTo($vs_output_units, 6, 'en_US');
                 while ($qr_res->nextRow()) {
                     $vn_meters = $qr_res->get('value_decimal1');
                     // measurement in meters
                     // convert to target dimensions
                     // normalize
                     $vo_dim = new Zend_Measure_Length($vn_meters, Zend_Measure_Length::METER, 'en_US');
                     $vs_dim = $vo_dim->convertTo($vs_output_units, 6, 'en_US');
                     $vn_dim = (double) $vs_dim;
                     $vn_normalized = floor($vn_dim / $vn_increment_in_current_units) * $vn_increment_in_current_units;
                     if (isset($va_criteria[$vn_normalized])) {
                         continue;
                     }
                     $vs_normalized_range_with_units = "{$vn_normalized} {$vs_units} - " . ($vn_normalized + $vn_increment_in_current_units) . " {$vs_units}";
                     $va_values[$vn_normalized][$vn_normalized] = array('id' => $vn_normalized, 'label' => $vs_normalized_range_with_units);
                     if (!is_null($vs_single_value) && $vn_normalized == $vs_single_value) {
                         $vb_single_value_is_present = true;
                     }
                 }
                 if (!is_null($vs_single_value) && !$vb_single_value_is_present) {
                     return array();
                 }
                 ksort($va_values);
                 if ($vs_dir == 'DESC') {
                     $va_values = array_reverse($va_values);
                 }
                 $va_sorted_values = array();
                 foreach ($va_values as $vn_sort_value => $va_values_for_sort_value) {
                     $va_sorted_values = array_merge($va_sorted_values, $va_values_for_sort_value);
                 }
                 return $va_sorted_values;
             }
             break;
             # -----------------------------------------------------
         # -----------------------------------------------------
         case 'authority':
             $vs_rel_table_name = $va_facet_info['table'];
             $va_params = $this->opo_ca_browse_cache->getParameters();
             // Make sure we honor type restrictions for the related authority
             $va_user_type_restrictions = caGetTypeRestrictionsForUser($vs_rel_table_name);
             $va_restrict_to_types = $va_facet_info['restrict_to_types'];
             if (is_array($va_user_type_restrictions)) {
                 if (!is_array($va_restrict_to_types)) {
                     $va_restrict_to_types = $va_user_type_restrictions;
                 } else {
                     $va_restrict_to_types = array_intersect($va_restrict_to_types, $va_user_type_restrictions);
                 }
             }
             if (!is_array($va_exclude_types = $va_facet_info['exclude_types'])) {
                 $va_exclude_types = array();
             }
             if (!is_array($va_restrict_to_relationship_types = $va_facet_info['restrict_to_relationship_types'])) {
                 $va_restrict_to_relationship_types = array();
             }
             if (!is_array($va_exclude_relationship_types = $va_facet_info['exclude_relationship_types'])) {
                 $va_exclude_relationship_types = array();
             }
             $t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true);
             if ($vs_browse_table_name == $vs_rel_table_name) {
                 // browsing on self-relations not supported
                 break;
             } else {
                 switch (sizeof($va_path = array_keys($this->opo_datamodel->getPath($vs_browse_table_name, $vs_rel_table_name)))) {
                     case __CA_ATTRIBUTE_VALUE_LIST__:
                         $t_item_rel = $this->opo_datamodel->getInstanceByTableName($va_path[1], true);
                         $t_rel_item = $this->opo_datamodel->getInstanceByTableName($va_path[2], true);
                         $vs_key = 'relation_id';
                         break;
                     case __CA_ATTRIBUTE_VALUE_DATERANGE__:
                         $t_item_rel = null;
                         $t_rel_item = $this->opo_datamodel->getInstanceByTableName($va_path[1], true);
                         $vs_key = $t_rel_item->primaryKey();
                         break;
                     default:
                         // bad related table
                         return null;
                         break;
                 }
             }
             $vb_rel_is_hierarchical = (bool) $t_rel_item->isHierarchical();
             //
             // Convert related item type_code specs in restrict_to_types and exclude_types lists to numeric type_ids we need for the query
             //
             if (!is_array($va_restrict_to_types = $this->_convertTypeCodesToIDs($va_restrict_to_types, array('instance' => $t_rel_item, 'dontExpandHierarchically' => true)))) {
                 $va_restrict_to_types = array();
             }
             if (!is_array($va_exclude_types = $this->_convertTypeCodesToIDs($va_exclude_types, array('instance' => $t_rel_item, 'dontExpandHierarchically' => true)))) {
                 $va_exclude_types = array();
             }
             $va_restrict_to_types_expanded = $this->_convertTypeCodesToIDs($va_restrict_to_types, array('instance' => $t_rel_item));
             $va_exclude_types_expanded = $this->_convertTypeCodesToIDs($va_exclude_types, array('instance' => $t_rel_item));
             // look up relationship type restrictions
             $va_restrict_to_relationship_types = $this->_getRelationshipTypeIDs($va_restrict_to_relationship_types, $va_facet_info['relationship_table']);
             $va_exclude_relationship_types = $this->_getRelationshipTypeIDs($va_exclude_relationship_types, $va_facet_info['relationship_table']);
             $va_joins = array();
             $va_selects = array();
             $va_wheres = array();
             $va_orderbys = array();
             if (!$va_facet_info['show_all_when_first_facet'] || $this->numCriteria() > 0) {
                 $vs_cur_table = array_shift($va_path);
                 foreach ($va_path as $vs_join_table) {
                     $va_rel_info = $this->opo_datamodel->getRelationships($vs_cur_table, $vs_join_table);
                     $va_joins[] = 'INNER JOIN ' . $vs_join_table . ' ON ' . $vs_cur_table . '.' . $va_rel_info[$vs_cur_table][$vs_join_table][0][0] . ' = ' . $vs_join_table . '.' . $va_rel_info[$vs_cur_table][$vs_join_table][0][1] . "\n";
                     $vs_cur_table = $vs_join_table;
                 }
             } else {
                 if ($va_facet_info['show_all_when_first_facet']) {
                     $va_path = array_reverse($va_path);
                     // in "show_all" mode we turn the browse on it's head and grab records by the "subject" table, rather than the browse table
                     $vs_cur_table = array_shift($va_path);
                     $vs_join_table = $va_path[0];
                     $va_rel_info = $this->opo_datamodel->getRelationships($vs_cur_table, $vs_join_table);
                     $va_joins[] = 'LEFT JOIN ' . $vs_join_table . ' ON ' . $vs_cur_table . '.' . $va_rel_info[$vs_cur_table][$vs_join_table][0][0] . ' = ' . $vs_join_table . '.' . $va_rel_info[$vs_cur_table][$vs_join_table][0][1] . "\n";
                 }
             }
             if (sizeof($va_results) && $this->numCriteria() > 0) {
                 $va_wheres[] = "(" . $t_subject->tableName() . '.' . $t_subject->primaryKey() . " IN (" . join(',', $va_results) . "))";
             }
             if (!is_array($va_restrict_to_lists = $va_facet_info['restrict_to_lists'])) {
                 $va_restrict_to_lists = array();
             }
             if (is_array($va_restrict_to_lists) && sizeof($va_restrict_to_lists) > 0 && $t_rel_item->tableName() == 'ca_list_items') {
                 $va_list_ids = array();
                 foreach ($va_restrict_to_lists as $vm_list) {
                     if (is_numeric($vm_list)) {
                         $vn_list_id = (int) $vm_list;
                     } else {
                         $vn_list_id = (int) ca_lists::getListID($vm_list);
                     }
                     if ($vn_list_id) {
                         $va_list_ids[] = $vn_list_id;
                     }
                 }
                 if (sizeof($va_list_ids) > 0) {
                     $va_wheres[] = "{$vs_rel_table_name}.list_id IN (" . join(',', $va_list_ids) . ")";
                 }
             }
             if (is_array($va_restrict_to_types) && sizeof($va_restrict_to_types) > 0 && method_exists($t_rel_item, "getTypeList")) {
                 $va_wheres[] = "{$vs_rel_table_name}.type_id IN (" . join(',', caGetOption('dont_include_subtypes', $va_facet_info, false) ? $va_restrict_to_types : $va_restrict_to_types_expanded) . ")" . ($t_rel_item->getFieldInfo('type_id', 'IS_NULL') ? " OR ({$vs_rel_table_name}.type_id IS NULL)" : '');
                 $va_selects[] = "{$vs_rel_table_name}.type_id";
             }
             if (is_array($va_exclude_types) && sizeof($va_exclude_types) > 0 && method_exists($t_rel_item, "getTypeList")) {
                 $va_wheres[] = "{$vs_rel_table_name}.type_id NOT IN (" . join(',', caGetOption('dont_include_subtypes', $va_facet_info, false) ? $va_exclude_types : $va_exclude_types_expanded) . ")";
             }
             if (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_rel_item->hasField('access')) {
                 $va_wheres[] = "(" . $t_rel_item->tableName() . ".access IN (" . join(',', $pa_options['checkAccess']) . "))";
                 // exclude non-accessible authority items
                 if (!$va_facet_info['show_all_when_first_facet'] || $this->numCriteria() > 0) {
                     $va_wheres[] = "(" . $vs_browse_table_name . ".access IN (" . join(',', $pa_options['checkAccess']) . "))";
                     // exclude non-accessible browse items
                 }
             }
             if ($t_item->hasField('deleted') && !$va_facet_info['show_all_when_first_facet']) {
                 $va_wheres[] = "(" . $t_item->tableName() . ".deleted = 0)";
             }
             if ($t_rel_item->hasField('deleted')) {
                 $va_wheres[] = "(" . $t_rel_item->tableName() . ".deleted = 0)";
             }
             $vs_rel_pk = $t_rel_item->primaryKey();
             $va_rel_attr_elements = $t_rel_item->getApplicableElementCodes(null, true, false);
             $va_attrs_to_fetch = array();
             if (!$va_facet_info['show_all_when_first_facet'] || $this->numCriteria() > 0) {
                 //$va_selects[] = $t_item->tableName().'.'.$t_item->primaryKey();			// get primary key of subject
             }
             $va_selects[] = $t_rel_item->tableName() . '.' . $vs_rel_pk;
             // get primary key of related
             $vs_hier_parent_id_fld = $vs_hier_id_fld = null;
             if ($vb_rel_is_hierarchical) {
                 $vs_hier_parent_id_fld = $t_rel_item->getProperty('HIERARCHY_PARENT_ID_FLD');
                 $va_selects[] = $t_rel_item->tableName() . '.' . $vs_hier_parent_id_fld;
                 if ($vs_hier_id_fld = $t_rel_item->getProperty('HIERARCHY_ID_FLD')) {
                     $va_selects[] = $t_rel_item->tableName() . '.' . $vs_hier_id_fld;
                 }
             }
             // analyze group_fields (if defined) and add them to the query
             $va_groupings_to_fetch = array();
             if (isset($va_facet_info['groupings']) && is_array($va_facet_info['groupings']) && sizeof($va_facet_info['groupings'])) {
                 foreach ($va_facet_info['groupings'] as $vs_grouping => $vs_grouping_name) {
                     // is grouping type_id?
                     if ($vs_grouping === 'type' && $t_rel_item->hasField('type_id')) {
                         $va_selects[] = $t_rel_item->tableName() . '.type_id';
                         $va_groupings_to_fetch[] = 'type_id';
                     }
                     // is group field a relationship type?
                     if ($vs_grouping === 'relationship_types') {
                         $va_selects[] = $va_facet_info['relationship_table'] . '.type_id rel_type_id';
                         $va_groupings_to_fetch[] = 'rel_type_id';
                     }
                     // is group field an attribute?
                     if (preg_match('!^ca_attribute_([^:]*)!', $vs_grouping, $va_matches)) {
                         if ($vn_element_id = array_search($va_matches[1], $va_rel_attr_elements)) {
                             $va_attrs_to_fetch[] = $vn_element_id;
                         }
                     }
                 }
             }
             if ($va_facet_info['relative_to']) {
                 // TODO: do this everywhere
                 $va_restrict_to_relationship_types = array();
                 $vs_browse_type_limit_sql = '';
                 if ($t_subject->hasField('deleted')) {
                     $va_wheres[] = "(" . $t_subject->tableName() . ".deleted = 0)";
                 }
                 if ($va_relative_sql_data = $this->_getRelativeFacetSQLData($va_facet_info['relative_to'], $pa_options)) {
                     $va_joins = array_merge($va_joins, $va_relative_sql_data['joins']);
                     $va_wheres = array_merge($va_wheres, $va_relative_sql_data['wheres']);
                 }
             }
             if (sizeof($va_restrict_to_relationship_types) > 0 && is_object($t_item_rel)) {
                 $va_wheres[] = $t_item_rel->tableName() . ".type_id IN (" . join(',', $va_restrict_to_relationship_types) . ")";
             }
             if (sizeof($va_exclude_relationship_types) > 0 && is_object($t_item_rel)) {
                 $va_wheres[] = $t_item_rel->tableName() . ".type_id NOT IN (" . join(',', $va_exclude_relationship_types) . ")";
             }
             if ($vs_browse_type_limit_sql) {
                 $va_wheres[] = $vs_browse_type_limit_sql;
             }
             if ($this->opo_config->get('perform_item_level_access_checking')) {
                 if ($t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true)) {
                     // Join to limit what browse table items are used to generate facet
                     $va_joins[] = 'LEFT JOIN ca_acl ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_acl.row_id AND ca_acl.table_num = ' . $t_item->tableNum() . "\n";
                     $va_wheres[] = "(\n\t\t\t\t\t\t\t\t((\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id = " . (int) $vn_user_id . ")\n\t\t\t\t\t\t\t\t\t" . (sizeof($va_group_ids) > 0 ? "OR\n\t\t\t\t\t\t\t\t\t(ca_acl.group_id IN (" . join(",", $va_group_ids) . "))" : "") . "\n\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id IS NULL and ca_acl.group_id IS NULL)\n\t\t\t\t\t\t\t\t) AND ca_acl.access >= " . __CA_ACL_READONLY_ACCESS__ . ")\n\t\t\t\t\t\t\t\t" . ($vb_show_if_no_acl ? "OR ca_acl.acl_id IS NULL" : "") . "\n\t\t\t\t\t\t\t)";
                     // Join to limit what related items are used to generate facet
                     $va_joins[] = 'LEFT JOIN ca_acl AS rel_acl ON ' . $t_rel_item->tableName() . '.' . $t_rel_item->primaryKey() . ' = rel_acl.row_id AND rel_acl.table_num = ' . $t_rel_item->tableNum() . "\n";
                     $va_wheres[] = "(\n\t\t\t\t\t\t\t\t((\n\t\t\t\t\t\t\t\t\t(rel_acl.user_id = " . (int) $vn_user_id . ")\n\t\t\t\t\t\t\t\t\t" . (sizeof($va_group_ids) > 0 ? "OR\n\t\t\t\t\t\t\t\t\t(rel_acl.group_id IN (" . join(",", $va_group_ids) . "))" : "") . "\n\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t(rel_acl.user_id IS NULL and rel_acl.group_id IS NULL)\n\t\t\t\t\t\t\t\t) AND rel_acl.access >= " . __CA_ACL_READONLY_ACCESS__ . ")\n\t\t\t\t\t\t\t\t" . ($vb_show_if_no_acl ? "OR rel_acl.acl_id IS NULL" : "") . "\n\t\t\t\t\t\t\t)";
                 }
             }
             $vs_join_sql = join("\n", $va_joins);
             if ($vb_check_availability_only) {
                 if (!$va_facet_info['show_all_when_first_facet'] || $this->numCriteria() > 0) {
                     $vs_sql = "\n\t\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\t\tFROM " . $vs_browse_table_name . "\n\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t" . (sizeof($va_wheres) ? ' WHERE ' : '') . join(" AND ", $va_wheres) . " LIMIT 1";
                 } else {
                     $vs_sql = "\n\t\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\t\tFROM " . $t_rel_item->tableName() . "\n\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t" . (sizeof($va_wheres) ? ' WHERE ' : '') . join(" AND ", $va_wheres) . " LIMIT 1";
                 }
                 $qr_res = $this->opo_db->query($vs_sql);
                 //print "<hr>$vs_sql<hr>\n";
                 return (int) $qr_res->numRows() > 0 ? true : false;
             } else {
                 if (!$va_facet_info['show_all_when_first_facet'] || $this->numCriteria() > 0) {
                     $vs_sql = "\n\t\t\t\t\t\t\tSELECT DISTINCT " . join(', ', $va_selects) . "\n\t\t\t\t\t\t\tFROM " . $vs_browse_table_name . "\n\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t" . (sizeof($va_wheres) ? ' WHERE ' : '') . join(" AND ", $va_wheres) . "\n\t\t\t\t\t\t\t\t" . (sizeof($va_orderbys) ? "ORDER BY " . join(', ', $va_orderbys) : '');
                 } else {
                     $vs_sql = "\n\t\t\t\t\t\t\tSELECT DISTINCT " . join(', ', $va_selects) . "\n\t\t\t\t\t\t\tFROM " . $t_rel_item->tableName() . "\n\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t" . (sizeof($va_wheres) ? ' WHERE ' : '') . join(" AND ", $va_wheres) . "\n\t\t\t\t\t\t\t\t" . (sizeof($va_orderbys) ? "ORDER BY " . join(', ', $va_orderbys) : '');
                 }
                 //print "<hr>$vs_sql<hr>\n";
                 $qr_res = $this->opo_db->query($vs_sql);
                 $va_facet = $va_facet_items = array();
                 $vs_rel_pk = $t_rel_item->primaryKey();
                 // First get related ids with type and relationship type values
                 // (You could get all of the data we need for the facet in a single query but it turns out to be faster for very large facets to
                 // do it in separate queries, one for the primary ids and another for the labels; a third is done if attributes need to be fetched.
                 // There appears to be a significant [~10%] performance for smaller facets and a larger one [~20-25%] for very large facets)
                 $vn_max_level = caGetOption('maximum_levels', $va_facet_info, null);
                 while ($qr_res->nextRow()) {
                     $va_fetched_row = $qr_res->getRow();
                     $vn_id = $va_fetched_row[$vs_rel_pk];
                     //if (isset($va_facet_items[$vn_id])) { continue; } --- we can't do this as then we don't detect items that have multiple rel_type_ids... argh.
                     if (isset($va_criteria[$vn_id])) {
                         continue;
                     }
                     // skip items that are used as browse critera - don't want to browse on something you're already browsing on
                     if (!$va_facet_items[$va_fetched_row[$vs_rel_pk]]) {
                         // if(!is_null($vn_max_level)) {
                         // 									if (sizeof($va_ancestors) + 1 > $vn_max_level) {
                         // 										if ($va_tmp = $va_ancestors[sizeof($va_ancestors) - $vn_max_level]) {
                         // 											$va_ancestors = array();
                         // 											$va_fetched_row = $va_tmp['NODE'];
                         // 										}
                         // 									}
                         // 								}
                         if (is_array($va_restrict_to_types) && sizeof($va_restrict_to_types) && $va_fetched_row['type_id'] && !in_array($va_fetched_row['type_id'], $va_restrict_to_types)) {
                             continue;
                         }
                         $va_facet_items[$va_fetched_row[$vs_rel_pk]] = array('id' => $va_fetched_row[$vs_rel_pk], 'type_id' => array(), 'parent_id' => $vb_rel_is_hierarchical ? $va_fetched_row[$vs_hier_parent_id_fld] : null, 'hierarchy_id' => $vb_rel_is_hierarchical ? $va_fetched_row[$vs_hier_id_fld] : null, 'rel_type_id' => array(), 'child_count' => 0);
                         if (!is_null($vs_single_value) && $va_fetched_row[$vs_rel_pk] == $vs_single_value) {
                             $vb_single_value_is_present = true;
                         }
                     }
                     if ($va_fetched_row['type_id']) {
                         $va_facet_items[$va_fetched_row[$vs_rel_pk]]['type_id'][] = $va_fetched_row['type_id'];
                     }
                     if ($va_fetched_row['rel_type_id']) {
                         $va_facet_items[$va_fetched_row[$vs_rel_pk]]['rel_type_id'][] = $va_fetched_row['rel_type_id'];
                     }
                 }
                 if (!isset($va_facet_info['dont_expand_hierarchically']) || !$va_facet_info['dont_expand_hierarchically']) {
                     $qr_res->seek(0);
                     $va_ids = $qr_res->getAllFieldValues($vs_rel_pk);
                     $qr_ancestors = call_user_func($t_rel_item->tableName() . '::getHierarchyAncestorsForIDs', $va_ids, array('returnAs' => 'SearchResult'));
                     $vs_rel_table = $t_rel_item->tableName();
                     $vs_rel_pk = $t_rel_item->primaryKey();
                     $vb_check_ancestor_access = (bool) (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_rel_item->hasField('access'));
                     if ($qr_ancestors) {
                         while ($qr_ancestors->nextHit()) {
                             $vn_parent_type_id = $qr_ancestors->get('type_id');
                             if (sizeof($va_exclude_types) > 0 && in_array($vn_parent_type_id, $va_exclude_types)) {
                                 continue;
                             }
                             if (sizeof($va_restrict_to_types) > 0 && !in_array($vn_parent_type_id, $va_restrict_to_types)) {
                                 continue;
                             }
                             if ($vb_check_ancestor_access && !in_array($qr_ancestors->get('access'), $pa_options['checkAccess'])) {
                                 continue;
                             }
                             $va_facet_items[$vn_ancestor_id = (int) $qr_ancestors->get("{$vs_rel_pk}")] = array('id' => $vn_ancestor_id, 'type_id' => array(), 'parent_id' => $vb_rel_is_hierarchical ? $qr_ancestors->get("{$vs_hier_parent_id_fld}") : null, 'hierarchy_id' => $vb_rel_is_hierarchical && $vs_hier_id_fld ? $qr_ancestors->get($vs_hier_id_fld) : null, 'rel_type_id' => array(), 'child_count' => 0);
                         }
                     }
                 }
                 // Set child counts
                 foreach ($va_facet_items as $vn_i => $va_item) {
                     if ($va_item['parent_id'] && isset($va_facet_items[$va_item['parent_id']])) {
                         $va_facet_items[$va_item['parent_id']]['child_count']++;
                     }
                 }
                 // Get labels for facet items
                 if (sizeof($va_row_ids = array_keys($va_facet_items))) {
                     if ($vs_label_table_name = $t_rel_item->getLabelTableName()) {
                         $t_rel_item_label = $this->opo_datamodel->getInstanceByTableName($vs_label_table_name, true);
                         $vs_label_display_field = $t_rel_item_label->getDisplayField();
                         $vs_rel_pk = $t_rel_item->primaryKey();
                         $va_label_wheres = array();
                         if ($t_rel_item_label->hasField('is_preferred')) {
                             $va_label_wheres[] = "({$vs_label_table_name}.is_preferred = 1)";
                         }
                         $va_label_wheres[] = "({$vs_label_table_name}.{$vs_rel_pk} IN (" . join(",", $va_row_ids) . "))";
                         $va_label_selects[] = "{$vs_label_table_name}.{$vs_rel_pk}";
                         $va_label_selects[] = "{$vs_label_table_name}.locale_id";
                         $va_label_fields = $t_rel_item->getLabelUIFields();
                         foreach ($va_label_fields as $vs_label_field) {
                             $va_label_selects[] = "{$vs_label_table_name}.{$vs_label_field}";
                         }
                         // Get label ordering fields
                         $va_ordering_fields_to_fetch = isset($va_facet_info['order_by_label_fields']) && is_array($va_facet_info['order_by_label_fields']) ? $va_facet_info['order_by_label_fields'] : array();
                         $va_orderbys = array();
                         foreach ($va_ordering_fields_to_fetch as $vs_sort_by_field) {
                             if (!$t_rel_item_label->hasField($vs_sort_by_field)) {
                                 continue;
                             }
                             $va_orderbys[] = $va_label_selects[] = $vs_label_table_name . '.' . $vs_sort_by_field;
                         }
                         // get labels
                         $vs_sql = "\n\t\t\t\t\t\t\t\t\tSELECT " . join(', ', $va_label_selects) . "\n\t\t\t\t\t\t\t\t\tFROM " . $vs_label_table_name . "\n\t\t\t\t\t\t\t\t\t\t" . (sizeof($va_label_wheres) ? ' WHERE ' : '') . join(" AND ", $va_label_wheres) . "\n\t\t\t\t\t\t\t\t\t\t" . (sizeof($va_orderbys) ? "ORDER BY " . join(', ', $va_orderbys) : '') . "";
                         //print $vs_sql;
                         $qr_labels = $this->opo_db->query($vs_sql);
                         while ($qr_labels->nextRow()) {
                             $va_fetched_row = $qr_labels->getRow();
                             $va_facet_item = array_merge($va_facet_items[$va_fetched_row[$vs_rel_pk]], array('label' => $va_fetched_row[$vs_label_display_field]));
                             foreach ($va_ordering_fields_to_fetch as $vs_to_fetch) {
                                 $va_facet_item[$vs_to_fetch] = $va_fetched_row[$vs_to_fetch];
                             }
                             $va_facet[$va_fetched_row[$vs_rel_pk]][$va_fetched_row['locale_id']] = $va_facet_item;
                         }
                     }
                     // get attributes for facet items
                     if (sizeof($va_attrs_to_fetch)) {
                         $qr_attrs = $this->opo_db->query("\n\t\t\t\t\t\t\t\t\tSELECT c_av.*, c_a.locale_id, c_a.row_id\n\t\t\t\t\t\t\t\t\tFROM ca_attributes c_a\n\t\t\t\t\t\t\t\t\tINNER JOIN ca_attribute_values c_av ON c_a.attribute_id = c_av.attribute_id\n\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\tc_av.element_id IN (" . join(',', $va_attrs_to_fetch) . ")\n\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\tc_a.table_num = ? \n\t\t\t\t\t\t\t\t\t\tAND \n\t\t\t\t\t\t\t\t\t\tc_a.row_id IN (" . join(',', $va_row_ids) . ")\n\t\t\t\t\t\t\t\t", $t_rel_item->tableNum());
                         while ($qr_attrs->nextRow()) {
                             $va_fetched_row = $qr_attrs->getRow();
                             $vn_id = $va_fetched_row['row_id'];
                             // if no locale is set for the attribute default it to whatever the locale for the item is
                             if (!($vn_locale_id = $va_fetched_row['locale_id'])) {
                                 $va_tmp = array_keys($va_facet[$vn_id]);
                                 $vn_locale_id = $va_tmp[0];
                             }
                             $va_facet[$vn_id][$vn_locale_id]['ca_attribute_' . $va_fetched_row['element_id']][] = $va_fetched_row;
                         }
                     }
                 }
                 if (!is_null($vs_single_value) && !$vb_single_value_is_present) {
                     return array();
                 }
                 return caExtractValuesByUserLocale($va_facet);
             }
             break;
             # -----------------------------------------------------
         # -----------------------------------------------------
         default:
             return null;
             break;
             # -----------------------------------------------------
     }
 }
 public static function updateSolrConfiguration($pb_invoked_from_command_line = false)
 {
     /* get search and search indexing configuration */
     $po_app_config = Configuration::load();
     $po_search_config = Configuration::load($po_app_config->get("search_config"));
     $po_search_indexing_config = Configuration::load($po_search_config->get("search_indexing_config"));
     $ps_solr_home_dir = $po_search_config->get('search_solr_home_dir');
     $po_datamodel = Datamodel::load();
     $po_search_base = new SearchBase();
     global $o_db;
     if (!is_object($o_db)) {
         /* catch command line usage */
         $o_db = new Db();
     }
     $t_element = new ca_metadata_elements();
     /* parse search indexing configuration to see which tables are indexed */
     $va_tables = $po_search_indexing_config->getAssocKeys();
     /* create solr.xml first to support multicore */
     $vs_solr_xml = "";
     $vs_solr_xml .= '<?xml version="1.0" encoding="UTF-8" ?>' . SolrConfiguration::nl();
     $vs_solr_xml .= '<solr persistent="true">' . SolrConfiguration::nl();
     $vs_solr_xml .= SolrConfiguration::tabs(1) . '<cores adminPath="/admin/cores">' . SolrConfiguration::nl();
     foreach ($va_tables as $vs_table) {
         /* I don't like tablenums, so we use the table name to name the cores */
         $vs_solr_xml .= SolrConfiguration::tabs(2) . '<core name="' . $vs_table . '" instanceDir="' . $vs_table . '" />' . SolrConfiguration::nl();
     }
     $vs_solr_xml .= SolrConfiguration::tabs(1) . '</cores>' . SolrConfiguration::nl();
     $vs_solr_xml .= '</solr>' . SolrConfiguration::nl();
     /* try to write configuration file */
     $vr_solr_xml_file = fopen($ps_solr_home_dir . "/solr.xml", 'w+');
     // overwrite old one
     if (!is_resource($vr_solr_xml_file)) {
         die("Couldn't write to solr.xml file in Solr home directory. Please check the permissions.\n");
     }
     fprintf($vr_solr_xml_file, "%s", $vs_solr_xml);
     fclose($vr_solr_xml_file);
     /* configure the cores */
     foreach ($va_tables as $vs_table) {
         $t_instance = $po_datamodel->getTableInstance($vs_table);
         /* create core directory */
         if (!file_exists($ps_solr_home_dir . "/" . $vs_table)) {
             if (!mkdir($ps_solr_home_dir . "/" . $vs_table, 0777)) {
                 /* TODO: think about permissions */
                 die("Couldn't create directory in Solr home. Please check the permissions.\n");
             }
         }
         /* create conf directory */
         if (!file_exists($ps_solr_home_dir . "/" . $vs_table . "/conf")) {
             if (!mkdir($ps_solr_home_dir . "/" . $vs_table . "/conf", 0777)) {
                 die("Couldn't create directory in core directory. Please check the permissions.\n");
             }
         }
         /* create solrconfig.xml for this core */
         $vr_solrconfig_xml_file = fopen($ps_solr_home_dir . "/" . $vs_table . "/conf/solrconfig.xml", 'w+');
         if (!is_resource($vr_solrconfig_xml_file)) {
             die("Couldn't write to solrconfig.xml file for core {$vs_table}. Please check the permissions.\n");
         }
         /* read template and copy it */
         $va_solrconfig_xml_template = file(__CA_LIB_DIR__ . "/core/Search/Solr/solrplugin_templates/solrconfig.xml");
         if (!is_array($va_solrconfig_xml_template)) {
             die("Couldn't read solrconfig.xml template.");
         }
         foreach ($va_solrconfig_xml_template as $vs_line) {
             fprintf($vr_solrconfig_xml_file, "%s", $vs_line);
         }
         fclose($vr_solrconfig_xml_file);
         /* create schema.xml for this core */
         $vr_schema_xml_file = fopen($ps_solr_home_dir . "/" . $vs_table . "/conf/schema.xml", 'w+');
         if (!is_resource($vr_schema_xml_file)) {
             die("Couldn't write to schema.xml file for core {$vs_table}. Please check the permissions.\n");
         }
         /* read template, modify it, add table-specific fields and write to schema.xml configuration for this core */
         $va_schema_xml_template = file(__CA_LIB_DIR__ . "/core/Search/Solr/solrplugin_templates/schema.xml");
         if (!is_array($va_schema_xml_template)) {
             die("Couldn't read solrconfig.xml template.");
         }
         foreach ($va_schema_xml_template as $vs_line) {
             /* 1st replacement: core name */
             if (strpos($vs_line, "CORE_NAME") !== false) {
                 fprintf($vr_schema_xml_file, "%s", str_replace("CORE_NAME", $vs_table, $vs_line));
                 continue;
             }
             /* 2nd replacement: fields - the big part */
             if (strpos($vs_line, "<!--FIELDS-->") !== false) {
                 $vs_field_schema = "";
                 $vs_subject_table_copyfields = "";
                 /* the schema is very very hardcoded, so we have to create a design that still fits
                  * when new metadata elements are created or sth like that. for now, we're just considering
                  * the "straightforward" fields
                  */
                 $va_schema_fields = array();
                 /* list of all fields created - is used for copyField directives after field block */
                 /* subject table */
                 /* we add the PK - this is used for incremental indexing */
                 $vs_field_schema .= SolrConfiguration::tabs(2) . '<field name="' . $vs_table . '.' . $t_instance->primaryKey() . '" type="int" indexed="true" stored="true" />' . SolrConfiguration::nl();
                 $vs_field_schema .= SolrConfiguration::tabs(2) . '<field name="' . $t_instance->primaryKey() . '" type="int" indexed="true" stored="true" />' . SolrConfiguration::nl();
                 $vs_subject_table_copyfields .= SolrConfiguration::tabs(1) . '<copyField source="' . $vs_table . '.' . $t_instance->primaryKey() . '" dest="' . $t_instance->primaryKey() . '" />' . SolrConfiguration::nl();
                 /* get fields-to-index from search indexing configuration */
                 if (!is_array($va_table_fields = $po_search_base->getFieldsToIndex($vs_table))) {
                     $va_table_fields = array();
                 }
                 $vn_table_num = $po_datamodel->getTableNum($vs_table);
                 $va_attributes = null;
                 if (is_array($va_table_fields)) {
                     foreach ($va_table_fields as $vs_field_name => $va_field_options) {
                         if (preg_match('!^_ca_attribute_(\\d+)$!', $vs_field_name, $va_matches)) {
                             $t_element->load($va_matches[1]);
                             $va_attributes[$t_element->getPrimaryKey()] = array('element_id' => $t_element->get('element_id'), 'element_code' => $t_element->get('element_code'), 'datatype' => $t_element->get('datatype'));
                         }
                     }
                 }
                 if (is_array($va_attributes)) {
                     $va_metadata_fields = array();
                     foreach ($va_attributes as $vn_element_id => $va_element_info) {
                         $va_metadata_fields += SolrConfiguration::getElementType($va_element_info);
                     }
                     /*set datatype for metadata elements in $va_table_fields array*/
                     foreach ($va_metadata_fields as $key => $value) {
                         if (array_key_exists($key, $va_table_fields)) {
                             unset($va_table_fields[$key]);
                         }
                         $va_table_fields[$key] = $value;
                     }
                 }
                 /* we now have the current configuration */
                 /* since Solr supports live updates only if changes are 'backwards-compatible'
                  * (i.e. no fields are deleted), we have to merge the current configuration with the
                  * cached one, create the new configuration based upon that and cache it.
                  *
                  * Invocation of the command-line script support/utils/createSolrConfiguration.php,
                  * however, creates a completely fresh configuration and caches it.
                  */
                 $va_frontend_options = array('lifetime' => null, 'logging' => false, 'write_control' => true, 'automatic_cleaning_factor' => 0, 'automatic_serialization' => true);
                 $vs_cache_dir = __CA_APP_DIR__ . '/tmp';
                 $va_backend_options = array('cache_dir' => $vs_cache_dir, 'file_locking' => true, 'read_control' => false, 'file_name_prefix' => 'ca_cache', 'cache_file_perm' => 0777);
                 $vo_cache = Zend_Cache::factory('Core', 'File', $va_frontend_options, $va_backend_options);
                 if (!($va_cache_data = $vo_cache->load('ca_search_indexing_info_' . $vs_table))) {
                     $va_cache_data = array();
                 }
                 if (!$pb_invoked_from_command_line) {
                     $va_table_fields = array_merge($va_cache_data, $va_table_fields);
                 }
                 $vo_cache->save($va_table_fields, 'ca_search_indexing_info_' . $vs_table);
                 if (is_array($va_table_fields)) {
                     foreach ($va_table_fields as $vs_field_name => $va_field_options) {
                         if (in_array("STORE", $va_field_options)) {
                             $vb_field_is_stored = true;
                         } else {
                             $vb_field_is_stored = false;
                         }
                         if (in_array("DONT_TOKENIZE", $va_field_options)) {
                             $vb_field_is_tokenized = false;
                         } else {
                             $vb_field_is_tokenized = true;
                         }
                         $va_schema_fields[] = $vs_table . '.' . SolrConfiguration::adjustFieldsToIndex($vs_field_name);
                         if (in_array($va_field_options['type'], array('text', 'string'))) {
                             $vs_type = $vb_field_is_tokenized ? 'text' : 'string';
                         } else {
                             if (!isset($va_field_options['type']) && $t_instance->hasField($vs_field_name)) {
                                 // if the primary key is configured to be indexed in search_indexing.conf, ignore it here
                                 // (we add it anyway and solr doesn't like duplicate fields!)
                                 if ($t_instance->primaryKey() == $vs_field_name) {
                                     continue;
                                 }
                                 switch ($t_instance->getFieldInfo($vs_field_name, "FIELD_TYPE")) {
                                     case FT_TEXT:
                                     case FT_MEDIA:
                                     case FT_FILE:
                                     case FT_PASSWORD:
                                     case FT_VARS:
                                         $va_field_options['type'] = 'text';
                                         break;
                                     case FT_NUMBER:
                                     case FT_TIME:
                                     case FT_TIMERANGE:
                                     case FT_TIMECODE:
                                         $va_field_options['type'] = 'float';
                                         break;
                                     case FT_TIMESTAMP:
                                     case FT_DATETIME:
                                     case FT_HISTORIC_DATETIME:
                                     case FT_DATE:
                                     case FT_HISTORIC_DATE:
                                     case FT_DATERANGE:
                                     case FT_HISTORIC_DATERANGE:
                                         $va_field_options['type'] = 'daterange';
                                         break;
                                     case FT_BIT:
                                         $va_field_options['type'] = 'bool';
                                         break;
                                     default:
                                         $va_field_options['type'] = null;
                                         break;
                                 }
                             }
                             $vs_type = isset($va_field_options['type']) && $va_field_options['type'] ? $va_field_options['type'] : 'text';
                         }
                         $vs_field_schema .= SolrConfiguration::tabs(2) . '<field name="' . $vs_table . '.' . SolrConfiguration::adjustFieldsToIndex($vs_field_name) . '" type="' . $vs_type;
                         $vs_field_schema .= '" indexed="true" ';
                         $vb_field_is_stored ? $vs_field_schema .= 'stored="true" ' : ($vs_field_schema .= 'stored="false" ');
                         $vs_field_schema .= '/>' . SolrConfiguration::nl();
                     }
                 }
                 /* related tables */
                 $va_related_tables = $po_search_base->getRelatedIndexingTables($vs_table);
                 foreach ($va_related_tables as $vs_related_table) {
                     $va_related_table_fields = $po_search_base->getFieldsToIndex($vs_table, $vs_related_table);
                     foreach ($va_related_table_fields as $vs_related_table_field => $va_related_table_field_options) {
                         if (in_array("STORE", $va_related_table_field_options)) {
                             $vb_field_is_stored = true;
                         } else {
                             $vb_field_is_stored = false;
                         }
                         if (in_array("DONT_TOKENIZE", $va_related_table_field_options)) {
                             $vb_field_is_tokenized = false;
                         } else {
                             $vb_field_is_tokenized = true;
                         }
                         $va_schema_fields[] = $vs_related_table . '.' . $vs_related_table_field;
                         $vs_field_schema .= SolrConfiguration::tabs(2) . '<field name="' . $vs_related_table . '.' . $vs_related_table_field . '" type="';
                         $vb_field_is_tokenized ? $vs_field_schema .= 'text' : ($vs_field_schema .= 'string');
                         $vs_field_schema .= '" indexed="true" ';
                         $vb_field_is_stored ? $vs_field_schema .= 'stored="true" ' : ($vs_field_schema .= 'stored="false" ');
                         $vs_field_schema .= '/>' . SolrConfiguration::nl();
                     }
                 }
                 /* copyfield directives
                  * we use a single field in each index (called "text") where
                  * all other fields are copied. the text field is the default
                  * search field. it is used if a field name specification is
                  * omitted in a search query.
                  */
                 $vs_copyfields = "";
                 foreach ($va_schema_fields as $vs_schema_field) {
                     $vs_copyfields .= SolrConfiguration::tabs(1) . '<copyField source="' . $vs_schema_field . '" dest="text" />' . SolrConfiguration::nl();
                 }
                 //
                 // Get access points
                 //
                 if (!is_array($va_access_points = $po_search_base->getAccessPoints($vs_table))) {
                     $va_access_points = array();
                 }
                 foreach ($va_access_points as $vs_access_point => $va_access_point_info) {
                     foreach ($va_access_point_info['fields'] as $vn_i => $vs_schema_field) {
                         $vs_copyfields .= SolrConfiguration::tabs(1) . '<copyField source="' . $vs_schema_field . '" dest="' . $vs_access_point . '" />' . SolrConfiguration::nl();
                     }
                     $vs_field_schema .= SolrConfiguration::tabs(2) . '<field name="' . $vs_access_point . '" type="text" indexed="true" stored="true" multiValued="true"/>' . SolrConfiguration::nl();
                 }
                 /* write field indexing config into file */
                 fprintf($vr_schema_xml_file, "%s", $vs_field_schema);
                 continue;
             }
             /* 3rd replacement: uniquekey */
             if (strpos($vs_line, "<!--KEY-->") !== false) {
                 $vs_pk = $t_instance->primaryKey();
                 fprintf($vr_schema_xml_file, "%s", str_replace("<!--KEY-->", $vs_table . "." . $vs_pk, $vs_line));
                 continue;
             }
             /* 4th replacement: copyFields */
             if (strpos($vs_line, "<!--COPYFIELDS-->") !== false) {
                 /* $vs_copyfields *should* be set, otherwise the template has been messed up */
                 fprintf($vr_schema_xml_file, "%s", $vs_copyfields);
                 // add copyField for the subject table fields so that the pk can be adressed in 2 ways:
                 // "objects.object_id" or "object.id"
                 fprintf($vr_schema_xml_file, "%s", $vs_subject_table_copyfields);
                 continue;
             }
             /* "normal" line */
             fprintf($vr_schema_xml_file, "%s", $vs_line);
         }
         fclose($vr_schema_xml_file);
     }
 }
Exemple #10
0
 /**
  *
  */
 public function sortHits(&$pa_hits, $ps_table, $ps_field, $ps_key, $ps_direction = 'asc', $pa_options = null)
 {
     //$t= new Timer();
     $vs_browse_tmp_table = $this->loadListIntoTemporaryResultTable($pa_hits, $ps_key);
     if (!in_array(strtolower($ps_direction), array('asc', 'desc'))) {
         $ps_direction = 'asc';
     }
     if (!is_array($pa_hits) || !sizeof($pa_hits)) {
         return $pa_hits;
     }
     $t_table = $this->opo_datamodel->getInstanceByTableName($ps_table, true);
     $vs_table_pk = $t_table->primaryKey();
     $vn_table_num = $t_table->tableNum();
     $va_sort_tmp = explode('/', $ps_field);
     $ps_field = $va_sort_tmp[0];
     $vs_rel_type = sizeof($va_sort_tmp) > 1 ? $va_sort_tmp[1] : null;
     $va_fields = explode(';', $ps_field);
     $va_sorted_hits = array();
     $va_joins = array();
     $vs_locale_where = $vs_is_preferred_sql = '';
     $vs_primary_sort_field = array_shift($va_fields);
     $va_primary_sort_field = explode('.', $vs_primary_sort_field);
     $va_additional_sort_fields = array();
     foreach ($va_fields as $vs_additional_sort_field) {
         $va_tmp = explode('.', $vs_additional_sort_field);
         if ($va_tmp[0] == $va_primary_sort_field[0]) {
             $va_additional_sort_fields[] = $va_tmp;
         }
     }
     if ($va_primary_sort_field[0] == $ps_table) {
         //
         // sort field is in search table
         //
         if (!$t_table->hasField($va_primary_sort_field[1])) {
             //
             // is it an attribute?
             //
             $t_element = new ca_metadata_elements();
             $vs_sort_element_code = array_pop($va_primary_sort_field);
             if ($t_element->load(array('element_code' => $vs_sort_element_code))) {
                 $vn_element_id = $t_element->getPrimaryKey();
                 if (!($vs_sortable_value_fld = Attribute::getSortFieldForDatatype($t_element->get('datatype')))) {
                     return $pa_hits;
                 }
                 if ((int) $t_element->get('datatype') == __CA_ATTRIBUTE_VALUE_LIST__) {
                     $vs_sortable_value_fld = 'lil.name_plural';
                     $vs_sort_field = array_pop(explode('.', $vs_sortable_value_fld));
                     $vs_sql = "\n\t\t\t\t\t\t\t\tSELECT attr.row_id, lil.locale_id, lower({$vs_sortable_value_fld}) {$vs_sort_field}\n\t\t\t\t\t\t\t\tFROM ca_attributes attr\n\t\t\t\t\t\t\t\tINNER JOIN ca_attribute_values AS attr_vals ON attr_vals.attribute_id = attr.attribute_id\n\t\t\t\t\t\t\t\tINNER JOIN ca_list_item_labels AS lil ON lil.item_id = attr_vals.item_id\n\t\t\t\t\t\t\t\tINNER JOIN {$vs_browse_tmp_table} ON {$vs_browse_tmp_table}.row_id = attr.row_id\n\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t(attr_vals.element_id = ?) AND (attr.table_num = ?) AND (lil.{$vs_sort_field} IS NOT NULL)\n\t\t\t\t\t\t\t\tORDER BY lil.{$vs_sort_field} {$ps_direction}\n\t\t\t\t\t\t\t";
                 } elseif ((int) $t_element->get('datatype') == __CA_ATTRIBUTE_VALUE_DATERANGE__) {
                     $vs_sortable_value_fld = 'attr_vals.' . $vs_sortable_value_fld;
                     $vs_sort_field = array_pop(explode('.', $vs_sortable_value_fld));
                     $vs_sql = "\n\t\t\t\t\t\t\t\tSELECT attr.row_id, attr.locale_id, {$vs_sortable_value_fld}\n\t\t\t\t\t\t\t\tFROM ca_attributes attr\n\t\t\t\t\t\t\t\tINNER JOIN ca_attribute_values AS attr_vals ON attr_vals.attribute_id = attr.attribute_id\n\t\t\t\t\t\t\t\tINNER JOIN {$vs_browse_tmp_table} ON {$vs_browse_tmp_table}.row_id = attr.row_id\n\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t(attr_vals.element_id = ?) AND (attr.table_num = ?) AND (attr_vals.{$vs_sort_field} IS NOT NULL)\n\t\t\t\t\t\t\t\tORDER BY attr_vals.{$vs_sort_field} {$ps_direction}, attr.row_id\n\t\t\t\t\t\t\t";
                 } else {
                     $vs_sortable_value_fld = 'attr_vals.' . $vs_sortable_value_fld;
                     $vs_sort_field = array_pop(explode('.', $vs_sortable_value_fld));
                     $vs_sql = "\n\t\t\t\t\t\t\t\tSELECT attr.row_id, attr.locale_id, lower({$vs_sortable_value_fld}) {$vs_sort_field}\n\t\t\t\t\t\t\t\tFROM ca_attributes attr\n\t\t\t\t\t\t\t\tINNER JOIN ca_attribute_values AS attr_vals ON attr_vals.attribute_id = attr.attribute_id\n\t\t\t\t\t\t\t\tINNER JOIN {$vs_browse_tmp_table} ON {$vs_browse_tmp_table}.row_id = attr.row_id\n\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t(attr_vals.element_id = ?) AND (attr.table_num = ?) AND (attr_vals.{$vs_sort_field} IS NOT NULL)\n\t\t\t\t\t\t\t\tORDER BY attr_vals.{$vs_sort_field} {$ps_direction}, attr.row_id\n\t\t\t\t\t\t\t";
                 }
                 $qr_sort = $this->opo_db->query($vs_sql, (int) $vn_element_id, (int) $vn_table_num);
                 $va_sorted_hits = $qr_sort->getAllFieldValues('row_id');
                 // Add on hits that aren't sorted because they don't have an attribute associated
                 $va_missing_items = array_diff($pa_hits, $va_sorted_hits);
                 $va_sorted_hits = array_merge($va_sorted_hits, $va_missing_items);
             }
             return $va_sorted_hits;
         } else {
             $va_field_info = $t_table->getFieldInfo($va_primary_sort_field[1]);
             if ($va_field_info['START'] && $va_field_info['END']) {
                 $vs_sortable_value_fld = $vs_primary_sort_field;
                 $va_additional_sort_fields[] = $va_field_info['END'];
             } else {
                 $vs_sortable_value_fld = $vs_primary_sort_field;
             }
         }
     } else {
         // sort field is in related table
         $va_path = $this->opo_datamodel->getPath($ps_table, $va_primary_sort_field[0]);
         if (sizeof($va_path) > 2) {
             // many-many
             $vs_last_table = null;
             // generate related joins
             foreach ($va_path as $vs_table => $va_info) {
                 $t_table = $this->opo_datamodel->getInstanceByTableName($vs_table, true);
                 $vs_rel_type_sql = null;
                 if ($t_table->isRelationship() && $vs_rel_type) {
                     if (is_array($va_rel_types = caMakeRelationshipTypeIDList($vs_table, array($vs_rel_type))) && sizeof($va_rel_types)) {
                         $vs_rel_type_sql = " AND {$vs_table}.type_id IN (" . join(",", $va_rel_types) . ")";
                     }
                 }
                 if ($vs_last_table) {
                     $va_rels = $this->opo_datamodel->getOneToManyRelations($vs_last_table, $vs_table);
                     if (!sizeof($va_rels)) {
                         $va_rels = $this->opo_datamodel->getOneToManyRelations($vs_table, $vs_last_table);
                     }
                     if ($vs_table == $va_rels['one_table']) {
                         $va_joins[$vs_table] = "INNER JOIN " . $va_rels['one_table'] . " ON " . $va_rels['one_table'] . "." . $va_rels['one_table_field'] . " = " . $va_rels['many_table'] . "." . $va_rels['many_table_field'] . $vs_rel_type_sql;
                     } else {
                         $va_joins[$vs_table] = "INNER JOIN " . $va_rels['many_table'] . " ON " . $va_rels['many_table'] . "." . $va_rels['many_table_field'] . " = " . $va_rels['one_table'] . "." . $va_rels['one_table_field'] . $vs_rel_type_sql;
                     }
                 }
                 $t_last_table = $t_table;
                 $vs_last_table = $vs_table;
             }
             $vs_sortable_value_fld = $vs_primary_sort_field;
         } else {
             $va_rels = $this->opo_datamodel->getRelationships($ps_table, $va_primary_sort_field[0]);
             if (!$va_rels) {
                 return $pa_hits;
             }
             // return hits unsorted if field is not valid
             $t_rel = $this->opo_datamodel->getInstanceByTableName($va_primary_sort_field[0], true);
             if (!$t_rel->hasField($va_primary_sort_field[1])) {
                 return $pa_hits;
             }
             $va_joins[$va_primary_sort_field[0]] = 'LEFT JOIN ' . $va_primary_sort_field[0] . ' ON ' . $ps_table . '.' . $va_rels[$ps_table][$va_primary_sort_field[0]][0][0] . ' = ' . $va_primary_sort_field[0] . '.' . $va_rels[$ps_table][$va_primary_sort_field[0]][0][1] . "\n";
             // if the related supports preferred values (eg. *_labels tables) then only consider those in the sort
             if ($t_rel->hasField('is_preferred')) {
                 $vs_is_preferred_sql = " " . $va_primary_sort_field[0] . ".is_preferred = 1";
             }
             $vs_sortable_value_fld = $vs_primary_sort_field;
         }
     }
     //
     // Grab values and index for sorting later
     //
     //Debug::msg("sort pre query ".$t->getTime(4));
     $va_primary_sort_field = explode('.', $vs_sortable_value_fld);
     $vs_join_sql = join("\n", $va_joins);
     $va_sort_fields = array("{$vs_sortable_value_fld} {$ps_direction}");
     foreach ($va_additional_sort_fields as $va_additional_sort_field) {
         $va_sort_fields[] = "{$va_additional_sort_field[0]}.{$va_additional_sort_field[1]} {$ps_direction}";
     }
     $vs_sql = "\n\t\t\t\tSELECT {$ps_table}.{$vs_table_pk}\n\t\t\t\tFROM {$ps_table}\n\t\t\t\t{$vs_join_sql}\n\t\t\t\tINNER JOIN {$vs_browse_tmp_table} ON {$vs_browse_tmp_table}.row_id = {$ps_table}.{$vs_table_pk}\n\t\t\t\t" . ($vs_is_preferred_sql ? 'WHERE' : '') . "\n\t\t\t\t\t{$vs_is_preferred_sql}\n\t\t\t\tORDER BY " . join(',', $va_sort_fields) . ", {$ps_table}.{$vs_table_pk}\n\t\t\t";
     $qr_sort = $this->opo_db->query($vs_sql);
     $va_sorted_hits = $qr_sort->getAllFieldValues($vs_table_pk);
     return $va_sorted_hits;
 }
 public function _getElementInstance($pm_element_code_or_id)
 {
     if (!$pm_element_code_or_id) {
         $this->postError(1950, _t("Element code or id must not be blank"), "BaseModelWithAttributes->_getElementInstance()");
         return false;
     }
     if (isset(BaseModelWithAttributes::$s_element_instance_cache[$pm_element_code_or_id]) && BaseModelWithAttributes::$s_element_instance_cache[$pm_element_code_or_id]) {
         return BaseModelWithAttributes::$s_element_instance_cache[$pm_element_code_or_id];
     }
     require_once __CA_APP_DIR__ . '/models/ca_metadata_elements.php';
     $t_element = new ca_metadata_elements();
     if (!is_numeric($pm_element_code_or_id)) {
         if ($t_element->load(array('element_code' => $pm_element_code_or_id))) {
             return BaseModelWithAttributes::$s_element_instance_cache[$pm_element_code_or_id] = $t_element;
         }
     }
     if ($t_element->load($pm_element_code_or_id)) {
         return BaseModelWithAttributes::$s_element_instance_cache[$pm_element_code_or_id] = $t_element;
     } else {
         //$this->postError(1950, _t("Element code or id '%1' is invalid", $pm_element_code_or_id), "BaseModelWithAttributes->_getElementInstance()");
         return false;
     }
 }
Exemple #12
0
 public function search($pn_subject_tablenum, $ps_search_expression, $pa_filters = array(), $po_rewritten_query = null)
 {
     $t = new Timer();
     $va_solr_search_filters = array();
     $vn_i = 0;
     $va_old_signs = $po_rewritten_query->getSigns();
     $va_terms = $va_signs = array();
     foreach ($po_rewritten_query->getSubqueries() as $o_lucene_query_element) {
         if (!$va_old_signs || !is_array($va_old_signs)) {
             // if array is null then according to Zend Lucene all subqueries should be "are required"... so we AND them
             $vs_op = "AND";
         } else {
             if (is_null($va_old_signs[$vn_i])) {
                 // is the sign for a particular query is null then OR is (it is "neither required nor prohibited")
                 $vs_op = 'OR';
             } else {
                 $vs_op = $va_old_signs[$vn_i] === false ? 'NOT' : 'AND';
                 // true sign indicated "required" (AND) operation, false indicated "prohibited" (NOT) operation
             }
         }
         if ($vn_i == 0) {
             $vs_op = 'OR';
         }
         switch ($vs_class = get_class($o_lucene_query_element)) {
             case 'Zend_Search_Lucene_Search_Query_Term':
             case 'Zend_Search_Lucene_Search_Query_MultiTerm':
             case 'Zend_Search_Lucene_Search_Query_Phrase':
                 if ($vs_class != 'Zend_Search_Lucene_Search_Query_Term') {
                     $va_raw_terms = array();
                     foreach ($o_lucene_query_element->getQueryTerms() as $o_term) {
                         if (!$vs_access_point && ($vs_field = $o_term->field)) {
                             $vs_access_point = $vs_field;
                         }
                         $va_raw_terms[] = $vs_text = (string) $o_term->text;
                     }
                     $vs_term = join(" ", $va_raw_terms);
                 } else {
                     $vs_access_point = $o_lucene_query_element->getTerm()->field;
                     $vs_term = $o_lucene_query_element->getTerm()->text;
                 }
                 if ($vs_access_point) {
                     list($vs_table, $vs_field, $vs_sub_field) = explode('.', $vs_access_point);
                     if (in_array($vs_table, array('created', 'modified'))) {
                         if (!$this->opo_tep->parse($vs_term)) {
                             break;
                         }
                         $va_range = $this->opo_tep->getUnixTimestamps();
                         $vn_user_id = null;
                         if ($vs_field = trim($vs_field)) {
                             if (!is_int($vs_field)) {
                                 $t_user = new ca_users();
                                 if ($t_user->load(array("user_name" => $vs_field))) {
                                     $vn_user_id = (int) $t_user->getPrimaryKey();
                                 }
                             } else {
                                 $vn_user_id = (int) $vs_field;
                             }
                         }
                         $vs_user_sql = $vn_user_id ? " AND (ccl.user_id = " . (int) $vn_user_id . ")" : "";
                         switch ($vs_table) {
                             case 'created':
                                 if ($vn_user_id) {
                                     $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Boolean(array(new Zend_Search_Lucene_Index_Term('[' . $this->opo_tep->getText(array('start_as_iso8601' => true)) . " TO " . $this->opo_tep->getText(array('end_as_iso8601' => true)) . ']', 'created'), new Zend_Search_Lucene_Index_Term($vn_user_id, 'created_user_id')), array(true, true));
                                 } else {
                                     $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term('[' . $this->opo_tep->getText(array('start_as_iso8601' => true)) . " TO " . $this->opo_tep->getText(array('end_as_iso8601' => true)) . ']', 'created'));
                                 }
                                 break;
                             case 'modified':
                                 if ($vn_user_id) {
                                     $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Boolean(array(new Zend_Search_Lucene_Index_Term('[' . $this->opo_tep->getText(array('start_as_iso8601' => true)) . " TO " . $this->opo_tep->getText(array('end_as_iso8601' => true)) . ']', 'modified'), new Zend_Search_Lucene_Index_Term($vn_user_id, 'modified_user_id')), array(true, true));
                                 } else {
                                     $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term('[' . $this->opo_tep->getText(array('start_as_iso8601' => true)) . " TO " . $this->opo_tep->getText(array('end_as_iso8601' => true)) . ']', 'modified'));
                                 }
                                 break;
                         }
                     } else {
                         if ($vs_table && $vs_field) {
                             $t_table = $this->opo_datamodel->getInstanceByTableName($vs_table, true);
                             if ($t_table) {
                                 $vs_table_num = $t_table->tableNum();
                                 if (is_numeric($vs_field)) {
                                     $vs_fld_num = $vs_field;
                                 } else {
                                     $vs_fld_num = $this->opo_datamodel->getFieldNum($vs_table, $vs_field);
                                     if (!$vs_fld_num) {
                                         $t_element = new ca_metadata_elements();
                                         if ($t_element->load(array('element_code' => $vs_sub_field ? $vs_sub_field : $vs_field))) {
                                             $vs_fld_num = $t_element->getPrimaryKey();
                                             //
                                             // For certain types of attributes we can directly query the
                                             // attributes in the database rather than using the full text index
                                             // This allows us to do "intelligent" querying... for example on date ranges
                                             // parsed from natural language input and for length dimensions using unit conversion
                                             //
                                             switch ($t_element->get('datatype')) {
                                                 case 2:
                                                     // dates
                                                     $vb_exact = $vs_term[0] == "#" ? true : false;
                                                     // dates prepended by "#" are considered "exact" or "contained - the matched dates must be wholly contained by the search term
                                                     if ($vb_exact) {
                                                         $vs_raw_term = substr($vs_term, 1);
                                                         if ($this->opo_tep->parse($vs_term)) {
                                                             $va_dates = $this->opo_tep->getHistoricTimestamps();
                                                             // TODO: fix date handling to reflect distinctions in ranges
                                                             $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term('[' . $this->opo_tep->getText(array('start_as_iso8601' => true)) . " TO " . $this->opo_tep->getText(array('end_as_iso8601' => true)) . ']', $vs_access_point));
                                                         }
                                                     } else {
                                                         if ($this->opo_tep->parse($vs_term)) {
                                                             $va_dates = $this->opo_tep->getHistoricTimestamps();
                                                             // TODO: fix date handling to reflect distinctions in ranges
                                                             $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term('[' . $this->opo_tep->getText(array('start_as_iso8601' => true)) . " TO " . $this->opo_tep->getText(array('end_as_iso8601' => true)) . ']', $vs_access_point));
                                                         }
                                                     }
                                                     break;
                                                 case 4:
                                                     // geocode
                                                     $t_geocode = new GeocodeAttributeValue();
                                                     if ($va_coords = caParseGISSearch($vs_term)) {
                                                         $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term('[' . $va_coords['min_latitude'] . ',' . $va_coords['min_longitude'] . " TO " . $va_coords['max_latitude'] . ',' . $va_coords['max_longitude'] . ']', $vs_access_point));
                                                     }
                                                     break;
                                                 case 6:
                                                     // currency
                                                     $t_cur = new CurrencyAttributeValue();
                                                     $va_parsed_value = $t_cur->parseValue($vs_term, $t_element->getFieldValuesArray());
                                                     $vn_amount = (double) $va_parsed_value['value_decimal1'];
                                                     $vs_currency = preg_replace('![^A-Z0-9]+!', '', $va_parsed_value['value_longtext1']);
                                                     $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term($vn_amount, $vs_access_point));
                                                     break;
                                                 case 8:
                                                     // length
                                                     $t_len = new LengthAttributeValue();
                                                     $va_parsed_value = $t_len->parseValue($vs_term, $t_element->getFieldValuesArray());
                                                     $vn_len = (double) $va_parsed_value['value_decimal1'];
                                                     // this is always in meters so we can compare this value to the one in the database
                                                     $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term($vn_len, $vs_access_point));
                                                     break;
                                                 case 9:
                                                     // weight
                                                     $t_weight = new WeightAttributeValue();
                                                     $va_parsed_value = $t_weight->parseValue($vs_term, $t_element->getFieldValuesArray());
                                                     $vn_weight = (double) $va_parsed_value['value_decimal1'];
                                                     // this is always in kilograms so we can compare this value to the one in the database
                                                     $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term($vn_weight, $vs_access_point));
                                                     break;
                                                 case 10:
                                                     // timecode
                                                     $t_timecode = new TimecodeAttributeValue();
                                                     $va_parsed_value = $t_timecode->parseValue($vs_term, $t_element->getFieldValuesArray());
                                                     $vn_timecode = (double) $va_parsed_value['value_decimal1'];
                                                     $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term($vn_timecode, $vs_access_point));
                                                     break;
                                                 case 11:
                                                     // integer
                                                     $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term((double) $vs_term, $vs_access_point));
                                                     break;
                                                 case 12:
                                                     // decimal
                                                     $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term((double) $vs_term, $vs_access_point));
                                                     break;
                                             }
                                         }
                                     }
                                 }
                             }
                         }
                     }
                 }
                 break;
         }
         $va_terms[] = $o_lucene_query_element;
         $va_signs[] = is_array($va_old_signs) ? array_key_exists($vn_i, $va_old_signs) ? $va_old_signs[$vn_i] : true : true;
         $vn_i++;
     }
     $o_rewritten_query = new Zend_Search_Lucene_Search_Query_Boolean($va_terms, $va_signs);
     $ps_search_expression = $this->_queryToString($o_rewritten_query);
     if ($vs_filter_query = $this->_filterValueToQueryValue($pa_filters)) {
         $ps_search_expression .= ' AND (' . $vs_filter_query . ')';
     }
     $vo_http_client = new Zend_Http_Client();
     $vo_http_client->setUri($this->opo_search_config->get('search_elasticsearch_base_url') . "/" . $this->opo_search_config->get('search_elasticsearch_index_name') . "/" . $this->opo_datamodel->getTableName($pn_subject_tablenum) . "/" . "_search");
     $vo_http_client->setParameterGet(array('size' => intval($this->opa_options["limit"]), 'q' => $ps_search_expression));
     $vo_http_response = $vo_http_client->request();
     $va_result = json_decode($vo_http_response->getBody(), true);
     return new WLPlugSearchEngineElasticSearchResult($va_result["hits"]["hits"], $pn_subject_tablenum);
 }
 /**
  * 
  */
 public static function getInstance($pm_element_code_or_id)
 {
     if (isset(ca_metadata_elements::$s_element_instance_cache[$pm_element_code_or_id])) {
         return ca_metadata_elements::$s_element_instance_cache[$pm_element_code_or_id];
     }
     $t_element = new ca_metadata_elements(is_numeric($pm_element_code_or_id) ? $pm_element_code_or_id : null);
     if (!($vn_element_id = $t_element->getPrimaryKey())) {
         if ($t_element->load(array('element_code' => $pm_element_code_or_id))) {
             return ca_metadata_elements::$s_element_instance_cache[$t_element->getPrimaryKey()] = ca_metadata_elements::$s_element_instance_cache[$t_element->get('element_code')] = $t_element;
         }
     } else {
         return ca_metadata_elements::$s_element_instance_cache[$vn_element_id] = ca_metadata_elements::$s_element_instance_cache[$t_element->get('element_code')] = $t_element;
     }
     return null;
 }
 /**
  * 
  */
 public static function getInstance($pm_element_code_or_id)
 {
     if (!$pm_element_code_or_id) {
         return null;
     }
     if (MemoryCache::contains($pm_element_code_or_id, 'ElementInstances')) {
         return MemoryCache::fetch($pm_element_code_or_id, 'ElementInstances');
     }
     $t_element = new ca_metadata_elements(is_numeric($pm_element_code_or_id) ? $pm_element_code_or_id : null);
     if (!($vn_element_id = $t_element->getPrimaryKey())) {
         if ($t_element->load(array('element_code' => $pm_element_code_or_id))) {
             MemoryCache::save($t_element->getPrimaryKey(), $t_element, 'ElementInstances');
             MemoryCache::save($t_element->get('element_code'), $t_element, 'ElementInstances');
             return $t_element;
         }
     } else {
         MemoryCache::save($vn_element_id, $t_element, 'ElementInstances');
         MemoryCache::save($t_element->get('element_code'), $t_element, 'ElementInstances');
         return $t_element;
     }
     return null;
 }
Exemple #15
0
 /**
  * @param $pa_hits Array of row_ids to sort. *MUST HAVE row_ids AS KEYS, NOT VALUES*
  */
 public function sortHits($pa_hits, $ps_field, $ps_direction = 'asc', $pa_options = null)
 {
     if (!in_array(strtolower($ps_direction), array('asc', 'desc'))) {
         $ps_direction = 'asc';
     }
     if (!is_array($pa_hits) || !sizeof($pa_hits)) {
         return $pa_hits;
     }
     $vs_search_tmp_table = $this->loadListIntoTemporaryResultTable($pa_hits, $pa_options['search']);
     $t_table = $this->opo_datamodel->getInstanceByTableNum($this->opn_tablenum, true);
     $vs_table_pk = $t_table->primaryKey();
     $vs_table_name = $this->ops_tablename;
     $va_fields = explode(';', $ps_field);
     $va_sorted_hits = array();
     $vn_num_locales = ca_locales::numberOfCataloguingLocales();
     foreach ($va_fields as $vs_field) {
         $va_joins = $va_orderbys = array();
         $vs_locale_where = $vs_is_preferred_sql = '';
         $va_tmp = explode('.', $vs_field);
         // Rewrite for <table>.preferred_labels.* syntax
         if ($va_tmp[1] == 'preferred_labels') {
             if ($t_labeled_item_table = $this->opo_datamodel->getInstanceByTableName($va_tmp[0], true)) {
                 if ($t_label_table = $t_labeled_item_table->getLabelTableInstance()) {
                     $va_tmp2 = array($t_label_table->tableName());
                     if (isset($va_tmp[2]) && $t_label_table->hasField($va_tmp[2])) {
                         $va_tmp2[] = $va_tmp[2];
                     } else {
                         $va_tmp2[] = $t_labeled_item_table->getLabelDisplayField();
                     }
                     $va_tmp = $va_tmp2;
                     $vs_field = join(".", $va_tmp);
                 }
             }
         }
         if ($va_tmp[0] == $vs_table_name) {
             //
             // sort field is in search table
             //
             if (!$t_table->hasField($va_tmp[1])) {
                 //
                 // is it an attribute?
                 //
                 $t_element = new ca_metadata_elements();
                 $vs_sort_element_code = array_pop($va_tmp);
                 if ($t_element->load(array('element_code' => $vs_sort_element_code))) {
                     $vn_element_id = $t_element->getPrimaryKey();
                     if (!($vs_sortable_value_fld = Attribute::getSortFieldForDatatype($t_element->get('datatype')))) {
                         return $pa_hits;
                     }
                     if ((int) $t_element->get('datatype') == 3) {
                         $vs_sortable_value_fld = 'lil.name_plural';
                         $vs_sort_field = array_pop(explode('.', $vs_sortable_value_fld));
                         $vs_locale_where = $vn_num_locales > 1 ? ', lil.locale_id' : '';
                         $vs_sql = "\n\t\t\t\t\t\t\t\tSELECT attr.row_id, lil.locale_id, lower({$vs_sortable_value_fld}) {$vs_sort_field}\n\t\t\t\t\t\t\t\tFROM ca_attributes attr\n\t\t\t\t\t\t\t\tINNER JOIN ca_attribute_values AS attr_vals ON attr_vals.attribute_id = attr.attribute_id\n\t\t\t\t\t\t\t\tINNER JOIN ca_list_item_labels AS lil ON lil.item_id = attr_vals.item_id\n\t\t\t\t\t\t\t\tINNER JOIN {$vs_browse_tmp_table} ON {$vs_browse_tmp_table}.row_id = attr.row_id\n\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t(attr_vals.element_id = ?) AND (attr.table_num = ?) AND (lil.{$vs_sort_field} IS NOT NULL)\n\t\t\t\t\t\t\t\tORDER BY lil.{$vs_sort_field}\n\t\t\t\t\t\t\t";
                     } else {
                         $vs_sortable_value_fld = 'attr_vals.' . $vs_sortable_value_fld;
                         $vs_sort_field = array_pop(explode('.', $vs_sortable_value_fld));
                         $vs_locale_where = $vn_num_locales > 1 ? 'attr.locale_id' : '';
                         $vs_sql = "\n\t\t\t\t\t\t\t\tSELECT attr.row_id, attr.locale_id, lower({$vs_sortable_value_fld}) {$vs_sort_field}\n\t\t\t\t\t\t\t\tFROM ca_attributes attr\n\t\t\t\t\t\t\t\tINNER JOIN ca_attribute_values AS attr_vals ON attr_vals.attribute_id = attr.attribute_id\n\t\t\t\t\t\t\t\tINNER JOIN {$vs_search_tmp_table} ON {$vs_search_tmp_table}.row_id = attr.row_id\n\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t(attr_vals.element_id = ?) AND (attr.table_num = ?) AND (attr_vals.{$vs_sort_field} IS NOT NULL)\n\t\t\t\t\t\t\t\tORDER BY attr_vals.{$vs_sort_field}\n\t\t\t\t\t\t\t";
                         //print $vs_sql." ; $vn_element_id/; ".$this->opn_tablenum."<br>";
                     }
                     $qr_sort = $this->opo_db->query($vs_sql, (int) $vn_element_id, (int) $this->opn_tablenum);
                     while ($qr_sort->nextRow()) {
                         $va_row = $qr_sort->getRow();
                         if (!$va_row['row_id']) {
                             continue;
                         }
                         if ($vn_num_locales > 1) {
                             $va_sorted_hits[$va_row['row_id']][$va_row['locale_id']] .= trim(str_replace(array("'", '"'), array('', ''), $va_row[$vs_sort_field]));
                         } else {
                             $va_sorted_hits[$va_row['row_id']] .= trim(str_replace(array("'", '"'), array('', ''), $va_row[$vs_sort_field]));
                         }
                         unset($pa_hits[$va_row['row_id']]);
                     }
                     // Add on hits that aren't sorted because they don't have an attribute associated
                     foreach ($pa_hits as $vn_id => $va_row) {
                         if (!is_array($va_row)) {
                             $va_row = array();
                         }
                         if ($vn_num_locales > 1) {
                             $va_sorted_hits[$vn_id][1] = $va_row;
                         } else {
                             $va_sorted_hits[$vn_id] = $va_row;
                         }
                     }
                 }
                 continue;
             } else {
                 $va_field_info = $t_table->getFieldInfo($va_tmp[1]);
                 if ($va_field_info['START'] && $va_field_info['END']) {
                     $va_orderbys[] = $va_field_info['START'] . ' ' . $ps_direction;
                     $va_orderbys[] = $va_field_info['END'] . ' ' . $ps_direction;
                 } else {
                     $va_orderbys[] = $vs_field . ' ' . $ps_direction;
                 }
                 if ($t_table->hasField('locale_id')) {
                     $vs_locale_where = ", " . $vs_table_name . ".locale_id";
                 }
                 $vs_sortable_value_fld = $vs_field;
             }
         } else {
             // sort field is in related table
             $va_path = $this->opo_datamodel->getPath($vs_table_name, $va_tmp[0]);
             if (sizeof($va_path) > 2) {
                 // many-many
                 $vs_last_table = null;
                 // generate related joins
                 foreach ($va_path as $vs_table => $va_info) {
                     $t_table = $this->opo_datamodel->getInstanceByTableName($vs_table, true);
                     if (!$vs_last_table) {
                         //$va_joins[$vs_table] = "INNER JOIN ".$vs_table." ON ".$vs_table.".".$t_table->primaryKey()." = ca_sql_search_search_final.row_id";
                     } else {
                         $va_rels = $this->opo_datamodel->getOneToManyRelations($vs_last_table, $vs_table);
                         if (!sizeof($va_rels)) {
                             $va_rels = $this->opo_datamodel->getOneToManyRelations($vs_table, $vs_last_table);
                         }
                         if ($vs_table == $va_rels['one_table']) {
                             $va_joins[$vs_table] = "INNER JOIN " . $va_rels['one_table'] . " ON " . $va_rels['one_table'] . "." . $va_rels['one_table_field'] . " = " . $va_rels['many_table'] . "." . $va_rels['many_table_field'];
                         } else {
                             $va_joins[$vs_table] = "INNER JOIN " . $va_rels['many_table'] . " ON " . $va_rels['many_table'] . "." . $va_rels['many_table_field'] . " = " . $va_rels['one_table'] . "." . $va_rels['one_table_field'];
                         }
                     }
                     $t_last_table = $t_table;
                     $vs_last_table = $vs_table;
                 }
                 $va_orderbys[] = $vs_field . ' ' . $ps_direction;
                 $vs_sortable_value_fld = $vs_field;
             } else {
                 $va_rels = $this->opo_datamodel->getRelationships($vs_table_name, $va_tmp[0]);
                 if (!$va_rels) {
                     return $pa_hits;
                 }
                 // return hits unsorted if field is not valid
                 $t_rel = $this->opo_datamodel->getInstanceByTableName($va_tmp[0], true);
                 if (!$t_rel->hasField($va_tmp[1])) {
                     return $pa_hits;
                 }
                 $va_joins[$va_tmp[0]] = 'LEFT JOIN ' . $va_tmp[0] . ' ON ' . $vs_table_name . '.' . $va_rels[$vs_table_name][$va_tmp[0]][0][0] . ' = ' . $va_tmp[0] . '.' . $va_rels[$vs_table_name][$va_tmp[0]][0][1] . "\n";
                 $va_orderbys[] = $vs_field . ' ' . $ps_direction;
                 // if the related supports preferred values (eg. *_labels tables) then only consider those in the sort
                 if ($t_rel->hasField('is_preferred')) {
                     $vs_is_preferred_sql = " " . $va_tmp[0] . ".is_preferred = 1";
                 }
                 if ($t_rel->hasField('locale_id')) {
                     $vs_locale_where = ", " . $va_tmp[0] . ".locale_id";
                 }
                 $vs_sortable_value_fld = $vs_field;
             }
         }
         //
         // Grab values and index for sorting later
         //
         $va_tmp = explode('.', $vs_sortable_value_fld);
         $vs_sort_field = array_pop($va_tmp);
         $vs_join_sql = join("\n", $va_joins);
         $vs_sql = "\n\t\t\t\tSELECT {$vs_table_name}.{$vs_table_pk}{$vs_locale_where}, lower({$vs_sortable_value_fld}) {$vs_sort_field}\n\t\t\t\tFROM {$vs_table_name}\n\t\t\t\t{$vs_join_sql}\n\t\t\t\tINNER JOIN {$vs_search_tmp_table} ON {$vs_search_tmp_table}.row_id = {$vs_table_name}.{$vs_table_pk}\n\t\t\t\t" . ($vs_is_preferred_sql ? 'WHERE' : '') . "\n\t\t\t\t\t{$vs_is_preferred_sql}\n\t\t\t";
         //print $vs_sql;
         $qr_sort = $this->opo_db->query($vs_sql);
         while ($qr_sort->nextRow()) {
             $va_row = $qr_sort->getRow();
             if (!($vs_sortable_value = str_replace(array("'", '"'), array('', ''), $va_row[$vs_sort_field]))) {
                 $vs_sortable_value = '';
             }
             if ($vn_num_locales > 1 && $vs_locale_where) {
                 $va_sorted_hits[$va_row[$vs_table_pk]][$va_row['locale_id']] .= $vs_sortable_value;
             } else {
                 $va_sorted_hits[$va_row[$vs_table_pk]] .= $vs_sortable_value;
             }
         }
     }
     //
     // Actually sort the hits here...
     //
     if ($vn_num_locales > 1 && $vs_locale_where) {
         $va_sorted_hits = caExtractValuesByUserLocale($va_sorted_hits);
     }
     asort($va_sorted_hits, SORT_STRING);
     if ($ps_direction == 'desc') {
         $va_sorted_hits = array_reverse($va_sorted_hits, true);
     }
     $this->cleanupTemporaryResultTable();
     return $va_sorted_hits;
 }
Exemple #16
0
 public function search($pn_subject_tablenum, $ps_search_expression, $pa_filters = array(), $po_rewritten_query = null)
 {
     $va_solr_search_filters = array();
     $vn_i = 0;
     $va_old_signs = is_object($po_rewritten_query) ? $po_rewritten_query->getSigns() : array();
     $va_terms = $va_signs = array();
     if ($po_rewritten_query) {
         foreach ($po_rewritten_query->getSubqueries() as $o_lucene_query_element) {
             if (!$va_old_signs || !is_array($va_old_signs)) {
                 // if array is null then according to Zend Lucene all subqueries should be "are required"... so we AND them
                 $vs_op = "AND";
             } else {
                 if (is_null($va_old_signs[$vn_i])) {
                     // is the sign for a particular query is null then OR is (it is "neither required nor prohibited")
                     $vs_op = 'OR';
                 } else {
                     $vs_op = $va_old_signs[$vn_i] === false ? 'NOT' : 'AND';
                     // true sign indicated "required" (AND) operation, false indicated "prohibited" (NOT) operation
                 }
             }
             if ($vn_i == 0) {
                 $vs_op = 'OR';
             }
             // advanced search queries are for some reason nested 1-element boolean queries in boolean queries
             if (get_class($o_lucene_query_element) == 'Zend_Search_Lucene_Search_Query_Boolean') {
                 $va_subqueries = $o_lucene_query_element->getSubqueries();
                 if (sizeof($va_subqueries) == 1) {
                     $o_lucene_query_element = array_shift($va_subqueries);
                 }
             }
             switch ($vs_class = get_class($o_lucene_query_element)) {
                 case 'Zend_Search_Lucene_Search_Query_Term':
                 case 'Zend_Search_Lucene_Search_Query_MultiTerm':
                 case 'Zend_Search_Lucene_Search_Query_Phrase':
                     $vs_access_point = '';
                     if ($vs_class != 'Zend_Search_Lucene_Search_Query_Term') {
                         $va_raw_terms = array();
                         foreach ($o_lucene_query_element->getQueryTerms() as $o_term) {
                             if (!$vs_access_point && ($vs_field = $o_term->field)) {
                                 $vs_access_point = $vs_field;
                             }
                             $va_raw_terms[] = $vs_text = (string) $o_term->text;
                         }
                         $vs_term = join(" ", $va_raw_terms);
                     } else {
                         $vs_access_point = $o_lucene_query_element->getTerm()->field;
                         $vs_term = $o_lucene_query_element->getTerm()->text;
                     }
                     if ($vs_access_point) {
                         list($vs_table, $vs_field, $vs_sub_field) = explode('.', $vs_access_point);
                         if (in_array($vs_table, array('created', 'modified'))) {
                             if (!$this->opo_tep->parse($vs_term)) {
                                 break;
                             }
                             $va_range = $this->opo_tep->getUnixTimestamps();
                             $vn_user_id = null;
                             if ($vs_field = trim($vs_field)) {
                                 if (!is_int($vs_field)) {
                                     $t_user = new ca_users();
                                     if ($t_user->load(array("user_name" => $vs_field))) {
                                         $vn_user_id = (int) $t_user->getPrimaryKey();
                                     }
                                 } else {
                                     $vn_user_id = (int) $vs_field;
                                 }
                             }
                             $vs_user_sql = $vn_user_id ? " AND (ccl.user_id = " . (int) $vn_user_id . ")" : "";
                             switch ($vs_table) {
                                 case 'created':
                                     if ($vn_user_id) {
                                         $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Boolean(array(new Zend_Search_Lucene_Index_Term('[' . $this->opo_tep->getText(array('start_as_iso8601' => true)) . " TO " . $this->opo_tep->getText(array('end_as_iso8601' => true)) . ']', 'created'), new Zend_Search_Lucene_Index_Term($vn_user_id, 'created_user_id')), array(true, true));
                                     } else {
                                         $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term('[' . $this->opo_tep->getText(array('start_as_iso8601' => true)) . " TO " . $this->opo_tep->getText(array('end_as_iso8601' => true)) . ']', 'created'));
                                     }
                                     break;
                                 case 'modified':
                                     if ($vn_user_id) {
                                         $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Boolean(array(new Zend_Search_Lucene_Index_Term('[' . $this->opo_tep->getText(array('start_as_iso8601' => true)) . " TO " . $this->opo_tep->getText(array('end_as_iso8601' => true)) . ']', 'modified'), new Zend_Search_Lucene_Index_Term($vn_user_id, 'modified_user_id')), array(true, true));
                                     } else {
                                         $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term('[' . $this->opo_tep->getText(array('start_as_iso8601' => true)) . " TO " . $this->opo_tep->getText(array('end_as_iso8601' => true)) . ']', 'modified'));
                                     }
                                     break;
                             }
                         } else {
                             if ($vs_table && $vs_field) {
                                 $t_table = $this->opo_datamodel->getInstanceByTableName($vs_table, true);
                                 if ($t_table) {
                                     $vs_table_num = $t_table->tableNum();
                                     if (is_numeric($vs_field)) {
                                         $vs_fld_num = 'I' . $vs_field;
                                         $vn_fld_num = (int) $vs_field;
                                     } else {
                                         $vn_fld_num = $this->getFieldNum($vs_table, $vs_field);
                                         $vs_fld_num = 'I' . $vn_fld_num;
                                         if (!strlen($vn_fld_num)) {
                                             $t_element = new ca_metadata_elements();
                                             if ($t_element->load(array('element_code' => $vs_sub_field ? $vs_sub_field : $vs_field))) {
                                                 $vn_fld_num = $t_element->getPrimaryKey();
                                                 $vs_fld_num = 'A' . $vn_fld_num;
                                                 //
                                                 // For certain types of attributes we can directly query the
                                                 // attributes in the database rather than using the full text index
                                                 // This allows us to do "intelligent" querying... for example on date ranges
                                                 // parsed from natural language input and for length dimensions using unit conversion
                                                 //
                                                 switch ($t_element->get('datatype')) {
                                                     case 2:
                                                         // dates
                                                         $vb_exact = $vs_term[0] == "#" ? true : false;
                                                         // dates prepended by "#" are considered "exact" or "contained - the matched dates must be wholly contained by the search term
                                                         if ($vb_exact) {
                                                             $vs_raw_term = substr($vs_term, 1);
                                                             if ($this->opo_tep->parse($vs_term)) {
                                                                 $va_dates = $this->opo_tep->getHistoricTimestamps();
                                                                 // TODO: fix date handling to reflect distinctions in ranges
                                                                 $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term('[' . $this->opo_tep->getText(array('start_as_iso8601' => true)) . " TO " . $this->opo_tep->getText(array('end_as_iso8601' => true)) . ']', $vs_table . '.' . $vs_fld_num));
                                                             }
                                                         } else {
                                                             if ($this->opo_tep->parse($vs_term)) {
                                                                 $va_dates = $this->opo_tep->getHistoricTimestamps();
                                                                 // TODO: fix date handling to reflect distinctions in ranges
                                                                 $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term('[' . $this->opo_tep->getText(array('start_as_iso8601' => true)) . " TO " . $this->opo_tep->getText(array('end_as_iso8601' => true)) . ']', $vs_table . '.' . $vs_fld_num));
                                                             }
                                                         }
                                                         break;
                                                     case 4:
                                                         // geocode
                                                         $t_geocode = new GeocodeAttributeValue();
                                                         if ($va_coords = caParseGISSearch($vs_term)) {
                                                             $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term('[' . $va_coords['min_latitude'] . ',' . $va_coords['min_longitude'] . " TO " . $va_coords['max_latitude'] . ',' . $va_coords['max_longitude'] . ']', $vs_table . '.' . $vs_fld_num));
                                                         }
                                                         break;
                                                     case 6:
                                                         // currency
                                                         $t_cur = new CurrencyAttributeValue();
                                                         $va_parsed_value = $t_cur->parseValue($vs_term, $t_element->getFieldValuesArray());
                                                         $vn_amount = (double) $va_parsed_value['value_decimal1'];
                                                         $vs_currency = preg_replace('![^A-Z0-9]+!', '', $va_parsed_value['value_longtext1']);
                                                         $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term($vn_amount, $vs_table . '.' . $vs_fld_num));
                                                         break;
                                                     case 8:
                                                         // length
                                                         $t_len = new LengthAttributeValue();
                                                         $va_parsed_value = $t_len->parseValue($vs_term, $t_element->getFieldValuesArray());
                                                         $vn_len = (double) $va_parsed_value['value_decimal1'];
                                                         // this is always in meters so we can compare this value to the one in the database
                                                         $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term($vn_len, $vs_table . '.' . $vs_fld_num));
                                                         break;
                                                     case 9:
                                                         // weight
                                                         $t_weight = new WeightAttributeValue();
                                                         $va_parsed_value = $t_weight->parseValue($vs_term, $t_element->getFieldValuesArray());
                                                         $vn_weight = (double) $va_parsed_value['value_decimal1'];
                                                         // this is always in kilograms so we can compare this value to the one in the database
                                                         $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term($vn_weight, $vs_table . '.' . $vs_fld_num));
                                                         break;
                                                     case 10:
                                                         // timecode
                                                         $t_timecode = new TimecodeAttributeValue();
                                                         $va_parsed_value = $t_timecode->parseValue($vs_term, $t_element->getFieldValuesArray());
                                                         $vn_timecode = (double) $va_parsed_value['value_decimal1'];
                                                         $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term($vn_timecode, $vs_table . '.' . $vs_fld_num));
                                                         break;
                                                     case 11:
                                                         // integer
                                                         $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term((double) $vs_term, $vs_table . '.' . $vs_fld_num));
                                                         break;
                                                     case 12:
                                                         // decimal
                                                         $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term((double) $vs_term, $vs_table . '.' . $vs_fld_num));
                                                         break;
                                                     default:
                                                         // everything else
                                                         $o_lucene_query_element->getTerm()->field = $vs_table . '.' . $vs_fld_num;
                                                         break;
                                                 }
                                             } else {
                                                 $vn_fld_num = false;
                                                 $vs_fld_num = $vs_field;
                                             }
                                         }
                                     }
                                 }
                             }
                         }
                     }
                     break;
             }
             $va_terms[] = $o_lucene_query_element;
             $va_signs[] = is_array($va_old_signs) ? array_key_exists($vn_i, $va_old_signs) ? $va_old_signs[$vn_i] : true : true;
             $vn_i++;
         }
         $o_rewritten_query = new Zend_Search_Lucene_Search_Query_Boolean($va_terms, $va_signs);
         $ps_search_expression = $this->_queryToString($o_rewritten_query);
     }
     if (is_array($pa_filters) && sizeof($pa_filters) && ($vs_filter_query = $this->_filterValueToQueryValue($pa_filters))) {
         $ps_search_expression = "({$ps_search_expression}) AND ({$vs_filter_query})";
     }
     if (preg_match_all("!([A-Za-z0-9_\\-\\.]+)[/]{1}([A-Za-z0-9_\\-]+):(\"[^\"]*\")!", $ps_search_expression, $va_matches) || preg_match_all("!([A-Za-z0-9_\\-\\.]+)[/]{1}([A-Za-z0-9_\\-]+):([^ ]*)!", $ps_search_expression, $va_matches)) {
         foreach ($va_matches[0] as $vn_i => $vs_element) {
             $vs_fld = $va_matches[1][$vn_i];
             if (!($vs_rel_type = trim($va_matches[2][$vn_i]))) {
                 continue;
             }
             $va_tmp = explode(".", $vs_fld);
             $vs_rel_table = caGetRelationshipTableName($pn_subject_tablenum, $va_tmp[0]);
             $va_rel_type_ids = $vs_rel_type && $vs_rel_table ? caMakeRelationshipTypeIDList($vs_rel_table, array($vs_rel_type)) : null;
             $va_new_elements = array();
             foreach ($va_rel_type_ids as $vn_rel_type_id) {
                 $va_new_elements[] = "(" . $va_matches[1][$vn_i] . "/" . $vn_rel_type_id . ":" . $va_matches[3][$vn_i] . ")";
             }
             $ps_search_expression = str_replace($vs_element, "(" . join(" OR ", $va_new_elements) . ")", $ps_search_expression);
         }
     }
     $ps_search_expression = str_replace("/", '\\/', $ps_search_expression);
     // escape forward slashes used to delimit relationship type qualifier
     Debug::msg("[SOLR] Running query {$ps_search_expression}");
     try {
         $vo_http_client = new Zend_Http_Client();
         $vo_http_client->setUri($this->ops_search_solr_url . "/" . $this->opo_datamodel->getTableName($pn_subject_tablenum) . "/select");
         $vo_http_client->setParameterGet($va_get = array('q' => utf8_decode($ps_search_expression), 'wt' => 'json', 'fl' => $this->opo_datamodel->getTablePrimaryKeyName($pn_subject_tablenum), 'start' => $this->getOption('start'), 'rows' => $this->getOption('limit')));
         $vo_http_response = $vo_http_client->request();
         $va_result = json_decode($vo_http_response->getBody(), true);
     } catch (Exception $e) {
         caLogEvent('ERR', _t('Could not connect to SOLR server: %1', $e->getMessage()), 'Solr->search()');
         $va_result["response"]["docs"] = array();
     }
     return new WLPlugSearchEngineSolrResult($va_result["response"]["docs"], $pn_subject_tablenum);
 }
Exemple #17
0
 /**
  * Given a item_id (request parameter 'id') returns a list of ancestors for use in the hierarchy browser
  * Returned data is JSON format
  */
 public function getFacetHierarchyAncestorList()
 {
     $pn_id = $this->request->getParameter('id', pInteger);
     $va_access_values = caGetUserAccessValues($this->request);
     $ps_facet_name = $this->request->getParameter('facet', pString);
     $this->view->setVar("facet_name", $ps_facet_name);
     $this->view->setVar("key", $this->request->getParameter('key', pString));
     $ps_browse_type = $this->request->getParameter('browseType', pString);
     if (!($va_browse_info = caGetInfoForBrowseType($ps_browse_type))) {
         // invalid browse type – throw error
         die("Invalid browse type");
     }
     $this->view->setVar("browse_type", $ps_browse_type);
     $vs_class = $va_browse_info['table'];
     $o_browse = caGetBrowseInstance($vs_class);
     if (!is_array($va_facet_info = $o_browse->getInfoForFacet($ps_facet_name))) {
         return null;
     }
     if ($ps_cache_key = $this->request->getParameter('key', pString)) {
         $o_browse->reload($ps_cache_key);
     }
     $va_ancestors = array();
     switch ($va_facet_info['type']) {
         case 'attribute':
             // is it a list attribute?
             $t_element = new ca_metadata_elements();
             if ($t_element->load(array('element_code' => $va_facet_info['element_code']))) {
                 if ($t_element->get('datatype') == 3) {
                     // 3=list
                     if (!$pn_id) {
                         $t_list = new ca_lists();
                         $pn_id = $t_list->getRootListItemID($t_element->get('list_id'));
                     }
                     $t_item = new ca_list_items($pn_id);
                     if ($t_item->getPrimaryKey()) {
                         $vs_primary_key = $t_item->primaryKey();
                         $this->view->setVar("primary_key", $vs_primary_key);
                         $vs_display_fld = $t_item->getLabelDisplayField();
                         $this->view->setVar("display_field", $vs_display_fld);
                         $vs_label_table_name = $t_item->getLabelTableName();
                         $va_ancestors = array_reverse($t_item->getHierarchyAncestors(null, array('includeSelf' => true, 'additionalTableToJoin' => $vs_label_table_name, 'additionalTableJoinType' => 'LEFT', 'additionalTableSelectFields' => array($vs_display_fld, 'locale_id'), 'additionalTableWheres' => array('(' . $vs_label_table_name . '.is_preferred = 1 OR ' . $vs_label_table_name . '.is_preferred IS NULL)'))));
                         array_shift($va_ancestors);
                     }
                 }
             }
             break;
         case 'label':
             // label facet
             $va_facet_info['table'] = $this->ops_tablename;
             // fall through to default case
         // fall through to default case
         default:
             $t_item = $this->request->datamodel->getInstanceByTableName($va_facet_info['table']);
             $t_item->load($pn_id);
             if (method_exists($t_item, "getHierarchyList")) {
                 $va_access_values = caGetUserAccessValues($this->request);
                 $va_facet = $o_browse->getFacet($ps_facet_name, array('sort' => 'name', 'checkAccess' => $va_access_values));
                 $va_hierarchy_list = $t_item->getHierarchyList(true);
                 $vn_hierarchies_in_use = 0;
                 foreach ($va_hierarchy_list as $vn_i => $va_item) {
                     if (isset($va_facet[$va_item[$t_item->primaryKey()]])) {
                         $vn_hierarchies_in_use++;
                         if ($vn_hierarchies_in_use > 1) {
                             break;
                         }
                     }
                 }
             }
             if ($t_item->getPrimaryKey()) {
                 $vs_primary_key = $t_item->primaryKey();
                 $this->view->setVar("primary_key", $vs_primary_key);
                 $vs_display_fld = $t_item->getLabelDisplayField();
                 $this->view->setVar("display_field", $vs_display_fld);
                 $vs_label_table_name = $t_item->getLabelTableName();
                 $va_ancestors = array_reverse($t_item->getHierarchyAncestors(null, array('includeSelf' => true, 'additionalTableToJoin' => $vs_label_table_name, 'additionalTableJoinType' => 'LEFT', 'additionalTableSelectFields' => array($vs_display_fld, 'locale_id'), 'additionalTableWheres' => array('(' . $vs_label_table_name . '.is_preferred = 1 OR ' . $vs_label_table_name . '.is_preferred IS NULL)'))));
             }
             if ($vn_hierarchies_in_use <= 1) {
                 array_shift($va_ancestors);
             }
             break;
     }
     $this->view->setVar('ancestors', $va_ancestors);
     switch ($this->request->getParameter('returnAs', pString)) {
         case "json":
             return $this->render('Browse/facet_hierarchy_ancestors_json.php');
             break;
             # ------------------------------------------------
         # ------------------------------------------------
         case "html":
         default:
             return $this->render('Browse/facet_hierarchy_ancestors_html.php');
             break;
             # ------------------------------------------------
     }
 }
 /**
  * Given a item_id (request parameter 'id') returns a list of ancestors for use in the hierarchy browser
  * Returned data is JSON format
  */
 public function getFacetHierarchyAncestorList()
 {
     $pn_id = $this->request->getParameter('id', pInteger);
     $ps_facet_name = $this->request->getParameter('facet', pString);
     if (!is_array($va_facet_info = $this->opo_browse->getInfoForFacet($ps_facet_name))) {
         return null;
     }
     $va_ancestors = array();
     switch ($va_facet_info['type']) {
         case 'attribute':
             // is it a list attribute?
             $t_element = new ca_metadata_elements();
             if ($t_element->load(array('element_code' => $va_facet_info['element_code']))) {
                 if ($t_element->get('datatype') == 3) {
                     // 3=list
                     if (!$pn_id) {
                         $t_list = new ca_lists();
                         $pn_id = $t_list->getRootListItemID($t_element->get('list_id'));
                     }
                     $t_item = new ca_list_items($pn_id);
                     if ($t_item->getPrimaryKey()) {
                         $va_ancestors = array_reverse($t_item->getHierarchyAncestors(null, array('includeSelf' => true, 'idsOnly' => true)));
                         array_shift($va_ancestors);
                     }
                 }
             }
             break;
         case 'label':
             // label facet
             $va_facet_info['table'] = $this->ops_tablename;
             // fall through to default case
         // fall through to default case
         default:
             $t_item = $this->opo_datamodel->getInstanceByTableName($va_facet_info['table']);
             $t_item->load($pn_id);
             if (method_exists($t_item, "getHierarchyList")) {
                 $va_access_values = caGetUserAccessValues($this->request);
                 $va_facet = $this->opo_browse->getFacet($ps_facet_name, array('sort' => 'name', 'checkAccess' => $va_access_values));
                 $va_hierarchy_list = $t_item->getHierarchyList(true);
                 $vn_hierarchies_in_use = 0;
                 foreach ($va_hierarchy_list as $vn_i => $va_item) {
                     if (isset($va_facet[$va_item[$t_item->primaryKey()]])) {
                         $vn_hierarchies_in_use++;
                         if ($vn_hierarchies_in_use > 1) {
                             break;
                         }
                     }
                 }
             }
             if ($t_item->getPrimaryKey()) {
                 $va_ancestors = array_reverse($t_item->getHierarchyAncestors(null, array('includeSelf' => true, 'idsOnly' => true)));
                 if (!is_array($va_ancestors)) {
                     $va_ancestors = array();
                 }
             }
             if ($vn_hierarchies_in_use <= 1) {
                 array_shift($va_ancestors);
             }
             break;
     }
     $this->view->setVar('ancestors', $va_ancestors);
     return $this->render('Browse/facet_hierarchy_ancestors_json.php');
 }
Exemple #19
0
 /**
  *
  */
 public static function getElementInstance($pm_element_code_or_id)
 {
     if (isset(ca_attributes::$s_ca_attributes_element_instance_cache[$pm_element_code_or_id])) {
         return ca_attributes::$s_ca_attributes_element_instance_cache[$pm_element_code_or_id];
     }
     //require_once(__CA_MODELS_DIR__.'/ca_metadata_elements.php');	// defer inclusion until runtime to ensure baseclasses are already loaded, otherwise you get circular dependencies
     $t_element = new ca_metadata_elements();
     if (!is_numeric($pm_element_code_or_id)) {
         if ($t_element->load(array('element_code' => $pm_element_code_or_id))) {
             return ca_attributes::$s_ca_attributes_element_instance_cache[$pm_element_code_or_id] = ca_attributes::$s_ca_attributes_element_instance_cache[$t_element->getPrimaryKey()] = $t_element;
         }
     }
     if ($t_element->load($pm_element_code_or_id)) {
         return ca_attributes::$s_ca_attributes_element_instance_cache[$pm_element_code_or_id] = $t_element;
     } else {
         //$this->postError(1950, _t("Element code or id is invalid"), "ca_attributes::getElementInstance()");
         return false;
     }
 }
 /**
  * Returns list of valid template placeholders for the specified bundle. These placeholders always begin
  * with a caret ("^") and will be replaced with content values when a bundle display is rendered by xxx()
  * 
  * @param $ps_bundle_name - name of bundle
  * @return array - list of placeholders as keys; values are text description of value; will return null if bundle name is invalid
  */
 public function getTemplatePlaceholderListForBundle($ps_bundle_name)
 {
     $o_dm = $this->getAppDatamodel();
     $t_instance = null;
     $va_tmp = explode('.', $ps_bundle_name);
     switch (sizeof($va_tmp)) {
         case 2:
             $vs_table = $va_tmp[0];
             $vs_bundle = $va_tmp[1];
             if ($vs_bundle == 'rel') {
                 $vs_bundle = 'preferred_labels';
             }
             break;
         case 1:
             if (!($t_instance = $o_dm->getInstanceByTableName($va_tmp[0], true))) {
                 $vs_table = $o_dm->getTableName($this->get('table_num'));
                 $vs_bundle = $va_tmp[0];
             } else {
                 $vs_table = $va_tmp[0];
                 $vs_bundle = 'preferred_labels';
             }
             break;
         default:
             return null;
             break;
     }
     if (!$t_instance) {
         if (!($t_instance = $o_dm->getInstanceByTableName($vs_table, true))) {
             return null;
         }
     }
     $va_key = array('^label' => array('label' => _t('Placement label'), 'description' => _t('The label for this placement as defined in the placements settings. The value used will be adjusted to reflect the current user&apos;s locale.')));
     if ($t_instance->hasField($vs_bundle)) {
         // is intrinsic field
         $va_key["^{$vs_bundle}"] = array('label' => $t_instance->getFieldInfo($vs_bundle, 'LABEL'), 'description' => $t_instance->getFieldInfo($vs_bundle, 'DESCRIPTION'));
         return $va_key;
     }
     $va_element_codes = array_flip($t_instance->getApplicableElementCodes(null, false, false));
     if ($va_element_codes[$vs_bundle]) {
         $t_element = new ca_metadata_elements();
         if ($this->inTransaction()) {
             $t_element->setTransaction($this->getTransaction());
         }
         if ($t_element->load(array('element_code' => $vs_bundle))) {
             // is attribute
             $va_hier = $t_element->getElementsInSet();
             if (is_array($va_hier) && sizeof($va_hier) > 0) {
                 // is container with children
                 foreach ($va_hier as $va_node) {
                     if ($va_node['datatype'] == 0) {
                         continue;
                     }
                     // skip containers
                     $va_key['^' . $va_node['element_code']] = array('label' => $t_instance->getAttributeLabel($va_node['element_code']), 'description' => $t_instance->getAttributeDescription($va_node['element_code']));
                 }
                 return $va_key;
             }
             // is simple single-element attribute
             $va_key["^{$vs_bundle}"] = array('label' => $t_instance->getAttributeLabel($vs_bundle), 'description' => $t_instance->getAttributeDescription($vs_bundle));
             return $va_key;
         }
     }
     if ($vs_bundle == 'preferred_labels') {
         if ($t_label = $t_instance->getLabelTableInstance()) {
             foreach ($t_label->getFormFields() as $vs_field => $va_field_info) {
                 $va_key['^' . $vs_field] = array('label' => $va_field_info['LABEL'], 'description' => $va_field_info['DESCRIPTION']);
             }
             return $va_key;
         }
     }
     if ($vs_bundle == 'nonpreferred_labels') {
         if ($t_label = $t_instance->getLabelTableInstance()) {
             foreach ($t_label->getFormFields() as $vs_field => $va_field_info) {
                 $va_key['^' . $vs_field] = array('label' => $va_field_info['LABEL'], 'description' => $va_field_info['DESCRIPTION']);
             }
             return $va_key;
         }
     }
     if ($vs_bundle == 'ca_object_representations') {
         if ($vs_table == 'ca_objects') {
             $t_rep = new ca_object_representations();
             foreach ($t_rep->getFormFields() as $vs_field => $va_field_info) {
                 $va_key['^' . $vs_field] = array('label' => $va_field_info['LABEL'], 'description' => $va_field_info['DESCRIPTION']);
             }
             $va_key['^media:{version name}'] = array('label' => _t('Specified version of media'), 'description' => _t('The version of the media representation specified by {version name} for display.'));
             return $va_key;
         }
     }
     return null;
 }
 /**
  *
  */
 public function getElementsForForm($pa_options = null)
 {
     $va_placements = $this->getPlacements();
     $t_element = new ca_metadata_elements();
     $va_elements = array();
     foreach ($va_placements as $vn_i => $va_placement) {
         $va_tmp = explode('.', $va_placement['bundle_name']);
         if ($t_element->load(array('element_code' => $va_tmp[1]))) {
             if ($t_element->get('datatype') > 0) {
                 $va_elements[] = $va_placement['bundle_name'];
             }
             if (sizeof($va_sub_elements = $t_element->getElementsInSet()) > 1) {
                 foreach ($va_sub_elements as $vn_element_id => $va_element_info) {
                     if ($va_tmp[1] == $va_element_info['element_code']) {
                         continue;
                     }
                     $va_elements[] = $va_tmp[0] . '.' . $va_element_info['element_code'];
                 }
             }
         } else {
             $va_elements[] = $va_placement['bundle_name'];
         }
     }
     return $va_elements;
 }
$vs_facet_name = $this->getVar('facet_name');
$va_facet_info = $this->getVar('facet_info');
$va_types = $this->getVar('type_list');
$va_relationship_types = $this->getVar('relationship_type_list');
if (!is_array($va_other_params = $this->getVar('other_parameters'))) {
    $va_other_params = array();
}
$vs_grouping_field = $this->getVar('grouping');
if ((!isset($va_facet_info['groupings'][$vs_grouping_field]) || !$va_facet_info['groupings'][$vs_grouping_field]) && is_array($va_facet_info['groupings'])) {
    $va_tmp = array_keys($va_facet_info['groupings']);
    $vs_grouping_field = $va_tmp[0];
}
$vn_element_datatype = null;
if ($vs_grouping_attribute_element_code = preg_match('!^ca_attribute_([\\w]+)!', $vs_grouping_field, $va_matches) ? $va_matches[1] : null) {
    $t_element = new ca_metadata_elements();
    $t_element->load(array('element_code' => $vs_grouping_attribute_element_code));
    $vn_grouping_attribute_id = $t_element->getPrimaryKey();
    $vn_element_datatype = $t_element->get('datatype');
}
$vs_group_mode = $this->getVar('group_mode');
if (!$va_facet || !$vs_facet_name) {
    print 'No facet defined';
    return;
}
if (!($vm_modify_id = $this->getVar('modify'))) {
    $vm_modify_id = '0';
}
?>
<div id="facetList">
<div id="facetTitle">
	<?php 
Exemple #23
0
 private function _doQueriesForSqlSearch($po_rewritten_query, $pn_subject_tablenum, $ps_dest_table, $pn_level = 0, $pa_options = null)
 {
     // query is always of type Zend_Search_Lucene_Search_Query_Boolean
     $vn_i = 0;
     switch (get_class($po_rewritten_query)) {
         case 'Zend_Search_Lucene_Search_Query_MultiTerm':
             $va_elements = $po_rewritten_query->getTerms();
             break;
         default:
             $va_elements = $po_rewritten_query->getSubqueries();
             break;
     }
     $o_base = new SearchBase();
     $va_old_signs = $po_rewritten_query->getSigns();
     foreach ($va_elements as $o_lucene_query_element) {
         $vb_is_blank_search = false;
         if (is_null($va_old_signs)) {
             // if array is null then according to Zend Lucene all subqueries should be "are required"... so we AND them
             $vs_op = "AND";
         } else {
             if (is_null($va_old_signs[$vn_i])) {
                 // is the sign for a particular query is null then OR is (it is "neither required nor prohibited")
                 $vs_op = 'OR';
             } else {
                 $vs_op = $va_old_signs[$vn_i] === false ? 'NOT' : 'AND';
                 // true sign indicated "required" (AND) operation, false indicated "prohibited" (NOT) operation
             }
         }
         if ($vn_i == 0) {
             $vs_op = 'OR';
         }
         $va_direct_query_temp_tables = array();
         // List of temporary tables created by direct search queries; tables listed here are dropped at the end of processing for the query element
         $pa_direct_sql_query_params = null;
         // set to array with values to use with direct SQL query placeholders or null to pass single standard table_num value as param (most queries just need this single value)
         $vs_direct_sql_query = null;
         $vn_direct_sql_target_table_num = $pn_subject_tablenum;
         switch ($vs_class = get_class($o_lucene_query_element)) {
             case 'Zend_Search_Lucene_Search_Query_Boolean':
             case 'Zend_Search_Lucene_Search_Query_MultiTerm':
                 $this->_createTempTable('ca_sql_search_temp_' . $pn_level);
                 if ($vs_op == 'AND' && $vn_i == 0) {
                     $this->_doQueriesForSqlSearch($o_lucene_query_element, $pn_subject_tablenum, $ps_dest_table, $pn_level + 1);
                 } else {
                     $this->_doQueriesForSqlSearch($o_lucene_query_element, $pn_subject_tablenum, 'ca_sql_search_temp_' . $pn_level, $pn_level + 1);
                 }
                 // merge with current destination
                 switch ($vs_op) {
                     case 'AND':
                         if ($vn_i > 0) {
                             $this->_createTempTable("{$ps_dest_table}_acc");
                             $vs_sql = "\n\t\t\t\t\t\t\t\t\tINSERT IGNORE INTO {$ps_dest_table}_acc\n\t\t\t\t\t\t\t\t\tSELECT mfs.row_id, SUM(mfs.boost)\n\t\t\t\t\t\t\t\t\tFROM {$ps_dest_table} mfs\n\t\t\t\t\t\t\t\t\tINNER JOIN ca_sql_search_temp_{$pn_level} AS ftmp1 ON ftmp1.row_id = mfs.row_id\n\t\t\t\t\t\t\t\t\tGROUP BY mfs.row_id\n\t\t\t\t\t\t\t\t";
                             $qr_res = $this->opo_db->query($vs_sql);
                             $qr_res = $this->opo_db->query("TRUNCATE TABLE {$ps_dest_table}");
                             $qr_res = $this->opo_db->query("INSERT INTO {$ps_dest_table} SELECT row_id, boost FROM {$ps_dest_table}_acc");
                             $this->_dropTempTable("{$ps_dest_table}_acc");
                         }
                         break;
                     case 'NOT':
                         $qr_res = $this->opo_db->query("SELECT row_id FROM ca_sql_search_temp_{$pn_level}");
                         if (is_array($va_ids = $qr_res->getAllFieldValues()) && sizeof($va_ids)) {
                             $vs_sql = "\n\t\t\t\t\t\t\t\t\tDELETE FROM {$ps_dest_table} WHERE row_id IN (?)\n\t\t\t\t\t\t\t\t";
                             $qr_res = $this->opo_db->query($vs_sql, array($va_ids));
                         }
                         break;
                     default:
                     case 'OR':
                         $vs_sql = "\n\t\t\t\t\t\t\t\tINSERT IGNORE INTO {$ps_dest_table}\n\t\t\t\t\t\t\t\tSELECT row_id, SUM(boost)\n\t\t\t\t\t\t\t\tFROM ca_sql_search_temp_{$pn_level}\n\t\t\t\t\t\t\t\tGROUP BY row_id\n\t\t\t\t\t\t\t";
                         $qr_res = $this->opo_db->query($vs_sql);
                         break;
                 }
                 $vn_i++;
                 $this->_dropTempTable('ca_sql_search_temp_' . $pn_level);
                 break;
             case 'Zend_Search_Lucene_Search_Query_Term':
             case 'Zend_Search_Lucene_Index_Term':
             case 'Zend_Search_Lucene_Search_Query_Phrase':
             case 'Zend_Search_Lucene_Search_Query_Range':
                 $va_ft_terms = array();
                 $va_ft_like_terms = array();
                 $va_ft_stem_terms = array();
                 $vs_access_point = '';
                 $va_raw_terms = array();
                 switch (get_class($o_lucene_query_element)) {
                     case 'Zend_Search_Lucene_Search_Query_Range':
                         $va_lower_term = $o_lucene_query_element->getLowerTerm();
                         $va_upper_term = $o_lucene_query_element->getUpperTerm();
                         $va_element = $this->_getElementIDForAccessPoint($pn_subject_tablenum, $va_lower_term->field);
                         $vn_direct_sql_target_table_num = $va_element['table_num'];
                         $va_indexed_fields = $o_base->getFieldsToIndex($pn_subject_tablenum, $vn_direct_sql_target_table_num);
                         $vn_root_element_id = $va_element['element_info']['hier_element_id'];
                         if (!isset($va_indexed_fields['_ca_attribute_' . $va_element['element_id']]) && (!$vn_root_element_id || $vn_root_element_id && !isset($va_indexed_fields['_ca_attribute_' . $vn_root_element_id]))) {
                             break 2;
                         }
                         // skip if not indexed
                         switch ($va_element['datatype']) {
                             case __CA_ATTRIBUTE_VALUE_GEOCODE__:
                                 $t_geocode = new GeocodeAttributeValue();
                                 $va_parsed_value = $t_geocode->parseValue('[' . $va_lower_term->text . ']', $va_element['element_info']);
                                 $vs_lower_lat = $va_parsed_value['value_decimal1'];
                                 $vs_lower_long = $va_parsed_value['value_decimal2'];
                                 $va_parsed_value = $t_geocode->parseValue('[' . $va_upper_term->text . ']', $va_element['element_info']);
                                 $vs_upper_lat = $va_parsed_value['value_decimal1'];
                                 $vs_upper_long = $va_parsed_value['value_decimal2'];
                                 // mysql BETWEEN always wants the lower value first ... BETWEEN 5 AND 3 wouldn't match 4 ... So we swap the values if necessary
                                 if ($vs_upper_lat < $vs_lower_lat) {
                                     $tmp = $vs_upper_lat;
                                     $vs_upper_lat = $vs_lower_lat;
                                     $vs_lower_lat = $tmp;
                                 }
                                 if ($vs_upper_long < $vs_lower_long) {
                                     $tmp = $vs_upper_long;
                                     $vs_upper_long = $vs_lower_long;
                                     $vs_lower_long = $tmp;
                                 }
                                 $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = " . intval($va_element['element_id']) . ") AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 BETWEEN " . floatval($vs_lower_lat) . " AND " . floatval($vs_upper_lat) . ")\n\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal2 BETWEEN " . floatval($vs_lower_long) . " AND " . floatval($vs_upper_long) . ")\t\n\t\t\t\t\t\t\t\t\t";
                                 break;
                             case __CA_ATTRIBUTE_VALUE_CURRENCY__:
                                 $t_cur = new CurrencyAttributeValue();
                                 $va_parsed_value = $t_cur->parseValue($va_lower_term->text, $va_element['element_info']);
                                 $vs_currency = preg_replace('![^A-Z0-9]+!', '', $va_parsed_value['value_longtext1']);
                                 $vn_lower_val = $va_parsed_value['value_decimal1'];
                                 $va_parsed_value = $t_cur->parseValue($va_upper_term->text, $va_element['element_info']);
                                 $vn_upper_val = $va_parsed_value['value_decimal1'];
                                 $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = " . intval($va_element['element_id']) . ") AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 BETWEEN " . floatval($vn_lower_val) . " AND " . floatval($vn_upper_val) . ")\n\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t(cav.value_longtext1 = '" . $this->opo_db->escape($vs_currency) . "')\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t";
                                 break;
                             case __CA_ATTRIBUTE_VALUE_TIMECODE__:
                                 $t_timecode = new TimecodeAttributeValue();
                                 $va_parsed_value = $t_timecode->parseValue($va_lower_term->text, $va_element['element_info']);
                                 $vn_lower_val = $va_parsed_value['value_decimal1'];
                                 $va_parsed_value = $t_timecode->parseValue($va_upper_term->text, $va_element['element_info']);
                                 $vn_upper_val = $va_parsed_value['value_decimal1'];
                                 break;
                             case __CA_ATTRIBUTE_VALUE_LENGTH__:
                                 $t_len = new LengthAttributeValue();
                                 $va_parsed_value = $t_len->parseValue($va_lower_term->text, $va_element['element_info']);
                                 $vn_lower_val = $va_parsed_value['value_decimal1'];
                                 $va_parsed_value = $t_len->parseValue($va_upper_term->text, $va_element['element_info']);
                                 $vn_upper_val = $va_parsed_value['value_decimal1'];
                                 break;
                             case __CA_ATTRIBUTE_VALUE_WEIGHT__:
                                 $t_weight = new WeightAttributeValue();
                                 $va_parsed_value = $t_weight->parseValue($va_lower_term->text, $va_element['element_info']);
                                 $vn_lower_val = $va_parsed_value['value_decimal1'];
                                 $va_parsed_value = $t_weight->parseValue($va_upper_term->text, $va_element['element_info']);
                                 $vn_upper_val = $va_parsed_value['value_decimal1'];
                                 break;
                             case __CA_ATTRIBUTE_VALUE_INTEGER__:
                                 $vn_lower_val = intval($va_lower_term->text);
                                 $vn_upper_val = intval($va_upper_term->text);
                                 $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = " . intval($va_element['element_id']) . ") AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t(cav.value_integer1 BETWEEN " . floatval($vn_lower_val) . " AND " . floatval($vn_upper_val) . ")\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t";
                                 break;
                             case __CA_ATTRIBUTE_VALUE_NUMERIC__:
                                 $vn_lower_val = floatval($va_lower_term->text);
                                 $vn_upper_val = floatval($va_upper_term->text);
                                 break;
                         }
                         if (!$vs_direct_sql_query) {
                             $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t(cav.element_id = " . intval($va_element['element_id']) . ") AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 BETWEEN " . floatval($vn_lower_val) . " AND " . floatval($vn_upper_val) . ")\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t";
                         }
                         break;
                     case 'Zend_Search_Lucene_Search_Query_Phrase':
                         if ($this->getOption('strictPhraseSearching')) {
                             $vs_search_tokenizer_regex = $this->opo_search_config->get('search_tokenizer_regex');
                             $va_words = array();
                             foreach ($o_lucene_query_element->getQueryTerms() as $o_term) {
                                 if (!$vs_access_point && ($vs_field = $o_term->field)) {
                                     $vs_access_point = $vs_field;
                                 }
                                 $va_terms = preg_split("![{$vs_search_tokenizer_regex}]+!u", (string) $o_term->text);
                                 $va_raw_terms[] = (string) $o_term->text;
                                 foreach ($va_terms as $vs_term) {
                                     if (strlen($vs_escaped_text = $this->opo_db->escape($vs_term))) {
                                         $va_words[] = $vs_escaped_text;
                                     }
                                 }
                             }
                             if (!sizeof($va_words)) {
                                 continue 3;
                             }
                             $va_ap_tmp = explode(".", $vs_access_point);
                             $vn_fld_table = $vn_fld_num = null;
                             if (sizeof($va_ap_tmp) >= 2) {
                                 $va_element = $this->_getElementIDForAccessPoint($pn_subject_tablenum, $vs_access_point);
                                 if ($va_element) {
                                     $vs_fld_num = $va_element['field_num'];
                                     $vs_fld_table_num = $va_element['table_num'];
                                     $vs_fld_limit_sql = " AND (swi.field_table_num = {$vs_fld_table_num} AND swi.field_num = '{$vs_fld_num}')";
                                     if (is_array($va_element['relationship_type_ids']) && sizeof($va_element['relationship_type_ids'])) {
                                         $vs_fld_limit_sql .= " AND (swi.rel_type_id IN (" . join(",", $va_element['relationship_type_ids']) . "))";
                                     }
                                 }
                             }
                             $va_temp_tables = array();
                             $vn_w = 0;
                             foreach ($va_words as $vs_word) {
                                 $vn_w++;
                                 $vs_temp_table = 'ca_sql_search_phrase_' . md5($pn_subject_tablenum . "/" . $vs_word . "/" . $vn_w);
                                 $this->_createTempTable($vs_temp_table);
                                 $vs_sql = "\n\t\t\t\t\t\t\t\t\tINSERT INTO {$vs_temp_table}\n\t\t\t\t\t\t\t\t\tSELECT swi.index_id + 1, 1\n\t\t\t\t\t\t\t\t\tFROM ca_sql_search_words sw \n\t\t\t\t\t\t\t\t\tINNER JOIN ca_sql_search_word_index AS swi ON sw.word_id = swi.word_id \n\t\t\t\t\t\t\t\t\t" . (sizeof($va_temp_tables) ? " INNER JOIN " . $va_temp_tables[sizeof($va_temp_tables) - 1] . " AS tt ON swi.index_id = tt.row_id" : "") . "\n\t\t\t\t\t\t\t\t\tWHERE \n\t\t\t\t\t\t\t\t\t\tsw.word = ? AND swi.table_num = ? {$vs_fld_limit_sql}\n \t\t\t\t\t\t\t\t\t\t" . ($this->getOption('omitPrivateIndexing') ? " AND swi.access = 0" : '') . "\n\t\t\t\t\t\t\t\t";
                                 $qr_res = $this->opo_db->query($vs_sql, $vs_word, (int) $pn_subject_tablenum);
                                 $qr_count = $this->opo_db->query("SELECT count(*) c FROM {$vs_temp_table}");
                                 if (!$qr_count->nextRow() || !(int) $qr_count->get('c')) {
                                     foreach ($va_temp_tables as $vs_temp_table) {
                                         $this->_dropTempTable($vs_temp_table);
                                     }
                                     break 2;
                                 }
                                 $va_temp_tables[] = $vs_temp_table;
                             }
                             $vs_results_temp_table = array_pop($va_temp_tables);
                             $this->opo_db->query("UPDATE {$vs_results_temp_table} SET row_id = row_id - 1");
                             $va_direct_query_temp_tables[$vs_results_temp_table] = true;
                             $vs_direct_sql_query = "SELECT swi.row_id, ca.boost \n\t\t\t\t\t\t\t\t\t\t\t\t\tFROM {$vs_results_temp_table} ca\n\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_sql_search_word_index AS swi ON swi.index_id = ca.row_id \n\t\t\t\t\t\t\t";
                             $pa_direct_sql_query_params = array();
                             // don't pass any params
                             foreach ($va_temp_tables as $vs_temp_table) {
                                 $this->_dropTempTable($vs_temp_table);
                             }
                             break;
                         }
                     default:
                         switch ($vs_class) {
                             case 'Zend_Search_Lucene_Search_Query_Phrase':
                                 $va_term_objs = $o_lucene_query_element->getQueryTerms();
                                 break;
                             case 'Zend_Search_Lucene_Index_Term':
                                 $va_term_objs = array($o_lucene_query_element);
                                 break;
                             default:
                                 $va_term_objs = array($o_lucene_query_element->getTerm());
                                 break;
                         }
                         foreach ($va_term_objs as $o_term) {
                             $va_access_point_info = $this->_getElementIDForAccessPoint($pn_subject_tablenum, $o_term->field);
                             $vs_access_point = $va_access_point_info['access_point'];
                             $vs_term = $o_term->text;
                             if ($vs_access_point && mb_strtoupper($vs_term) == _t('[BLANK]')) {
                                 $t_ap = $this->opo_datamodel->getInstanceByTableNum($va_access_point_info['table_num'], true);
                                 if (is_a($t_ap, 'BaseLabel')) {
                                     // labels have the literal text "[Blank]" indexed to "blank" to indicate blank-ness
                                     $vb_is_blank_search = false;
                                     $vs_term = _t('blank');
                                 } else {
                                     $vb_is_blank_search = true;
                                     break;
                                 }
                             }
                             $va_terms = array($vs_term);
                             //$this->_tokenize($vs_term, true, $vn_i);
                             $vb_has_wildcard = (bool) preg_match('!\\*$!', $vs_term);
                             $vb_output_term = false;
                             foreach ($va_terms as $vs_term) {
                                 if ($vb_has_wildcard) {
                                     $vs_term .= '*';
                                 }
                                 if (in_array(trim(mb_strtolower($vs_term, 'UTF-8')), WLPlugSearchEngineSqlSearch::$s_stop_words)) {
                                     continue;
                                 }
                                 $vs_stripped_term = preg_replace('!\\*+$!u', '', $vs_term);
                                 if ($vb_has_wildcard) {
                                     $va_ft_like_terms[] = $vs_stripped_term;
                                 } else {
                                     // do stemming
                                     $vb_do_stemming = $this->opb_do_stemming;
                                     if (mb_substr($vs_term, -1) == '|') {
                                         $vs_term = mb_substr($vs_term, 0, mb_strlen($vs_term) - 1);
                                         $vb_do_stemming = false;
                                     }
                                     if ($vb_do_stemming) {
                                         $vs_to_stem = preg_replace('!\\*$!u', '', $vs_term);
                                         if (!preg_match('!y$!u', $vs_to_stem) && !preg_match('![0-9]+!', $vs_to_stem)) {
                                             // don't stem things ending in 'y' as that can cause problems (eg "Bowery" becomes "Boweri")
                                             if (!($vs_stem = trim($this->opo_stemmer->stem($vs_to_stem)))) {
                                                 $vs_stem = (string) $vs_term;
                                             }
                                             $va_ft_stem_terms[] = "'" . $this->opo_db->escape($vs_stem) . "'";
                                         } else {
                                             $va_ft_terms[] = '"' . $this->opo_db->escape($vs_term) . '"';
                                         }
                                     } else {
                                         $va_ft_terms[] = '"' . $this->opo_db->escape($vs_term) . '"';
                                     }
                                 }
                                 $vb_output_term = true;
                             }
                             if ($vb_output_term) {
                                 $va_raw_terms[] = $vs_term;
                             }
                         }
                         break;
                 }
                 $vs_fld_num = $vs_table_num = $t_table = null;
                 $vb_ft_bit_optimization = false;
                 if ($vs_access_point) {
                     list($vs_table, $vs_field, $vs_sub_field) = explode('.', $vs_access_point);
                     if (in_array($vs_table, array('created', 'modified'))) {
                         $o_tep = new TimeExpressionParser();
                         $vs_date = join(' ', $va_raw_terms);
                         if (!$o_tep->parse($vs_date)) {
                             break;
                         }
                         $va_range = $o_tep->getUnixTimestamps();
                         $vn_user_id = null;
                         if ($vs_field = trim($vs_field)) {
                             if (!is_int($vs_field)) {
                                 $t_user = new ca_users();
                                 if ($t_user->load(array("user_name" => $vs_field))) {
                                     $vn_user_id = (int) $t_user->getPrimaryKey();
                                 }
                             } else {
                                 $vn_user_id = (int) $vs_field;
                             }
                         }
                         $vs_user_sql = $vn_user_id ? " AND (ccl.user_id = " . (int) $vn_user_id . ")" : "";
                         switch ($vs_table) {
                             case 'created':
                                 $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\tSELECT ccl.logged_row_id row_id, 1\n\t\t\t\t\t\t\t\t\t\t\tFROM ca_change_log ccl\n\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t(ccl.log_datetime BETWEEN " . (int) $va_range['start'] . " AND " . (int) $va_range['end'] . ")\n\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t(ccl.logged_table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t(ccl.changetype = 'I')\n\t\t\t\t\t\t\t\t\t\t\t\t{$vs_user_sql}\n\t\t\t\t\t\t\t\t\t\t";
                                 break;
                             case 'modified':
                                 $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\tSELECT ccl.logged_row_id row_id, 1\n\t\t\t\t\t\t\t\t\t\t\tFROM ca_change_log ccl\n\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t(ccl.log_datetime BETWEEN " . (int) $va_range['start'] . " AND " . (int) $va_range['end'] . ")\n\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t(ccl.logged_table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t(ccl.changetype = 'U')\n\t\t\t\t\t\t\t\t\t\t\t\t{$vs_user_sql}\n\t\t\t\t\t\t\t\t\t\tUNION\n\t\t\t\t\t\t\t\t\t\t\tSELECT ccls.subject_row_id row_id, 1\n\t\t\t\t\t\t\t\t\t\t\tFROM ca_change_log ccl\n\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_change_log_subjects AS ccls ON ccls.log_id = ccl.log_id\n\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t(ccl.log_datetime BETWEEN " . (int) $va_range['start'] . " AND " . (int) $va_range['end'] . ")\n\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t(ccls.subject_table_num = {$pn_subject_tablenum})\n\t\t\t\t\t\t\t\t\t\t\t\t{$vs_user_sql}\n\t\t\t\t\t\t\t\t\t\t";
                                 break;
                         }
                     } else {
                         if ($vs_table && $vs_field && ($t_table = $this->opo_datamodel->getInstanceByTableName($vs_table, true))) {
                             $vs_table_num = $t_table->tableNum();
                             if (is_numeric($vs_field)) {
                                 $vs_fld_num = 'I' . $vs_field;
                                 $vn_fld_num = (int) $vs_field;
                             } else {
                                 $vn_fld_num = $this->getFieldNum($vs_table, $vs_field);
                                 $vs_fld_num = 'I' . $vn_fld_num;
                                 $vn_direct_sql_target_table_num = $vs_table_num;
                                 if (!strlen($vn_fld_num)) {
                                     $t_element = new ca_metadata_elements();
                                     if ($t_element->load(array('element_code' => $vs_sub_field ? $vs_sub_field : $vs_field))) {
                                         $va_indexed_fields = $o_base->getFieldsToIndex($pn_subject_tablenum, $vn_direct_sql_target_table_num);
                                         $vn_fld_num = $t_element->getPrimaryKey();
                                         $vn_root_element_id = $t_element->get('hier_element_id');
                                         if (!isset($va_indexed_fields['_ca_attribute_' . $vn_fld_num]) && (!$vn_root_element_id || $vn_root_element_id && !isset($va_indexed_fields['_ca_attribute_' . $vn_root_element_id]))) {
                                             break 2;
                                         }
                                         // skip if not indexed
                                         $vs_fld_num = 'A' . $vn_fld_num;
                                         if (!$vb_is_blank_search) {
                                             //
                                             // For certain types of attributes we can directly query the
                                             // attributes in the database rather than using the full text index
                                             // This allows us to do "intelligent" querying... for example on date ranges
                                             // parsed from natural language input and for length dimensions using unit conversion
                                             //
                                             switch ($t_element->get('datatype')) {
                                                 case __CA_ATTRIBUTE_VALUE_DATERANGE__:
                                                     $vb_all_numbers = true;
                                                     foreach ($va_raw_terms as $vs_term) {
                                                         if (!is_numeric($vs_term)) {
                                                             $vb_all_numbers = false;
                                                             break;
                                                         }
                                                     }
                                                     $vs_raw_term = join(' ', $va_raw_terms);
                                                     $vb_exact = $vs_raw_term[0] == "#" ? true : false;
                                                     // dates prepended by "#" are considered "exact" or "contained - the matched dates must be wholly contained by the search term
                                                     if ($vb_exact) {
                                                         $vs_raw_term = substr($vs_raw_term, 1);
                                                         if ($this->opo_tep->parse($vs_raw_term)) {
                                                             $va_dates = $this->opo_tep->getHistoricTimestamps();
                                                             $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 BETWEEN " . floatval($va_dates['start']) . " AND " . floatval($va_dates['end']) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal2 BETWEEN " . floatval($va_dates['start']) . " AND " . floatval($va_dates['end']) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                         }
                                                     } else {
                                                         if ($this->opo_tep->parse($vs_raw_term)) {
                                                             $va_dates = $this->opo_tep->getHistoricTimestamps();
                                                             $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 BETWEEN " . floatval($va_dates['start']) . " AND " . floatval($va_dates['end']) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal2 BETWEEN " . floatval($va_dates['start']) . " AND " . floatval($va_dates['end']) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 <= " . floatval($va_dates['start']) . " AND cav.value_decimal2 >= " . floatval($va_dates['end']) . ")\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                         }
                                                     }
                                                     break;
                                                 case __CA_ATTRIBUTE_VALUE_GEOCODE__:
                                                     // At this point $va_raw_terms has been tokenized by Lucene into oblivion
                                                     // and is also dependent on the search_tokenizer_regex so we can't really do anything with it.
                                                     // We now build our own un-tokenized term array instead. caParseGISSearch() can handle it.
                                                     $va_gis_terms = array();
                                                     foreach ($o_lucene_query_element->getQueryTerms() as $o_term) {
                                                         $va_gis_terms[] = trim((string) $o_term->text);
                                                     }
                                                     if ($va_coords = caParseGISSearch(join(' ', $va_gis_terms))) {
                                                         $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 BETWEEN {$va_coords['min_latitude']} AND {$va_coords['max_latitude']})\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal2 BETWEEN {$va_coords['min_longitude']} AND {$va_coords['max_longitude']})\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                     }
                                                     break;
                                                 case __CA_ATTRIBUTE_VALUE_CURRENCY__:
                                                     $t_cur = new CurrencyAttributeValue();
                                                     $va_parsed_value = $t_cur->parseValue(join(' ', $va_raw_terms), $t_element->getFieldValuesArray());
                                                     $vn_amount = $va_parsed_value['value_decimal1'];
                                                     $vs_currency = preg_replace('![^A-Z0-9]+!', '', $va_parsed_value['value_longtext1']);
                                                     $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 = " . floatval($vn_amount) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_longtext1 = '" . $this->opo_db->escape($vs_currency) . "')\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                     break;
                                                 case __CA_ATTRIBUTE_VALUE_LENGTH__:
                                                     // If it looks like a dimension that has been tokenized by Lucene
                                                     // into oblivion rehydrate it here.
                                                     try {
                                                         switch (sizeof($va_raw_terms)) {
                                                             case 2:
                                                                 $vs_dimension = $va_raw_terms[0] . caGetDecimalSeparator() . $va_raw_terms[1];
                                                                 break;
                                                             case 3:
                                                                 $vs_dimension = $va_raw_terms[0] . caGetDecimalSeparator() . $va_raw_terms[1] . " " . $va_raw_terms[2];
                                                                 break;
                                                             default:
                                                                 $vs_dimension = join(' ', $va_raw_terms);
                                                         }
                                                         $vo_parsed_measurement = caParseLengthDimension($vs_dimension);
                                                         $vn_len = $vo_parsed_measurement->convertTo('METER', 6, 'en_US');
                                                     } catch (Exception $e) {
                                                         $vs_direct_sql_query = null;
                                                         break;
                                                     }
                                                     $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 = " . floatval($vn_len) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                     break;
                                                 case __CA_ATTRIBUTE_VALUE_WEIGHT__:
                                                     // If it looks like a weight that has been tokenized by Lucene
                                                     // into oblivion rehydrate it here.
                                                     try {
                                                         switch (sizeof($va_raw_terms)) {
                                                             case 2:
                                                                 $vs_dimension = $va_raw_terms[0] . caGetDecimalSeparator() . $va_raw_terms[1];
                                                                 break;
                                                             case 3:
                                                                 $vs_dimension = $va_raw_terms[0] . caGetDecimalSeparator() . $va_raw_terms[1] . " " . $va_raw_terms[2];
                                                                 break;
                                                             default:
                                                                 $vs_dimension = join(' ', $va_raw_terms);
                                                         }
                                                         $vo_parsed_measurement = caParseWeightDimension($vs_dimension);
                                                         $vn_weight = $vo_parsed_measurement->convertTo('KILOGRAM', 6, 'en_US');
                                                     } catch (Exception $e) {
                                                         $vs_direct_sql_query = null;
                                                         break;
                                                     }
                                                     $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 = " . floatval($vn_weight) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                     break;
                                                 case __CA_ATTRIBUTE_VALUE_TIMECODE__:
                                                     $t_timecode = new TimecodeAttributeValue();
                                                     $va_parsed_value = $t_timecode->parseValue(join(' ', $va_raw_terms), $t_element->getFieldValuesArray());
                                                     $vn_timecode = $va_parsed_value['value_decimal1'];
                                                     $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 = " . floatval($vn_timecode) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                     break;
                                                 case __CA_ATTRIBUTE_VALUE_INTEGER__:
                                                     $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_integer1 = " . intval(array_shift($va_raw_terms)) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                     break;
                                                 case __CA_ATTRIBUTE_VALUE_NUMERIC__:
                                                     $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 = " . floatval(array_shift($va_raw_terms)) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                     break;
                                             }
                                         }
                                     } else {
                                         // neither table fields nor elements, i.e. 'virtual' fields like _count should
                                         $vn_fld_num = false;
                                         $vs_fld_num = $vs_field;
                                     }
                                 }
                             }
                             if (($vs_intrinsic_field_name = $t_table->fieldName($vn_fld_num)) && ($vn_intrinsic_type = $t_table->getFieldInfo($vs_intrinsic_field_name, 'FIELD_TYPE')) == FT_BIT) {
                                 $vb_ft_bit_optimization = true;
                             } elseif ($vn_intrinsic_type == FT_HISTORIC_DATERANGE) {
                                 $vb_all_numbers = true;
                                 foreach ($va_raw_terms as $vs_term) {
                                     if (!is_numeric($vs_term)) {
                                         $vb_all_numbers = false;
                                         break;
                                     }
                                 }
                                 $vs_date_start_fld = $t_table->getFieldInfo($vs_intrinsic_field_name, 'START');
                                 $vs_date_end_fld = $t_table->getFieldInfo($vs_intrinsic_field_name, 'END');
                                 $vs_raw_term = join(' ', $va_raw_terms);
                                 $vb_exact = $vs_raw_term[0] == "#" ? true : false;
                                 // dates prepended by "#" are considered "exact" or "contained - the matched dates must be wholly contained by the search term
                                 if ($vb_exact) {
                                     $vs_raw_term = substr($vs_raw_term, 1);
                                     if ($this->opo_tep->parse($vs_raw_term)) {
                                         $va_dates = $this->opo_tep->getHistoricTimestamps();
                                         $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\tSELECT " . $t_table->primaryKey() . ", 1\n\t\t\t\t\t\t\t\t\t\t\t\tFROM " . $t_table->tableName() . "\n\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t({$vs_date_start_fld} BETWEEN " . floatval($va_dates['start']) . " AND " . floatval($va_dates['end']) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t({$vs_date_end_fld} BETWEEN " . floatval($va_dates['start']) . " AND " . floatval($va_dates['end']) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t";
                                     }
                                 } else {
                                     if ($this->opo_tep->parse($vs_raw_term)) {
                                         $va_dates = $this->opo_tep->getHistoricTimestamps();
                                         $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\tSELECT " . $t_table->primaryKey() . ", 1\n\t\t\t\t\t\t\t\t\t\t\t\tFROM " . $t_table->tableName() . "\n\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t({$vs_date_start_fld} BETWEEN " . floatval($va_dates['start']) . " AND " . floatval($va_dates['end']) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t({$vs_date_end_fld} BETWEEN " . floatval($va_dates['start']) . " AND " . floatval($va_dates['end']) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t({$vs_date_start_fld} <= " . floatval($va_dates['start']) . " AND {$vs_date_end_fld} >= " . floatval($va_dates['end']) . ")\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t";
                                     }
                                 }
                                 $pa_direct_sql_query_params = array();
                             }
                         }
                     }
                 }
                 //
                 // If we're querying on the fulltext index then we need to construct
                 // the query here... if we already have a direct SQL query to run then we can skip this
                 //
                 if ($vb_is_blank_search) {
                     $va_sql_where[] = "((swi.field_table_num = " . intval($vs_table_num) . ") AND (swi.field_num = '{$vs_fld_num}') AND (swi.word_id = 0))";
                     if (!sizeof($va_sql_where)) {
                         continue;
                     }
                     $vs_sql_where = join(' OR ', $va_sql_where);
                 } elseif (!$vs_direct_sql_query) {
                     $va_sql_where = array();
                     if (sizeof($va_ft_terms)) {
                         if ($t_table && strlen($vs_fld_num) > 1) {
                             $va_sql_where[] = "((swi.field_table_num = " . intval($vs_table_num) . ") AND (swi.field_num = '{$vs_fld_num}') AND (sw.word IN (" . join(',', $va_ft_terms) . ")))";
                         } else {
                             if (sizeof($va_ft_terms) == 1) {
                                 $va_sql_where[] = "(sw.word = " . $va_ft_terms[0] . ")";
                             } else {
                                 $va_sql_where[] = "(sw.word IN (" . join(',', $va_ft_terms) . "))";
                             }
                         }
                     }
                     if (sizeof($va_ft_like_terms)) {
                         $va_tmp = array();
                         foreach ($va_ft_like_terms as $vs_term) {
                             if ($vb_ft_bit_optimization) {
                                 $va_tmp[] = '(sw.word = \' ' . $this->opo_db->escape(trim($vs_term)) . ' \')';
                             } else {
                                 $va_tmp[] = '(sw.word LIKE \'' . $this->opo_db->escape(trim($vs_term)) . '%\')';
                             }
                         }
                         if ($t_table && strlen($vs_fld_num) > 1) {
                             $va_sql_where[] = "((swi.field_table_num = " . intval($vs_table_num) . ") AND (swi.field_num = '{$vs_fld_num}') AND (" . join(' AND ', $va_tmp) . "))";
                         } else {
                             $va_sql_where[] = "(" . join(' AND ', $va_tmp) . ")";
                         }
                     }
                     if (sizeof($va_ft_stem_terms)) {
                         if ($t_table && strlen($vs_fld_num) > 1) {
                             $va_sql_where[] = "((swi.field_table_num = " . intval($vs_table_num) . ") AND (swi.field_num = '{$vs_fld_num}') AND (sw.stem IN (" . join(',', $va_ft_stem_terms) . ")))";
                         } else {
                             $va_sql_where[] = "(sw.stem IN (" . join(',', $va_ft_stem_terms) . "))";
                         }
                     }
                     if (!sizeof($va_sql_where)) {
                         continue;
                     }
                     $vs_sql_where = join(' OR ', $va_sql_where);
                 } else {
                     $va_ft_terms = $va_ft_like_terms = $va_ft_like_terms = array();
                 }
                 $vs_rel_type_id_sql = null;
                 if (is_array($va_access_point_info['relationship_type_ids']) && sizeof($va_access_point_info['relationship_type_ids'])) {
                     $vs_rel_type_id_sql = " AND (swi.rel_type_id IN (" . join(",", $va_access_point_info['relationship_type_ids']) . "))";
                 }
                 if (!$vs_fld_num && is_array($va_restrict_to_fields = caGetOption('restrictSearchToFields', $pa_options, null)) && sizeof($va_restrict_to_fields)) {
                     $va_field_restrict_sql = array();
                     foreach ($va_restrict_to_fields as $va_restrict) {
                         $va_field_restrict_sql[] = "((swi.field_table_num = " . intval($va_restrict['table_num']) . ") AND (swi.field_num = '" . $va_restrict['field_num'] . "'))";
                     }
                     $vs_sql_where .= " AND (" . join(" OR ", $va_field_restrict_sql) . ")";
                 }
                 $va_join = array();
                 if ($vn_direct_sql_target_table_num != $pn_subject_tablenum) {
                     // We're doing direct queries on metadata in a related table, fun!
                     // Now let's rewrite the direct query to work...
                     if ($t_target = $this->opo_datamodel->getInstanceByTableNum($vn_direct_sql_target_table_num, true)) {
                         // First we create the join from the related table to our subject
                         $vs_target_table_name = $t_target->tableName();
                         $va_path = array_keys($this->opo_datamodel->getPath($vn_direct_sql_target_table_num, $pn_subject_tablenum));
                         $vs_left_table = array_shift($va_path);
                         $vn_cj = 0;
                         foreach ($va_path as $vs_right_table) {
                             if (sizeof($va_rels = $this->opo_datamodel->getRelationships($vs_left_table, $vs_right_table)) > 0) {
                                 $va_join[] = "INNER JOIN {$vs_right_table} ON {$vs_right_table}." . $va_rels[$vs_left_table][$vs_right_table][0][1] . " = " . ($vn_cj == 0 ? 'ca.row_id' : "{$vs_left_table}." . $va_rels[$vs_left_table][$vs_right_table][0][0]);
                             }
                             $vs_left_table = $vs_right_table;
                             $vn_cj++;
                         }
                         // Next we rewrite the key we're pulling to be from our subject
                         $vs_direct_sql_query = str_replace("SELECT ca.row_id", "SELECT " . $this->opo_datamodel->primaryKey($pn_subject_tablenum, true), $vs_direct_sql_query);
                         // Finally we pray
                     }
                 }
                 if ($vn_i == 0) {
                     if ($vs_direct_sql_query) {
                         $vs_direct_sql_query = str_replace('^JOIN', join("\n", $va_join), $vs_direct_sql_query);
                         $vs_sql = "INSERT IGNORE INTO {$ps_dest_table} {$vs_direct_sql_query}";
                         if (strpos($vs_sql, '?') !== false && (!is_array($pa_direct_sql_query_params) || sizeof($pa_direct_sql_query_params) == 0)) {
                             $pa_direct_sql_query_params = array($vn_direct_sql_target_table_num != $pn_subject_tablenum ? $vn_direct_sql_target_table_num : (int) $pn_subject_tablenum);
                         }
                     } else {
                         $vs_sql = "\n\t\t\t\t\t\t\t\tINSERT IGNORE INTO {$ps_dest_table}\n\t\t\t\t\t\t\t\tSELECT swi.row_id, SUM(swi.boost)\n\t\t\t\t\t\t\t\tFROM ca_sql_search_word_index swi\n\t\t\t\t\t\t\t\t" . (!$vb_is_blank_search ? "INNER JOIN ca_sql_search_words AS sw ON sw.word_id = swi.word_id" : '') . "\n\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t{$vs_sql_where}\n\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\tswi.table_num = ?\n\t\t\t\t\t\t\t\t\t{$vs_rel_type_id_sql}\n\t\t\t\t\t\t\t\t\t" . ($this->getOption('omitPrivateIndexing') ? " AND swi.access = 0" : '') . "\n\t\t\t\t\t\t\t\tGROUP BY swi.row_id\n\t\t\t\t\t\t\t";
                         $pa_direct_sql_query_params = array((int) $pn_subject_tablenum);
                     }
                     if (($vn_num_terms = sizeof($va_ft_terms) + sizeof($va_ft_like_terms) + sizeof($va_ft_stem_terms)) > 1 && !$vs_direct_sql_query) {
                         $vs_sql .= " HAVING count(distinct sw.word_id) = {$vn_num_terms}";
                     }
                     $t = new Timer();
                     $pa_direct_sql_query_params = is_array($pa_direct_sql_query_params) ? $pa_direct_sql_query_params : array();
                     if (strpos($vs_sql, '?') === false) {
                         $pa_direct_sql_query_params = array();
                     }
                     $this->opo_db->query($vs_sql, $pa_direct_sql_query_params);
                     $vn_i++;
                     if ($this->debug) {
                         Debug::msg('FIRST: ' . $vs_sql . " [{$pn_subject_tablenum}] " . $t->GetTime(4));
                     }
                 } else {
                     switch ($vs_op) {
                         case 'AND':
                             if ($vs_direct_sql_query) {
                                 if ($vn_direct_sql_target_table_num != $pn_subject_tablenum) {
                                     array_push($va_join, "INNER JOIN {$ps_dest_table} AS ftmp1 ON ftmp1.row_id = " . $this->opo_datamodel->primaryKey($pn_subject_tablenum, true));
                                 } else {
                                     array_unshift($va_join, "INNER JOIN {$ps_dest_table} AS ftmp1 ON ftmp1.row_id = ca.row_id");
                                 }
                                 $vs_direct_sql_query = str_replace('^JOIN', join("\n", $va_join), $vs_direct_sql_query);
                                 $pa_direct_sql_query_params = array($vn_direct_sql_target_table_num != $pn_subject_tablenum ? $vn_direct_sql_target_table_num : (int) $pn_subject_tablenum);
                             }
                             $vs_sql = $vs_direct_sql_query ? "{$vs_direct_sql_query}" : "\n\t\t\t\t\t\t\t\t\tSELECT swi.row_id\n\t\t\t\t\t\t\t\t\tFROM ca_sql_search_word_index swi\n\t\t\t\t\t\t\t\t\tINNER JOIN ca_sql_search_words AS sw ON sw.word_id = swi.word_id\n\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t{$vs_sql_where}\n\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\tswi.table_num = ?\n\t\t\t\t\t\t\t\t\t\t{$vs_rel_type_id_sql}\n\t\t\t\t\t\t\t\t\t\t" . ($this->getOption('omitPrivateIndexing') ? " AND swi.access = 0" : '') . "\n\t\t\t\t\t\t\t\t\tGROUP BY swi.row_id\n\t\t\t\t\t\t\t\t";
                             if (($vn_num_terms = sizeof($va_ft_terms) + sizeof($va_ft_like_terms) + sizeof($va_ft_stem_terms)) > 1) {
                                 $vs_sql .= " HAVING count(distinct sw.word_id) = {$vn_num_terms}";
                             }
                             $t = new Timer();
                             $pa_direct_sql_query_params = is_array($pa_direct_sql_query_params) ? $pa_direct_sql_query_params : array((int) $pn_subject_tablenum);
                             if (strpos($vs_sql, '?') === false) {
                                 $pa_direct_sql_query_params = array();
                             }
                             $qr_res = $this->opo_db->query($vs_sql, $pa_direct_sql_query_params);
                             if ($this->debug) {
                                 Debug::msg('AND: ' . $vs_sql . ' ' . $t->GetTime(4) . ' ' . $qr_res->numRows());
                             }
                             if (is_array($va_ids = $qr_res->getAllFieldValues($vs_direct_sql_query && $vn_direct_sql_target_table_num != $pn_subject_tablenum ? $this->opo_datamodel->primaryKey($pn_subject_tablenum) : 'row_id')) && sizeof($va_ids)) {
                                 $vs_sql = "DELETE FROM {$ps_dest_table} WHERE row_id NOT IN (?)";
                                 $qr_res = $this->opo_db->query($vs_sql, array($va_ids));
                                 if ($this->debug) {
                                     Debug::msg('AND DELETE: ' . $vs_sql . ' ' . $t->GetTime(4));
                                 }
                             } else {
                                 // we don't have any results left, ie. our AND query should yield an empty result
                                 $this->opo_db->query("DELETE FROM {$ps_dest_table}");
                             }
                             $vn_i++;
                             break;
                         case 'NOT':
                             if ($vs_direct_sql_query) {
                                 $vs_direct_sql_query = str_replace('^JOIN', join("\n", $va_join), $vs_direct_sql_query);
                                 $pa_direct_sql_query_params = array($vn_direct_sql_target_table_num != $pn_subject_tablenum ? $vn_direct_sql_target_table_num : (int) $pn_subject_tablenum);
                             }
                             $vs_sql = "\n\t\t\t\t\t\t\t\t\tSELECT row_id\n\t\t\t\t\t\t\t\t\tFROM ca_sql_search_words sw\n\t\t\t\t\t\t\t\t\tINNER JOIN ca_sql_search_word_index AS swi ON sw.word_id = swi.word_id\n\t\t\t\t\t\t\t\t\tWHERE \n\t\t\t\t\t\t\t\t\t\t" . ($vs_sql_where ? "{$vs_sql_where} AND " : "") . " swi.table_num = ? \n\t\t\t\t\t\t\t\t\t\t{$vs_rel_type_id_sql}\n\t\t\t\t\t\t\t\t\t\t" . ($this->getOption('omitPrivateIndexing') ? " AND swi.access = 0" : '');
                             $pa_direct_sql_query_params = is_array($pa_direct_sql_query_params) ? $pa_direct_sql_query_params : array((int) $pn_subject_tablenum);
                             if (strpos($vs_sql, '?') === false) {
                                 $pa_direct_sql_query_params = array();
                             }
                             $qr_res = $this->opo_db->query($vs_sql, $pa_direct_sql_query_params);
                             $va_ids = $qr_res->getAllFieldValues($vs_direct_sql_query && $vn_direct_sql_target_table_num != $pn_subject_tablenum ? $this->opo_datamodel->primaryKey($pn_subject_tablenum) : 'row_id');
                             if (sizeof($va_ids) > 0) {
                                 $vs_sql = "\n\t\t\t\t\t\t\t\t\t\tDELETE FROM {$ps_dest_table} \n\t\t\t\t\t\t\t\t\t\tWHERE \n\t\t\t\t\t\t\t\t\t\t\trow_id IN (?)\n\t\t\t\t\t\t\t\t\t";
                                 if ($this->debug) {
                                     Debug::msg('NOT ' . $vs_sql);
                                 }
                                 $qr_res = $this->opo_db->query($vs_sql, array($va_ids));
                             }
                             $vn_i++;
                             break;
                         default:
                         case 'OR':
                             if ($vs_direct_sql_query) {
                                 $vs_direct_sql_query = str_replace('^JOIN', join("\n", $va_join), $vs_direct_sql_query);
                                 $pa_direct_sql_query_params = array($vn_direct_sql_target_table_num != $pn_subject_tablenum ? $vn_direct_sql_target_table_num : (int) $pn_subject_tablenum);
                             }
                             $vs_sql = $vs_direct_sql_query ? "INSERT IGNORE INTO {$ps_dest_table} {$vs_direct_sql_query}" : "\n\t\t\t\t\t\t\t\t\tINSERT IGNORE INTO {$ps_dest_table}\n\t\t\t\t\t\t\t\t\tSELECT swi.row_id, SUM(swi.boost)\n\t\t\t\t\t\t\t\t\tFROM ca_sql_search_word_index swi\n\t\t\t\t\t\t\t\t\tINNER JOIN ca_sql_search_words AS sw ON sw.word_id = swi.word_id\n\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t{$vs_sql_where}\n\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\tswi.table_num = ?\n\t\t\t\t\t\t\t\t\t\t{$vs_rel_type_id_sql}\n\t\t\t\t\t\t\t\t\t\t" . ($this->getOption('omitPrivateIndexing') ? " AND swi.access = 0" : '') . "\n\t\t\t\t\t\t\t\t\tGROUP BY\n\t\t\t\t\t\t\t\t\t\tswi.row_id\n\t\t\t\t\t\t\t\t";
                             if ($this->debug) {
                                 Debug::msg('OR ' . $vs_sql);
                             }
                             $vn_i++;
                             $pa_direct_sql_query_params = is_array($pa_direct_sql_query_params) ? $pa_direct_sql_query_params : array((int) $pn_subject_tablenum);
                             if (strpos($vs_sql, '?') === false) {
                                 $pa_direct_sql_query_params = array();
                             }
                             $qr_res = $this->opo_db->query($vs_sql, $pa_direct_sql_query_params);
                             break;
                     }
                 }
                 // Drop any temporary tables created by direct search queries
                 foreach (array_keys($va_direct_query_temp_tables) as $vs_temp_table_to_drop) {
                     $this->_dropTempTable($vs_temp_table_to_drop);
                 }
                 break;
             default:
                 //print get_class($o_lucene_query_element);
                 break;
         }
     }
 }
 /**
  *
  * @param string $type can be one of: ["ca_objects", "ca_entities", "ca_places", "ca_occurrences", "ca_collections", "ca_list_items", "ca_object_representations", "ca_storage_locations", "ca_movements", "ca_loans", "ca_tours", "ca_tour_stops"]
  * @param int $item_id primary key
  * @param string $attribute_code_or_id
  * @return array
  * @throws SoapFault
  */
 public function getAttributesByElement($type, $item_id, $attribute_code_or_id)
 {
     if (!($t_subject_instance = $this->getTableInstance($type, $item_id, true))) {
         throw new SoapFault("Server", "Invalid type or item_id");
     }
     $t_locale = new ca_locales();
     $t_element = new ca_metadata_elements();
     $va_attrs = $t_subject_instance->getAttributesByElement($attribute_code_or_id);
     $va_return = array();
     $va_element_type_cfg = ca_metadata_elements::getAttributeTypes();
     foreach ($va_attrs as $vo_attr) {
         $va_attr = array();
         foreach ($vo_attr->getValues() as $vo_value) {
             $t_element->load($vo_value->getElementID());
             $va_attr[] = array("value_id" => $vo_value->getValueID(), "display_value" => $vo_value->getDisplayValue(), "element_code" => $vo_value->getElementCode(), "element_id" => $vo_value->getElementID(), "attribute_info" => $t_subject_instance->getAttributeLabelAndDescription($vo_value->getElementCode()), "datatype" => $va_element_type_cfg[$t_element->get("datatype")], "locale" => $t_locale->localeIDToCode($vo_attr->getLocaleID()));
         }
         $va_return[$vo_attr->getAttributeID()] = $va_attr;
     }
     return $va_return;
 }
Exemple #25
0
 public function search($pn_subject_tablenum, $ps_search_expression, $pa_filters = array(), $po_rewritten_query = null)
 {
     $vn_i = 0;
     $va_old_signs = $po_rewritten_query->getSigns();
     $va_terms = $va_signs = array();
     foreach ($po_rewritten_query->getSubqueries() as $o_lucene_query_element) {
         switch ($vs_class = get_class($o_lucene_query_element)) {
             case 'Zend_Search_Lucene_Search_Query_Term':
             case 'Zend_Search_Lucene_Search_Query_MultiTerm':
             case 'Zend_Search_Lucene_Search_Query_Phrase':
                 $vs_access_point = null;
                 if ($vs_class != 'Zend_Search_Lucene_Search_Query_Term') {
                     $va_raw_terms = array();
                     foreach ($o_lucene_query_element->getQueryTerms() as $o_term) {
                         if (!$vs_access_point && ($vs_field = $o_term->field)) {
                             $vs_access_point = $vs_field;
                         }
                         $va_raw_terms[] = $vs_text = (string) $o_term->text;
                     }
                     $vs_term = join(" ", $va_raw_terms);
                 } else {
                     $vs_access_point = $o_lucene_query_element->getTerm()->field;
                     $vs_term = $o_lucene_query_element->getTerm()->text;
                     $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term('"' . $vs_term . '"', $vs_access_point));
                 }
                 if ($vs_access_point) {
                     list($vs_table, $vs_field, $vs_sub_field) = explode('.', $vs_access_point);
                     if (in_array($vs_table, array('created', 'modified'))) {
                         if (!$this->opo_tep->parse($vs_term)) {
                             break;
                         }
                         $vn_user_id = null;
                         if ($vs_field = trim($vs_field)) {
                             if (!is_int($vs_field)) {
                                 $t_user = new ca_users();
                                 if ($t_user->load(array("user_name" => $vs_field))) {
                                     $vn_user_id = (int) $t_user->getPrimaryKey();
                                 }
                             } else {
                                 $vn_user_id = (int) $vs_field;
                             }
                         }
                         switch ($vs_table) {
                             case 'created':
                                 if ($vn_user_id) {
                                     $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Boolean(array(new Zend_Search_Lucene_Index_Term('[' . $this->opo_tep->getText(array('start_as_iso8601' => true)) . " TO " . $this->opo_tep->getText(array('end_as_iso8601' => true)) . ']', 'created'), new Zend_Search_Lucene_Index_Term($vn_user_id, 'created_user_id')), array(true, true));
                                 } else {
                                     $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term('[' . $this->opo_tep->getText(array('start_as_iso8601' => true)) . " TO " . $this->opo_tep->getText(array('end_as_iso8601' => true)) . ']', 'created'));
                                 }
                                 break;
                             case 'modified':
                                 if ($vn_user_id) {
                                     $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Boolean(array(new Zend_Search_Lucene_Index_Term('[' . $this->opo_tep->getText(array('start_as_iso8601' => true)) . " TO " . $this->opo_tep->getText(array('end_as_iso8601' => true)) . ']', 'modified'), new Zend_Search_Lucene_Index_Term($vn_user_id, 'modified_user_id')), array(true, true));
                                 } else {
                                     $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term('[' . $this->opo_tep->getText(array('start_as_iso8601' => true)) . " TO " . $this->opo_tep->getText(array('end_as_iso8601' => true)) . ']', 'modified'));
                                 }
                                 break;
                         }
                     } else {
                         if ($vs_table && $vs_field) {
                             $t_table = $this->opo_datamodel->getInstanceByTableName($vs_table, true);
                             if (!$t_table) {
                                 break;
                             }
                             $vs_fld_num = $this->opo_datamodel->getFieldNum($vs_table, $vs_field);
                             if (!$vs_fld_num) {
                                 // probably attribute
                                 $t_element = new ca_metadata_elements();
                                 if ($t_element->load(array('element_code' => $vs_sub_field ? $vs_sub_field : $vs_field))) {
                                     //
                                     // For certain types of attributes we can directly query the
                                     // attributes in the database rather than using the full text index
                                     // This allows us to do "intelligent" querying... for example on date ranges
                                     // parsed from natural language input and for length dimensions using unit conversion
                                     //
                                     switch ($t_element->get('datatype')) {
                                         case 2:
                                             // dates
                                             $vb_exact = $vs_term[0] == "#" ? true : false;
                                             // dates prepended by "#" are considered "exact" or "contained - the matched dates must be wholly contained by the search term
                                             if ($vb_exact) {
                                                 if ($this->opo_tep->parse($vs_term)) {
                                                     // TODO: fix date handling to reflect distinctions in ranges
                                                     $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term('[' . $this->opo_tep->getText(array('start_as_iso8601' => true)) . " TO " . $this->opo_tep->getText(array('end_as_iso8601' => true)) . ']', $vs_access_point));
                                                 }
                                             } else {
                                                 if ($this->opo_tep->parse($vs_term)) {
                                                     // TODO: fix date handling to reflect distinctions in ranges
                                                     $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term('[' . $this->opo_tep->getText(array('start_as_iso8601' => true)) . " TO " . $this->opo_tep->getText(array('end_as_iso8601' => true)) . ']', $vs_access_point));
                                                 }
                                             }
                                             break;
                                         case 4:
                                             // geocode
                                             $t_geocode = new GeocodeAttributeValue();
                                             if ($va_coords = caParseGISSearch($vs_term)) {
                                                 $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term('[' . $va_coords['min_latitude'] . ',' . $va_coords['min_longitude'] . " TO " . $va_coords['max_latitude'] . ',' . $va_coords['max_longitude'] . ']', $vs_access_point));
                                             }
                                             break;
                                         case 6:
                                             // currency
                                             $t_cur = new CurrencyAttributeValue();
                                             $va_parsed_value = $t_cur->parseValue($vs_term, $t_element->getFieldValuesArray());
                                             $vn_amount = (double) $va_parsed_value['value_decimal1'];
                                             $vs_currency = preg_replace('![^A-Z0-9]+!', '', $va_parsed_value['value_longtext1']);
                                             $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term($vn_amount, $vs_access_point));
                                             break;
                                         case 8:
                                             // length
                                             $t_len = new LengthAttributeValue();
                                             $va_parsed_value = $t_len->parseValue($vs_term, $t_element->getFieldValuesArray());
                                             $vn_len = (double) $va_parsed_value['value_decimal1'];
                                             // this is always in meters so we can compare this value to the one in the database
                                             $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term($vn_len, $vs_access_point));
                                             break;
                                         case 9:
                                             // weight
                                             $t_weight = new WeightAttributeValue();
                                             $va_parsed_value = $t_weight->parseValue($vs_term, $t_element->getFieldValuesArray());
                                             $vn_weight = (double) $va_parsed_value['value_decimal1'];
                                             // this is always in kilograms so we can compare this value to the one in the database
                                             $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term($vn_weight, $vs_access_point));
                                             break;
                                         case 10:
                                             // timecode
                                             $t_timecode = new TimecodeAttributeValue();
                                             $va_parsed_value = $t_timecode->parseValue($vs_term, $t_element->getFieldValuesArray());
                                             $vn_timecode = (double) $va_parsed_value['value_decimal1'];
                                             $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term($vn_timecode, $vs_access_point));
                                             break;
                                         case 11:
                                             // integer
                                             $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term((double) $vs_term, $vs_access_point));
                                             break;
                                         case 12:
                                             // decimal
                                             $o_lucene_query_element = new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term((double) $vs_term, $vs_access_point));
                                             break;
                                     }
                                 }
                             }
                         }
                     }
                 }
                 break;
         }
         $va_terms[] = $o_lucene_query_element;
         $va_signs[] = is_array($va_old_signs) ? array_key_exists($vn_i, $va_old_signs) ? $va_old_signs[$vn_i] : true : true;
         $vn_i++;
     }
     $o_rewritten_query = new Zend_Search_Lucene_Search_Query_Boolean($va_terms, $va_signs);
     $ps_search_expression = $this->_queryToString($o_rewritten_query);
     if ($vs_filter_query = $this->_filterValueToQueryValue($pa_filters)) {
         $ps_search_expression = "({$ps_search_expression}) AND ({$vs_filter_query})";
     }
     $vo_http_client = new Zend_Http_Client();
     $vo_http_client->setUri($this->ops_elasticsearch_base_url . "/" . $this->ops_elasticsearch_index_name . "/" . $this->opo_datamodel->getTableName($pn_subject_tablenum) . "/" . "_search");
     if (preg_match_all("!([A-Za-z0-9_\\-\\.]+)[/]{1}([A-Za-z0-9_\\-]+):(\"[^\"]*\")!", $ps_search_expression, $va_matches) || preg_match_all("!([A-Za-z0-9_\\-\\.]+)[/]{1}([A-Za-z0-9_\\-]+):([^ ]*)!", $ps_search_expression, $va_matches)) {
         foreach ($va_matches[0] as $vn_i => $vs_element) {
             $vs_fld = $va_matches[1][$vn_i];
             if (!($vs_rel_type = trim($va_matches[2][$vn_i]))) {
                 continue;
             }
             $va_tmp = explode(".", $vs_fld);
             $vs_rel_table = caGetRelationshipTableName($pn_subject_tablenum, $va_tmp[0]);
             $va_rel_type_ids = $vs_rel_type && $vs_rel_table ? caMakeRelationshipTypeIDList($vs_rel_table, array($vs_rel_type)) : null;
             $va_new_elements = array();
             foreach ($va_rel_type_ids as $vn_rel_type_id) {
                 $va_new_elements[] = "(" . $va_matches[1][$vn_i] . "/" . $vn_rel_type_id . ":" . $va_matches[3][$vn_i] . ")";
             }
             $ps_search_expression = str_replace($vs_element, "(" . join(" OR ", $va_new_elements) . ")", $ps_search_expression);
         }
     }
     $ps_search_expression = str_replace("/", '\\/', $ps_search_expression);
     // escape forward slashes used to delimit relationship type qualifier
     Debug::msg("[ElasticSearch] Running query {$ps_search_expression}");
     $vo_http_client->setParameterGet(array('size' => intval($this->opa_options["limit"]), 'q' => $ps_search_expression, 'fields' => ''));
     $vo_http_response = $vo_http_client->request();
     $va_result = json_decode($vo_http_response->getBody(), true);
     return new WLPlugSearchEngineElasticSearchResult($va_result["hits"]["hits"], $pn_subject_tablenum);
 }