Phalcon Framework 3.4.1

PDOException: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ')\n ORDER BY FIELD(up.id, )\n LIMIT 10' at line 19

/var/www/rc/drooble/trunk/projects/drooble_v1/services/web/private/logic/search.class.php (1644)
#0PDOStatement->execute()
#1Phalcon\Db\Adapter\Pdo->executePrepared(Object(PDOStatement), Array([fuid] => (empty string)), null)
#2Phalcon\Db\Adapter\Pdo->query(\n SELECT\n `search_cache`.`short_info_json` ,\n `up`.`id`,\n `up`.`user_status`,\n `up`.`system_status`,\n `up`.`lastseen`,\n `up`.`is_page`,\n IF ( `ucm`.`id` IS NOT NULL, 1, 0 ) as `isInCircle`\n FROM\n `search_cache`\n JOIN\n `users_profiles` `up`\n ON\n `up`.`id` = `search_cache`.`user_id`\n LEFT JOIN\n `users_circles_members` `ucm`\n ON\n (`ucm`.`user_id` = :fuid AND `ucm`.`target_user_id` = `up`.`id`)\n WHERE `up`.`id` IN()\n ORDER BY FIELD(up.id, )\n LIMIT 10\n ;, Array([fuid] => (empty string)))
/var/www/rc/drooble/trunk/projects/drooble_v1/services/web/private/logic/search.class.php (1644)
<?php
class search{
 
  private $db;
  private $c;
    private $app;
 
  public function __construct($db,&$c, $app) {
    $this->db = $db;
    $this->c = $c;
        $this->app = $app;
  }
/*
  public function doReindex($data){
    $ret=array();
    foreach($this->db->q("SELECT `id` FROM `users_profiles` WHERE 1") as $el){
      $ret[]=$this->lib->profileSearchIndexing($el['id']);
    }
 
    return array(true,$ret);
  }
*/
    public function searchCity($data) {
        $return = new \stdClass();
 
        $return->items = \Helpers\Search::getInstance()->searchCity($data['name']);
 
        return array(true, $return);
 
 
        /*
        // SELECT *, CASE WHEN name LIKE 'Sofia%' THEN 1 ELSE 0 END AS keymatch FROM `cities` WHERE MATCH (name) AGAINST ('+Sofia' IN BOOLEAN MODE) ORDER BY keymatch DESC
        $cities = $this->app->db->fetchAll("
            SELECT *, CASE
                WHEN search_by_me LIKE ".$this->app->db->escapeString($keywords)." THEN 4
                WHEN search_by_me LIKE ".$this->app->db->escapeString($keywords." %")." THEN 3
                WHEN search_by_me LIKE ".$this->app->db->escapeString("% ".$keywords." %")." THEN 2
                WHEN search_by_me LIKE ".$this->app->db->escapeString("% ".$keywords)." THEN 1
                ELSE 0 END AS keymatch
            FROM `cities`
            WHERE
            MATCH (search_by_me) AGAINST (".$this->app->db->escapeString($keywords).")
            ORDER BY keymatch DESC
            LIMIT 22
        ");
 
        $items = array();
 
        if ($cities) {
            foreach($cities as $city)
            {
                $title_suffix = $city['country'];
                if ($city['region']) {
                    $title_suffix = $city['region'].", ".$city['country'];
                }
 
                $items[] = array(
                    'id' => $city['id'],
                    'title' => $city['name'].", ".$title_suffix,
                    'name' => $city['name'],
                    'country' => $city['country'],
                    'region' => $city['region']
                );
            }
        }
 
        $return->items = $items;
 
        return array(true, $return);
        */
    }
 
  public function searchByMail($data){
    $email=$data['email'];
 
    if(!filter_var($email, FILTER_VALIDATE_EMAIL)){
      return array(false,"not valid");
    }
 
        /*
    $search=$email;
    $search=str_replace("*","",$search);
    $search=str_replace(" ","",$search);
    $search=str_replace(")","",$search);
    $search=str_replace("(","",$search);
 
 
    $sql="SELECT `short_info` FROM `search_cache` WHERE MATCH (`email`) AGAINST ('".'"'.$search.'"'."')";
    $r=$this->db->one($sql);
         *
         */
 
        $person = \Models\UserCache::findFirst([
            'conditions' => 'email LIKE {email:str}',
            'bind' => ['email' => $email]
        ]);
 
        if ($person) {
            return array(true, json_decode($person->short_info_json, true));
        } else {
            return array(true,'notexists');
        }
  }
 
  public function otherPersonContactsSuggester($data){
    $user_id=\Helpers\Users::getIdFromUsername($data['profile']);
    if(isset($data['circle_id']) && $data['profile'] != $_SESSION['profile']['username']){
      unset($data['circle_id']);
    }
    if(!$data['results']){
      $data['results']=9;
    }
    if(isset($data['mix_order'])){
      $data['mix_order']=true;
    }
 
    // \Helpers\Debug::alert($data['profile']);
    if($user_id){
      return $this->ContactsSuggester($user_id,$data);
    }else{
      return array(false);
    }
  }
 
  public function myContactsSuggester($data){
 
    return $this->ContactsSuggester($_SESSION['profile']['id'],$data,true, isset($data['online_first']) ? $data['online_first'] : true);
  }
 
  public function otherPersonFollowSuggester($data) {
    $data["filterUsername"] = $data["profile"];
    unset($data["profile"]);
 
    $data["follow_type"] = "followings";
 
    return $this->ContactsSuggester($_SESSION['profile']['id'],$data,true, false);
  }
 
    public function searchInMyMessages($data){
        // Used data
        $user_id = (int)$_SESSION['profile']['id'];
        if ($data['band_id'] > 0) {
            if (!\Helpers\Basic::checkUserRights((int) $data['band_id'], $user_id)) {
                return array(false,false);
            } else {
                $user_id = (int) $data['band_id'];
            }
        }
        
        $offset = $data['offset'];
        $limit = $data['limit'];
 
        $search = trim(strip_tags($data['search']));
        $search = str_replace(array("%", "'", '"'), "", $search);
 
        $length_to_get = 50; // How much characters to get
        $symbols_before = 10; // How much symbols before match to get
 
        $return = array();
 
        // Search query
        $query = "
            SELECT * FROM
            (
                SELECT
                    um.text, um.conversations_id, c.is_group, c.members,
                    IF (um.reciever_id = '".$user_id."', scsender.short_info_json, screciever.short_info_json) as partner,
                    IF (um.reciever_id = '".$user_id."', scsender.user_id, screciever.user_id) as partner_id
                FROM users_messages as um
                LEFT JOIN conversations AS c ON c.id = um.conversations_id
                LEFT JOIN search_cache AS scsender ON scsender.user_id = um.sender_id
                LEFT JOIN search_cache AS screciever ON screciever.user_id = um.reciever_id
                WHERE
                    um.sender_id != um.reciever_id
                    AND (um.sender_id = '".$user_id."' OR um.reciever_id = '".$user_id."')
                    AND um.is_deleted=0
                    AND (
                        um.text LIKE :keywords
                        OR (scsender.cache LIKE :keywords AND scsender.user_id != '".$user_id."')
                        OR (screciever.cache LIKE :keywords AND screciever.user_id != '".$user_id."')
                    )
                ORDER BY um.send_time DESC
            ) as results
            GROUP BY results.conversations_id, results.text, results.partner, results.partner_id
        ";
 
        $prepare = array(
            'keywords' => "%".$search."%"
        );
        
        // Here we store user information - reason - when we have a user, does not look for it again
        $cache_profiles = array();
 
        // So... lets begin... just in case string was more than 3 symbols
        if (mb_strlen($search) >= 3)
        {
            $cids = [];
            // Put information
            foreach($this->db->prepare($query, $prepare)->execute()->fetchAllAssoc()->cache( 1 )->result as $el) {
                $cids[] = $el['conversations_id'];
                continue; // !!!! NOTE we'll be using getMail at end of func to parse the response
                
                //this below -- NOT executed !!! 
                
                
                $text = trim(strip_tags($el['text']));
 
                $find_position = mb_strpos($search, $text);
                $text_length = mb_strlen($text);
                $string_length = mb_strlen($search);
 
                // Stupid way but... i cant regex :/
                if ($text_length > $length_to_get)
                {
                    if (($find_position - $symbols_before) <= 0)
                    {
                        $text = mb_substr($text, 0, $length_to_get)."...";
                    }
                    else if (($find_position - $symbols_before) > 0 AND ($find_position - $symbols_before + $length_to_get) <= $text_length)
                    {
                        $text = "...".mb_substr($text, ($find_position - $symbols_before), $length_to_get)."...";
                    }
                    else
                    {
                        $text = "...".mb_substr($text, ($length_to_get*-1));
                    }
                }
 
                $line = array(
                    'quote' => $text,
                    'conversation' => "#".$el['conversations_id']
                );
 
                // Put information about users in converstation
                if ((int)$el['is_group'] == 0)
                {
                    if (!isset($cache_profiles[$el['partner_id']]))
                    {
                        $user = unserialize($el['partner']);
 
                        $cache_profiles[$el['partner_id']] = array(
                            'names' => $user['display_name']
                        );
 
                        if (isset($user['avatar_icon']) AND $user['avatar_icon'] != "")
                        {
                            $cache_profiles[$el['partner_id']]['avatar_icon'] = $user['avatar_icon'];
                        }
                    }
 
                    $line['names'] = $cache_profiles[$el['partner_id']]['names'];
                    $line['avatar_icon'] = $cache_profiles[$el['partner_id']]['avatar_icon'];
                }
                else
                {
                    // Check collection
                    $collection = array();
                    $user_ids = explode(",", $el['members']);
                    foreach($user_ids as $key => $val)
                    {
                        if ((int)$val != 0 AND (int)$val != $user_id)
                        {
                            $collection[(int)$val] = false;
                        }
                    }
 
                    // Filter which users we have
                    $search_user_ids = array();
                    foreach($collection as $key => $val)
                    {
                        if (isset($cache_profiles[$key]))
                        {
                            // Put it in our collection
                            $collection[$key] = $cache_profiles[$key]['names'];
                        }
                        else
                        {
                            $search_user_ids[] = $key;
                        }
                    }
 
                    // Get missed users
                    if (!empty($search_user_ids))
                    {
                        $query = "SELECT user_id, short_info_json FROM search_cache WHERE user_id IN (".implode(",", $search_user_ids).")";
 
                        foreach($this->db->prepare($query, array())->execute()->fetchAllAssoc()->cache( 1 )->result as $usr) {
                            $user = json_decode($usr['short_info_json'], true);
                            $cache_profiles[(int)$usr['user_id']] = array(
                                'names' => $user['display_name']
                            );
 
                            if (isset($user['avatar_icon']) AND $user['avatar_icon'] != "")
                            {
                                $cache_profiles[(int)$usr['user_id']]['avatar_icon'] = $user['avatar_icon'];
                            }
 
                            // Put it in our collection
                            $collection[(int)$usr['user_id']] = $cache_profiles[(int)$usr['user_id']]['names'];
                        }
                    }
 
                    // Now merge names
                    $line['names'] = implode(", ", $collection);
                    $line['avatar_icon'] = DEF_GROUP_CHAT_ICON;
                }
 
                // Some modifications
                // $text = str_replace($search, "<em>".$search."</em>", $text);
                $seek_and_destroy = '#\b'.$search.'\b#iu';
                $line['quote'] = preg_replace($seek_and_destroy, '<em>$0</em>', $line['quote']);
                $line['names'] = preg_replace($seek_and_destroy, '<em>$0</em>', $line['names']);
 
                $return[] = $line;
            }
        }
        
        // NOTE!!  overwrite response here
        $mc = new \Controllers\API\MessengerController();
        $cids = array_reverse($cids);
        $cids = array_slice($cids, $offset, $limit);
        $getMailData = [
            'folder' => 'All',
            'mode' => 'search',
            'results' => $limit,
            'cids' => $cids,
            'pid' => $data['band_id']
        ];
        
        $getMailRe = $mc->getMail($getMailData);
        $return = is_array($getMailRe) ? $getMailRe : ['conversations' => []];
        
        // Return
        return array(true, array("length" => count($return['conversations']) , "conversations" => $return['conversations']));
    }
 
  private function ContactsSuggester($user_id,$data,$myContactsOnly=false, $onlineFirst = false){
    $uid=$user_id;
    $prepare=array();
        
        $excludeMothership = array_key_exists('exclude_mothership', $data) ? $data['exclude_mothership'] : true;
 
    // $prepare['uid']=$uid;
    $search=$data['search'];
 
    /*if (empty($search)) {
      return array(false, 'empty search query');
    }*/
 
    $select   = array();
    $leftJoin   = array();
    $order     = array();
 
    if($myContactsOnly){
      $data['follow_type']='followings';
    }
    $search=trim($search);
    $search=str_replace("*","",$search);
    $search=str_replace(")","",$search);
    $search=str_replace("(","",$search);
    $search=str_replace(" ","* +*",$search);
 
    if($search){
      $search=$this->db->quote("*".$search."*");
    }else{
      $search=NULL;
    }
    $circle_id=$data['circle_id'];
    // $prepare['circle_id']=$circle_id;
 
        $show_status=$onlineFirst ? true : $data['show_status'];
    $latest=isset($data['latest']) ? true : false;
    $is_online=$data['is_online'];
 
    $mix_order=$data['mix_order'];
 
        $where = [
            '`up`.`is_banned` IS NULL AND `up`.`is_disabled` = 0'
        ];
        
    if($is_online){
      $show_status=1;
      $where[]="(`up`.`user_status`>0 AND `up`.`lastseen`> " . (time() - \Config::defaults()->DEF_LASTSEEN_OFFLINE_TIME).") ";
    }
 
    if (isset($data["user_type"])) {
//      $LeftJoinUserStatus="LEFT JOIN `users_profiles` AS `up` ON `up`.`id` = `sc`.`user_id` ";
 
//      if ($data["user_type"] == "page") {
//        $where[] = "`up`.`is_page` = 1";
//      } else if ($data["user_type"] == "user") {
//                $where[] = "`up`.`is_page` IS NULL";
//            }
    }
 
    if(isset($show_status)){
            $SelectUserStatus=" fn_calculateProfilePublicStatus(`up`.`user_status`,`up`.`system_status`,`up`.`lastseen`) as `publicStatus`, `up`.`id` as `user_suggest_id`,
                                if(`chat_status` > 0,fn_calculateProfilePublicStatus(`user_status`, `system_status`, `lastseen`),0) as `cstatus`,";
      //$SelectUserStatus="`up`.`user_status`, `up`.`lastseen`, `up`.`system_status`, ";
//      $LeftJoinUserStatus="LEFT JOIN `users_profiles` AS `up` ON `up`.`id` = `sc`.`user_id` ";
      $where[] = "`up`.`is_page` IS NULL";
    }
 
    if ( $data['gender'] ) {
      $select[] = "`up`.`gender`";
 
//      if ( !$LeftJoinUserStatus ) {
//        $leftJoin[] = "LEFT JOIN `users_profiles` AS `up` ON `up`.`id` = `sc`.`user_id` ";
//      }
    }
 
        if ( $data['is_page'] ) {
            //\Helpers\Debug::log('is_page passed');
      $select[] = "`up`.`is_page`";
//      if ( !$LeftJoinUserStatus ) {
//        $leftJoin[] = "LEFT JOIN `users_profiles` AS `up` ON `up`.`id` = `sc`.`user_id` ";
//      }
    }
 
    if ( $data['pictures_first'] ) {
      $select[]   = 'IF(`upi`.`id`, 1, 0) AS `has_picture`'; // moje da stane greshka, zashtoto ne znaeme koi e profile album-a
      //$leftJoin[] = "LEFT JOIN `users_pictures` AS `upi` ON ( `upi`.`user_id` = `sc`.`user_id` AND `upi`.`profilepic` = 1 ) ";
      $leftJoin[] = "LEFT JOIN `users_elements` AS `upi` ON `sc`.`id` = `upi`.`creator_id` AND `upi`.`type`='picture' AND `upi`.`is_default` = '1' AND `upi`.`deleted_time` IS NULL AND `upi`.`published_time` IS NOT NULL AND `upi`.`deployed_time` IS NOT NULL ";
      $order[]   = '`has_picture` DESC';
    }
 
    if ( $data['compare_followers'] && in_array($data['follow_type'], array('followers', 'followings')) && isset($_SESSION['profile']['id']) ) {
      $select[]   = 'IF(`my_ucm`.`id`, 1, 0) AS `same_follower`';
      $select[]   = 'IF(`my_ucm`.`id`, `my_ucm`.`circle_id`, -1) AS `circle_id`';
      $leftJoin[] = "LEFT JOIN `users_circles_members` AS `my_ucm` ON ( `my_ucm`.`user_id` = " . $_SESSION['profile']['id'] . " AND `my_ucm`.`target_user_id` = " . ( $data['follow_type'] == 'followers' ? "`ucm`.`user_id`" : "`ucm`.`target_user_id`" ) . " ) ";
      $order[]   = '`same_follower` DESC';
    }
 
    if ($data['filterUsername']) {
      $otherUserId = \Helpers\Users::getIdFromUsername($data['filterUsername']);
      if ($otherUserId) {
        $leftJoin[] = "LEFT JOIN `users_circles_members` AS `other_ucm` ON ( `other_ucm`.`user_id` = " . $otherUserId . " AND `other_ucm`.`target_user_id` = `ucm`.`target_user_id` ) ";
        $where[] = "`other_ucm`.`id` IS NULL";
        $where[] = "`up`.`id` != {$otherUserId}";
      }
    }
 
    switch($data['follow_type']){
      case "followers":
        $where[]="`ucm`.`target_user_id` = '{$uid}'";
        $leftJoinCS="LEFT JOIN `search_cache` AS `sc` ON `sc`.`user_id` = `ucm`.`user_id`";
      break;
      case "followings":
        $where[]="`ucm`.`user_id` = '{$uid}'";
        $leftJoinCS="LEFT JOIN `search_cache` AS `sc` ON `sc`.`user_id` = `ucm`.`target_user_id`";
      break;
      default:
        $leftJoinCS="LEFT JOIN `search_cache` AS `sc` ON (`sc`.`user_id` = `ucm`.`target_user_id` OR `sc`.`user_id` =`ucm`.`user_id`)";
    }
 
    $limit = " ";
    if(isset($data["limit"])){
      $prepare['limit']=$data['limit'];
      $limit = " LIMIT :limit ";
 
      if (isset($data["offset"])) {
        $prepare['offset']=$data['offset'];
        $limit .= "OFFSET :offset ";
      }
    }
 
    if($search){
      $where[]="MATCH (`cache`) AGAINST ($search IN BOOLEAN MODE)";
    }
 
    if($circle_id and $circle_id!='All'){
      $where[]="`uc`.`id` = '{$circle_id}'";
    }
 
    $where[] ="`sc`.`user_id`!= '{$uid}'";
 
    if ( $uid != 1 && $excludeMothership) {
            $where[]="`ucm`.`target_user_id` != 1";
        }
 
        if (isset($data['exclude_ids'])) {
            $exclude = is_array($data['exclude_ids']) ? implode(',', $data['exclude_ids']) : $data['exclude_ids'];
 
            if ( strlen($exclude) > 0 ) {
           $where[]="`ucm`.`target_user_id` NOT IN ($exclude)";
            }
        }
       
    $where = implode(" AND ",$where);
    if($where=='') $where = '1';
 
        if ($latest) {
            $order[] = "`ucm`.`create_time` DESC";
        }
 
    if ($onlineFirst) {
      $order[]='FIELD(`publicStatus`, 2) DESC';
      $order[]='`up`.`first_name`';
      $order[]='`up`.`last_name`';
    }
 
    if(isset($mix_order)) {
      $order[]='RAND()';
    }
 
    if ( count($select) > 0 ) {
      $select = implode(', ', $select) . ',';
    } else {
      $select = '';
    }
    $leftJoin = implode(' ', $leftJoin);
 
    if ( count($order) > 0 ) {
      $order = 'ORDER BY ' . implode(', ', $order);
    } else {
      $order = '';
    }
 
    $sql="SELECT
           {$select}
           {$SelectUserStatus}
           `sc`.`short_info_json`,
           `uc`.`name` as `circle_name`,
           `ucm`.`target_user_id`
        FROM `users_circles_members` AS `ucm`
        {$leftJoinCS}
        {$LeftJoinUserStatus}
        {$leftJoinProfiles}
        {$leftJoin}
       LEFT JOIN `users_circles` AS `uc` ON `uc`.`id` = `ucm`.`circle_id`
           LEFT JOIN `users_profiles` AS `up` ON `up`.`id` = `sc`.`user_id`
         WHERE  ( {$where} ) AND `sc`.`is_disabled` = 0 /*AND `ucm`.`target_user_id`!='1'*/
         GROUP BY `sc`.`id`
         {$order}
         {$limit}
    ";
               
    // \Helpers\Debug::log($sql);
        // echo $sql;
    $ret=array();
 
    //foreach($this->db->q($sql,$prepare) as $el){
    $cache = isset($data['cache']) ? $data['cache'] : 1;
    // echo print_r(array($sql, $prepare), 1);
    // \Helpers\Debug::log($sql);
    if (isset($prepare["limit"])) $prepare["limit"] = intval($prepare["limit"]);
    if (isset($prepare["offset"])) $prepare["offset"] = intval($prepare["offset"]);
 
    $mem_before = memory_get_usage();
        $res = $this->db->prepare($sql,$prepare)->execute()->fetchAllAssoc()->result;
  //  \Helpers\Debug::log("Memory used by the contacts array: ", memory_get_usage() - $mem_before);
        // $res = $this->db->prepare($sql,$prepare)->execute()->fetchAllAssoc()->cache( $cache )->result;
        // echo "$sql - " . print_r($prepare,1);
    foreach($res as $el){
      $a=json_decode($el['short_info_json'], true);
 
      if($show_status){
                $status=array();
                $statuses=unserialize(USER_STATUS_STATEMENTS);
                $status['status_id']=$el['publicStatus'];
                $status['status_name']=$statuses[$status['status_id']];
        $chatStatus=array();
                $chatStatus['status_id']=$el['cstatus'];
        $chatStatus['status_name']=$statuses[$chatStatus['status_id']];
                $a['status']=$status;
        $a['chat_status']=$chatStatus;
      }
 
      if($a!=false){
        $a['gender']     = $el['gender'];
        $a['has_picture']   = $el['has_picture'];
        $a['same_follower'] = $el['same_follower'];
        $a['circle_id']   = $el['circle_id'];
        //Compatibility
        $a['profile_tags']  = $a['tags']['profile'];
 
        if($el['user_id'] == $_SESSION['profile']['id'] and $uid == $_SESSION['profile']['id']){
          $a['circle']=$el['circle_name'];
        }
        $ret[]=$a;
      }
    }
 
    return array(true,array("profiles"=>$ret));
    //return array(true,array("profiles"=>$ret),str_replace("\t","",str_replace("\n", "", $sql)),$prepare);
    //return array(true,$ret);
  }
 
    public function chatBarSuggester($data) {
        $uid = (int)$_SESSION['profile']['id'];
        $limit = isset($data['limit']) ? $data['limit'] : 15;
        $return = array();
 
        $query = "
                    SELECT
                            DISTINCT
                            `sender_id`,
                            `reciever_id`,
                            /*fn_calculateProfilePublicStatus(`up`.`user_status`,`up`.`system_status`,`up`.`lastseen`) as `publicStatus`,*/
                            if(`chat_status` > 0,fn_calculateProfilePublicStatus(`user_status`, `system_status`, `lastseen`),0) as `cstatus`
                    FROM
                            `users_messages` AS `um`
                        JOIN
                            `conversations` AS `c`
                        ON
                            (`um`.`conversations_id` = `c`.`id`)
                        LEFT JOIN
                            `users_profiles` AS `up`
                        ON
                            `up`.`id` = IF(`sender_id` = :uid, `reciever_id`, `sender_id`)
                    WHERE
                            (`sender_id` = :sid OR `reciever_id` = :rid)
                        AND
                            `sender_id` <> `reciever_id`
                        AND
                            `up`.`is_page` IS NULL
                    GROUP BY
                            `um`.`conversations_id`
                    ORDER BY
                            `send_time` DESC
                    LIMIT
                            0,$limit";
 
        $prep = array("uid" => $uid, "sid" => $uid, "rid" => $uid);
        
        $result = $this->db->prepare($query, $prep)->execute()->fetchAllAssoc()->result;
        $exclude = array();
        $statuses=unserialize(USER_STATUS_STATEMENTS);
        $chatstatuses=unserialize(CHAT_STATUS_STATEMENTS);
 
        foreach ( $result as $r ) {
            $id = $r['sender_id'] == $uid ? $r['reciever_id'] : $r['sender_id'];
            $user = \Helpers\Users::getUserShortInfoFromId($id);
 
            $status=array();
            $status['status_id']=\Helpers\Users::getInstance()->getProfilePublicStatus($id);//$r['publicStatus'];
            $status['status_name']=$statuses[$status['status_id']];
            $chatStatus=array();
            $chatStatus['status_id']=\Helpers\Users::getInstance()->getProfilePublicChatStatus($id);
            $chatStatus['status_name']=$chatstatuses[$chatStatus['status_id']];
            $user['status']=$status;
            $user['chat_status']=$chatStatus;
 
            if (!in_array($id, $exclude)) {
                $return[] = $user;
                $exclude[] = $id;
                //\Helpers\Debug::log($id,$status);
            }
        }
 
 
        if ( count($return) < $limit ) {
            $data["limit"] = $limit - count($return);
            $data["exclude_ids"] = $exclude;
            $additional = $this->myContactsSuggester($data);
 
            if ($additional[0] && count($additional[1]['profiles']) > 0) {
                $return = array_merge($return, $additional[1]['profiles']);
            }
        }
 
        foreach ( $return as &$r ) {
            $r['profile_tags']  = $r['tags']['profile'];
        }
 
        return array(true, array("profiles" => $return));
    }
    /* DEPRECATED
  public function followSuggester($data) {
    return array(true);
    $users   = array();
    $offset = $data['offset'] || 0;
    $lat = $this->app->request->ipInfo["latitude"];
        $long = $this->app->request->ipInfo["longitude"];
    $userTags   = array();
    //$results = $this->db->q( "SELECT `name`, `type` FROM `users_tags` WHERE `user_id` = :uid", array("uid" => $_SESSION['profile']['id']));
    $results = $this->db->prepare( "SELECT `name`, `type` FROM `users_tags` WHERE `user_id` = :uid", array("uid" => $_SESSION['profile']['id']))->execute()->fetchAllAssoc()->cache()->result;
 
    foreach ( $results as $el ) {
      $userTags[ $el[ 'type' ] ][]   = ucwords(strtolower($el['name']));
    }
 
    // foreach ( $userTags as $k => $e ) {
    //   $tagsCache[$k] = implode( " ", str_replace( " ", "_", $e ) );
    // }
 
    $query   = "
      SELECT
        `upr`.`id` as `user_id`,
        `sc`.`short_info`,
        " . ( ( $lat != 0 && $long != 0 ) ? "IF ( " . ($lat - 10) . " < `upr`.`lat` AND `upr`.`lat` < " . ($lat + 10) . " AND " . ($long - 10) . " < `upr`.`long` AND `upr`.`long` < " . ($long + 10) . ", 1, 0 )" : "0" ) . " as `is_near`,
        MATCH (instruments) AGAINST (:instruments IN NATURAL LANGUAGE MODE) AS score_instruments,
        MATCH (genres) AGAINST (:genres IN NATURAL LANGUAGE MODE) AS score_genres,
        MATCH (influences) AGAINST (:influences IN NATURAL LANGUAGE MODE) AS score_influences,
        IF ( `ucm`.`id` IS NOT NULL, 1, 0 ) as `followed`,
        IF ( `uc`.`id` IS NOT NULL, 1, 0 ) as `has_posts`
      FROM
        `users_profiles` `upr`
      JOIN
        `users_elements` `upi`
          ON
            (`upr`.`id` = `upi`.`creator_id` AND `upr`.`profile_album_id` = `upi`.`album_id` AND `upi`.`type`='picture' AND `upi`.`is_default` = '1' AND `upi`.`deleted_time` IS NULL AND `upi`.`published_time` IS NOT NULL AND `upi`.`deployed_time` IS NOT NULL)
      JOIN
        `search_cache` `sc`
          ON
            (`sc`.`user_id` = `upr`.`id`)
      LEFT JOIN
        `users_comments` `uc`
          ON
            (`uc`.`owner_element_id` = `upr`.`id` AND `uc`.`model` = 'post')
      LEFT JOIN
        `users_circles_members` `ucm`
          ON
            (`ucm`.`user_id` = :uid AND `ucm`.`target_user_id` = `upr`.`id`)
      WHERE
        `upr`.`id` != :uuid
      GROUP BY
        `uc`.`owner_element_id`
      HAVING
        `followed` = 0
      ORDER BY
        `score_genres` DESC,
        `score_influences` DESC,
        `score_instruments` DESC,
        `is_near` DESC,
        `has_posts` DESC
      LIMIT
        :offset, 9
    ";
 
    $results = $this->db->prepare($query, array(
      'offset'     => $offset,
      'uid'       => $_SESSION['profile']['id'],
      'uuid'       => $_SESSION['profile']['id'],
      'genres'     => ( isset( $userTags['genres'] )     ? implode( " ", str_replace( " ", "_", $userTags['genres'] ) ) : '' ),
      'instruments'   => ( isset( $userTags['instruments'] )   ? implode( " ", str_replace( " ", "_", $userTags['instruments'] ) ) : '' ),
      'influences'   => ( isset( $userTags['influences'] )   ? implode( " ", str_replace( " ", "_", $userTags['influences'] ) ) : '' )
    ))->execute()->fetchAllAssoc()->cache()->result;
 
 
    foreach ( $results as $result ) {
      $user = unserialize( $result['short_info'] );
 
      if ( $user != false ) {
        $users[] = $user;
      }
    }
 
    return array(true, $users);
  }*/
 
  public function searchSuggester($data) {
        $results = isset($data['limit']) ? $data['limit'] : \Config::defaults()->RESULTS_TO_SHOW->search;
        if ($results > \Config::defaults()->MAX_RESULTS_TO_SHOW->search) {
            $results = \Config::defaults()->MAX_RESULTS_TO_SHOW->search;
        }
 
        $reminder = $result % 2;
 
        $search = $data['autocomplete'];
        $msc = microtime(true);
        if (!isset($data['skipTags']) || !$data['skipTags']) {
            $tags = $this->tagsSuggester(array('autocomplete' => $search, 'limit' => (int) ($results / 2) + $reminder));
        }
 
        $profiles = [];
        $pages = [];
        if (isset($data['skipPages']) || $data['skipPages'] == 'true') { // legacy string true
            $profilesRes = $this->search(array('autocomplete' => $search, 'show_profiles' => true, 'visible' => $data['visible']));
            if ($profilesRes[0]) {
                $profiles = $profilesRes[1];
            }
        } else { // search both users and pages and separate them here
 
            $searchRes = $this->search(['autocomplete' => $search, 'visible' => $data['visible']]); 
 
            if ($searchRes[0]) {
                foreach ($searchRes[1] as $i => $sr) {
                    if ($i == 0) {
                        $firstType = $sr['is_page'] ? "page" : "profile";
                    }
                    if ($sr['is_page']) {
                        $pages[] = $sr;
                    } else {
                        $profiles[] = $sr;
                    }
                }
            }
        }
 
        $communities = \Helpers\Tags::getCommunities(null, false, $search, $results);
 
 
        $sCtrl = new \Controllers\API\SearchController();
        $posts = $sCtrl->searchPosts($data);
        
        $msc=microtime(true)-$msc;
        $res=array();
        $res['profiles'] = $profiles;
        $res['pages'] = $pages;
        $res['posts'] = $posts;
        $res['communities'] = $communities;
        $res['tags'] = $tags[0] ? $tags[1]['tags'] : array();
        $res['first_type'] = isset($firstType) ? $firstType : false;
        return array(true, $res);
    }
 
    public function getFacebookFriends ()
    {
        $uid = (int)$this->app->session->profile['id'];
        $friends = \Helpers\Users::getUnfollowedFacebookFriends($uid);
        return [true, $friends];
    }
 
    //polzva se samo za chat-a, no i tam e spriano i vrushta []
    public function generatePeopleSuggestions ($data) {
        return array(true,[]);
        if (isset($data['chat'])) {
            $uid = $_SESSION["profile"]["id"];
            $usedUids = [];
            if (isset($data['used_ids']) AND is_array($data['used_ids'])) {
                $usedUids = $data['used_ids'];
            }
 
            $result = [
                'explore' =>  \Helpers\Suggestions::generateExploreSuggestions($uid, $usedUids)
            ];
 
            return array(true, $result);
        }
 
        if (!isset($data["sideMenu"]) && !isset($data["explore"])) {
            return array(
                "sideMenu"   => array(),
                "explore"  => array()
            );
        }
 
        $result = \Helpers\Suggestions::generateSuggestions($_SESSION['profile']['id'], $data["sideMenu"], $data["explore"]);
 
        if (!$result) {
            return array(false, "insert/update failed");
        }
 
        return array(true, $result);
    }
 
  public function tagsSuggesterDEPRECATED($data){
    // DEPRECATED !!!!!! ! !! !
// DONE elasticsearch!
        
    $search=trim($data['search']);
    $type = $data['type'];
 
    if(!$search) return array(true,array());
        if ( strlen($search) < 3 ) {
            return [true, []];
        }
 
    $limit = isset($data['limit']) ? intval($data['limit']) : \Config::defaults()->RESULTS_TO_SHOW->search;
    if($limit > \Config::defaults()->MAX_RESULTS_TO_SHOW->search){
      $limit = \Config::defaults()->MAX_RESULTS_TO_SHOW->search;
    }
 
    $offset = isset($data['offset']) ? intval($data['offset']) : 0;
        $lang = \Helpers\Basic::lang();
        
        $regex = new \MongoDB\BSON\Regex('^'.$search, 'i');
        
        if ($type == "" || $type == "all") {
            $conditions = [
                "translation.{$lang}.text" => $regex,
            ];
 
            $project = [
                "type" => 1,
                'translation' => 1,
                'posts_rating' => ['$sum' => ['$type.posts.moderator_rating', '$type.posts.user_rating']],
                'profile_rating' => ['$sum' => ['$type.profile.moderator_rating', '$type.profile.user_rating']],
                'instruments_rating' => ['$sum' => ['$type.instruments.moderator_rating', '$type.instruments.user_rating']],
                'influences_rating' => ['$sum' => ['$type.influences.moderator_rating', '$type.influences.user_rating']],
                'equipment_rating' => ['$sum' => ['$type.equipment.moderator_rating', '$type.equipment.user_rating']],
                'genres_rating' => ['$sum' => ['$type.genres.moderator_rating', '$type.genres.user_rating']],
                'rating' => ['$max' => ['$posts_rating', '$profile_rating', '$instruments_rating', '$influences_rating', '$equipment_rating', '$genres_rating']],
            ];
        } else {
            $conditions = [
                "translation.{$lang}.text" => $regex,
            ];
                
            $project = [
                "type.{$type}" => 1,
                'translation' => 1,
                'rating' => ['$sum' => ['$' . "type.{$type}." . 'moderator_rating', '$' . "type.{$type}." . 'user_rating']]
            ];
        }
 
        $res = \Collections\Tags::aggregate([
            ['$match' => $conditions],
            ['$project' => $project],
            ['$sort' => ['rating' => -1]],
            ['$skip' => $offset],
            ['$limit' => $limit],
        ]);
        
        $tags = [];
        foreach ($res as $tag) {
            $tagName = mb_convert_case(stripslashes($tag->translation->{$lang}->text), MB_CASE_TITLE, 'UTF-8');
            foreach ($tag->type as $type => $notneed) {
                $tags[$type][] = ['index' => \Helpers\Tags::transformIndex($tagName), "url" => \Helpers\Basic::getUrl($tagName, $type), "name"=> $tagName ];
            }
        }
        
    return array(true,array("tags"=>$tags));
  }
    
    public function tagsSuggester($data) {
        $search = trim($data['search']);
        $type = $data['type'];
 
        if (!$search) {
            return [true, []];
        }
        if (strlen($search) < 3) {
            return [true, []];
        }
 
        $limit = isset($data['limit']) ? intval($data['limit']) : \Config::defaults()->RESULTS_TO_SHOW->search;
        if ($limit > \Config::defaults()->MAX_RESULTS_TO_SHOW->search) {
            $limit = \Config::defaults()->MAX_RESULTS_TO_SHOW->search;
        }
 
        $offset = isset($data['offset']) ? intval($data['offset']) : 0;
        $lang = \Helpers\Basic::lang();
        
        $sort = [["rating" => "desc"]];
        
        $bool = [];
        
        if (strlen($type) && $type != 'all') {
            $tagTypes = unserialize(TAGS_TYPES);
            if (!in_array($type, $tagTypes)) {
                return [true, []];    
            }
            $bool['filter'] = [
                'term' => [
                    'types' => $type
                ]
            ];
        }
        
        $bool["must"] = [
            [
                "match_phrase_prefix" => [
                    "text" => $search
                ]
            ]
        ];
        $query = ["bool" => $bool];
        
        $params = [
            "from" => $offset,
            "size" => $limit,
            "index" => \Config::elasticsearch()->INDICES->tags->{$lang},
//            "explain" => 1,
            "body" => [
                "query" => $query,
                "sort" => $sort,
            ]
        ];
            
        $dsm = $this->app->droobleSearchManager;
        $result = $dsm->search($params);
        
        $tags = [];
        
        foreach ($result['hits']['hits'] as $item){
            $tagName = mb_convert_case(stripslashes($item['_source']['text']), MB_CASE_TITLE, 'UTF-8');
            $tags[$type][] = ["name"=> $tagName ];
        }
 
        return [true, ['tags' => $tags]];
    }
    
    // looks like I did this to track stuff for Segment, without making too many requests (due to the $data['count'] that is used for counting search results,
    // but now it just wraps the search() func
    public function advancedSearchPage($data){
        
        $searchStr = strlen(trim($data['autocomplete'])) ? $data['autocomplete'] : trim($data['search']);
        $properties =array(
                "category" => 'Home page',
                "search_query" => "'".$searchStr."'"
            );
 
        if (isset($data['profile']) && is_array($data['profile'])) {
            // create Segment properties from search tags
            foreach ($data['profile'] as $tag) {
                if (preg_match('/^[0-9a-z\s]+$/i', $tag)) {
                    $properties[ preg_replace('/\s+/', '_', strtolower($tag)) ] = true;
                }
            }
        }
 
        // why not just 0/1 ? ... strings 'true' | 'false'
        if(isset($data['bands']) && ($data['bands'] == 'true' or $data['bands'] == true)){
            $properties['bands'] = true;
        }
 
        if(isset($data['venues']) && ($data['venues'] == 'true' or $data['venues'] == true)){
            $properties['venues'] = true;
        }
 
        if(isset($data['clubs']) && ($data['clubs'] == 'true' or $data['clubs'] == true)){
            $properties['clubs'] = true;
        }
 
        if(isset($data['recording_studios']) && ($data['recording_studios'] == 'true' or $data['recording_studios'] == true)){
            $properties['recording_studios'] = true;
        }
 
        if(isset($data['rehearsal_space']) && ($data['rehearsal_space'] == 'true' or $data['rehearsal_space'] == true)){
            $properties['rehearsal_space'] = true;
        }
 
        if(isset($data['music_school']) && ($data['music_school'] == 'true' or $data['music_school'] == true)){
            $properties['music_school'] = true;
        }
 
        if(isset($data['jam']) && ($data['jam'] == 'true' or $data['jam'] == true)) {
            $properties['jammers'] = true;
        }
 
        if(isset($data['teach']) && ($data['teach'] == 'true' or $data['teach'] == true)){
            $properties['teachers'] = true;
 
            if (isset($data['price_min'])) {
                $properties['price_min'] = $data['price_min'];
            }
            if (isset($data['price_max'])) {
                $properties['price_max'] = $data['price_max'];
                
                // When its picked maximum of 150+... 
                if ((int)$data['price_max'] == 150) {
                    $properties['price_max'] = 999;
                }
            }
        }
 
        if(isset($data['available']) && ($data['available'] == 'true' or $data['available'] == true)){
            $properties['available'] = true;
        }
 
        if(isset($data['looking_bandmates']) && ($data['looking_bandmates'] == 'true' or $data['looking_bandmates'] == true)){
            $properties['looking_bandmates'] = true;
        }
 
        if(isset($data['instruments']) && $data['instruments']){
            $properties['instruments'] = $data['instruments'];
        }
 
        if(isset($data['instruments_primary_to_join']) && $data['instruments_primary_to_join']){
            $properties['instruments_primary_to_join'] = $data['instruments_primary_to_join'];
        }
 
        if(isset($data['instruments_primary_for_bandmates']) && $data['instruments_primary_for_bandmates']){
            $properties['instruments_primary_for_bandmates'] = $data['instruments_primary_for_bandmates'];
        }
 
        if(isset($data['genres']) && $data['genres']){
            $properties['genres'] = $data['genres'];
        }
 
        if(isset($data['influences']) && $data['influences']){
            $properties['influences'] = $data['influences'];
        }
 
        if(isset($data['equipment']) && $data['equipment']){
            $properties['equipment'] = $data['equipment'];
        }
 
        if(isset($data['languages']) && $data['languages']){
            $properties['languages'] = $data['languages'];
        }
 
        if(isset($data['location']) && $data['location']){
            $properties['location'] = $data['location'];
        }
 
        if(isset($data['place']) && $data['place']){
            $properties['place'] = $data['place'];
        }
 
        $track = array();
 
        $track['event'] = "Advanced Search";
 
        if($this->app->session->has('profile')){
            $track['userId'] = $this->app->session->get('profile')['id'];
        }else{
//            $track['anonymousId'] = rand(100000,999999);
        }
 
        $track['properties'] = $properties;
 
        if( $this->app->session->has('profile') && (!isset($data['count']) or $data['count'] == 2) ){
            if(!$_COOKIE["druast"]) {
                $secureConnection = strtolower(\Helpers\Basic::getInstance()->get_php_http_scheme()) == 'https' ? true : false;
                setcookie("druast", date("Y-m-d"), time() + 60 * 60, '/', NULL, $secureConnection, true); // 60 minutes
                \Drooble\Activities\Activities::add( "advancedSearch", $track['userId'], (object) ['type' => 'user', 'id' => $track['userId']], ['segment' => ['track' => $track]] );
            }
        }
 
        return $this->search($data);
 
    }
    
    // $data['autocomplete'] string - the search works as autocomplete e.g. Joh -> John
    // $data['search'] string - the search works as search (bad at autocomplete, but with more accurate results)
    // TODO - think if possible to use instead of getContacts - you'll need your 'followings/folloers' on top of results
    public function search($data) {
        $go_search=false;
    foreach(array_keys($data) as $e){
      if(!in_array($e, array_keys( get_object_vars( \Config::api()->MAP->search->data ) ))){
        return array(false,"input error1 '".$e."'");
      }
      if($data[$e]){
        $go_search=true;
      }
    }
    if(!$go_search){
      return array(false,"empty search");
    }
        
        $dsm = $this->app->droobleSearchManager;
        $lang = \Helpers\Basic::lang();
        $analyzer = strtolower(\Config::languages()->language->{$lang}->name_in_english);
        $offset = $data['offset'] ? $data['offset'] : 0;
        $results = isset($data['limit']) ? $data['limit'] : \Config::defaults()->RESULTS_TO_SHOW->search;
    if($results > 100){ // way too many
      $results = \Config::defaults()->MAX_RESULTS_TO_SHOW->search;
    }
        
        $count = false;
       
       $pageTypeIds = [];
       $matchTags = [
           'profile' => [],
           'instruments' => [],
           'genres' => [],
           'equipment' => [],
           'influences' => [],
           'languages' => [],
           'instruments_primary_to_join' => [],
           'instruments_primary_for_bandmates' => [],
       ];
       
       if ((int) $data['venues'] == 1) {
            $pageTypeIds[] = 106;
        }
 
        if ((int) $data['clubs'] == 1) {
            $pageTypeIds[] = 107;
        }
 
        if ((int) $data['music_school'] == 1) {
            $pageTypeIds[] = 120;
        }
 
        if ((int) $data['recording_studios'] == 1) {
            $pageTypeIds[] = 115;
        }
 
        if ((int) $data['rehearsal_space'] == 1) {
            $pageTypeIds[] = 128;
        }
 
        if (is_array($data['instruments']) AND ! empty($data['instruments'])) {
           foreach ($data['instruments'] as $item) {
               $matchTags['instruments']['should'][] = $item;
           }
       }
       if (is_array($data['genres']) AND ! empty($data['genres'])) {
           foreach ($data['genres'] as $genre) {
               $matchTags['genres']['should'][] = $genre;
           }
       }
       if (is_array($data['equipment']) AND ! empty($data['equipment'])) {
           foreach ($data['equipment'] as $item) {
               $matchTags['equipment']['should'][] = $item;
           }
       }
       if (is_array($data['influences']) AND ! empty($data['influences'])) {
           foreach ($data['influences'] as $item) {
               $matchTags['influences']['should'][] = $item;
           }
       }
       if (is_array($data['languages']) AND ! empty($data['languages'])) {
           foreach ($data['languages'] as $item) {
               $matchTags['languages']['should'][] = $item;
           }
       }
       // these are reversed ... to match users who want to play an instrument and bands that are looking for the same
       if (is_array($data['instruments_primary_to_join']) AND ! empty($data['instruments_primary_to_join'])) {
           foreach ($data['instruments_primary_to_join'] as $item) {
               $matchTags['instruments_primary_for_bandmates']['should'][] = $item;
           }
       }
       if (is_array($data['instruments_primary_for_bandmates']) AND ! empty($data['instruments_primary_for_bandmates'])) {
           foreach ($data['instruments_primary_for_bandmates'] as $item) {
               $matchTags['instruments_primary_to_join']['should'][] = $item;//
           }
       }
       
        if (is_array($data['profile']) AND !empty($data['profile'])) {
            $tmp = array();
            foreach ($data['profile'] as $el) {
                if (!trim($el))
                    continue;
                
                // Some predefined
                // TODO config with translations in all languages VS cache ...
                if ($el == $this->app->translate->get('drummers')) {
                    $tag = \Helpers\Tags::findByText('drums', null);
                    if ($tag) {
                        $matchTags['instruments']['should'][] = $tag->name;
                    }
                    $tag = \Helpers\Tags::findByText('drummer', null);
                    if ($tag) {
                        $matchTags['profile']['should'][] = $tag->name;
                    }
                }
 
                if ($el == $this->app->translate->get('singers') || $el == $this->app->translate->get('vocalists')) {
                    $tag = \Helpers\Tags::findByText('vocals', null);
                    if ($tag) {
                        $matchTags['profile']['should'][] = $tag->name;
                        $matchTags['instruments']['should'][] = $tag->name;
                    }
                    $tag = \Helpers\Tags::findByText('singer', null);
                    if ($tag) {
                        $matchTags['profile']['should'][] = $tag->name;
                    }
                    $tag = \Helpers\Tags::findByText('vocalist', null);
                    if ($tag) {
                        $matchTags['profile']['should'][] = $tag->name;
                    }
                }
 
                if ($el == $this->app->translate->get('guitarists')) {
                    $tag = \Helpers\Tags::findByText($this->app->translate->get('guitar'), null);
                    if ($tag) {
                        $matchTags['instruments']['should'][] = $tag->name;
                    }
                    $tag = \Helpers\Tags::findByText($this->app->translate->get('guitarist'), null);
                    if ($tag) {
                        $matchTags['profile']['should'][] = $tag->name;
                    }
                    $tag = \Helpers\Tags::findByText($this->app->translate->get('acoustic_guitar'), null);
                    if ($tag) {
                        $matchTags['instruments']['should'][] = $tag->name;
                    }
                    $tag = \Helpers\Tags::findByText($this->app->translate->get('electric_guitar'), null);
                    if ($tag) {
                        $matchTags['instruments']['should'][] = $tag->name;
                    }
                }
 
                if ($el == $this->app->translate->get('bass_players') || $el == $this->app->translate->get('bassists')) {
                    $tag = \Helpers\Tags::findByText($this->app->translate->get('bass'), null);
                    if ($tag) {
                        $matchTags['instruments']['should'][] = $tag->name;
                    }
                    $tag = \Helpers\Tags::findByText($this->app->translate->get('bass_player'), null);
                    if ($tag) {
                        $matchTags['profile']['should'][] = $tag->name;
                    }
                    $tag = \Helpers\Tags::findByText($this->app->translate->get('bassist'), null);
                    if ($tag) {
                        $matchTags['profile']['should'][] = $tag->name;
                    }
                }
 
                if ($el == $this->app->translate->get('producers')) {
                    $tag = \Helpers\Tags::findByText($this->app->translate->get('producer'), null);
                    if ($tag) {
                        $matchTags['profile']['should'][] = $tag->name;
                    }
                }
 
                if ($el == $this->app->translate->get('musicians')) {
                    $tag = \Helpers\Tags::findByText($this->app->translate->get('musician'), null);
                    if ($tag) {
                        $matchTags['profile']['should'][] = $tag->name;
                    }
                }
            }
        }
        
        $bool = [];
        $must = [];
        $should = [];
        
        if (isset($data['autocomplete']) && trim($data['autocomplete'])) {
            $searchStr = trim($data['autocomplete']);
            // phrase_prefix is the "poorman's autocomplete" of Elastic. Works great
            // In future we can create more complex autocomplete, but it's totally unnecessary for now
            $combineType = "phrase_prefix";
            $minLength = 2;
        } else {
            $searchStr = trim($data['search']);
            $combineType = "cross_fields";
            $minLength = 3;
        }
        
        if (strtolower($searchStr) == "search" || strtolower($searchStr) == "%%tag%%") {
            try {
                // we got weird "search" requests .. log em
//                \Helpers\Debug::log("weird search ", $_SERVER['HTTP_REFERER']);
//                \Helpers\Debug::log($_SERVER['REQUEST_URI']);
//                \Helpers\Debug::log($_SERVER['QUERY_STRING']);
//                \Helpers\Debug::log($_SERVER['HTTP_USER_AGENT']);
            } catch (\Exception $e) {
                
            }
        }
        
        if (strlen($searchStr) >= $minLength) {
            // keep search string for 'tag' cloud of frequent searches
            \Helpers\SearchTags::add($searchStr);
            $combine = [];
            $combine[] = [
                "multi_match" => [
                    "query" => $searchStr,
                    "analyzer" => $analyzer,
                    "type" => $combineType,
                    "fields" => [
                        "name^5", // full name of User / Page
                        "username^1",
//                        "about.title^4",
//                        "about.text^2",
//                        "type.{$lang}^2", // Pages only
//                        "tags.profile.{$lang}^3",
//                        "tags.equipment.{$lang}^2",
//                        "tags.genres.{$lang}^2", // Pages only
//                        "tags.genres_primary.{$lang}^2",
//                        "tags.genres_secondary.{$lang}",
//                        "tags.influences.{$lang}^2",
//                        "tags.instruments_primary.{$lang}^2",
//                        "tags.instruments_secondary.{$lang}",
//                        "tags.languages.{$lang}",
                    ]
                ]
            ];
            $combine[] = [
                "multi_match" => [
                    "query" => $searchStr,
                    "analyzer" => $analyzer,
                    "fields" => [
                        "name^3", // full name of User / Page
//                        "username^1",
//                        "about.title^3",
//                        "about.text^2",
//                        "type.{$lang}^2", // Pages only
//                        "tags.profile.{$lang}^3",
//                        "tags.equipment.{$lang}^2",
//                        "tags.genres.{$lang}^2", // Pages only
//                        "tags.genres_primary.{$lang}^2",
//                        "tags.genres_secondary.{$lang}",
//                        "tags.influences.{$lang}^2",
//                        "tags.instruments_primary.{$lang}^2",
//                        "tags.instruments_secondary.{$lang}",
//                        "tags.languages.{$lang}",
                    ],
                    "fuzziness" => "AUTO"
                ]
            ];
            $must[] = ['bool' => ['should' => $combine, "boost" => 5]];
        }
 
        /*        
        if ($data['teach'] == 1) {
            $must[] = ['match' => ['teach' => 1]];
        }
        
        if (intval($data['price_min']) > 0 || intval($data['price_max']) > 0) {
            $price = [];
            if (intval($data['price_min']) > 0) {
                $this_drooble_fee = (intval($data['price_min']) / (\Config::defaults()->DROOBLE_FEE_IN_PERCENTS + 100) * \Config::defaults()->DROOBLE_FEE_IN_PERCENTS);
                $price_from = intval($data['price_min']) - $this_drooble_fee;
                if ($price_from < 0) {
                    $price_from = 0;
                }
                $price['gte'] = intval($price_from);
            }
 
            if (intval($data['price_max']) > 0) {
                $this_drooble_fee = (intval($data['price_max']) / (\Config::defaults()->DROOBLE_FEE_IN_PERCENTS + 100) * \Config::defaults()->DROOBLE_FEE_IN_PERCENTS);
                $price_to = intval($data['price_max']) - $this_drooble_fee;
                if ($price_to < 0) {
                    $price_to = 0;
                }
                $price['lte'] = intval($price_to);
            }
            $must[] = ['range' => ['price' => $price] ];
        }
         */
        
 
        if ($data['place']) {
            $data['place'] = str_replace("'", "", $data['place']);
            $data['place'] = str_replace("`", "", $data['place']);
            $data['place'] = str_replace("\"", "", $data['place']);
            
            $place = \Helpers\Basic::getGooglePlace($data['place']);
            if ($place && $place->is_country) {
                $must[] = ['match' => ['country' => $place->country_code]];
            } else {
                $must[] = ['match' => ['city' => $data['place']]];
            }
            
        }elseif($data['location']){
            $google_places = new \Lib\GooglePlaces(\Config::googleplaces()->API_KEY);
 
            $cor = array($this->app->request->ipInfo['latitude'], $this->app->request->ipInfo['longitude']);
 
            $google_places->location = $cor;
            $google_places->radius   = 100;
            $google_places->rankby   = 'distance';
            $google_places->types    = '(cities)';
            $google_places->input    = $data['location'];
            $google_places_result = $google_places->autocomplete();
            $searched_places_ids = array();
 
            if($google_places_result['status'] == 'OK' AND count($google_places_result['predictions'])>0){
                foreach($google_places_result['predictions'] as $__prediction){
                    $searched_places_ids[] = $__prediction['place_id'];
                }
            }
            if (count($searched_places_ids)) {
                $tmp = [];
                foreach ($searched_places_ids as $placeid) {
                    $tmp[] = ['match' => ["city" => $placeid]];
                }
                $must[] = ['bool' => ['should' => $tmp]];
            }
        }
        
        if (count($data['tags'])) {
            foreach ($data['tags'] as $tag) {
                foreach ($matchTags as $type => $info) {
                    $matchTags[$type]['should'][] = $tag;
                }
            }
        }
        foreach ($matchTags as $type => $info) {
            foreach ($info as $boolOperator => $tags) { // $boolOperator fills $must or $should arrays
                if (count($tags)) {
                    foreach ($tags as $tag) {
                        if ($type == "genres") {
                            ${$boolOperator}[] = ['match' => ["tags.genres.{$lang}" => $tag]]; // this is for Pages
                            ${$boolOperator}[] = ['match' => ["tags.genres_primary.{$lang}" => $tag]];
                            ${$boolOperator}[] = ['match' => ["tags.genres_secondary.{$lang}" => $tag]];
 
                        } elseif ($type == "instruments") {
                            ${$boolOperator}[] = ['match' => ["tags.instruments_primary.{$lang}" => $tag]];
                            ${$boolOperator}[] = ['match' => ["tags.instruments_secondary.{$lang}" => $tag]];
                        } else {
                            ${$boolOperator}[] = ['match' => ["tags.{$type}.{$lang}" => $tag]];
                        }
                        
                        /* this here was again because someone doesn't follow YAGNI
                        if ($type == "genres") {
                            $tmp[] = ['match_phrase' => ["tags.genres.{$lang}" => $tag]]; // this is for Pages
                            $tmp[] = ['match_phrase' => ["tags.genres_primary.{$lang}" => $tag]];
                            $tmp[] = ['match_phrase' => ["tags.genres_secondary.{$lang}" => $tag]];
 
                        } elseif ($type == "instruments") {
                            $tmp[] = ['match_phrase' => ["tags.instruments_primary.{$lang}" => $tag]];
                            $tmp[] = ['match_phrase' => ["tags.instruments_secondary.{$lang}" => $tag]];
                        } else {
                            $tmp[] = ['match_phrase' => ["tags.{$type}.{$lang}" => $tag]];
                        }
                        if ($tmp) {
                            ${$boolOperator}[] = ['bool' => ['should' => $tmp]];
                        }*/
                        
                    }
                }
            }
        }
        
//        if ($data['visible'] == 'profile_completed') {
//            $must[] = [
//                'term' => [
//                    'visibility' => 'profile_completed'
//                ]
//            ];
//        }
 
        if (count($pageTypeIds)) {
            $must[] = ['terms' => ['page_type_id' => $pageTypeIds]];
        }
        
        if (count($must)) {
            $bool['must'] = $must;
        }
        
        if (count($should)) {
            $bool['should'] = $should;
            $bool['minimum_should_match'] = 1;
            // setting minimum_should_match would mess up the has_avatar boost
        }
        
//        if ($data['autocomplete']) {
//            $indexStr = \Config::elasticsearch()->INDICES->users . ',' . \Config::elasticsearch()->INDICES->pages;
            $indexStr = \Config::elasticsearch()->INDICES->users;
//        } else {
//            $indexStr = \Config::elasticsearch()->INDICES->users;
//        }
//        if ((int) $data['bands'] == 1 || count($pageTypeIds) || $data['show_pages'] || $data['type'] == "pages") {
//            $indexStr = \Config::elasticsearch()->INDICES->pages;
//        }
        if ($data['show_profiles'] /*|| $data['type'] == "people" */) {
            $indexStr = \Config::elasticsearch()->INDICES->users;
        }
        if (count($bool)) {
            // boost has_avatar. I personally argued that we should not boost, but rather filter by has_avatar, but it was pointless .. so enjoy
//            if (!isset($tagBoost)) { // NO SOLUTION for now ...
//                $bool['should'][] = ['term' => ['has_avatar' => ["value" => true, "boost" => 100 ]]];
//            }
//            // enough BS
//            if ($indexStr == \Config::elasticsearch()->INDICES->users) {
//                $bool['should'][] = ['exists' => ['field' => 'tags.profile', 'boost' => 80]];
//                $bool['should'][] = ['exists' => ['field' => 'tags.instruments_primary', 'boost' => 70]];
//                $bool['should'][] = ['exists' => ['field' => 'tags.genres_primary', 'boost' => 60]];
//            } else {
//                $bool['should'][] = ['exists' => ['field' => 'tags.genres', 'boost' => 80]];
//                $bool['should'][] = ['exists' => ['field' => 'tags.influences', 'boost' => 70]];
//            }
            $query = ["bool" => $bool];
        } else {
            // empty search
            $bool = [
                'must' => [
                    ['match' => ['has_avatar' => true]]
                ]
            ];
            // enough BS
//            if ($indexStr == \Config::elasticsearch()->INDICES->users) {
//                $boostTags = [
//                    ['exists' => ['field' => 'tags.profile', 'boost' => 80]],
//                    ['exists' => ['field' => 'tags.instruments_primary', 'boost' => 70]],
//                    ['exists' => ['field' => 'tags.genres_primary', 'boost' => 60]],
//                ];
//            } else {
//                $boostTags = [
//                    ['exists' => ['field' => 'tags.genres', 'boost' => 80]],
//                    ['exists' => ['field' => 'tags.influences', 'boost' => 70]],
//                ];
//            }
//            if (isset($boostTags)) {
//                $bool['should'] = $boostTags;
//            }
            $query = [
                'function_score' => [
                    'query' => ['bool' => $bool],
                    'random_score' => new \stdClass()
                ]
            ];
        }
        
        $sort = [["_score" => "desc"]]; // second and third sort params were ["has_avatar" => "desc"], ["lastseen" => "desc"] they are insignificant
        
        $params = [
            "from" => $offset,
            "size" => $results,
            "index" => $indexStr,
//            "explain" => 1,
            "body" => [
                "query" => $query,
                "sort" => $sort,
            ]
        ];
 
        //\Helpers\Debug::log('dht', json_encode($params));
 
        $result = $dsm->search($params);
        
        if ((int) $data['count'] === 1) { //OMG all the legacy .. humanity
            $count = $result['hits']['total'];
            return array(true, $count);
        } else {
            return $this->viewSearchUsersAndPages($result, $searchStr);
        }
    }
    
    /*
     * @result DroobleSearchManager search result (Elastic client search result)
     * @searchStr - string from search (needed to 'inject' the search string as a Tag to all Users in the View)
     */
    public function viewSearchUsersAndPages($result, $searchStr) {
        $lang = \Helpers\Basic::lang();
        $hitIds = [];
        foreach ($result['hits']['hits'] as $item){
            $hitIds[] = $item['_id'];
        }
        $hitIdsImploded = implode(",", $hitIds);
        $count = $result['hits']['total'];
        $ret = [];
        // get data for hits
        if ($count) {
            
            if (strlen($searchStr)) {
                $foundTag = \Helpers\Tags::findByText($searchStr, null);
            }
            
            $elsCtrl = new \Controllers\API\ElementsController();
            
            $sql="
                SELECT
                    `search_cache`.`short_info_json` ,
                    `up`.`id`,
                    `up`.`user_status`,
                    `up`.`system_status`,
                    `up`.`lastseen`,
                    `up`.`is_page`,
                    IF ( `ucm`.`id` IS NOT NULL, 1, 0 ) as `isInCircle`
                FROM
                    `search_cache`
                JOIN
                    `users_profiles` `up`
                        ON
                            `up`.`id` = `search_cache`.`user_id`
                LEFT JOIN
                    `users_circles_members` `ucm`
                        ON
                            (`ucm`.`user_id` = :fuid AND `ucm`.`target_user_id` = `up`.`id`)
                WHERE `up`.`id` IN(" . $hitIdsImploded . ")
                    ORDER BY FIELD(up.id, " . $hitIdsImploded . ")
                    LIMIT " . $count . "
                   ;";
 
            $prepare = [ 'fuid' => $_SESSION['profile']['id'] ];
            $qres = $this->app->db->query($sql, $prepare);
            $qres->setFetchMode(\Phalcon\Db::FETCH_ASSOC);
            $qelements = $qres->fetchAll();
            foreach ($qelements as $el) {
                $a = json_decode($el['short_info_json'], true);
                if ($a != false) {
                    $a['uid'] = $el['id'];
                    $a['isInCircle'] = $el['isInCircle'];
//                    $a['status'] = $el['publicStatus'];
                    $a['is_page'] = $el['is_page'];
//                    if ($a['rates']['session_price'] and $el['id'] != $_SESSION['profile']['id']) {
//                        $a['rates']['session_price'] = floatval($a['rates']['session_price']) + floatval($a['rates']['session_price']) * ( \Config::defaults()->DROOBLE_FEE_IN_PERCENTS / 100 );
//                    }
                    if ((int) $count >= 0) {
                        $a['counter'] = $count;
                    }
                    $tags = \Helpers\Users::getUserTags($a['uid'], 3); // get with 3 - categorized with remapped type, cause otherwise you'll get random instruments (they have 4 subtypes)
                    
                    if ($a['uid'] == 1) {
                        $a['open_to_string'] = "All music";
                    } else {
                        $open_to_string = "";
                        if ( count($tags['profile']) > 1 ) {
                            $open_to_string = $tags['profile'][0] . " " . $this->app->translate->get('and') . " " . $tags['profile'][1];
                        } elseif (count($tags['profile']) == 1) {
                            $open_to_string = $tags['profile'][0];
                        }
                        $a['open_to_string'] = $open_to_string;
                    }
                    
                    $flatList = [];
                    foreach ($tags as $arr) {
                        foreach ($arr as $tag) {
                            $flatList[] = $tag;
                        }
                    }
                    if ($foundTag) {
                        $searchAsTag = mb_convert_case(stripslashes($foundTag->translation->{$lang}->text), MB_CASE_TITLE, 'UTF-8');
                        if (!in_array($searchAsTag, array_slice($flatList, 0, 2))) {
                            // 'inject' the search string as a Tag @ first position if it's not in the top 2 tags
                            foreach ($tags as $key => $arr) {
                                array_unshift($arr, $searchAsTag);
                                $tags[$key] = $arr;
                                break;
                            }
                        }
                    }
                    // remove duplicates
                    $firstParts = array_slice($tags, 0, 3);
                    $first3keys = array_keys($firstParts);
 
                    $countTypes = count($first3keys);
                    if ($countTypes > 1) {
                        $tags[$first3keys[1]] = array_diff($tags[$first3keys[1]], $tags[$first3keys[0]]);
                    } 
                    if ($countTypes > 2) {
                        $tags[$first3keys[2]] = array_diff($tags[$first3keys[2]], $tags[$first3keys[1]]);
                        $tags[$first3keys[2]] = array_diff($tags[$first3keys[2]], $tags[$first3keys[0]]);
                    }
                        
                    $a['tags'] = $tags;
                    $ret[] = $a;
                }
            }
        }
        return [true, $ret];
 
    }
 
    public function getContacts($data) {
        return array(true, \Helpers\Search::getContactsAndTheRest($data['uid'], $data));
    }
 
}
 
?>
#3search->viewSearchUsersAndPages(Array([took] => 1, [timed_out] => , [_shards] => Array([total] => 5, [successful] => 5, [failed] => 0), [hits] => Array([total] => 10, [max_score] => 11.247131, [hits] => Array())), )
/var/www/rc/drooble/trunk/projects/drooble_v1/services/web/private/logic/search.class.php (1593)
<?php
class search{
 
  private $db;
  private $c;
    private $app;
 
  public function __construct($db,&$c, $app) {
    $this->db = $db;
    $this->c = $c;
        $this->app = $app;
  }
/*
  public function doReindex($data){
    $ret=array();
    foreach($this->db->q("SELECT `id` FROM `users_profiles` WHERE 1") as $el){
      $ret[]=$this->lib->profileSearchIndexing($el['id']);
    }
 
    return array(true,$ret);
  }
*/
    public function searchCity($data) {
        $return = new \stdClass();
 
        $return->items = \Helpers\Search::getInstance()->searchCity($data['name']);
 
        return array(true, $return);
 
 
        /*
        // SELECT *, CASE WHEN name LIKE 'Sofia%' THEN 1 ELSE 0 END AS keymatch FROM `cities` WHERE MATCH (name) AGAINST ('+Sofia' IN BOOLEAN MODE) ORDER BY keymatch DESC
        $cities = $this->app->db->fetchAll("
            SELECT *, CASE
                WHEN search_by_me LIKE ".$this->app->db->escapeString($keywords)." THEN 4
                WHEN search_by_me LIKE ".$this->app->db->escapeString($keywords." %")." THEN 3
                WHEN search_by_me LIKE ".$this->app->db->escapeString("% ".$keywords." %")." THEN 2
                WHEN search_by_me LIKE ".$this->app->db->escapeString("% ".$keywords)." THEN 1
                ELSE 0 END AS keymatch
            FROM `cities`
            WHERE
            MATCH (search_by_me) AGAINST (".$this->app->db->escapeString($keywords).")
            ORDER BY keymatch DESC
            LIMIT 22
        ");
 
        $items = array();
 
        if ($cities) {
            foreach($cities as $city)
            {
                $title_suffix = $city['country'];
                if ($city['region']) {
                    $title_suffix = $city['region'].", ".$city['country'];
                }
 
                $items[] = array(
                    'id' => $city['id'],
                    'title' => $city['name'].", ".$title_suffix,
                    'name' => $city['name'],
                    'country' => $city['country'],
                    'region' => $city['region']
                );
            }
        }
 
        $return->items = $items;
 
        return array(true, $return);
        */
    }
 
  public function searchByMail($data){
    $email=$data['email'];
 
    if(!filter_var($email, FILTER_VALIDATE_EMAIL)){
      return array(false,"not valid");
    }
 
        /*
    $search=$email;
    $search=str_replace("*","",$search);
    $search=str_replace(" ","",$search);
    $search=str_replace(")","",$search);
    $search=str_replace("(","",$search);
 
 
    $sql="SELECT `short_info` FROM `search_cache` WHERE MATCH (`email`) AGAINST ('".'"'.$search.'"'."')";
    $r=$this->db->one($sql);
         *
         */
 
        $person = \Models\UserCache::findFirst([
            'conditions' => 'email LIKE {email:str}',
            'bind' => ['email' => $email]
        ]);
 
        if ($person) {
            return array(true, json_decode($person->short_info_json, true));
        } else {
            return array(true,'notexists');
        }
  }
 
  public function otherPersonContactsSuggester($data){
    $user_id=\Helpers\Users::getIdFromUsername($data['profile']);
    if(isset($data['circle_id']) && $data['profile'] != $_SESSION['profile']['username']){
      unset($data['circle_id']);
    }
    if(!$data['results']){
      $data['results']=9;
    }
    if(isset($data['mix_order'])){
      $data['mix_order']=true;
    }
 
    // \Helpers\Debug::alert($data['profile']);
    if($user_id){
      return $this->ContactsSuggester($user_id,$data);
    }else{
      return array(false);
    }
  }
 
  public function myContactsSuggester($data){
 
    return $this->ContactsSuggester($_SESSION['profile']['id'],$data,true, isset($data['online_first']) ? $data['online_first'] : true);
  }
 
  public function otherPersonFollowSuggester($data) {
    $data["filterUsername"] = $data["profile"];
    unset($data["profile"]);
 
    $data["follow_type"] = "followings";
 
    return $this->ContactsSuggester($_SESSION['profile']['id'],$data,true, false);
  }
 
    public function searchInMyMessages($data){
        // Used data
        $user_id = (int)$_SESSION['profile']['id'];
        if ($data['band_id'] > 0) {
            if (!\Helpers\Basic::checkUserRights((int) $data['band_id'], $user_id)) {
                return array(false,false);
            } else {
                $user_id = (int) $data['band_id'];
            }
        }
        
        $offset = $data['offset'];
        $limit = $data['limit'];
 
        $search = trim(strip_tags($data['search']));
        $search = str_replace(array("%", "'", '"'), "", $search);
 
        $length_to_get = 50; // How much characters to get
        $symbols_before = 10; // How much symbols before match to get
 
        $return = array();
 
        // Search query
        $query = "
            SELECT * FROM
            (
                SELECT
                    um.text, um.conversations_id, c.is_group, c.members,
                    IF (um.reciever_id = '".$user_id."', scsender.short_info_json, screciever.short_info_json) as partner,
                    IF (um.reciever_id = '".$user_id."', scsender.user_id, screciever.user_id) as partner_id
                FROM users_messages as um
                LEFT JOIN conversations AS c ON c.id = um.conversations_id
                LEFT JOIN search_cache AS scsender ON scsender.user_id = um.sender_id
                LEFT JOIN search_cache AS screciever ON screciever.user_id = um.reciever_id
                WHERE
                    um.sender_id != um.reciever_id
                    AND (um.sender_id = '".$user_id."' OR um.reciever_id = '".$user_id."')
                    AND um.is_deleted=0
                    AND (
                        um.text LIKE :keywords
                        OR (scsender.cache LIKE :keywords AND scsender.user_id != '".$user_id."')
                        OR (screciever.cache LIKE :keywords AND screciever.user_id != '".$user_id."')
                    )
                ORDER BY um.send_time DESC
            ) as results
            GROUP BY results.conversations_id, results.text, results.partner, results.partner_id
        ";
 
        $prepare = array(
            'keywords' => "%".$search."%"
        );
        
        // Here we store user information - reason - when we have a user, does not look for it again
        $cache_profiles = array();
 
        // So... lets begin... just in case string was more than 3 symbols
        if (mb_strlen($search) >= 3)
        {
            $cids = [];
            // Put information
            foreach($this->db->prepare($query, $prepare)->execute()->fetchAllAssoc()->cache( 1 )->result as $el) {
                $cids[] = $el['conversations_id'];
                continue; // !!!! NOTE we'll be using getMail at end of func to parse the response
                
                //this below -- NOT executed !!! 
                
                
                $text = trim(strip_tags($el['text']));
 
                $find_position = mb_strpos($search, $text);
                $text_length = mb_strlen($text);
                $string_length = mb_strlen($search);
 
                // Stupid way but... i cant regex :/
                if ($text_length > $length_to_get)
                {
                    if (($find_position - $symbols_before) <= 0)
                    {
                        $text = mb_substr($text, 0, $length_to_get)."...";
                    }
                    else if (($find_position - $symbols_before) > 0 AND ($find_position - $symbols_before + $length_to_get) <= $text_length)
                    {
                        $text = "...".mb_substr($text, ($find_position - $symbols_before), $length_to_get)."...";
                    }
                    else
                    {
                        $text = "...".mb_substr($text, ($length_to_get*-1));
                    }
                }
 
                $line = array(
                    'quote' => $text,
                    'conversation' => "#".$el['conversations_id']
                );
 
                // Put information about users in converstation
                if ((int)$el['is_group'] == 0)
                {
                    if (!isset($cache_profiles[$el['partner_id']]))
                    {
                        $user = unserialize($el['partner']);
 
                        $cache_profiles[$el['partner_id']] = array(
                            'names' => $user['display_name']
                        );
 
                        if (isset($user['avatar_icon']) AND $user['avatar_icon'] != "")
                        {
                            $cache_profiles[$el['partner_id']]['avatar_icon'] = $user['avatar_icon'];
                        }
                    }
 
                    $line['names'] = $cache_profiles[$el['partner_id']]['names'];
                    $line['avatar_icon'] = $cache_profiles[$el['partner_id']]['avatar_icon'];
                }
                else
                {
                    // Check collection
                    $collection = array();
                    $user_ids = explode(",", $el['members']);
                    foreach($user_ids as $key => $val)
                    {
                        if ((int)$val != 0 AND (int)$val != $user_id)
                        {
                            $collection[(int)$val] = false;
                        }
                    }
 
                    // Filter which users we have
                    $search_user_ids = array();
                    foreach($collection as $key => $val)
                    {
                        if (isset($cache_profiles[$key]))
                        {
                            // Put it in our collection
                            $collection[$key] = $cache_profiles[$key]['names'];
                        }
                        else
                        {
                            $search_user_ids[] = $key;
                        }
                    }
 
                    // Get missed users
                    if (!empty($search_user_ids))
                    {
                        $query = "SELECT user_id, short_info_json FROM search_cache WHERE user_id IN (".implode(",", $search_user_ids).")";
 
                        foreach($this->db->prepare($query, array())->execute()->fetchAllAssoc()->cache( 1 )->result as $usr) {
                            $user = json_decode($usr['short_info_json'], true);
                            $cache_profiles[(int)$usr['user_id']] = array(
                                'names' => $user['display_name']
                            );
 
                            if (isset($user['avatar_icon']) AND $user['avatar_icon'] != "")
                            {
                                $cache_profiles[(int)$usr['user_id']]['avatar_icon'] = $user['avatar_icon'];
                            }
 
                            // Put it in our collection
                            $collection[(int)$usr['user_id']] = $cache_profiles[(int)$usr['user_id']]['names'];
                        }
                    }
 
                    // Now merge names
                    $line['names'] = implode(", ", $collection);
                    $line['avatar_icon'] = DEF_GROUP_CHAT_ICON;
                }
 
                // Some modifications
                // $text = str_replace($search, "<em>".$search."</em>", $text);
                $seek_and_destroy = '#\b'.$search.'\b#iu';
                $line['quote'] = preg_replace($seek_and_destroy, '<em>$0</em>', $line['quote']);
                $line['names'] = preg_replace($seek_and_destroy, '<em>$0</em>', $line['names']);
 
                $return[] = $line;
            }
        }
        
        // NOTE!!  overwrite response here
        $mc = new \Controllers\API\MessengerController();
        $cids = array_reverse($cids);
        $cids = array_slice($cids, $offset, $limit);
        $getMailData = [
            'folder' => 'All',
            'mode' => 'search',
            'results' => $limit,
            'cids' => $cids,
            'pid' => $data['band_id']
        ];
        
        $getMailRe = $mc->getMail($getMailData);
        $return = is_array($getMailRe) ? $getMailRe : ['conversations' => []];
        
        // Return
        return array(true, array("length" => count($return['conversations']) , "conversations" => $return['conversations']));
    }
 
  private function ContactsSuggester($user_id,$data,$myContactsOnly=false, $onlineFirst = false){
    $uid=$user_id;
    $prepare=array();
        
        $excludeMothership = array_key_exists('exclude_mothership', $data) ? $data['exclude_mothership'] : true;
 
    // $prepare['uid']=$uid;
    $search=$data['search'];
 
    /*if (empty($search)) {
      return array(false, 'empty search query');
    }*/
 
    $select   = array();
    $leftJoin   = array();
    $order     = array();
 
    if($myContactsOnly){
      $data['follow_type']='followings';
    }
    $search=trim($search);
    $search=str_replace("*","",$search);
    $search=str_replace(")","",$search);
    $search=str_replace("(","",$search);
    $search=str_replace(" ","* +*",$search);
 
    if($search){
      $search=$this->db->quote("*".$search."*");
    }else{
      $search=NULL;
    }
    $circle_id=$data['circle_id'];
    // $prepare['circle_id']=$circle_id;
 
        $show_status=$onlineFirst ? true : $data['show_status'];
    $latest=isset($data['latest']) ? true : false;
    $is_online=$data['is_online'];
 
    $mix_order=$data['mix_order'];
 
        $where = [
            '`up`.`is_banned` IS NULL AND `up`.`is_disabled` = 0'
        ];
        
    if($is_online){
      $show_status=1;
      $where[]="(`up`.`user_status`>0 AND `up`.`lastseen`> " . (time() - \Config::defaults()->DEF_LASTSEEN_OFFLINE_TIME).") ";
    }
 
    if (isset($data["user_type"])) {
//      $LeftJoinUserStatus="LEFT JOIN `users_profiles` AS `up` ON `up`.`id` = `sc`.`user_id` ";
 
//      if ($data["user_type"] == "page") {
//        $where[] = "`up`.`is_page` = 1";
//      } else if ($data["user_type"] == "user") {
//                $where[] = "`up`.`is_page` IS NULL";
//            }
    }
 
    if(isset($show_status)){
            $SelectUserStatus=" fn_calculateProfilePublicStatus(`up`.`user_status`,`up`.`system_status`,`up`.`lastseen`) as `publicStatus`, `up`.`id` as `user_suggest_id`,
                                if(`chat_status` > 0,fn_calculateProfilePublicStatus(`user_status`, `system_status`, `lastseen`),0) as `cstatus`,";
      //$SelectUserStatus="`up`.`user_status`, `up`.`lastseen`, `up`.`system_status`, ";
//      $LeftJoinUserStatus="LEFT JOIN `users_profiles` AS `up` ON `up`.`id` = `sc`.`user_id` ";
      $where[] = "`up`.`is_page` IS NULL";
    }
 
    if ( $data['gender'] ) {
      $select[] = "`up`.`gender`";
 
//      if ( !$LeftJoinUserStatus ) {
//        $leftJoin[] = "LEFT JOIN `users_profiles` AS `up` ON `up`.`id` = `sc`.`user_id` ";
//      }
    }
 
        if ( $data['is_page'] ) {
            //\Helpers\Debug::log('is_page passed');
      $select[] = "`up`.`is_page`";
//      if ( !$LeftJoinUserStatus ) {
//        $leftJoin[] = "LEFT JOIN `users_profiles` AS `up` ON `up`.`id` = `sc`.`user_id` ";
//      }
    }
 
    if ( $data['pictures_first'] ) {
      $select[]   = 'IF(`upi`.`id`, 1, 0) AS `has_picture`'; // moje da stane greshka, zashtoto ne znaeme koi e profile album-a
      //$leftJoin[] = "LEFT JOIN `users_pictures` AS `upi` ON ( `upi`.`user_id` = `sc`.`user_id` AND `upi`.`profilepic` = 1 ) ";
      $leftJoin[] = "LEFT JOIN `users_elements` AS `upi` ON `sc`.`id` = `upi`.`creator_id` AND `upi`.`type`='picture' AND `upi`.`is_default` = '1' AND `upi`.`deleted_time` IS NULL AND `upi`.`published_time` IS NOT NULL AND `upi`.`deployed_time` IS NOT NULL ";
      $order[]   = '`has_picture` DESC';
    }
 
    if ( $data['compare_followers'] && in_array($data['follow_type'], array('followers', 'followings')) && isset($_SESSION['profile']['id']) ) {
      $select[]   = 'IF(`my_ucm`.`id`, 1, 0) AS `same_follower`';
      $select[]   = 'IF(`my_ucm`.`id`, `my_ucm`.`circle_id`, -1) AS `circle_id`';
      $leftJoin[] = "LEFT JOIN `users_circles_members` AS `my_ucm` ON ( `my_ucm`.`user_id` = " . $_SESSION['profile']['id'] . " AND `my_ucm`.`target_user_id` = " . ( $data['follow_type'] == 'followers' ? "`ucm`.`user_id`" : "`ucm`.`target_user_id`" ) . " ) ";
      $order[]   = '`same_follower` DESC';
    }
 
    if ($data['filterUsername']) {
      $otherUserId = \Helpers\Users::getIdFromUsername($data['filterUsername']);
      if ($otherUserId) {
        $leftJoin[] = "LEFT JOIN `users_circles_members` AS `other_ucm` ON ( `other_ucm`.`user_id` = " . $otherUserId . " AND `other_ucm`.`target_user_id` = `ucm`.`target_user_id` ) ";
        $where[] = "`other_ucm`.`id` IS NULL";
        $where[] = "`up`.`id` != {$otherUserId}";
      }
    }
 
    switch($data['follow_type']){
      case "followers":
        $where[]="`ucm`.`target_user_id` = '{$uid}'";
        $leftJoinCS="LEFT JOIN `search_cache` AS `sc` ON `sc`.`user_id` = `ucm`.`user_id`";
      break;
      case "followings":
        $where[]="`ucm`.`user_id` = '{$uid}'";
        $leftJoinCS="LEFT JOIN `search_cache` AS `sc` ON `sc`.`user_id` = `ucm`.`target_user_id`";
      break;
      default:
        $leftJoinCS="LEFT JOIN `search_cache` AS `sc` ON (`sc`.`user_id` = `ucm`.`target_user_id` OR `sc`.`user_id` =`ucm`.`user_id`)";
    }
 
    $limit = " ";
    if(isset($data["limit"])){
      $prepare['limit']=$data['limit'];
      $limit = " LIMIT :limit ";
 
      if (isset($data["offset"])) {
        $prepare['offset']=$data['offset'];
        $limit .= "OFFSET :offset ";
      }
    }
 
    if($search){
      $where[]="MATCH (`cache`) AGAINST ($search IN BOOLEAN MODE)";
    }
 
    if($circle_id and $circle_id!='All'){
      $where[]="`uc`.`id` = '{$circle_id}'";
    }
 
    $where[] ="`sc`.`user_id`!= '{$uid}'";
 
    if ( $uid != 1 && $excludeMothership) {
            $where[]="`ucm`.`target_user_id` != 1";
        }
 
        if (isset($data['exclude_ids'])) {
            $exclude = is_array($data['exclude_ids']) ? implode(',', $data['exclude_ids']) : $data['exclude_ids'];
 
            if ( strlen($exclude) > 0 ) {
           $where[]="`ucm`.`target_user_id` NOT IN ($exclude)";
            }
        }
       
    $where = implode(" AND ",$where);
    if($where=='') $where = '1';
 
        if ($latest) {
            $order[] = "`ucm`.`create_time` DESC";
        }
 
    if ($onlineFirst) {
      $order[]='FIELD(`publicStatus`, 2) DESC';
      $order[]='`up`.`first_name`';
      $order[]='`up`.`last_name`';
    }
 
    if(isset($mix_order)) {
      $order[]='RAND()';
    }
 
    if ( count($select) > 0 ) {
      $select = implode(', ', $select) . ',';
    } else {
      $select = '';
    }
    $leftJoin = implode(' ', $leftJoin);
 
    if ( count($order) > 0 ) {
      $order = 'ORDER BY ' . implode(', ', $order);
    } else {
      $order = '';
    }
 
    $sql="SELECT
           {$select}
           {$SelectUserStatus}
           `sc`.`short_info_json`,
           `uc`.`name` as `circle_name`,
           `ucm`.`target_user_id`
        FROM `users_circles_members` AS `ucm`
        {$leftJoinCS}
        {$LeftJoinUserStatus}
        {$leftJoinProfiles}
        {$leftJoin}
       LEFT JOIN `users_circles` AS `uc` ON `uc`.`id` = `ucm`.`circle_id`
           LEFT JOIN `users_profiles` AS `up` ON `up`.`id` = `sc`.`user_id`
         WHERE  ( {$where} ) AND `sc`.`is_disabled` = 0 /*AND `ucm`.`target_user_id`!='1'*/
         GROUP BY `sc`.`id`
         {$order}
         {$limit}
    ";
               
    // \Helpers\Debug::log($sql);
        // echo $sql;
    $ret=array();
 
    //foreach($this->db->q($sql,$prepare) as $el){
    $cache = isset($data['cache']) ? $data['cache'] : 1;
    // echo print_r(array($sql, $prepare), 1);
    // \Helpers\Debug::log($sql);
    if (isset($prepare["limit"])) $prepare["limit"] = intval($prepare["limit"]);
    if (isset($prepare["offset"])) $prepare["offset"] = intval($prepare["offset"]);
 
    $mem_before = memory_get_usage();
        $res = $this->db->prepare($sql,$prepare)->execute()->fetchAllAssoc()->result;
  //  \Helpers\Debug::log("Memory used by the contacts array: ", memory_get_usage() - $mem_before);
        // $res = $this->db->prepare($sql,$prepare)->execute()->fetchAllAssoc()->cache( $cache )->result;
        // echo "$sql - " . print_r($prepare,1);
    foreach($res as $el){
      $a=json_decode($el['short_info_json'], true);
 
      if($show_status){
                $status=array();
                $statuses=unserialize(USER_STATUS_STATEMENTS);
                $status['status_id']=$el['publicStatus'];
                $status['status_name']=$statuses[$status['status_id']];
        $chatStatus=array();
                $chatStatus['status_id']=$el['cstatus'];
        $chatStatus['status_name']=$statuses[$chatStatus['status_id']];
                $a['status']=$status;
        $a['chat_status']=$chatStatus;
      }
 
      if($a!=false){
        $a['gender']     = $el['gender'];
        $a['has_picture']   = $el['has_picture'];
        $a['same_follower'] = $el['same_follower'];
        $a['circle_id']   = $el['circle_id'];
        //Compatibility
        $a['profile_tags']  = $a['tags']['profile'];
 
        if($el['user_id'] == $_SESSION['profile']['id'] and $uid == $_SESSION['profile']['id']){
          $a['circle']=$el['circle_name'];
        }
        $ret[]=$a;
      }
    }
 
    return array(true,array("profiles"=>$ret));
    //return array(true,array("profiles"=>$ret),str_replace("\t","",str_replace("\n", "", $sql)),$prepare);
    //return array(true,$ret);
  }
 
    public function chatBarSuggester($data) {
        $uid = (int)$_SESSION['profile']['id'];
        $limit = isset($data['limit']) ? $data['limit'] : 15;
        $return = array();
 
        $query = "
                    SELECT
                            DISTINCT
                            `sender_id`,
                            `reciever_id`,
                            /*fn_calculateProfilePublicStatus(`up`.`user_status`,`up`.`system_status`,`up`.`lastseen`) as `publicStatus`,*/
                            if(`chat_status` > 0,fn_calculateProfilePublicStatus(`user_status`, `system_status`, `lastseen`),0) as `cstatus`
                    FROM
                            `users_messages` AS `um`
                        JOIN
                            `conversations` AS `c`
                        ON
                            (`um`.`conversations_id` = `c`.`id`)
                        LEFT JOIN
                            `users_profiles` AS `up`
                        ON
                            `up`.`id` = IF(`sender_id` = :uid, `reciever_id`, `sender_id`)
                    WHERE
                            (`sender_id` = :sid OR `reciever_id` = :rid)
                        AND
                            `sender_id` <> `reciever_id`
                        AND
                            `up`.`is_page` IS NULL
                    GROUP BY
                            `um`.`conversations_id`
                    ORDER BY
                            `send_time` DESC
                    LIMIT
                            0,$limit";
 
        $prep = array("uid" => $uid, "sid" => $uid, "rid" => $uid);
        
        $result = $this->db->prepare($query, $prep)->execute()->fetchAllAssoc()->result;
        $exclude = array();
        $statuses=unserialize(USER_STATUS_STATEMENTS);
        $chatstatuses=unserialize(CHAT_STATUS_STATEMENTS);
 
        foreach ( $result as $r ) {
            $id = $r['sender_id'] == $uid ? $r['reciever_id'] : $r['sender_id'];
            $user = \Helpers\Users::getUserShortInfoFromId($id);
 
            $status=array();
            $status['status_id']=\Helpers\Users::getInstance()->getProfilePublicStatus($id);//$r['publicStatus'];
            $status['status_name']=$statuses[$status['status_id']];
            $chatStatus=array();
            $chatStatus['status_id']=\Helpers\Users::getInstance()->getProfilePublicChatStatus($id);
            $chatStatus['status_name']=$chatstatuses[$chatStatus['status_id']];
            $user['status']=$status;
            $user['chat_status']=$chatStatus;
 
            if (!in_array($id, $exclude)) {
                $return[] = $user;
                $exclude[] = $id;
                //\Helpers\Debug::log($id,$status);
            }
        }
 
 
        if ( count($return) < $limit ) {
            $data["limit"] = $limit - count($return);
            $data["exclude_ids"] = $exclude;
            $additional = $this->myContactsSuggester($data);
 
            if ($additional[0] && count($additional[1]['profiles']) > 0) {
                $return = array_merge($return, $additional[1]['profiles']);
            }
        }
 
        foreach ( $return as &$r ) {
            $r['profile_tags']  = $r['tags']['profile'];
        }
 
        return array(true, array("profiles" => $return));
    }
    /* DEPRECATED
  public function followSuggester($data) {
    return array(true);
    $users   = array();
    $offset = $data['offset'] || 0;
    $lat = $this->app->request->ipInfo["latitude"];
        $long = $this->app->request->ipInfo["longitude"];
    $userTags   = array();
    //$results = $this->db->q( "SELECT `name`, `type` FROM `users_tags` WHERE `user_id` = :uid", array("uid" => $_SESSION['profile']['id']));
    $results = $this->db->prepare( "SELECT `name`, `type` FROM `users_tags` WHERE `user_id` = :uid", array("uid" => $_SESSION['profile']['id']))->execute()->fetchAllAssoc()->cache()->result;
 
    foreach ( $results as $el ) {
      $userTags[ $el[ 'type' ] ][]   = ucwords(strtolower($el['name']));
    }
 
    // foreach ( $userTags as $k => $e ) {
    //   $tagsCache[$k] = implode( " ", str_replace( " ", "_", $e ) );
    // }
 
    $query   = "
      SELECT
        `upr`.`id` as `user_id`,
        `sc`.`short_info`,
        " . ( ( $lat != 0 && $long != 0 ) ? "IF ( " . ($lat - 10) . " < `upr`.`lat` AND `upr`.`lat` < " . ($lat + 10) . " AND " . ($long - 10) . " < `upr`.`long` AND `upr`.`long` < " . ($long + 10) . ", 1, 0 )" : "0" ) . " as `is_near`,
        MATCH (instruments) AGAINST (:instruments IN NATURAL LANGUAGE MODE) AS score_instruments,
        MATCH (genres) AGAINST (:genres IN NATURAL LANGUAGE MODE) AS score_genres,
        MATCH (influences) AGAINST (:influences IN NATURAL LANGUAGE MODE) AS score_influences,
        IF ( `ucm`.`id` IS NOT NULL, 1, 0 ) as `followed`,
        IF ( `uc`.`id` IS NOT NULL, 1, 0 ) as `has_posts`
      FROM
        `users_profiles` `upr`
      JOIN
        `users_elements` `upi`
          ON
            (`upr`.`id` = `upi`.`creator_id` AND `upr`.`profile_album_id` = `upi`.`album_id` AND `upi`.`type`='picture' AND `upi`.`is_default` = '1' AND `upi`.`deleted_time` IS NULL AND `upi`.`published_time` IS NOT NULL AND `upi`.`deployed_time` IS NOT NULL)
      JOIN
        `search_cache` `sc`
          ON
            (`sc`.`user_id` = `upr`.`id`)
      LEFT JOIN
        `users_comments` `uc`
          ON
            (`uc`.`owner_element_id` = `upr`.`id` AND `uc`.`model` = 'post')
      LEFT JOIN
        `users_circles_members` `ucm`
          ON
            (`ucm`.`user_id` = :uid AND `ucm`.`target_user_id` = `upr`.`id`)
      WHERE
        `upr`.`id` != :uuid
      GROUP BY
        `uc`.`owner_element_id`
      HAVING
        `followed` = 0
      ORDER BY
        `score_genres` DESC,
        `score_influences` DESC,
        `score_instruments` DESC,
        `is_near` DESC,
        `has_posts` DESC
      LIMIT
        :offset, 9
    ";
 
    $results = $this->db->prepare($query, array(
      'offset'     => $offset,
      'uid'       => $_SESSION['profile']['id'],
      'uuid'       => $_SESSION['profile']['id'],
      'genres'     => ( isset( $userTags['genres'] )     ? implode( " ", str_replace( " ", "_", $userTags['genres'] ) ) : '' ),
      'instruments'   => ( isset( $userTags['instruments'] )   ? implode( " ", str_replace( " ", "_", $userTags['instruments'] ) ) : '' ),
      'influences'   => ( isset( $userTags['influences'] )   ? implode( " ", str_replace( " ", "_", $userTags['influences'] ) ) : '' )
    ))->execute()->fetchAllAssoc()->cache()->result;
 
 
    foreach ( $results as $result ) {
      $user = unserialize( $result['short_info'] );
 
      if ( $user != false ) {
        $users[] = $user;
      }
    }
 
    return array(true, $users);
  }*/
 
  public function searchSuggester($data) {
        $results = isset($data['limit']) ? $data['limit'] : \Config::defaults()->RESULTS_TO_SHOW->search;
        if ($results > \Config::defaults()->MAX_RESULTS_TO_SHOW->search) {
            $results = \Config::defaults()->MAX_RESULTS_TO_SHOW->search;
        }
 
        $reminder = $result % 2;
 
        $search = $data['autocomplete'];
        $msc = microtime(true);
        if (!isset($data['skipTags']) || !$data['skipTags']) {
            $tags = $this->tagsSuggester(array('autocomplete' => $search, 'limit' => (int) ($results / 2) + $reminder));
        }
 
        $profiles = [];
        $pages = [];
        if (isset($data['skipPages']) || $data['skipPages'] == 'true') { // legacy string true
            $profilesRes = $this->search(array('autocomplete' => $search, 'show_profiles' => true, 'visible' => $data['visible']));
            if ($profilesRes[0]) {
                $profiles = $profilesRes[1];
            }
        } else { // search both users and pages and separate them here
 
            $searchRes = $this->search(['autocomplete' => $search, 'visible' => $data['visible']]); 
 
            if ($searchRes[0]) {
                foreach ($searchRes[1] as $i => $sr) {
                    if ($i == 0) {
                        $firstType = $sr['is_page'] ? "page" : "profile";
                    }
                    if ($sr['is_page']) {
                        $pages[] = $sr;
                    } else {
                        $profiles[] = $sr;
                    }
                }
            }
        }
 
        $communities = \Helpers\Tags::getCommunities(null, false, $search, $results);
 
 
        $sCtrl = new \Controllers\API\SearchController();
        $posts = $sCtrl->searchPosts($data);
        
        $msc=microtime(true)-$msc;
        $res=array();
        $res['profiles'] = $profiles;
        $res['pages'] = $pages;
        $res['posts'] = $posts;
        $res['communities'] = $communities;
        $res['tags'] = $tags[0] ? $tags[1]['tags'] : array();
        $res['first_type'] = isset($firstType) ? $firstType : false;
        return array(true, $res);
    }
 
    public function getFacebookFriends ()
    {
        $uid = (int)$this->app->session->profile['id'];
        $friends = \Helpers\Users::getUnfollowedFacebookFriends($uid);
        return [true, $friends];
    }
 
    //polzva se samo za chat-a, no i tam e spriano i vrushta []
    public function generatePeopleSuggestions ($data) {
        return array(true,[]);
        if (isset($data['chat'])) {
            $uid = $_SESSION["profile"]["id"];
            $usedUids = [];
            if (isset($data['used_ids']) AND is_array($data['used_ids'])) {
                $usedUids = $data['used_ids'];
            }
 
            $result = [
                'explore' =>  \Helpers\Suggestions::generateExploreSuggestions($uid, $usedUids)
            ];
 
            return array(true, $result);
        }
 
        if (!isset($data["sideMenu"]) && !isset($data["explore"])) {
            return array(
                "sideMenu"   => array(),
                "explore"  => array()
            );
        }
 
        $result = \Helpers\Suggestions::generateSuggestions($_SESSION['profile']['id'], $data["sideMenu"], $data["explore"]);
 
        if (!$result) {
            return array(false, "insert/update failed");
        }
 
        return array(true, $result);
    }
 
  public function tagsSuggesterDEPRECATED($data){
    // DEPRECATED !!!!!! ! !! !
// DONE elasticsearch!
        
    $search=trim($data['search']);
    $type = $data['type'];
 
    if(!$search) return array(true,array());
        if ( strlen($search) < 3 ) {
            return [true, []];
        }
 
    $limit = isset($data['limit']) ? intval($data['limit']) : \Config::defaults()->RESULTS_TO_SHOW->search;
    if($limit > \Config::defaults()->MAX_RESULTS_TO_SHOW->search){
      $limit = \Config::defaults()->MAX_RESULTS_TO_SHOW->search;
    }
 
    $offset = isset($data['offset']) ? intval($data['offset']) : 0;
        $lang = \Helpers\Basic::lang();
        
        $regex = new \MongoDB\BSON\Regex('^'.$search, 'i');
        
        if ($type == "" || $type == "all") {
            $conditions = [
                "translation.{$lang}.text" => $regex,
            ];
 
            $project = [
                "type" => 1,
                'translation' => 1,
                'posts_rating' => ['$sum' => ['$type.posts.moderator_rating', '$type.posts.user_rating']],
                'profile_rating' => ['$sum' => ['$type.profile.moderator_rating', '$type.profile.user_rating']],
                'instruments_rating' => ['$sum' => ['$type.instruments.moderator_rating', '$type.instruments.user_rating']],
                'influences_rating' => ['$sum' => ['$type.influences.moderator_rating', '$type.influences.user_rating']],
                'equipment_rating' => ['$sum' => ['$type.equipment.moderator_rating', '$type.equipment.user_rating']],
                'genres_rating' => ['$sum' => ['$type.genres.moderator_rating', '$type.genres.user_rating']],
                'rating' => ['$max' => ['$posts_rating', '$profile_rating', '$instruments_rating', '$influences_rating', '$equipment_rating', '$genres_rating']],
            ];
        } else {
            $conditions = [
                "translation.{$lang}.text" => $regex,
            ];
                
            $project = [
                "type.{$type}" => 1,
                'translation' => 1,
                'rating' => ['$sum' => ['$' . "type.{$type}." . 'moderator_rating', '$' . "type.{$type}." . 'user_rating']]
            ];
        }
 
        $res = \Collections\Tags::aggregate([
            ['$match' => $conditions],
            ['$project' => $project],
            ['$sort' => ['rating' => -1]],
            ['$skip' => $offset],
            ['$limit' => $limit],
        ]);
        
        $tags = [];
        foreach ($res as $tag) {
            $tagName = mb_convert_case(stripslashes($tag->translation->{$lang}->text), MB_CASE_TITLE, 'UTF-8');
            foreach ($tag->type as $type => $notneed) {
                $tags[$type][] = ['index' => \Helpers\Tags::transformIndex($tagName), "url" => \Helpers\Basic::getUrl($tagName, $type), "name"=> $tagName ];
            }
        }
        
    return array(true,array("tags"=>$tags));
  }
    
    public function tagsSuggester($data) {
        $search = trim($data['search']);
        $type = $data['type'];
 
        if (!$search) {
            return [true, []];
        }
        if (strlen($search) < 3) {
            return [true, []];
        }
 
        $limit = isset($data['limit']) ? intval($data['limit']) : \Config::defaults()->RESULTS_TO_SHOW->search;
        if ($limit > \Config::defaults()->MAX_RESULTS_TO_SHOW->search) {
            $limit = \Config::defaults()->MAX_RESULTS_TO_SHOW->search;
        }
 
        $offset = isset($data['offset']) ? intval($data['offset']) : 0;
        $lang = \Helpers\Basic::lang();
        
        $sort = [["rating" => "desc"]];
        
        $bool = [];
        
        if (strlen($type) && $type != 'all') {
            $tagTypes = unserialize(TAGS_TYPES);
            if (!in_array($type, $tagTypes)) {
                return [true, []];    
            }
            $bool['filter'] = [
                'term' => [
                    'types' => $type
                ]
            ];
        }
        
        $bool["must"] = [
            [
                "match_phrase_prefix" => [
                    "text" => $search
                ]
            ]
        ];
        $query = ["bool" => $bool];
        
        $params = [
            "from" => $offset,
            "size" => $limit,
            "index" => \Config::elasticsearch()->INDICES->tags->{$lang},
//            "explain" => 1,
            "body" => [
                "query" => $query,
                "sort" => $sort,
            ]
        ];
            
        $dsm = $this->app->droobleSearchManager;
        $result = $dsm->search($params);
        
        $tags = [];
        
        foreach ($result['hits']['hits'] as $item){
            $tagName = mb_convert_case(stripslashes($item['_source']['text']), MB_CASE_TITLE, 'UTF-8');
            $tags[$type][] = ["name"=> $tagName ];
        }
 
        return [true, ['tags' => $tags]];
    }
    
    // looks like I did this to track stuff for Segment, without making too many requests (due to the $data['count'] that is used for counting search results,
    // but now it just wraps the search() func
    public function advancedSearchPage($data){
        
        $searchStr = strlen(trim($data['autocomplete'])) ? $data['autocomplete'] : trim($data['search']);
        $properties =array(
                "category" => 'Home page',
                "search_query" => "'".$searchStr."'"
            );
 
        if (isset($data['profile']) && is_array($data['profile'])) {
            // create Segment properties from search tags
            foreach ($data['profile'] as $tag) {
                if (preg_match('/^[0-9a-z\s]+$/i', $tag)) {
                    $properties[ preg_replace('/\s+/', '_', strtolower($tag)) ] = true;
                }
            }
        }
 
        // why not just 0/1 ? ... strings 'true' | 'false'
        if(isset($data['bands']) && ($data['bands'] == 'true' or $data['bands'] == true)){
            $properties['bands'] = true;
        }
 
        if(isset($data['venues']) && ($data['venues'] == 'true' or $data['venues'] == true)){
            $properties['venues'] = true;
        }
 
        if(isset($data['clubs']) && ($data['clubs'] == 'true' or $data['clubs'] == true)){
            $properties['clubs'] = true;
        }
 
        if(isset($data['recording_studios']) && ($data['recording_studios'] == 'true' or $data['recording_studios'] == true)){
            $properties['recording_studios'] = true;
        }
 
        if(isset($data['rehearsal_space']) && ($data['rehearsal_space'] == 'true' or $data['rehearsal_space'] == true)){
            $properties['rehearsal_space'] = true;
        }
 
        if(isset($data['music_school']) && ($data['music_school'] == 'true' or $data['music_school'] == true)){
            $properties['music_school'] = true;
        }
 
        if(isset($data['jam']) && ($data['jam'] == 'true' or $data['jam'] == true)) {
            $properties['jammers'] = true;
        }
 
        if(isset($data['teach']) && ($data['teach'] == 'true' or $data['teach'] == true)){
            $properties['teachers'] = true;
 
            if (isset($data['price_min'])) {
                $properties['price_min'] = $data['price_min'];
            }
            if (isset($data['price_max'])) {
                $properties['price_max'] = $data['price_max'];
                
                // When its picked maximum of 150+... 
                if ((int)$data['price_max'] == 150) {
                    $properties['price_max'] = 999;
                }
            }
        }
 
        if(isset($data['available']) && ($data['available'] == 'true' or $data['available'] == true)){
            $properties['available'] = true;
        }
 
        if(isset($data['looking_bandmates']) && ($data['looking_bandmates'] == 'true' or $data['looking_bandmates'] == true)){
            $properties['looking_bandmates'] = true;
        }
 
        if(isset($data['instruments']) && $data['instruments']){
            $properties['instruments'] = $data['instruments'];
        }
 
        if(isset($data['instruments_primary_to_join']) && $data['instruments_primary_to_join']){
            $properties['instruments_primary_to_join'] = $data['instruments_primary_to_join'];
        }
 
        if(isset($data['instruments_primary_for_bandmates']) && $data['instruments_primary_for_bandmates']){
            $properties['instruments_primary_for_bandmates'] = $data['instruments_primary_for_bandmates'];
        }
 
        if(isset($data['genres']) && $data['genres']){
            $properties['genres'] = $data['genres'];
        }
 
        if(isset($data['influences']) && $data['influences']){
            $properties['influences'] = $data['influences'];
        }
 
        if(isset($data['equipment']) && $data['equipment']){
            $properties['equipment'] = $data['equipment'];
        }
 
        if(isset($data['languages']) && $data['languages']){
            $properties['languages'] = $data['languages'];
        }
 
        if(isset($data['location']) && $data['location']){
            $properties['location'] = $data['location'];
        }
 
        if(isset($data['place']) && $data['place']){
            $properties['place'] = $data['place'];
        }
 
        $track = array();
 
        $track['event'] = "Advanced Search";
 
        if($this->app->session->has('profile')){
            $track['userId'] = $this->app->session->get('profile')['id'];
        }else{
//            $track['anonymousId'] = rand(100000,999999);
        }
 
        $track['properties'] = $properties;
 
        if( $this->app->session->has('profile') && (!isset($data['count']) or $data['count'] == 2) ){
            if(!$_COOKIE["druast"]) {
                $secureConnection = strtolower(\Helpers\Basic::getInstance()->get_php_http_scheme()) == 'https' ? true : false;
                setcookie("druast", date("Y-m-d"), time() + 60 * 60, '/', NULL, $secureConnection, true); // 60 minutes
                \Drooble\Activities\Activities::add( "advancedSearch", $track['userId'], (object) ['type' => 'user', 'id' => $track['userId']], ['segment' => ['track' => $track]] );
            }
        }
 
        return $this->search($data);
 
    }
    
    // $data['autocomplete'] string - the search works as autocomplete e.g. Joh -> John
    // $data['search'] string - the search works as search (bad at autocomplete, but with more accurate results)
    // TODO - think if possible to use instead of getContacts - you'll need your 'followings/folloers' on top of results
    public function search($data) {
        $go_search=false;
    foreach(array_keys($data) as $e){
      if(!in_array($e, array_keys( get_object_vars( \Config::api()->MAP->search->data ) ))){
        return array(false,"input error1 '".$e."'");
      }
      if($data[$e]){
        $go_search=true;
      }
    }
    if(!$go_search){
      return array(false,"empty search");
    }
        
        $dsm = $this->app->droobleSearchManager;
        $lang = \Helpers\Basic::lang();
        $analyzer = strtolower(\Config::languages()->language->{$lang}->name_in_english);
        $offset = $data['offset'] ? $data['offset'] : 0;
        $results = isset($data['limit']) ? $data['limit'] : \Config::defaults()->RESULTS_TO_SHOW->search;
    if($results > 100){ // way too many
      $results = \Config::defaults()->MAX_RESULTS_TO_SHOW->search;
    }
        
        $count = false;
       
       $pageTypeIds = [];
       $matchTags = [
           'profile' => [],
           'instruments' => [],
           'genres' => [],
           'equipment' => [],
           'influences' => [],
           'languages' => [],
           'instruments_primary_to_join' => [],
           'instruments_primary_for_bandmates' => [],
       ];
       
       if ((int) $data['venues'] == 1) {
            $pageTypeIds[] = 106;
        }
 
        if ((int) $data['clubs'] == 1) {
            $pageTypeIds[] = 107;
        }
 
        if ((int) $data['music_school'] == 1) {
            $pageTypeIds[] = 120;
        }
 
        if ((int) $data['recording_studios'] == 1) {
            $pageTypeIds[] = 115;
        }
 
        if ((int) $data['rehearsal_space'] == 1) {
            $pageTypeIds[] = 128;
        }
 
        if (is_array($data['instruments']) AND ! empty($data['instruments'])) {
           foreach ($data['instruments'] as $item) {
               $matchTags['instruments']['should'][] = $item;
           }
       }
       if (is_array($data['genres']) AND ! empty($data['genres'])) {
           foreach ($data['genres'] as $genre) {
               $matchTags['genres']['should'][] = $genre;
           }
       }
       if (is_array($data['equipment']) AND ! empty($data['equipment'])) {
           foreach ($data['equipment'] as $item) {
               $matchTags['equipment']['should'][] = $item;
           }
       }
       if (is_array($data['influences']) AND ! empty($data['influences'])) {
           foreach ($data['influences'] as $item) {
               $matchTags['influences']['should'][] = $item;
           }
       }
       if (is_array($data['languages']) AND ! empty($data['languages'])) {
           foreach ($data['languages'] as $item) {
               $matchTags['languages']['should'][] = $item;
           }
       }
       // these are reversed ... to match users who want to play an instrument and bands that are looking for the same
       if (is_array($data['instruments_primary_to_join']) AND ! empty($data['instruments_primary_to_join'])) {
           foreach ($data['instruments_primary_to_join'] as $item) {
               $matchTags['instruments_primary_for_bandmates']['should'][] = $item;
           }
       }
       if (is_array($data['instruments_primary_for_bandmates']) AND ! empty($data['instruments_primary_for_bandmates'])) {
           foreach ($data['instruments_primary_for_bandmates'] as $item) {
               $matchTags['instruments_primary_to_join']['should'][] = $item;//
           }
       }
       
        if (is_array($data['profile']) AND !empty($data['profile'])) {
            $tmp = array();
            foreach ($data['profile'] as $el) {
                if (!trim($el))
                    continue;
                
                // Some predefined
                // TODO config with translations in all languages VS cache ...
                if ($el == $this->app->translate->get('drummers')) {
                    $tag = \Helpers\Tags::findByText('drums', null);
                    if ($tag) {
                        $matchTags['instruments']['should'][] = $tag->name;
                    }
                    $tag = \Helpers\Tags::findByText('drummer', null);
                    if ($tag) {
                        $matchTags['profile']['should'][] = $tag->name;
                    }
                }
 
                if ($el == $this->app->translate->get('singers') || $el == $this->app->translate->get('vocalists')) {
                    $tag = \Helpers\Tags::findByText('vocals', null);
                    if ($tag) {
                        $matchTags['profile']['should'][] = $tag->name;
                        $matchTags['instruments']['should'][] = $tag->name;
                    }
                    $tag = \Helpers\Tags::findByText('singer', null);
                    if ($tag) {
                        $matchTags['profile']['should'][] = $tag->name;
                    }
                    $tag = \Helpers\Tags::findByText('vocalist', null);
                    if ($tag) {
                        $matchTags['profile']['should'][] = $tag->name;
                    }
                }
 
                if ($el == $this->app->translate->get('guitarists')) {
                    $tag = \Helpers\Tags::findByText($this->app->translate->get('guitar'), null);
                    if ($tag) {
                        $matchTags['instruments']['should'][] = $tag->name;
                    }
                    $tag = \Helpers\Tags::findByText($this->app->translate->get('guitarist'), null);
                    if ($tag) {
                        $matchTags['profile']['should'][] = $tag->name;
                    }
                    $tag = \Helpers\Tags::findByText($this->app->translate->get('acoustic_guitar'), null);
                    if ($tag) {
                        $matchTags['instruments']['should'][] = $tag->name;
                    }
                    $tag = \Helpers\Tags::findByText($this->app->translate->get('electric_guitar'), null);
                    if ($tag) {
                        $matchTags['instruments']['should'][] = $tag->name;
                    }
                }
 
                if ($el == $this->app->translate->get('bass_players') || $el == $this->app->translate->get('bassists')) {
                    $tag = \Helpers\Tags::findByText($this->app->translate->get('bass'), null);
                    if ($tag) {
                        $matchTags['instruments']['should'][] = $tag->name;
                    }
                    $tag = \Helpers\Tags::findByText($this->app->translate->get('bass_player'), null);
                    if ($tag) {
                        $matchTags['profile']['should'][] = $tag->name;
                    }
                    $tag = \Helpers\Tags::findByText($this->app->translate->get('bassist'), null);
                    if ($tag) {
                        $matchTags['profile']['should'][] = $tag->name;
                    }
                }
 
                if ($el == $this->app->translate->get('producers')) {
                    $tag = \Helpers\Tags::findByText($this->app->translate->get('producer'), null);
                    if ($tag) {
                        $matchTags['profile']['should'][] = $tag->name;
                    }
                }
 
                if ($el == $this->app->translate->get('musicians')) {
                    $tag = \Helpers\Tags::findByText($this->app->translate->get('musician'), null);
                    if ($tag) {
                        $matchTags['profile']['should'][] = $tag->name;
                    }
                }
            }
        }
        
        $bool = [];
        $must = [];
        $should = [];
        
        if (isset($data['autocomplete']) && trim($data['autocomplete'])) {
            $searchStr = trim($data['autocomplete']);
            // phrase_prefix is the "poorman's autocomplete" of Elastic. Works great
            // In future we can create more complex autocomplete, but it's totally unnecessary for now
            $combineType = "phrase_prefix";
            $minLength = 2;
        } else {
            $searchStr = trim($data['search']);
            $combineType = "cross_fields";
            $minLength = 3;
        }
        
        if (strtolower($searchStr) == "search" || strtolower($searchStr) == "%%tag%%") {
            try {
                // we got weird "search" requests .. log em
//                \Helpers\Debug::log("weird search ", $_SERVER['HTTP_REFERER']);
//                \Helpers\Debug::log($_SERVER['REQUEST_URI']);
//                \Helpers\Debug::log($_SERVER['QUERY_STRING']);
//                \Helpers\Debug::log($_SERVER['HTTP_USER_AGENT']);
            } catch (\Exception $e) {
                
            }
        }
        
        if (strlen($searchStr) >= $minLength) {
            // keep search string for 'tag' cloud of frequent searches
            \Helpers\SearchTags::add($searchStr);
            $combine = [];
            $combine[] = [
                "multi_match" => [
                    "query" => $searchStr,
                    "analyzer" => $analyzer,
                    "type" => $combineType,
                    "fields" => [
                        "name^5", // full name of User / Page
                        "username^1",
//                        "about.title^4",
//                        "about.text^2",
//                        "type.{$lang}^2", // Pages only
//                        "tags.profile.{$lang}^3",
//                        "tags.equipment.{$lang}^2",
//                        "tags.genres.{$lang}^2", // Pages only
//                        "tags.genres_primary.{$lang}^2",
//                        "tags.genres_secondary.{$lang}",
//                        "tags.influences.{$lang}^2",
//                        "tags.instruments_primary.{$lang}^2",
//                        "tags.instruments_secondary.{$lang}",
//                        "tags.languages.{$lang}",
                    ]
                ]
            ];
            $combine[] = [
                "multi_match" => [
                    "query" => $searchStr,
                    "analyzer" => $analyzer,
                    "fields" => [
                        "name^3", // full name of User / Page
//                        "username^1",
//                        "about.title^3",
//                        "about.text^2",
//                        "type.{$lang}^2", // Pages only
//                        "tags.profile.{$lang}^3",
//                        "tags.equipment.{$lang}^2",
//                        "tags.genres.{$lang}^2", // Pages only
//                        "tags.genres_primary.{$lang}^2",
//                        "tags.genres_secondary.{$lang}",
//                        "tags.influences.{$lang}^2",
//                        "tags.instruments_primary.{$lang}^2",
//                        "tags.instruments_secondary.{$lang}",
//                        "tags.languages.{$lang}",
                    ],
                    "fuzziness" => "AUTO"
                ]
            ];
            $must[] = ['bool' => ['should' => $combine, "boost" => 5]];
        }
 
        /*        
        if ($data['teach'] == 1) {
            $must[] = ['match' => ['teach' => 1]];
        }
        
        if (intval($data['price_min']) > 0 || intval($data['price_max']) > 0) {
            $price = [];
            if (intval($data['price_min']) > 0) {
                $this_drooble_fee = (intval($data['price_min']) / (\Config::defaults()->DROOBLE_FEE_IN_PERCENTS + 100) * \Config::defaults()->DROOBLE_FEE_IN_PERCENTS);
                $price_from = intval($data['price_min']) - $this_drooble_fee;
                if ($price_from < 0) {
                    $price_from = 0;
                }
                $price['gte'] = intval($price_from);
            }
 
            if (intval($data['price_max']) > 0) {
                $this_drooble_fee = (intval($data['price_max']) / (\Config::defaults()->DROOBLE_FEE_IN_PERCENTS + 100) * \Config::defaults()->DROOBLE_FEE_IN_PERCENTS);
                $price_to = intval($data['price_max']) - $this_drooble_fee;
                if ($price_to < 0) {
                    $price_to = 0;
                }
                $price['lte'] = intval($price_to);
            }
            $must[] = ['range' => ['price' => $price] ];
        }
         */
        
 
        if ($data['place']) {
            $data['place'] = str_replace("'", "", $data['place']);
            $data['place'] = str_replace("`", "", $data['place']);
            $data['place'] = str_replace("\"", "", $data['place']);
            
            $place = \Helpers\Basic::getGooglePlace($data['place']);
            if ($place && $place->is_country) {
                $must[] = ['match' => ['country' => $place->country_code]];
            } else {
                $must[] = ['match' => ['city' => $data['place']]];
            }
            
        }elseif($data['location']){
            $google_places = new \Lib\GooglePlaces(\Config::googleplaces()->API_KEY);
 
            $cor = array($this->app->request->ipInfo['latitude'], $this->app->request->ipInfo['longitude']);
 
            $google_places->location = $cor;
            $google_places->radius   = 100;
            $google_places->rankby   = 'distance';
            $google_places->types    = '(cities)';
            $google_places->input    = $data['location'];
            $google_places_result = $google_places->autocomplete();
            $searched_places_ids = array();
 
            if($google_places_result['status'] == 'OK' AND count($google_places_result['predictions'])>0){
                foreach($google_places_result['predictions'] as $__prediction){
                    $searched_places_ids[] = $__prediction['place_id'];
                }
            }
            if (count($searched_places_ids)) {
                $tmp = [];
                foreach ($searched_places_ids as $placeid) {
                    $tmp[] = ['match' => ["city" => $placeid]];
                }
                $must[] = ['bool' => ['should' => $tmp]];
            }
        }
        
        if (count($data['tags'])) {
            foreach ($data['tags'] as $tag) {
                foreach ($matchTags as $type => $info) {
                    $matchTags[$type]['should'][] = $tag;
                }
            }
        }
        foreach ($matchTags as $type => $info) {
            foreach ($info as $boolOperator => $tags) { // $boolOperator fills $must or $should arrays
                if (count($tags)) {
                    foreach ($tags as $tag) {
                        if ($type == "genres") {
                            ${$boolOperator}[] = ['match' => ["tags.genres.{$lang}" => $tag]]; // this is for Pages
                            ${$boolOperator}[] = ['match' => ["tags.genres_primary.{$lang}" => $tag]];
                            ${$boolOperator}[] = ['match' => ["tags.genres_secondary.{$lang}" => $tag]];
 
                        } elseif ($type == "instruments") {
                            ${$boolOperator}[] = ['match' => ["tags.instruments_primary.{$lang}" => $tag]];
                            ${$boolOperator}[] = ['match' => ["tags.instruments_secondary.{$lang}" => $tag]];
                        } else {
                            ${$boolOperator}[] = ['match' => ["tags.{$type}.{$lang}" => $tag]];
                        }
                        
                        /* this here was again because someone doesn't follow YAGNI
                        if ($type == "genres") {
                            $tmp[] = ['match_phrase' => ["tags.genres.{$lang}" => $tag]]; // this is for Pages
                            $tmp[] = ['match_phrase' => ["tags.genres_primary.{$lang}" => $tag]];
                            $tmp[] = ['match_phrase' => ["tags.genres_secondary.{$lang}" => $tag]];
 
                        } elseif ($type == "instruments") {
                            $tmp[] = ['match_phrase' => ["tags.instruments_primary.{$lang}" => $tag]];
                            $tmp[] = ['match_phrase' => ["tags.instruments_secondary.{$lang}" => $tag]];
                        } else {
                            $tmp[] = ['match_phrase' => ["tags.{$type}.{$lang}" => $tag]];
                        }
                        if ($tmp) {
                            ${$boolOperator}[] = ['bool' => ['should' => $tmp]];
                        }*/
                        
                    }
                }
            }
        }
        
//        if ($data['visible'] == 'profile_completed') {
//            $must[] = [
//                'term' => [
//                    'visibility' => 'profile_completed'
//                ]
//            ];
//        }
 
        if (count($pageTypeIds)) {
            $must[] = ['terms' => ['page_type_id' => $pageTypeIds]];
        }
        
        if (count($must)) {
            $bool['must'] = $must;
        }
        
        if (count($should)) {
            $bool['should'] = $should;
            $bool['minimum_should_match'] = 1;
            // setting minimum_should_match would mess up the has_avatar boost
        }
        
//        if ($data['autocomplete']) {
//            $indexStr = \Config::elasticsearch()->INDICES->users . ',' . \Config::elasticsearch()->INDICES->pages;
            $indexStr = \Config::elasticsearch()->INDICES->users;
//        } else {
//            $indexStr = \Config::elasticsearch()->INDICES->users;
//        }
//        if ((int) $data['bands'] == 1 || count($pageTypeIds) || $data['show_pages'] || $data['type'] == "pages") {
//            $indexStr = \Config::elasticsearch()->INDICES->pages;
//        }
        if ($data['show_profiles'] /*|| $data['type'] == "people" */) {
            $indexStr = \Config::elasticsearch()->INDICES->users;
        }
        if (count($bool)) {
            // boost has_avatar. I personally argued that we should not boost, but rather filter by has_avatar, but it was pointless .. so enjoy
//            if (!isset($tagBoost)) { // NO SOLUTION for now ...
//                $bool['should'][] = ['term' => ['has_avatar' => ["value" => true, "boost" => 100 ]]];
//            }
//            // enough BS
//            if ($indexStr == \Config::elasticsearch()->INDICES->users) {
//                $bool['should'][] = ['exists' => ['field' => 'tags.profile', 'boost' => 80]];
//                $bool['should'][] = ['exists' => ['field' => 'tags.instruments_primary', 'boost' => 70]];
//                $bool['should'][] = ['exists' => ['field' => 'tags.genres_primary', 'boost' => 60]];
//            } else {
//                $bool['should'][] = ['exists' => ['field' => 'tags.genres', 'boost' => 80]];
//                $bool['should'][] = ['exists' => ['field' => 'tags.influences', 'boost' => 70]];
//            }
            $query = ["bool" => $bool];
        } else {
            // empty search
            $bool = [
                'must' => [
                    ['match' => ['has_avatar' => true]]
                ]
            ];
            // enough BS
//            if ($indexStr == \Config::elasticsearch()->INDICES->users) {
//                $boostTags = [
//                    ['exists' => ['field' => 'tags.profile', 'boost' => 80]],
//                    ['exists' => ['field' => 'tags.instruments_primary', 'boost' => 70]],
//                    ['exists' => ['field' => 'tags.genres_primary', 'boost' => 60]],
//                ];
//            } else {
//                $boostTags = [
//                    ['exists' => ['field' => 'tags.genres', 'boost' => 80]],
//                    ['exists' => ['field' => 'tags.influences', 'boost' => 70]],
//                ];
//            }
//            if (isset($boostTags)) {
//                $bool['should'] = $boostTags;
//            }
            $query = [
                'function_score' => [
                    'query' => ['bool' => $bool],
                    'random_score' => new \stdClass()
                ]
            ];
        }
        
        $sort = [["_score" => "desc"]]; // second and third sort params were ["has_avatar" => "desc"], ["lastseen" => "desc"] they are insignificant
        
        $params = [
            "from" => $offset,
            "size" => $results,
            "index" => $indexStr,
//            "explain" => 1,
            "body" => [
                "query" => $query,
                "sort" => $sort,
            ]
        ];
 
        //\Helpers\Debug::log('dht', json_encode($params));
 
        $result = $dsm->search($params);
        
        if ((int) $data['count'] === 1) { //OMG all the legacy .. humanity
            $count = $result['hits']['total'];
            return array(true, $count);
        } else {
            return $this->viewSearchUsersAndPages($result, $searchStr);
        }
    }
    
    /*
     * @result DroobleSearchManager search result (Elastic client search result)
     * @searchStr - string from search (needed to 'inject' the search string as a Tag to all Users in the View)
     */
    public function viewSearchUsersAndPages($result, $searchStr) {
        $lang = \Helpers\Basic::lang();
        $hitIds = [];
        foreach ($result['hits']['hits'] as $item){
            $hitIds[] = $item['_id'];
        }
        $hitIdsImploded = implode(",", $hitIds);
        $count = $result['hits']['total'];
        $ret = [];
        // get data for hits
        if ($count) {
            
            if (strlen($searchStr)) {
                $foundTag = \Helpers\Tags::findByText($searchStr, null);
            }
            
            $elsCtrl = new \Controllers\API\ElementsController();
            
            $sql="
                SELECT
                    `search_cache`.`short_info_json` ,
                    `up`.`id`,
                    `up`.`user_status`,
                    `up`.`system_status`,
                    `up`.`lastseen`,
                    `up`.`is_page`,
                    IF ( `ucm`.`id` IS NOT NULL, 1, 0 ) as `isInCircle`
                FROM
                    `search_cache`
                JOIN
                    `users_profiles` `up`
                        ON
                            `up`.`id` = `search_cache`.`user_id`
                LEFT JOIN
                    `users_circles_members` `ucm`
                        ON
                            (`ucm`.`user_id` = :fuid AND `ucm`.`target_user_id` = `up`.`id`)
                WHERE `up`.`id` IN(" . $hitIdsImploded . ")
                    ORDER BY FIELD(up.id, " . $hitIdsImploded . ")
                    LIMIT " . $count . "
                   ;";
 
            $prepare = [ 'fuid' => $_SESSION['profile']['id'] ];
            $qres = $this->app->db->query($sql, $prepare);
            $qres->setFetchMode(\Phalcon\Db::FETCH_ASSOC);
            $qelements = $qres->fetchAll();
            foreach ($qelements as $el) {
                $a = json_decode($el['short_info_json'], true);
                if ($a != false) {
                    $a['uid'] = $el['id'];
                    $a['isInCircle'] = $el['isInCircle'];
//                    $a['status'] = $el['publicStatus'];
                    $a['is_page'] = $el['is_page'];
//                    if ($a['rates']['session_price'] and $el['id'] != $_SESSION['profile']['id']) {
//                        $a['rates']['session_price'] = floatval($a['rates']['session_price']) + floatval($a['rates']['session_price']) * ( \Config::defaults()->DROOBLE_FEE_IN_PERCENTS / 100 );
//                    }
                    if ((int) $count >= 0) {
                        $a['counter'] = $count;
                    }
                    $tags = \Helpers\Users::getUserTags($a['uid'], 3); // get with 3 - categorized with remapped type, cause otherwise you'll get random instruments (they have 4 subtypes)
                    
                    if ($a['uid'] == 1) {
                        $a['open_to_string'] = "All music";
                    } else {
                        $open_to_string = "";
                        if ( count($tags['profile']) > 1 ) {
                            $open_to_string = $tags['profile'][0] . " " . $this->app->translate->get('and') . " " . $tags['profile'][1];
                        } elseif (count($tags['profile']) == 1) {
                            $open_to_string = $tags['profile'][0];
                        }
                        $a['open_to_string'] = $open_to_string;
                    }
                    
                    $flatList = [];
                    foreach ($tags as $arr) {
                        foreach ($arr as $tag) {
                            $flatList[] = $tag;
                        }
                    }
                    if ($foundTag) {
                        $searchAsTag = mb_convert_case(stripslashes($foundTag->translation->{$lang}->text), MB_CASE_TITLE, 'UTF-8');
                        if (!in_array($searchAsTag, array_slice($flatList, 0, 2))) {
                            // 'inject' the search string as a Tag @ first position if it's not in the top 2 tags
                            foreach ($tags as $key => $arr) {
                                array_unshift($arr, $searchAsTag);
                                $tags[$key] = $arr;
                                break;
                            }
                        }
                    }
                    // remove duplicates
                    $firstParts = array_slice($tags, 0, 3);
                    $first3keys = array_keys($firstParts);
 
                    $countTypes = count($first3keys);
                    if ($countTypes > 1) {
                        $tags[$first3keys[1]] = array_diff($tags[$first3keys[1]], $tags[$first3keys[0]]);
                    } 
                    if ($countTypes > 2) {
                        $tags[$first3keys[2]] = array_diff($tags[$first3keys[2]], $tags[$first3keys[1]]);
                        $tags[$first3keys[2]] = array_diff($tags[$first3keys[2]], $tags[$first3keys[0]]);
                    }
                        
                    $a['tags'] = $tags;
                    $ret[] = $a;
                }
            }
        }
        return [true, $ret];
 
    }
 
    public function getContacts($data) {
        return array(true, \Helpers\Search::getContactsAndTheRest($data['uid'], $data));
    }
 
}
 
?>
#4search->search(Array(10))
/var/www/rc/drooble/trunk/projects/drooble_v1/services/web/private/logic/search.class.php (1113)
<?php
class search{
 
  private $db;
  private $c;
    private $app;
 
  public function __construct($db,&$c, $app) {
    $this->db = $db;
    $this->c = $c;
        $this->app = $app;
  }
/*
  public function doReindex($data){
    $ret=array();
    foreach($this->db->q("SELECT `id` FROM `users_profiles` WHERE 1") as $el){
      $ret[]=$this->lib->profileSearchIndexing($el['id']);
    }
 
    return array(true,$ret);
  }
*/
    public function searchCity($data) {
        $return = new \stdClass();
 
        $return->items = \Helpers\Search::getInstance()->searchCity($data['name']);
 
        return array(true, $return);
 
 
        /*
        // SELECT *, CASE WHEN name LIKE 'Sofia%' THEN 1 ELSE 0 END AS keymatch FROM `cities` WHERE MATCH (name) AGAINST ('+Sofia' IN BOOLEAN MODE) ORDER BY keymatch DESC
        $cities = $this->app->db->fetchAll("
            SELECT *, CASE
                WHEN search_by_me LIKE ".$this->app->db->escapeString($keywords)." THEN 4
                WHEN search_by_me LIKE ".$this->app->db->escapeString($keywords." %")." THEN 3
                WHEN search_by_me LIKE ".$this->app->db->escapeString("% ".$keywords." %")." THEN 2
                WHEN search_by_me LIKE ".$this->app->db->escapeString("% ".$keywords)." THEN 1
                ELSE 0 END AS keymatch
            FROM `cities`
            WHERE
            MATCH (search_by_me) AGAINST (".$this->app->db->escapeString($keywords).")
            ORDER BY keymatch DESC
            LIMIT 22
        ");
 
        $items = array();
 
        if ($cities) {
            foreach($cities as $city)
            {
                $title_suffix = $city['country'];
                if ($city['region']) {
                    $title_suffix = $city['region'].", ".$city['country'];
                }
 
                $items[] = array(
                    'id' => $city['id'],
                    'title' => $city['name'].", ".$title_suffix,
                    'name' => $city['name'],
                    'country' => $city['country'],
                    'region' => $city['region']
                );
            }
        }
 
        $return->items = $items;
 
        return array(true, $return);
        */
    }
 
  public function searchByMail($data){
    $email=$data['email'];
 
    if(!filter_var($email, FILTER_VALIDATE_EMAIL)){
      return array(false,"not valid");
    }
 
        /*
    $search=$email;
    $search=str_replace("*","",$search);
    $search=str_replace(" ","",$search);
    $search=str_replace(")","",$search);
    $search=str_replace("(","",$search);
 
 
    $sql="SELECT `short_info` FROM `search_cache` WHERE MATCH (`email`) AGAINST ('".'"'.$search.'"'."')";
    $r=$this->db->one($sql);
         *
         */
 
        $person = \Models\UserCache::findFirst([
            'conditions' => 'email LIKE {email:str}',
            'bind' => ['email' => $email]
        ]);
 
        if ($person) {
            return array(true, json_decode($person->short_info_json, true));
        } else {
            return array(true,'notexists');
        }
  }
 
  public function otherPersonContactsSuggester($data){
    $user_id=\Helpers\Users::getIdFromUsername($data['profile']);
    if(isset($data['circle_id']) && $data['profile'] != $_SESSION['profile']['username']){
      unset($data['circle_id']);
    }
    if(!$data['results']){
      $data['results']=9;
    }
    if(isset($data['mix_order'])){
      $data['mix_order']=true;
    }
 
    // \Helpers\Debug::alert($data['profile']);
    if($user_id){
      return $this->ContactsSuggester($user_id,$data);
    }else{
      return array(false);
    }
  }
 
  public function myContactsSuggester($data){
 
    return $this->ContactsSuggester($_SESSION['profile']['id'],$data,true, isset($data['online_first']) ? $data['online_first'] : true);
  }
 
  public function otherPersonFollowSuggester($data) {
    $data["filterUsername"] = $data["profile"];
    unset($data["profile"]);
 
    $data["follow_type"] = "followings";
 
    return $this->ContactsSuggester($_SESSION['profile']['id'],$data,true, false);
  }
 
    public function searchInMyMessages($data){
        // Used data
        $user_id = (int)$_SESSION['profile']['id'];
        if ($data['band_id'] > 0) {
            if (!\Helpers\Basic::checkUserRights((int) $data['band_id'], $user_id)) {
                return array(false,false);
            } else {
                $user_id = (int) $data['band_id'];
            }
        }
        
        $offset = $data['offset'];
        $limit = $data['limit'];
 
        $search = trim(strip_tags($data['search']));
        $search = str_replace(array("%", "'", '"'), "", $search);
 
        $length_to_get = 50; // How much characters to get
        $symbols_before = 10; // How much symbols before match to get
 
        $return = array();
 
        // Search query
        $query = "
            SELECT * FROM
            (
                SELECT
                    um.text, um.conversations_id, c.is_group, c.members,
                    IF (um.reciever_id = '".$user_id."', scsender.short_info_json, screciever.short_info_json) as partner,
                    IF (um.reciever_id = '".$user_id."', scsender.user_id, screciever.user_id) as partner_id
                FROM users_messages as um
                LEFT JOIN conversations AS c ON c.id = um.conversations_id
                LEFT JOIN search_cache AS scsender ON scsender.user_id = um.sender_id
                LEFT JOIN search_cache AS screciever ON screciever.user_id = um.reciever_id
                WHERE
                    um.sender_id != um.reciever_id
                    AND (um.sender_id = '".$user_id."' OR um.reciever_id = '".$user_id."')
                    AND um.is_deleted=0
                    AND (
                        um.text LIKE :keywords
                        OR (scsender.cache LIKE :keywords AND scsender.user_id != '".$user_id."')
                        OR (screciever.cache LIKE :keywords AND screciever.user_id != '".$user_id."')
                    )
                ORDER BY um.send_time DESC
            ) as results
            GROUP BY results.conversations_id, results.text, results.partner, results.partner_id
        ";
 
        $prepare = array(
            'keywords' => "%".$search."%"
        );
        
        // Here we store user information - reason - when we have a user, does not look for it again
        $cache_profiles = array();
 
        // So... lets begin... just in case string was more than 3 symbols
        if (mb_strlen($search) >= 3)
        {
            $cids = [];
            // Put information
            foreach($this->db->prepare($query, $prepare)->execute()->fetchAllAssoc()->cache( 1 )->result as $el) {
                $cids[] = $el['conversations_id'];
                continue; // !!!! NOTE we'll be using getMail at end of func to parse the response
                
                //this below -- NOT executed !!! 
                
                
                $text = trim(strip_tags($el['text']));
 
                $find_position = mb_strpos($search, $text);
                $text_length = mb_strlen($text);
                $string_length = mb_strlen($search);
 
                // Stupid way but... i cant regex :/
                if ($text_length > $length_to_get)
                {
                    if (($find_position - $symbols_before) <= 0)
                    {
                        $text = mb_substr($text, 0, $length_to_get)."...";
                    }
                    else if (($find_position - $symbols_before) > 0 AND ($find_position - $symbols_before + $length_to_get) <= $text_length)
                    {
                        $text = "...".mb_substr($text, ($find_position - $symbols_before), $length_to_get)."...";
                    }
                    else
                    {
                        $text = "...".mb_substr($text, ($length_to_get*-1));
                    }
                }
 
                $line = array(
                    'quote' => $text,
                    'conversation' => "#".$el['conversations_id']
                );
 
                // Put information about users in converstation
                if ((int)$el['is_group'] == 0)
                {
                    if (!isset($cache_profiles[$el['partner_id']]))
                    {
                        $user = unserialize($el['partner']);
 
                        $cache_profiles[$el['partner_id']] = array(
                            'names' => $user['display_name']
                        );
 
                        if (isset($user['avatar_icon']) AND $user['avatar_icon'] != "")
                        {
                            $cache_profiles[$el['partner_id']]['avatar_icon'] = $user['avatar_icon'];
                        }
                    }
 
                    $line['names'] = $cache_profiles[$el['partner_id']]['names'];
                    $line['avatar_icon'] = $cache_profiles[$el['partner_id']]['avatar_icon'];
                }
                else
                {
                    // Check collection
                    $collection = array();
                    $user_ids = explode(",", $el['members']);
                    foreach($user_ids as $key => $val)
                    {
                        if ((int)$val != 0 AND (int)$val != $user_id)
                        {
                            $collection[(int)$val] = false;
                        }
                    }
 
                    // Filter which users we have
                    $search_user_ids = array();
                    foreach($collection as $key => $val)
                    {
                        if (isset($cache_profiles[$key]))
                        {
                            // Put it in our collection
                            $collection[$key] = $cache_profiles[$key]['names'];
                        }
                        else
                        {
                            $search_user_ids[] = $key;
                        }
                    }
 
                    // Get missed users
                    if (!empty($search_user_ids))
                    {
                        $query = "SELECT user_id, short_info_json FROM search_cache WHERE user_id IN (".implode(",", $search_user_ids).")";
 
                        foreach($this->db->prepare($query, array())->execute()->fetchAllAssoc()->cache( 1 )->result as $usr) {
                            $user = json_decode($usr['short_info_json'], true);
                            $cache_profiles[(int)$usr['user_id']] = array(
                                'names' => $user['display_name']
                            );
 
                            if (isset($user['avatar_icon']) AND $user['avatar_icon'] != "")
                            {
                                $cache_profiles[(int)$usr['user_id']]['avatar_icon'] = $user['avatar_icon'];
                            }
 
                            // Put it in our collection
                            $collection[(int)$usr['user_id']] = $cache_profiles[(int)$usr['user_id']]['names'];
                        }
                    }
 
                    // Now merge names
                    $line['names'] = implode(", ", $collection);
                    $line['avatar_icon'] = DEF_GROUP_CHAT_ICON;
                }
 
                // Some modifications
                // $text = str_replace($search, "<em>".$search."</em>", $text);
                $seek_and_destroy = '#\b'.$search.'\b#iu';
                $line['quote'] = preg_replace($seek_and_destroy, '<em>$0</em>', $line['quote']);
                $line['names'] = preg_replace($seek_and_destroy, '<em>$0</em>', $line['names']);
 
                $return[] = $line;
            }
        }
        
        // NOTE!!  overwrite response here
        $mc = new \Controllers\API\MessengerController();
        $cids = array_reverse($cids);
        $cids = array_slice($cids, $offset, $limit);
        $getMailData = [
            'folder' => 'All',
            'mode' => 'search',
            'results' => $limit,
            'cids' => $cids,
            'pid' => $data['band_id']
        ];
        
        $getMailRe = $mc->getMail($getMailData);
        $return = is_array($getMailRe) ? $getMailRe : ['conversations' => []];
        
        // Return
        return array(true, array("length" => count($return['conversations']) , "conversations" => $return['conversations']));
    }
 
  private function ContactsSuggester($user_id,$data,$myContactsOnly=false, $onlineFirst = false){
    $uid=$user_id;
    $prepare=array();
        
        $excludeMothership = array_key_exists('exclude_mothership', $data) ? $data['exclude_mothership'] : true;
 
    // $prepare['uid']=$uid;
    $search=$data['search'];
 
    /*if (empty($search)) {
      return array(false, 'empty search query');
    }*/
 
    $select   = array();
    $leftJoin   = array();
    $order     = array();
 
    if($myContactsOnly){
      $data['follow_type']='followings';
    }
    $search=trim($search);
    $search=str_replace("*","",$search);
    $search=str_replace(")","",$search);
    $search=str_replace("(","",$search);
    $search=str_replace(" ","* +*",$search);
 
    if($search){
      $search=$this->db->quote("*".$search."*");
    }else{
      $search=NULL;
    }
    $circle_id=$data['circle_id'];
    // $prepare['circle_id']=$circle_id;
 
        $show_status=$onlineFirst ? true : $data['show_status'];
    $latest=isset($data['latest']) ? true : false;
    $is_online=$data['is_online'];
 
    $mix_order=$data['mix_order'];
 
        $where = [
            '`up`.`is_banned` IS NULL AND `up`.`is_disabled` = 0'
        ];
        
    if($is_online){
      $show_status=1;
      $where[]="(`up`.`user_status`>0 AND `up`.`lastseen`> " . (time() - \Config::defaults()->DEF_LASTSEEN_OFFLINE_TIME).") ";
    }
 
    if (isset($data["user_type"])) {
//      $LeftJoinUserStatus="LEFT JOIN `users_profiles` AS `up` ON `up`.`id` = `sc`.`user_id` ";
 
//      if ($data["user_type"] == "page") {
//        $where[] = "`up`.`is_page` = 1";
//      } else if ($data["user_type"] == "user") {
//                $where[] = "`up`.`is_page` IS NULL";
//            }
    }
 
    if(isset($show_status)){
            $SelectUserStatus=" fn_calculateProfilePublicStatus(`up`.`user_status`,`up`.`system_status`,`up`.`lastseen`) as `publicStatus`, `up`.`id` as `user_suggest_id`,
                                if(`chat_status` > 0,fn_calculateProfilePublicStatus(`user_status`, `system_status`, `lastseen`),0) as `cstatus`,";
      //$SelectUserStatus="`up`.`user_status`, `up`.`lastseen`, `up`.`system_status`, ";
//      $LeftJoinUserStatus="LEFT JOIN `users_profiles` AS `up` ON `up`.`id` = `sc`.`user_id` ";
      $where[] = "`up`.`is_page` IS NULL";
    }
 
    if ( $data['gender'] ) {
      $select[] = "`up`.`gender`";
 
//      if ( !$LeftJoinUserStatus ) {
//        $leftJoin[] = "LEFT JOIN `users_profiles` AS `up` ON `up`.`id` = `sc`.`user_id` ";
//      }
    }
 
        if ( $data['is_page'] ) {
            //\Helpers\Debug::log('is_page passed');
      $select[] = "`up`.`is_page`";
//      if ( !$LeftJoinUserStatus ) {
//        $leftJoin[] = "LEFT JOIN `users_profiles` AS `up` ON `up`.`id` = `sc`.`user_id` ";
//      }
    }
 
    if ( $data['pictures_first'] ) {
      $select[]   = 'IF(`upi`.`id`, 1, 0) AS `has_picture`'; // moje da stane greshka, zashtoto ne znaeme koi e profile album-a
      //$leftJoin[] = "LEFT JOIN `users_pictures` AS `upi` ON ( `upi`.`user_id` = `sc`.`user_id` AND `upi`.`profilepic` = 1 ) ";
      $leftJoin[] = "LEFT JOIN `users_elements` AS `upi` ON `sc`.`id` = `upi`.`creator_id` AND `upi`.`type`='picture' AND `upi`.`is_default` = '1' AND `upi`.`deleted_time` IS NULL AND `upi`.`published_time` IS NOT NULL AND `upi`.`deployed_time` IS NOT NULL ";
      $order[]   = '`has_picture` DESC';
    }
 
    if ( $data['compare_followers'] && in_array($data['follow_type'], array('followers', 'followings')) && isset($_SESSION['profile']['id']) ) {
      $select[]   = 'IF(`my_ucm`.`id`, 1, 0) AS `same_follower`';
      $select[]   = 'IF(`my_ucm`.`id`, `my_ucm`.`circle_id`, -1) AS `circle_id`';
      $leftJoin[] = "LEFT JOIN `users_circles_members` AS `my_ucm` ON ( `my_ucm`.`user_id` = " . $_SESSION['profile']['id'] . " AND `my_ucm`.`target_user_id` = " . ( $data['follow_type'] == 'followers' ? "`ucm`.`user_id`" : "`ucm`.`target_user_id`" ) . " ) ";
      $order[]   = '`same_follower` DESC';
    }
 
    if ($data['filterUsername']) {
      $otherUserId = \Helpers\Users::getIdFromUsername($data['filterUsername']);
      if ($otherUserId) {
        $leftJoin[] = "LEFT JOIN `users_circles_members` AS `other_ucm` ON ( `other_ucm`.`user_id` = " . $otherUserId . " AND `other_ucm`.`target_user_id` = `ucm`.`target_user_id` ) ";
        $where[] = "`other_ucm`.`id` IS NULL";
        $where[] = "`up`.`id` != {$otherUserId}";
      }
    }
 
    switch($data['follow_type']){
      case "followers":
        $where[]="`ucm`.`target_user_id` = '{$uid}'";
        $leftJoinCS="LEFT JOIN `search_cache` AS `sc` ON `sc`.`user_id` = `ucm`.`user_id`";
      break;
      case "followings":
        $where[]="`ucm`.`user_id` = '{$uid}'";
        $leftJoinCS="LEFT JOIN `search_cache` AS `sc` ON `sc`.`user_id` = `ucm`.`target_user_id`";
      break;
      default:
        $leftJoinCS="LEFT JOIN `search_cache` AS `sc` ON (`sc`.`user_id` = `ucm`.`target_user_id` OR `sc`.`user_id` =`ucm`.`user_id`)";
    }
 
    $limit = " ";
    if(isset($data["limit"])){
      $prepare['limit']=$data['limit'];
      $limit = " LIMIT :limit ";
 
      if (isset($data["offset"])) {
        $prepare['offset']=$data['offset'];
        $limit .= "OFFSET :offset ";
      }
    }
 
    if($search){
      $where[]="MATCH (`cache`) AGAINST ($search IN BOOLEAN MODE)";
    }
 
    if($circle_id and $circle_id!='All'){
      $where[]="`uc`.`id` = '{$circle_id}'";
    }
 
    $where[] ="`sc`.`user_id`!= '{$uid}'";
 
    if ( $uid != 1 && $excludeMothership) {
            $where[]="`ucm`.`target_user_id` != 1";
        }
 
        if (isset($data['exclude_ids'])) {
            $exclude = is_array($data['exclude_ids']) ? implode(',', $data['exclude_ids']) : $data['exclude_ids'];
 
            if ( strlen($exclude) > 0 ) {
           $where[]="`ucm`.`target_user_id` NOT IN ($exclude)";
            }
        }
       
    $where = implode(" AND ",$where);
    if($where=='') $where = '1';
 
        if ($latest) {
            $order[] = "`ucm`.`create_time` DESC";
        }
 
    if ($onlineFirst) {
      $order[]='FIELD(`publicStatus`, 2) DESC';
      $order[]='`up`.`first_name`';
      $order[]='`up`.`last_name`';
    }
 
    if(isset($mix_order)) {
      $order[]='RAND()';
    }
 
    if ( count($select) > 0 ) {
      $select = implode(', ', $select) . ',';
    } else {
      $select = '';
    }
    $leftJoin = implode(' ', $leftJoin);
 
    if ( count($order) > 0 ) {
      $order = 'ORDER BY ' . implode(', ', $order);
    } else {
      $order = '';
    }
 
    $sql="SELECT
           {$select}
           {$SelectUserStatus}
           `sc`.`short_info_json`,
           `uc`.`name` as `circle_name`,
           `ucm`.`target_user_id`
        FROM `users_circles_members` AS `ucm`
        {$leftJoinCS}
        {$LeftJoinUserStatus}
        {$leftJoinProfiles}
        {$leftJoin}
       LEFT JOIN `users_circles` AS `uc` ON `uc`.`id` = `ucm`.`circle_id`
           LEFT JOIN `users_profiles` AS `up` ON `up`.`id` = `sc`.`user_id`
         WHERE  ( {$where} ) AND `sc`.`is_disabled` = 0 /*AND `ucm`.`target_user_id`!='1'*/
         GROUP BY `sc`.`id`
         {$order}
         {$limit}
    ";
               
    // \Helpers\Debug::log($sql);
        // echo $sql;
    $ret=array();
 
    //foreach($this->db->q($sql,$prepare) as $el){
    $cache = isset($data['cache']) ? $data['cache'] : 1;
    // echo print_r(array($sql, $prepare), 1);
    // \Helpers\Debug::log($sql);
    if (isset($prepare["limit"])) $prepare["limit"] = intval($prepare["limit"]);
    if (isset($prepare["offset"])) $prepare["offset"] = intval($prepare["offset"]);
 
    $mem_before = memory_get_usage();
        $res = $this->db->prepare($sql,$prepare)->execute()->fetchAllAssoc()->result;
  //  \Helpers\Debug::log("Memory used by the contacts array: ", memory_get_usage() - $mem_before);
        // $res = $this->db->prepare($sql,$prepare)->execute()->fetchAllAssoc()->cache( $cache )->result;
        // echo "$sql - " . print_r($prepare,1);
    foreach($res as $el){
      $a=json_decode($el['short_info_json'], true);
 
      if($show_status){
                $status=array();
                $statuses=unserialize(USER_STATUS_STATEMENTS);
                $status['status_id']=$el['publicStatus'];
                $status['status_name']=$statuses[$status['status_id']];
        $chatStatus=array();
                $chatStatus['status_id']=$el['cstatus'];
        $chatStatus['status_name']=$statuses[$chatStatus['status_id']];
                $a['status']=$status;
        $a['chat_status']=$chatStatus;
      }
 
      if($a!=false){
        $a['gender']     = $el['gender'];
        $a['has_picture']   = $el['has_picture'];
        $a['same_follower'] = $el['same_follower'];
        $a['circle_id']   = $el['circle_id'];
        //Compatibility
        $a['profile_tags']  = $a['tags']['profile'];
 
        if($el['user_id'] == $_SESSION['profile']['id'] and $uid == $_SESSION['profile']['id']){
          $a['circle']=$el['circle_name'];
        }
        $ret[]=$a;
      }
    }
 
    return array(true,array("profiles"=>$ret));
    //return array(true,array("profiles"=>$ret),str_replace("\t","",str_replace("\n", "", $sql)),$prepare);
    //return array(true,$ret);
  }
 
    public function chatBarSuggester($data) {
        $uid = (int)$_SESSION['profile']['id'];
        $limit = isset($data['limit']) ? $data['limit'] : 15;
        $return = array();
 
        $query = "
                    SELECT
                            DISTINCT
                            `sender_id`,
                            `reciever_id`,
                            /*fn_calculateProfilePublicStatus(`up`.`user_status`,`up`.`system_status`,`up`.`lastseen`) as `publicStatus`,*/
                            if(`chat_status` > 0,fn_calculateProfilePublicStatus(`user_status`, `system_status`, `lastseen`),0) as `cstatus`
                    FROM
                            `users_messages` AS `um`
                        JOIN
                            `conversations` AS `c`
                        ON
                            (`um`.`conversations_id` = `c`.`id`)
                        LEFT JOIN
                            `users_profiles` AS `up`
                        ON
                            `up`.`id` = IF(`sender_id` = :uid, `reciever_id`, `sender_id`)
                    WHERE
                            (`sender_id` = :sid OR `reciever_id` = :rid)
                        AND
                            `sender_id` <> `reciever_id`
                        AND
                            `up`.`is_page` IS NULL
                    GROUP BY
                            `um`.`conversations_id`
                    ORDER BY
                            `send_time` DESC
                    LIMIT
                            0,$limit";
 
        $prep = array("uid" => $uid, "sid" => $uid, "rid" => $uid);
        
        $result = $this->db->prepare($query, $prep)->execute()->fetchAllAssoc()->result;
        $exclude = array();
        $statuses=unserialize(USER_STATUS_STATEMENTS);
        $chatstatuses=unserialize(CHAT_STATUS_STATEMENTS);
 
        foreach ( $result as $r ) {
            $id = $r['sender_id'] == $uid ? $r['reciever_id'] : $r['sender_id'];
            $user = \Helpers\Users::getUserShortInfoFromId($id);
 
            $status=array();
            $status['status_id']=\Helpers\Users::getInstance()->getProfilePublicStatus($id);//$r['publicStatus'];
            $status['status_name']=$statuses[$status['status_id']];
            $chatStatus=array();
            $chatStatus['status_id']=\Helpers\Users::getInstance()->getProfilePublicChatStatus($id);
            $chatStatus['status_name']=$chatstatuses[$chatStatus['status_id']];
            $user['status']=$status;
            $user['chat_status']=$chatStatus;
 
            if (!in_array($id, $exclude)) {
                $return[] = $user;
                $exclude[] = $id;
                //\Helpers\Debug::log($id,$status);
            }
        }
 
 
        if ( count($return) < $limit ) {
            $data["limit"] = $limit - count($return);
            $data["exclude_ids"] = $exclude;
            $additional = $this->myContactsSuggester($data);
 
            if ($additional[0] && count($additional[1]['profiles']) > 0) {
                $return = array_merge($return, $additional[1]['profiles']);
            }
        }
 
        foreach ( $return as &$r ) {
            $r['profile_tags']  = $r['tags']['profile'];
        }
 
        return array(true, array("profiles" => $return));
    }
    /* DEPRECATED
  public function followSuggester($data) {
    return array(true);
    $users   = array();
    $offset = $data['offset'] || 0;
    $lat = $this->app->request->ipInfo["latitude"];
        $long = $this->app->request->ipInfo["longitude"];
    $userTags   = array();
    //$results = $this->db->q( "SELECT `name`, `type` FROM `users_tags` WHERE `user_id` = :uid", array("uid" => $_SESSION['profile']['id']));
    $results = $this->db->prepare( "SELECT `name`, `type` FROM `users_tags` WHERE `user_id` = :uid", array("uid" => $_SESSION['profile']['id']))->execute()->fetchAllAssoc()->cache()->result;
 
    foreach ( $results as $el ) {
      $userTags[ $el[ 'type' ] ][]   = ucwords(strtolower($el['name']));
    }
 
    // foreach ( $userTags as $k => $e ) {
    //   $tagsCache[$k] = implode( " ", str_replace( " ", "_", $e ) );
    // }
 
    $query   = "
      SELECT
        `upr`.`id` as `user_id`,
        `sc`.`short_info`,
        " . ( ( $lat != 0 && $long != 0 ) ? "IF ( " . ($lat - 10) . " < `upr`.`lat` AND `upr`.`lat` < " . ($lat + 10) . " AND " . ($long - 10) . " < `upr`.`long` AND `upr`.`long` < " . ($long + 10) . ", 1, 0 )" : "0" ) . " as `is_near`,
        MATCH (instruments) AGAINST (:instruments IN NATURAL LANGUAGE MODE) AS score_instruments,
        MATCH (genres) AGAINST (:genres IN NATURAL LANGUAGE MODE) AS score_genres,
        MATCH (influences) AGAINST (:influences IN NATURAL LANGUAGE MODE) AS score_influences,
        IF ( `ucm`.`id` IS NOT NULL, 1, 0 ) as `followed`,
        IF ( `uc`.`id` IS NOT NULL, 1, 0 ) as `has_posts`
      FROM
        `users_profiles` `upr`
      JOIN
        `users_elements` `upi`
          ON
            (`upr`.`id` = `upi`.`creator_id` AND `upr`.`profile_album_id` = `upi`.`album_id` AND `upi`.`type`='picture' AND `upi`.`is_default` = '1' AND `upi`.`deleted_time` IS NULL AND `upi`.`published_time` IS NOT NULL AND `upi`.`deployed_time` IS NOT NULL)
      JOIN
        `search_cache` `sc`
          ON
            (`sc`.`user_id` = `upr`.`id`)
      LEFT JOIN
        `users_comments` `uc`
          ON
            (`uc`.`owner_element_id` = `upr`.`id` AND `uc`.`model` = 'post')
      LEFT JOIN
        `users_circles_members` `ucm`
          ON
            (`ucm`.`user_id` = :uid AND `ucm`.`target_user_id` = `upr`.`id`)
      WHERE
        `upr`.`id` != :uuid
      GROUP BY
        `uc`.`owner_element_id`
      HAVING
        `followed` = 0
      ORDER BY
        `score_genres` DESC,
        `score_influences` DESC,
        `score_instruments` DESC,
        `is_near` DESC,
        `has_posts` DESC
      LIMIT
        :offset, 9
    ";
 
    $results = $this->db->prepare($query, array(
      'offset'     => $offset,
      'uid'       => $_SESSION['profile']['id'],
      'uuid'       => $_SESSION['profile']['id'],
      'genres'     => ( isset( $userTags['genres'] )     ? implode( " ", str_replace( " ", "_", $userTags['genres'] ) ) : '' ),
      'instruments'   => ( isset( $userTags['instruments'] )   ? implode( " ", str_replace( " ", "_", $userTags['instruments'] ) ) : '' ),
      'influences'   => ( isset( $userTags['influences'] )   ? implode( " ", str_replace( " ", "_", $userTags['influences'] ) ) : '' )
    ))->execute()->fetchAllAssoc()->cache()->result;
 
 
    foreach ( $results as $result ) {
      $user = unserialize( $result['short_info'] );
 
      if ( $user != false ) {
        $users[] = $user;
      }
    }
 
    return array(true, $users);
  }*/
 
  public function searchSuggester($data) {
        $results = isset($data['limit']) ? $data['limit'] : \Config::defaults()->RESULTS_TO_SHOW->search;
        if ($results > \Config::defaults()->MAX_RESULTS_TO_SHOW->search) {
            $results = \Config::defaults()->MAX_RESULTS_TO_SHOW->search;
        }
 
        $reminder = $result % 2;
 
        $search = $data['autocomplete'];
        $msc = microtime(true);
        if (!isset($data['skipTags']) || !$data['skipTags']) {
            $tags = $this->tagsSuggester(array('autocomplete' => $search, 'limit' => (int) ($results / 2) + $reminder));
        }
 
        $profiles = [];
        $pages = [];
        if (isset($data['skipPages']) || $data['skipPages'] == 'true') { // legacy string true
            $profilesRes = $this->search(array('autocomplete' => $search, 'show_profiles' => true, 'visible' => $data['visible']));
            if ($profilesRes[0]) {
                $profiles = $profilesRes[1];
            }
        } else { // search both users and pages and separate them here
 
            $searchRes = $this->search(['autocomplete' => $search, 'visible' => $data['visible']]); 
 
            if ($searchRes[0]) {
                foreach ($searchRes[1] as $i => $sr) {
                    if ($i == 0) {
                        $firstType = $sr['is_page'] ? "page" : "profile";
                    }
                    if ($sr['is_page']) {
                        $pages[] = $sr;
                    } else {
                        $profiles[] = $sr;
                    }
                }
            }
        }
 
        $communities = \Helpers\Tags::getCommunities(null, false, $search, $results);
 
 
        $sCtrl = new \Controllers\API\SearchController();
        $posts = $sCtrl->searchPosts($data);
        
        $msc=microtime(true)-$msc;
        $res=array();
        $res['profiles'] = $profiles;
        $res['pages'] = $pages;
        $res['posts'] = $posts;
        $res['communities'] = $communities;
        $res['tags'] = $tags[0] ? $tags[1]['tags'] : array();
        $res['first_type'] = isset($firstType) ? $firstType : false;
        return array(true, $res);
    }
 
    public function getFacebookFriends ()
    {
        $uid = (int)$this->app->session->profile['id'];
        $friends = \Helpers\Users::getUnfollowedFacebookFriends($uid);
        return [true, $friends];
    }
 
    //polzva se samo za chat-a, no i tam e spriano i vrushta []
    public function generatePeopleSuggestions ($data) {
        return array(true,[]);
        if (isset($data['chat'])) {
            $uid = $_SESSION["profile"]["id"];
            $usedUids = [];
            if (isset($data['used_ids']) AND is_array($data['used_ids'])) {
                $usedUids = $data['used_ids'];
            }
 
            $result = [
                'explore' =>  \Helpers\Suggestions::generateExploreSuggestions($uid, $usedUids)
            ];
 
            return array(true, $result);
        }
 
        if (!isset($data["sideMenu"]) && !isset($data["explore"])) {
            return array(
                "sideMenu"   => array(),
                "explore"  => array()
            );
        }
 
        $result = \Helpers\Suggestions::generateSuggestions($_SESSION['profile']['id'], $data["sideMenu"], $data["explore"]);
 
        if (!$result) {
            return array(false, "insert/update failed");
        }
 
        return array(true, $result);
    }
 
  public function tagsSuggesterDEPRECATED($data){
    // DEPRECATED !!!!!! ! !! !
// DONE elasticsearch!
        
    $search=trim($data['search']);
    $type = $data['type'];
 
    if(!$search) return array(true,array());
        if ( strlen($search) < 3 ) {
            return [true, []];
        }
 
    $limit = isset($data['limit']) ? intval($data['limit']) : \Config::defaults()->RESULTS_TO_SHOW->search;
    if($limit > \Config::defaults()->MAX_RESULTS_TO_SHOW->search){
      $limit = \Config::defaults()->MAX_RESULTS_TO_SHOW->search;
    }
 
    $offset = isset($data['offset']) ? intval($data['offset']) : 0;
        $lang = \Helpers\Basic::lang();
        
        $regex = new \MongoDB\BSON\Regex('^'.$search, 'i');
        
        if ($type == "" || $type == "all") {
            $conditions = [
                "translation.{$lang}.text" => $regex,
            ];
 
            $project = [
                "type" => 1,
                'translation' => 1,
                'posts_rating' => ['$sum' => ['$type.posts.moderator_rating', '$type.posts.user_rating']],
                'profile_rating' => ['$sum' => ['$type.profile.moderator_rating', '$type.profile.user_rating']],
                'instruments_rating' => ['$sum' => ['$type.instruments.moderator_rating', '$type.instruments.user_rating']],
                'influences_rating' => ['$sum' => ['$type.influences.moderator_rating', '$type.influences.user_rating']],
                'equipment_rating' => ['$sum' => ['$type.equipment.moderator_rating', '$type.equipment.user_rating']],
                'genres_rating' => ['$sum' => ['$type.genres.moderator_rating', '$type.genres.user_rating']],
                'rating' => ['$max' => ['$posts_rating', '$profile_rating', '$instruments_rating', '$influences_rating', '$equipment_rating', '$genres_rating']],
            ];
        } else {
            $conditions = [
                "translation.{$lang}.text" => $regex,
            ];
                
            $project = [
                "type.{$type}" => 1,
                'translation' => 1,
                'rating' => ['$sum' => ['$' . "type.{$type}." . 'moderator_rating', '$' . "type.{$type}." . 'user_rating']]
            ];
        }
 
        $res = \Collections\Tags::aggregate([
            ['$match' => $conditions],
            ['$project' => $project],
            ['$sort' => ['rating' => -1]],
            ['$skip' => $offset],
            ['$limit' => $limit],
        ]);
        
        $tags = [];
        foreach ($res as $tag) {
            $tagName = mb_convert_case(stripslashes($tag->translation->{$lang}->text), MB_CASE_TITLE, 'UTF-8');
            foreach ($tag->type as $type => $notneed) {
                $tags[$type][] = ['index' => \Helpers\Tags::transformIndex($tagName), "url" => \Helpers\Basic::getUrl($tagName, $type), "name"=> $tagName ];
            }
        }
        
    return array(true,array("tags"=>$tags));
  }
    
    public function tagsSuggester($data) {
        $search = trim($data['search']);
        $type = $data['type'];
 
        if (!$search) {
            return [true, []];
        }
        if (strlen($search) < 3) {
            return [true, []];
        }
 
        $limit = isset($data['limit']) ? intval($data['limit']) : \Config::defaults()->RESULTS_TO_SHOW->search;
        if ($limit > \Config::defaults()->MAX_RESULTS_TO_SHOW->search) {
            $limit = \Config::defaults()->MAX_RESULTS_TO_SHOW->search;
        }
 
        $offset = isset($data['offset']) ? intval($data['offset']) : 0;
        $lang = \Helpers\Basic::lang();
        
        $sort = [["rating" => "desc"]];
        
        $bool = [];
        
        if (strlen($type) && $type != 'all') {
            $tagTypes = unserialize(TAGS_TYPES);
            if (!in_array($type, $tagTypes)) {
                return [true, []];    
            }
            $bool['filter'] = [
                'term' => [
                    'types' => $type
                ]
            ];
        }
        
        $bool["must"] = [
            [
                "match_phrase_prefix" => [
                    "text" => $search
                ]
            ]
        ];
        $query = ["bool" => $bool];
        
        $params = [
            "from" => $offset,
            "size" => $limit,
            "index" => \Config::elasticsearch()->INDICES->tags->{$lang},
//            "explain" => 1,
            "body" => [
                "query" => $query,
                "sort" => $sort,
            ]
        ];
            
        $dsm = $this->app->droobleSearchManager;
        $result = $dsm->search($params);
        
        $tags = [];
        
        foreach ($result['hits']['hits'] as $item){
            $tagName = mb_convert_case(stripslashes($item['_source']['text']), MB_CASE_TITLE, 'UTF-8');
            $tags[$type][] = ["name"=> $tagName ];
        }
 
        return [true, ['tags' => $tags]];
    }
    
    // looks like I did this to track stuff for Segment, without making too many requests (due to the $data['count'] that is used for counting search results,
    // but now it just wraps the search() func
    public function advancedSearchPage($data){
        
        $searchStr = strlen(trim($data['autocomplete'])) ? $data['autocomplete'] : trim($data['search']);
        $properties =array(
                "category" => 'Home page',
                "search_query" => "'".$searchStr."'"
            );
 
        if (isset($data['profile']) && is_array($data['profile'])) {
            // create Segment properties from search tags
            foreach ($data['profile'] as $tag) {
                if (preg_match('/^[0-9a-z\s]+$/i', $tag)) {
                    $properties[ preg_replace('/\s+/', '_', strtolower($tag)) ] = true;
                }
            }
        }
 
        // why not just 0/1 ? ... strings 'true' | 'false'
        if(isset($data['bands']) && ($data['bands'] == 'true' or $data['bands'] == true)){
            $properties['bands'] = true;
        }
 
        if(isset($data['venues']) && ($data['venues'] == 'true' or $data['venues'] == true)){
            $properties['venues'] = true;
        }
 
        if(isset($data['clubs']) && ($data['clubs'] == 'true' or $data['clubs'] == true)){
            $properties['clubs'] = true;
        }
 
        if(isset($data['recording_studios']) && ($data['recording_studios'] == 'true' or $data['recording_studios'] == true)){
            $properties['recording_studios'] = true;
        }
 
        if(isset($data['rehearsal_space']) && ($data['rehearsal_space'] == 'true' or $data['rehearsal_space'] == true)){
            $properties['rehearsal_space'] = true;
        }
 
        if(isset($data['music_school']) && ($data['music_school'] == 'true' or $data['music_school'] == true)){
            $properties['music_school'] = true;
        }
 
        if(isset($data['jam']) && ($data['jam'] == 'true' or $data['jam'] == true)) {
            $properties['jammers'] = true;
        }
 
        if(isset($data['teach']) && ($data['teach'] == 'true' or $data['teach'] == true)){
            $properties['teachers'] = true;
 
            if (isset($data['price_min'])) {
                $properties['price_min'] = $data['price_min'];
            }
            if (isset($data['price_max'])) {
                $properties['price_max'] = $data['price_max'];
                
                // When its picked maximum of 150+... 
                if ((int)$data['price_max'] == 150) {
                    $properties['price_max'] = 999;
                }
            }
        }
 
        if(isset($data['available']) && ($data['available'] == 'true' or $data['available'] == true)){
            $properties['available'] = true;
        }
 
        if(isset($data['looking_bandmates']) && ($data['looking_bandmates'] == 'true' or $data['looking_bandmates'] == true)){
            $properties['looking_bandmates'] = true;
        }
 
        if(isset($data['instruments']) && $data['instruments']){
            $properties['instruments'] = $data['instruments'];
        }
 
        if(isset($data['instruments_primary_to_join']) && $data['instruments_primary_to_join']){
            $properties['instruments_primary_to_join'] = $data['instruments_primary_to_join'];
        }
 
        if(isset($data['instruments_primary_for_bandmates']) && $data['instruments_primary_for_bandmates']){
            $properties['instruments_primary_for_bandmates'] = $data['instruments_primary_for_bandmates'];
        }
 
        if(isset($data['genres']) && $data['genres']){
            $properties['genres'] = $data['genres'];
        }
 
        if(isset($data['influences']) && $data['influences']){
            $properties['influences'] = $data['influences'];
        }
 
        if(isset($data['equipment']) && $data['equipment']){
            $properties['equipment'] = $data['equipment'];
        }
 
        if(isset($data['languages']) && $data['languages']){
            $properties['languages'] = $data['languages'];
        }
 
        if(isset($data['location']) && $data['location']){
            $properties['location'] = $data['location'];
        }
 
        if(isset($data['place']) && $data['place']){
            $properties['place'] = $data['place'];
        }
 
        $track = array();
 
        $track['event'] = "Advanced Search";
 
        if($this->app->session->has('profile')){
            $track['userId'] = $this->app->session->get('profile')['id'];
        }else{
//            $track['anonymousId'] = rand(100000,999999);
        }
 
        $track['properties'] = $properties;
 
        if( $this->app->session->has('profile') && (!isset($data['count']) or $data['count'] == 2) ){
            if(!$_COOKIE["druast"]) {
                $secureConnection = strtolower(\Helpers\Basic::getInstance()->get_php_http_scheme()) == 'https' ? true : false;
                setcookie("druast", date("Y-m-d"), time() + 60 * 60, '/', NULL, $secureConnection, true); // 60 minutes
                \Drooble\Activities\Activities::add( "advancedSearch", $track['userId'], (object) ['type' => 'user', 'id' => $track['userId']], ['segment' => ['track' => $track]] );
            }
        }
 
        return $this->search($data);
 
    }
    
    // $data['autocomplete'] string - the search works as autocomplete e.g. Joh -> John
    // $data['search'] string - the search works as search (bad at autocomplete, but with more accurate results)
    // TODO - think if possible to use instead of getContacts - you'll need your 'followings/folloers' on top of results
    public function search($data) {
        $go_search=false;
    foreach(array_keys($data) as $e){
      if(!in_array($e, array_keys( get_object_vars( \Config::api()->MAP->search->data ) ))){
        return array(false,"input error1 '".$e."'");
      }
      if($data[$e]){
        $go_search=true;
      }
    }
    if(!$go_search){
      return array(false,"empty search");
    }
        
        $dsm = $this->app->droobleSearchManager;
        $lang = \Helpers\Basic::lang();
        $analyzer = strtolower(\Config::languages()->language->{$lang}->name_in_english);
        $offset = $data['offset'] ? $data['offset'] : 0;
        $results = isset($data['limit']) ? $data['limit'] : \Config::defaults()->RESULTS_TO_SHOW->search;
    if($results > 100){ // way too many
      $results = \Config::defaults()->MAX_RESULTS_TO_SHOW->search;
    }
        
        $count = false;
       
       $pageTypeIds = [];
       $matchTags = [
           'profile' => [],
           'instruments' => [],
           'genres' => [],
           'equipment' => [],
           'influences' => [],
           'languages' => [],
           'instruments_primary_to_join' => [],
           'instruments_primary_for_bandmates' => [],
       ];
       
       if ((int) $data['venues'] == 1) {
            $pageTypeIds[] = 106;
        }
 
        if ((int) $data['clubs'] == 1) {
            $pageTypeIds[] = 107;
        }
 
        if ((int) $data['music_school'] == 1) {
            $pageTypeIds[] = 120;
        }
 
        if ((int) $data['recording_studios'] == 1) {
            $pageTypeIds[] = 115;
        }
 
        if ((int) $data['rehearsal_space'] == 1) {
            $pageTypeIds[] = 128;
        }
 
        if (is_array($data['instruments']) AND ! empty($data['instruments'])) {
           foreach ($data['instruments'] as $item) {
               $matchTags['instruments']['should'][] = $item;
           }
       }
       if (is_array($data['genres']) AND ! empty($data['genres'])) {
           foreach ($data['genres'] as $genre) {
               $matchTags['genres']['should'][] = $genre;
           }
       }
       if (is_array($data['equipment']) AND ! empty($data['equipment'])) {
           foreach ($data['equipment'] as $item) {
               $matchTags['equipment']['should'][] = $item;
           }
       }
       if (is_array($data['influences']) AND ! empty($data['influences'])) {
           foreach ($data['influences'] as $item) {
               $matchTags['influences']['should'][] = $item;
           }
       }
       if (is_array($data['languages']) AND ! empty($data['languages'])) {
           foreach ($data['languages'] as $item) {
               $matchTags['languages']['should'][] = $item;
           }
       }
       // these are reversed ... to match users who want to play an instrument and bands that are looking for the same
       if (is_array($data['instruments_primary_to_join']) AND ! empty($data['instruments_primary_to_join'])) {
           foreach ($data['instruments_primary_to_join'] as $item) {
               $matchTags['instruments_primary_for_bandmates']['should'][] = $item;
           }
       }
       if (is_array($data['instruments_primary_for_bandmates']) AND ! empty($data['instruments_primary_for_bandmates'])) {
           foreach ($data['instruments_primary_for_bandmates'] as $item) {
               $matchTags['instruments_primary_to_join']['should'][] = $item;//
           }
       }
       
        if (is_array($data['profile']) AND !empty($data['profile'])) {
            $tmp = array();
            foreach ($data['profile'] as $el) {
                if (!trim($el))
                    continue;
                
                // Some predefined
                // TODO config with translations in all languages VS cache ...
                if ($el == $this->app->translate->get('drummers')) {
                    $tag = \Helpers\Tags::findByText('drums', null);
                    if ($tag) {
                        $matchTags['instruments']['should'][] = $tag->name;
                    }
                    $tag = \Helpers\Tags::findByText('drummer', null);
                    if ($tag) {
                        $matchTags['profile']['should'][] = $tag->name;
                    }
                }
 
                if ($el == $this->app->translate->get('singers') || $el == $this->app->translate->get('vocalists')) {
                    $tag = \Helpers\Tags::findByText('vocals', null);
                    if ($tag) {
                        $matchTags['profile']['should'][] = $tag->name;
                        $matchTags['instruments']['should'][] = $tag->name;
                    }
                    $tag = \Helpers\Tags::findByText('singer', null);
                    if ($tag) {
                        $matchTags['profile']['should'][] = $tag->name;
                    }
                    $tag = \Helpers\Tags::findByText('vocalist', null);
                    if ($tag) {
                        $matchTags['profile']['should'][] = $tag->name;
                    }
                }
 
                if ($el == $this->app->translate->get('guitarists')) {
                    $tag = \Helpers\Tags::findByText($this->app->translate->get('guitar'), null);
                    if ($tag) {
                        $matchTags['instruments']['should'][] = $tag->name;
                    }
                    $tag = \Helpers\Tags::findByText($this->app->translate->get('guitarist'), null);
                    if ($tag) {
                        $matchTags['profile']['should'][] = $tag->name;
                    }
                    $tag = \Helpers\Tags::findByText($this->app->translate->get('acoustic_guitar'), null);
                    if ($tag) {
                        $matchTags['instruments']['should'][] = $tag->name;
                    }
                    $tag = \Helpers\Tags::findByText($this->app->translate->get('electric_guitar'), null);
                    if ($tag) {
                        $matchTags['instruments']['should'][] = $tag->name;
                    }
                }
 
                if ($el == $this->app->translate->get('bass_players') || $el == $this->app->translate->get('bassists')) {
                    $tag = \Helpers\Tags::findByText($this->app->translate->get('bass'), null);
                    if ($tag) {
                        $matchTags['instruments']['should'][] = $tag->name;
                    }
                    $tag = \Helpers\Tags::findByText($this->app->translate->get('bass_player'), null);
                    if ($tag) {
                        $matchTags['profile']['should'][] = $tag->name;
                    }
                    $tag = \Helpers\Tags::findByText($this->app->translate->get('bassist'), null);
                    if ($tag) {
                        $matchTags['profile']['should'][] = $tag->name;
                    }
                }
 
                if ($el == $this->app->translate->get('producers')) {
                    $tag = \Helpers\Tags::findByText($this->app->translate->get('producer'), null);
                    if ($tag) {
                        $matchTags['profile']['should'][] = $tag->name;
                    }
                }
 
                if ($el == $this->app->translate->get('musicians')) {
                    $tag = \Helpers\Tags::findByText($this->app->translate->get('musician'), null);
                    if ($tag) {
                        $matchTags['profile']['should'][] = $tag->name;
                    }
                }
            }
        }
        
        $bool = [];
        $must = [];
        $should = [];
        
        if (isset($data['autocomplete']) && trim($data['autocomplete'])) {
            $searchStr = trim($data['autocomplete']);
            // phrase_prefix is the "poorman's autocomplete" of Elastic. Works great
            // In future we can create more complex autocomplete, but it's totally unnecessary for now
            $combineType = "phrase_prefix";
            $minLength = 2;
        } else {
            $searchStr = trim($data['search']);
            $combineType = "cross_fields";
            $minLength = 3;
        }
        
        if (strtolower($searchStr) == "search" || strtolower($searchStr) == "%%tag%%") {
            try {
                // we got weird "search" requests .. log em
//                \Helpers\Debug::log("weird search ", $_SERVER['HTTP_REFERER']);
//                \Helpers\Debug::log($_SERVER['REQUEST_URI']);
//                \Helpers\Debug::log($_SERVER['QUERY_STRING']);
//                \Helpers\Debug::log($_SERVER['HTTP_USER_AGENT']);
            } catch (\Exception $e) {
                
            }
        }
        
        if (strlen($searchStr) >= $minLength) {
            // keep search string for 'tag' cloud of frequent searches
            \Helpers\SearchTags::add($searchStr);
            $combine = [];
            $combine[] = [
                "multi_match" => [
                    "query" => $searchStr,
                    "analyzer" => $analyzer,
                    "type" => $combineType,
                    "fields" => [
                        "name^5", // full name of User / Page
                        "username^1",
//                        "about.title^4",
//                        "about.text^2",
//                        "type.{$lang}^2", // Pages only
//                        "tags.profile.{$lang}^3",
//                        "tags.equipment.{$lang}^2",
//                        "tags.genres.{$lang}^2", // Pages only
//                        "tags.genres_primary.{$lang}^2",
//                        "tags.genres_secondary.{$lang}",
//                        "tags.influences.{$lang}^2",
//                        "tags.instruments_primary.{$lang}^2",
//                        "tags.instruments_secondary.{$lang}",
//                        "tags.languages.{$lang}",
                    ]
                ]
            ];
            $combine[] = [
                "multi_match" => [
                    "query" => $searchStr,
                    "analyzer" => $analyzer,
                    "fields" => [
                        "name^3", // full name of User / Page
//                        "username^1",
//                        "about.title^3",
//                        "about.text^2",
//                        "type.{$lang}^2", // Pages only
//                        "tags.profile.{$lang}^3",
//                        "tags.equipment.{$lang}^2",
//                        "tags.genres.{$lang}^2", // Pages only
//                        "tags.genres_primary.{$lang}^2",
//                        "tags.genres_secondary.{$lang}",
//                        "tags.influences.{$lang}^2",
//                        "tags.instruments_primary.{$lang}^2",
//                        "tags.instruments_secondary.{$lang}",
//                        "tags.languages.{$lang}",
                    ],
                    "fuzziness" => "AUTO"
                ]
            ];
            $must[] = ['bool' => ['should' => $combine, "boost" => 5]];
        }
 
        /*        
        if ($data['teach'] == 1) {
            $must[] = ['match' => ['teach' => 1]];
        }
        
        if (intval($data['price_min']) > 0 || intval($data['price_max']) > 0) {
            $price = [];
            if (intval($data['price_min']) > 0) {
                $this_drooble_fee = (intval($data['price_min']) / (\Config::defaults()->DROOBLE_FEE_IN_PERCENTS + 100) * \Config::defaults()->DROOBLE_FEE_IN_PERCENTS);
                $price_from = intval($data['price_min']) - $this_drooble_fee;
                if ($price_from < 0) {
                    $price_from = 0;
                }
                $price['gte'] = intval($price_from);
            }
 
            if (intval($data['price_max']) > 0) {
                $this_drooble_fee = (intval($data['price_max']) / (\Config::defaults()->DROOBLE_FEE_IN_PERCENTS + 100) * \Config::defaults()->DROOBLE_FEE_IN_PERCENTS);
                $price_to = intval($data['price_max']) - $this_drooble_fee;
                if ($price_to < 0) {
                    $price_to = 0;
                }
                $price['lte'] = intval($price_to);
            }
            $must[] = ['range' => ['price' => $price] ];
        }
         */
        
 
        if ($data['place']) {
            $data['place'] = str_replace("'", "", $data['place']);
            $data['place'] = str_replace("`", "", $data['place']);
            $data['place'] = str_replace("\"", "", $data['place']);
            
            $place = \Helpers\Basic::getGooglePlace($data['place']);
            if ($place && $place->is_country) {
                $must[] = ['match' => ['country' => $place->country_code]];
            } else {
                $must[] = ['match' => ['city' => $data['place']]];
            }
            
        }elseif($data['location']){
            $google_places = new \Lib\GooglePlaces(\Config::googleplaces()->API_KEY);
 
            $cor = array($this->app->request->ipInfo['latitude'], $this->app->request->ipInfo['longitude']);
 
            $google_places->location = $cor;
            $google_places->radius   = 100;
            $google_places->rankby   = 'distance';
            $google_places->types    = '(cities)';
            $google_places->input    = $data['location'];
            $google_places_result = $google_places->autocomplete();
            $searched_places_ids = array();
 
            if($google_places_result['status'] == 'OK' AND count($google_places_result['predictions'])>0){
                foreach($google_places_result['predictions'] as $__prediction){
                    $searched_places_ids[] = $__prediction['place_id'];
                }
            }
            if (count($searched_places_ids)) {
                $tmp = [];
                foreach ($searched_places_ids as $placeid) {
                    $tmp[] = ['match' => ["city" => $placeid]];
                }
                $must[] = ['bool' => ['should' => $tmp]];
            }
        }
        
        if (count($data['tags'])) {
            foreach ($data['tags'] as $tag) {
                foreach ($matchTags as $type => $info) {
                    $matchTags[$type]['should'][] = $tag;
                }
            }
        }
        foreach ($matchTags as $type => $info) {
            foreach ($info as $boolOperator => $tags) { // $boolOperator fills $must or $should arrays
                if (count($tags)) {
                    foreach ($tags as $tag) {
                        if ($type == "genres") {
                            ${$boolOperator}[] = ['match' => ["tags.genres.{$lang}" => $tag]]; // this is for Pages
                            ${$boolOperator}[] = ['match' => ["tags.genres_primary.{$lang}" => $tag]];
                            ${$boolOperator}[] = ['match' => ["tags.genres_secondary.{$lang}" => $tag]];
 
                        } elseif ($type == "instruments") {
                            ${$boolOperator}[] = ['match' => ["tags.instruments_primary.{$lang}" => $tag]];
                            ${$boolOperator}[] = ['match' => ["tags.instruments_secondary.{$lang}" => $tag]];
                        } else {
                            ${$boolOperator}[] = ['match' => ["tags.{$type}.{$lang}" => $tag]];
                        }
                        
                        /* this here was again because someone doesn't follow YAGNI
                        if ($type == "genres") {
                            $tmp[] = ['match_phrase' => ["tags.genres.{$lang}" => $tag]]; // this is for Pages
                            $tmp[] = ['match_phrase' => ["tags.genres_primary.{$lang}" => $tag]];
                            $tmp[] = ['match_phrase' => ["tags.genres_secondary.{$lang}" => $tag]];
 
                        } elseif ($type == "instruments") {
                            $tmp[] = ['match_phrase' => ["tags.instruments_primary.{$lang}" => $tag]];
                            $tmp[] = ['match_phrase' => ["tags.instruments_secondary.{$lang}" => $tag]];
                        } else {
                            $tmp[] = ['match_phrase' => ["tags.{$type}.{$lang}" => $tag]];
                        }
                        if ($tmp) {
                            ${$boolOperator}[] = ['bool' => ['should' => $tmp]];
                        }*/
                        
                    }
                }
            }
        }
        
//        if ($data['visible'] == 'profile_completed') {
//            $must[] = [
//                'term' => [
//                    'visibility' => 'profile_completed'
//                ]
//            ];
//        }
 
        if (count($pageTypeIds)) {
            $must[] = ['terms' => ['page_type_id' => $pageTypeIds]];
        }
        
        if (count($must)) {
            $bool['must'] = $must;
        }
        
        if (count($should)) {
            $bool['should'] = $should;
            $bool['minimum_should_match'] = 1;
            // setting minimum_should_match would mess up the has_avatar boost
        }
        
//        if ($data['autocomplete']) {
//            $indexStr = \Config::elasticsearch()->INDICES->users . ',' . \Config::elasticsearch()->INDICES->pages;
            $indexStr = \Config::elasticsearch()->INDICES->users;
//        } else {
//            $indexStr = \Config::elasticsearch()->INDICES->users;
//        }
//        if ((int) $data['bands'] == 1 || count($pageTypeIds) || $data['show_pages'] || $data['type'] == "pages") {
//            $indexStr = \Config::elasticsearch()->INDICES->pages;
//        }
        if ($data['show_profiles'] /*|| $data['type'] == "people" */) {
            $indexStr = \Config::elasticsearch()->INDICES->users;
        }
        if (count($bool)) {
            // boost has_avatar. I personally argued that we should not boost, but rather filter by has_avatar, but it was pointless .. so enjoy
//            if (!isset($tagBoost)) { // NO SOLUTION for now ...
//                $bool['should'][] = ['term' => ['has_avatar' => ["value" => true, "boost" => 100 ]]];
//            }
//            // enough BS
//            if ($indexStr == \Config::elasticsearch()->INDICES->users) {
//                $bool['should'][] = ['exists' => ['field' => 'tags.profile', 'boost' => 80]];
//                $bool['should'][] = ['exists' => ['field' => 'tags.instruments_primary', 'boost' => 70]];
//                $bool['should'][] = ['exists' => ['field' => 'tags.genres_primary', 'boost' => 60]];
//            } else {
//                $bool['should'][] = ['exists' => ['field' => 'tags.genres', 'boost' => 80]];
//                $bool['should'][] = ['exists' => ['field' => 'tags.influences', 'boost' => 70]];
//            }
            $query = ["bool" => $bool];
        } else {
            // empty search
            $bool = [
                'must' => [
                    ['match' => ['has_avatar' => true]]
                ]
            ];
            // enough BS
//            if ($indexStr == \Config::elasticsearch()->INDICES->users) {
//                $boostTags = [
//                    ['exists' => ['field' => 'tags.profile', 'boost' => 80]],
//                    ['exists' => ['field' => 'tags.instruments_primary', 'boost' => 70]],
//                    ['exists' => ['field' => 'tags.genres_primary', 'boost' => 60]],
//                ];
//            } else {
//                $boostTags = [
//                    ['exists' => ['field' => 'tags.genres', 'boost' => 80]],
//                    ['exists' => ['field' => 'tags.influences', 'boost' => 70]],
//                ];
//            }
//            if (isset($boostTags)) {
//                $bool['should'] = $boostTags;
//            }
            $query = [
                'function_score' => [
                    'query' => ['bool' => $bool],
                    'random_score' => new \stdClass()
                ]
            ];
        }
        
        $sort = [["_score" => "desc"]]; // second and third sort params were ["has_avatar" => "desc"], ["lastseen" => "desc"] they are insignificant
        
        $params = [
            "from" => $offset,
            "size" => $results,
            "index" => $indexStr,
//            "explain" => 1,
            "body" => [
                "query" => $query,
                "sort" => $sort,
            ]
        ];
 
        //\Helpers\Debug::log('dht', json_encode($params));
 
        $result = $dsm->search($params);
        
        if ((int) $data['count'] === 1) { //OMG all the legacy .. humanity
            $count = $result['hits']['total'];
            return array(true, $count);
        } else {
            return $this->viewSearchUsersAndPages($result, $searchStr);
        }
    }
    
    /*
     * @result DroobleSearchManager search result (Elastic client search result)
     * @searchStr - string from search (needed to 'inject' the search string as a Tag to all Users in the View)
     */
    public function viewSearchUsersAndPages($result, $searchStr) {
        $lang = \Helpers\Basic::lang();
        $hitIds = [];
        foreach ($result['hits']['hits'] as $item){
            $hitIds[] = $item['_id'];
        }
        $hitIdsImploded = implode(",", $hitIds);
        $count = $result['hits']['total'];
        $ret = [];
        // get data for hits
        if ($count) {
            
            if (strlen($searchStr)) {
                $foundTag = \Helpers\Tags::findByText($searchStr, null);
            }
            
            $elsCtrl = new \Controllers\API\ElementsController();
            
            $sql="
                SELECT
                    `search_cache`.`short_info_json` ,
                    `up`.`id`,
                    `up`.`user_status`,
                    `up`.`system_status`,
                    `up`.`lastseen`,
                    `up`.`is_page`,
                    IF ( `ucm`.`id` IS NOT NULL, 1, 0 ) as `isInCircle`
                FROM
                    `search_cache`
                JOIN
                    `users_profiles` `up`
                        ON
                            `up`.`id` = `search_cache`.`user_id`
                LEFT JOIN
                    `users_circles_members` `ucm`
                        ON
                            (`ucm`.`user_id` = :fuid AND `ucm`.`target_user_id` = `up`.`id`)
                WHERE `up`.`id` IN(" . $hitIdsImploded . ")
                    ORDER BY FIELD(up.id, " . $hitIdsImploded . ")
                    LIMIT " . $count . "
                   ;";
 
            $prepare = [ 'fuid' => $_SESSION['profile']['id'] ];
            $qres = $this->app->db->query($sql, $prepare);
            $qres->setFetchMode(\Phalcon\Db::FETCH_ASSOC);
            $qelements = $qres->fetchAll();
            foreach ($qelements as $el) {
                $a = json_decode($el['short_info_json'], true);
                if ($a != false) {
                    $a['uid'] = $el['id'];
                    $a['isInCircle'] = $el['isInCircle'];
//                    $a['status'] = $el['publicStatus'];
                    $a['is_page'] = $el['is_page'];
//                    if ($a['rates']['session_price'] and $el['id'] != $_SESSION['profile']['id']) {
//                        $a['rates']['session_price'] = floatval($a['rates']['session_price']) + floatval($a['rates']['session_price']) * ( \Config::defaults()->DROOBLE_FEE_IN_PERCENTS / 100 );
//                    }
                    if ((int) $count >= 0) {
                        $a['counter'] = $count;
                    }
                    $tags = \Helpers\Users::getUserTags($a['uid'], 3); // get with 3 - categorized with remapped type, cause otherwise you'll get random instruments (they have 4 subtypes)
                    
                    if ($a['uid'] == 1) {
                        $a['open_to_string'] = "All music";
                    } else {
                        $open_to_string = "";
                        if ( count($tags['profile']) > 1 ) {
                            $open_to_string = $tags['profile'][0] . " " . $this->app->translate->get('and') . " " . $tags['profile'][1];
                        } elseif (count($tags['profile']) == 1) {
                            $open_to_string = $tags['profile'][0];
                        }
                        $a['open_to_string'] = $open_to_string;
                    }
                    
                    $flatList = [];
                    foreach ($tags as $arr) {
                        foreach ($arr as $tag) {
                            $flatList[] = $tag;
                        }
                    }
                    if ($foundTag) {
                        $searchAsTag = mb_convert_case(stripslashes($foundTag->translation->{$lang}->text), MB_CASE_TITLE, 'UTF-8');
                        if (!in_array($searchAsTag, array_slice($flatList, 0, 2))) {
                            // 'inject' the search string as a Tag @ first position if it's not in the top 2 tags
                            foreach ($tags as $key => $arr) {
                                array_unshift($arr, $searchAsTag);
                                $tags[$key] = $arr;
                                break;
                            }
                        }
                    }
                    // remove duplicates
                    $firstParts = array_slice($tags, 0, 3);
                    $first3keys = array_keys($firstParts);
 
                    $countTypes = count($first3keys);
                    if ($countTypes > 1) {
                        $tags[$first3keys[1]] = array_diff($tags[$first3keys[1]], $tags[$first3keys[0]]);
                    } 
                    if ($countTypes > 2) {
                        $tags[$first3keys[2]] = array_diff($tags[$first3keys[2]], $tags[$first3keys[1]]);
                        $tags[$first3keys[2]] = array_diff($tags[$first3keys[2]], $tags[$first3keys[0]]);
                    }
                        
                    $a['tags'] = $tags;
                    $ret[] = $a;
                }
            }
        }
        return [true, $ret];
 
    }
 
    public function getContacts($data) {
        return array(true, \Helpers\Search::getContactsAndTheRest($data['uid'], $data));
    }
 
}
 
?>
#5search->advancedSearchPage(Array(10), true)
/var/www/rc/drooble/trunk/projects/drooble_v1/services/web/private/lib/client.class.php (294)
<?php
class client{
 
  public $t;
  public $p;
  public $l;
  public $c;
  public $pname;//page name
  private $db;
//  private $lib;
  public $lang;
  public $init=false;
  public $cached_profiles=array();// stre selected profiles;
  public $globalTexts;
  private $pageTexts;
  private $jsTexts;
  private $app;
 
  public function __construct($db,$app,$lang='en'){
    $this->db = $db;
//    $this->lib = $lib;
    $this->lang = $lang;
    $this->app = $app;
  }
 
 
// DEPRECATED
  //page in english (default)
  //TODO: move in init
//  public function initPageTexts($page) {
//            $di = Phalcon\DI\FactoryDefault::getDefault();
//            $translate = $di->getShared('translate');
//            $translate->initPageTexts($page);
//            $this->globalTexts = $translate->getGlobalTexts();
//            $this->pageTexts = $translate->getPageTexts();
//            $this->jsTexts = $translate->getJsTexts();
//            return;
//
//  }
 
// DEPRECATED
  //page in english (default)
//  public function getPageText ($code) {
//
//    if (!isset($this->globalTexts) || !isset($this->pageTexts)) {
//      $this->initPageTexts();
//    }
//
//        // Defautl
//        $text = $code;
//
//        if (isset($this->globalTexts[$code])) {
//            $text = $this->globalTexts[$code];
//        }
//
//    if (isset($this->pageTexts[$code])) {
//      $text = $this->pageTexts[$code];
//        }
//
//    return $text;
//  }
 
// DEPRECATED
//  public function getJsJsonTexts () {
//    if (!isset($this->jsTexts))
//      return "";
//
//    return json_encode($this->jsTexts);
//  }
 
  public function setTitle ($title) {
    if (!isset($this->globalTexts) || !isset($this->pageTexts))
      return;
 
    $this->pageTexts['title'] = $title;
  }
 
 
// DEPRECATED
//  public function get_page_button($page){
//    return $this->t["page"][$page]["button"];
//  }
 
  public function downloadAttach($file){ //'/'.$_b.'/'.$_c.'/'.$_d //DEPRECATED - see ElementsController.php DownloadAction()
    exit();
    if($uid=$_SESSION['profile']['id']){
            $tmp = explode('/', $file);
            $prof_id = $tmp[1]; //uid
            if ($prof_id != $uid) { // probably band, so check it's authorized
                $pr = \Models\UserPageRights::findFirst(array('page_id = :page_id:', 'bind' => array('page_id' => $prof_id)));
                if (is_object($pr) && $pr->user_id == $_SESSION['profile']['id']) {
                    $uid = $prof_id;
                }
            }
 
      $sql="SELECT `uma`.`original_filename` AS `filename`, `uma`.`stored_filename`
       FROM `users_messages_attachment` AS `uma`
    LEFT JOIN `users_messages` AS `um` ON `um`.`sender_id` = `uma`.`user_id` AND `um`.`attachments` LIKE CONCAT('%',`uma`.`attach_id`,'%')
      WHERE `uma`.`stored_filename` = :file  AND `um`.`reciever_id` = :rid";
 
      if(!isset($_GET['element']) AND $r=$this->db->q($sql,array("file"=>$file, "rid" => $uid))){ // SOME FILE
        $filename=$r[0]['filename'];
                if ($tmp[2] == 'attachments') {
                    $stored_filename = $r[0]['stored_filename'];
                    try {
                        require __DIR__."/../../../../resources/library/aws/aws-autoloader.php";
            $configArr = json_decode(json_encode(\Config::aws()->S3->CONFIG), true);
            $s3 = new \Aws\S3\S3Client($configArr);
 
                        $original_key = \Config::paths()->UPLOAD_WEB_STORAGE_ROOT_FOLDER . $stored_filename;
//\Helpers\Debug::log('$original_key', $original_key);
                        $result = $s3->getObject(array(
                            'Bucket' => \Config::aws()->S3->BUKCETS->ATTACHMENTS,
                            'Key' => $original_key,
                        ));
                         // Display the object in the browser
                        header('Content-Description: File Transfer');
                        header('Content-Type: application/octet-stream');
                        header("Content-Type: {$result['ContentType']}");
                        header('Content-Disposition: attachment; filename='.basename($filename));
                        header('Content-Transfer-Encoding: binary');
                        header('Expires: 0');
                        header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
                        header('Pragma: public');
                        echo $result['Body'];
                        exit;
                    } catch (Exception $e) {
                        \Helpers\Debug::log($e->getTraceAsString());
                        return array(false, "can't get object");
                    }
 
                }/* else {
                    $file = \Config::paths()->ATTACH_DIR . $file;
                }*/
 
        if (file_exists($file))
        {
          header('Content-Description: File Transfer');
          header('Content-Type: application/octet-stream');
          header('Content-Disposition: attachment; filename='.basename($filename));
          header('Content-Transfer-Encoding: binary');
          header('Expires: 0');
          header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
          header('Pragma: public');
          header('Content-Length: ' . filesize($file)); //Remove
          ob_clean();
          flush();
          readfile($file);
          exit;
        }
        exit ('File does not exist!');
            } else if (isset($_GET['element'])) { // ELEMENT (AUDIO FOR NOW)
                if ($_GET['b'] == 'audio') {
                    $check = \Models\UserElements::findFirst(array(
                        'conditions' => "type='audio' AND id=:id: AND is_downloadable=1",
                        'bind' => array(
                            'id' => (int)$_GET['element']
                        )
                    ));
 
                    if ($check) {
                        // $original_filename = explode(".", $check->original_filename);
                        // To remove extension
                        // array_pop($original_filename);
                        // $original_filename = implode("_", $original_filename);
 
                        $new_extension = explode(".", $check->external_media_url);
                        $new_extension = array_pop($new_extension);
 
                        $creator_name = $check->Creator->first_name;
                        if ($check->Creator->last_name) {
                            $creator_name .= " ".$check->Creator->last_name;
                        }
 
                        $title = \Helpers\Inflector::urlize($creator_name);
                        $title .= "-".\Helpers\Inflector::urlize($check->title);
 
                        header('Content-Description: File Transfer');
                        header('Content-Type: application/octet-stream');
                        header('Content-Disposition: attachment; filename='.$title.'.'.$new_extension);
                        header('Content-Transfer-Encoding: binary');
                        header('Expires: 0');
                        header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
                        header('Pragma: public');
                        header('Content-Length: ' . filesize($check->external_media_url)); //Remove
                        ob_clean();
                        flush();
                        readfile($check->external_media_url);
                        exit;
                    } else {
                        exit("error 1");
                    }
                } else {
                    exit("error 2");
                }
      } else {
        exit("error");
      }
    }else{
      exit("access denied");
    }
  }
 
 
  public function load_square($page){
 
//    $this->initPageTexts();
    if (!isset($c)) {
      $c = $this;
    }
 
    ob_start();
    if(file_exists( SQUARE_PAGES_LOGIC . $page . ".php" )){
      include( SQUARE_PAGES_LOGIC . $page . ".php" );
    }
 
    if(file_exists( SQUARE_PAGES_CSS . $page . ".css" )){
      include( SQUARE_PAGES_CSS . $page . ".css" );
    }
 
    if(file_exists( SQUARE_PAGES_HTML . $page . ".html" )){
      include( SQUARE_PAGES_HTML . $page . ".html" );
    }
 
    if(file_exists(SQUARE_PAGES_JS . $page . ".js")){
      echo "<script type=\"text/javascript\">";
      include( SQUARE_PAGES_JS . $page . ".js" );
      echo "</script>";
    }
 
    $content = ob_get_clean();
 
    echo $content;
 
  }
 
  public function api($type,$action=false,$data=false,$internal=true){
 
    //$this->lib->api($type,$action=false,$data=false);
        $app = $this->app; // expose app (to use in squares)
 
    switch($type){
      case "php":
        if(!isset($action)){
          $action=$data['action'];
        }
                
                //$API1_MAP = $this->lib->config['API1']['MAP'];
 
        if(\Config::api()->MAP->{$action}->status===true){
                    
                    
          if(\Config::api()->MAP->{$action}->sessionRequire==true and !isset($_SESSION['profile']['id'])){
            return(array(false,"nosess"));
          }
          $object=\Config::api()->MAP->{$action}->class;
          //TODO check za input data-ta
                    
                    
          file_exists(LOGIC . $object . ".class.php") || exit("wrong file");
 
                    
          class_exists($object) || include(LOGIC . $object . ".class.php");
                    
 
          class_exists($object) || exit("wrong class1");
                    
 
//          $module = new $object($this->db,$this->lib, $this, $this->app);
          $module = new $object($this->db, $this, $this->app);
          method_exists($module, $action) || exit("wrong method");
                    
                    
// Valio: Turned validator off for 'php' calls, because there was a bad case with Advanced search page. I actually fixed it since then, but haven't tested if its ok in this case. But anyway - this api function will be deprecated at some point.
//                    $validator = \Helpers\Basic::validator();
//                    if ( $data && !is_array($data) ) { // In theory we should not get inside this if statement.
//                        $data = array();
//                       \Helpers\Debug::log('validateActionData expects array as first param');
//                    }
//                    if (!$data) {
//                        $data = array();
//                    }
//                    $validator->validateActionData($data, $action);
//                    $cleanData = $validator->getInput();
 
                    //Valio: keep the legacy filter for now
//                     strip html
                    $filter = new \Phalcon\Filter();
                    $stripHTML = function(&$val) use ($filter) {
                        $val = $filter->sanitize($val, 'striptags');
                    };
                    array_walk_recursive($data, $stripHTML);
                    
          return( $module->$action( $data , $internal) );
        }else{
          return( array(false,"api error1"));
        }
      break;
      //ajax
      case "api1":
 
        //\Helpers\Debug::log("action name",$this->app->router->getActionName(),$this->app->router->getParams());
 
        //$start = microtime(1);
        if($this->app->router->getActionName()){
          $action = $this->app->router->getActionName();
        }elseif($_POST['action']){
          $action=$_POST['action'];
        }else{
          $a=json_decode($_POST['data'],true);
          $action=$a['action'];
        }
        header('Content-Type: application/json');
        if($action AND \Config::api()->MAP->{$action}->status===true){
 
          if(\Config::api()->MAP->{$action}->sessionRequire==true and !isset($_SESSION['profile']['id'])){
            exit(json_encode(array(false,"nosess")));
          }
          $object=\Config::api()->MAP->{$action}->class;
          file_exists(LOGIC . $object . ".class.php") || exit("wrong file");
 
          class_exists($object) || include(LOGIC . $object . ".class.php");
 
          class_exists($object) || exit("wrong class2");
//          $module = new $object($this->db,$this->lib, $this, $this->app);
          $module = new $object($this->db, $this, $this->app);
          method_exists($module, $action) || exit("wrong method");
 
                    // Valio: I commented the old filters
          //check za input data-ta
//          if(isset($_POST["data"])){
//            foreach($_POST["data"] as $key=>$val){
//              switch(\Config::api()->MAP->{$action}->data->{$key}){
//                case "text":
//                //  $val=$this->db->quote($val);
//                  $_POST["data"][$key]=$val;
//                break;
//                case "int":
//                  $val=intval($val);
//                  $_POST["data"][$key]=$val;
//                break;
//
//              }
//            }
//          }
 
          //$this->requests_intensity_checker($_SERVER['REMOTE_ADDR'],$object,$action);
          if($this->app->router->getParams()){
            $cleanData = $this->app->router->getParams();
          }else{
                      $dirtyData = [];
                      if (is_array($_POST['data'])) {
                          $dirtyData = $_POST['data'];
                      } else if (is_numeric($_POST['data'])) {
                          $dirtyData['id'] = (int)$_POST['data'];
                      }
 
                      // $dirtyData = is_array($_POST['data']) ? $_POST['data'] : array();
                      $di = \Phalcon\DI\FactoryDefault::getDefault();
                      $validator = $di->getValidator();
                      $validator->validateActionData($dirtyData, $action);
                      $cleanData = $validator->getInput();
                  }
                    // LEGACY (we now use DroobleValidator):
                    // strip html
//                    $filter = new \Phalcon\Filter();
//                    $stripHTML = function(&$val) use ($filter) {
//                        $val = str_replace('<3', '&lt;3', $val); // temporary fix for <3 emoticon
//                        $val = $filter->sanitize($val, 'striptags');
//                    };
//                    array_walk_recursive($_POST['data'], $stripHTML);
 
                    $result=$module->$action( $cleanData );
          //$result['queryCount']=$this->db->queryCount;
          //$result['queries']=$this->db->queries;
          // \Helpers\Debug::log("metchod :".$action." time: ".(microtime(1)-$start));
          exit( json_encode( $result ) );
        }else{
          exit( json_encode(array(false,"api error 1")));
        }
      break;
      case "square":
        $this->init();
//        $this->initPageTexts();
 
        $page = $_POST["square"];
 
        if (!isset($c)) {
          $c = $this;
        }
 
        //$_SESSION["t"]["square"][$page] || exit("wrong page");
        $this->t["square"][$page] || exit("wrong page");
 
        ob_start();
 
        if(file_exists( SQUARE_PAGES_LOGIC . $page . ".php" )){
          include( SQUARE_PAGES_LOGIC . $page . ".php" );
        }
 
        if(file_exists( SQUARE_PAGES_CSS . $page . ".css" )){
          include( SQUARE_PAGES_CSS . $page . ".css" );
        }
 
        if(file_exists( SQUARE_PAGES_HTML . $page . ".html" )){
          include( SQUARE_PAGES_HTML . $page . ".html" );
        }
 
        if(file_exists(SQUARE_PAGES_JS . $page . ".js")){
          echo "<script type=\"text/javascript\">";
          include( SQUARE_PAGES_JS . $page . ".js" );
          echo "</script>";
        }
 
        $content = ob_get_clean();
 
        exit($content);
 
      break;
 
      default: exit("wrong type"); break;
 
    }
  }
}
 
$c = new client($db,$app);
?>
#6client->api(php, advancedSearchPage, Array(10))
/var/www/rc/drooble/trunk/projects/drooble_v1/services/web/private/pages_content/0_php_scripting/search_result_page.php (35)
<?php
$type = $_GET['type']== "people" ? "people" : "pages";
 
$looking_bandmates = $_GET['looking_bandmates'] ? true : false;
 
$search = $_b;
$profile = (array) $_GET['profile'];
$instruments = (array) $_GET['instruments'];
$genres = (array) $_GET['genres'];
$influences = (array) $_GET['influences'];
$equipment = (array) $_GET['equipment'];
$place = (string) $_GET['place'];
$languages = (string) $_GET['languages'];
 
 
$search_params = [
    "search" => $search,
    "profile" => $profile,
    "instruments" => $instruments,
    "genres" => $genres,
    "influences" => $influences,
    "equipment" => $equipment,
    "place" => $place,
    "languages" => $languages,
];
 
 
$page_number = (int)$_GET['page'] ? (int)$_GET['page'] : 1;
$elements_per_page = 10;
$offset = ($page_number - 1) * $elements_per_page ? ($page_number - 1) * $elements_per_page : 0; 
 
$search_params['offset'] = $offset;
$search_params['limit'] = $elements_per_page+1;
 
$s_results = $c->api("php", "advancedSearchPage", $search_params);
 
if($s_results[0]){
    $results = $s_results[1];
}
 
 
$query = parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY);
parse_str($query, $url_params);
 
//print_r($url_params);
// Returns a string if the URL has parameters or NULL if not
 
if ($query) {
    $url_params['page'] = $page_number - 1;
    $prev_url = http_build_query($url_params);
    $prev_url = '/search/?' . preg_replace('/%5B[0-9]+%5D/simU', '[]', $prev_url);
 
    $url_params['page'] = $page_number + 1;
    $next_url = http_build_query($url_params);
    $next_url = '/search/?' . preg_replace('/%5B[0-9]+%5D/simU', '[]', $next_url);
 
 
} else {
    $prev_url = $_SERVER['REQUEST_URI'] . '?page=' . ($page_number - 1);
    $next_url = $_SERVER['REQUEST_URI'] . '?page=' . ($page_number + 1);
}
 
//print_r([$query, $prev_url, $next_url, $url_params]);
 
if (count($results) > $elements_per_page) {
    $has_next_page = true;
    array_pop($results);
} else {
    $has_next_page = false;
}
 
 
#7include(/var/www/rc/drooble/trunk/projects/drooble_v1/services/web/private/pages_content/0_php_scripting/search_result_page.php)
/var/www/rc/drooble/trunk/projects/drooble_v1/services/web/private/pages_content/layout.php (9)
<?php
 
// needed for frontend segment events
$forward_segment = \Drooble\FW\DroobleSegment::isForward();
setcookie("forward_segment", $forward_segment, time() + 2 * 3600, '/');
 
/** PHP SCRIPTIONG ON PAGE BEGIN */
if(file_exists( $_SCRIPTING_FILE = BACKEND_GENERATED_PHP_SCRIPTING_DIR ."/" .$app->getLegacyPageName() . ".php")){ 
  include( $_SCRIPTING_FILE );
}
/** PHP SCRIPTIONG ON PAGE END */
 
 
/** GENERATING HEADER HTML BEGIN */
ob_start();
if(file_exists( $FILE = BACKEND_GENERATED_HEADERS_DIR . "/" .$app->getLegacyPageName() . ".html"))
{
  include( $FILE );
}
else
{
  include( BACKEND_GENERATED_HEADERS_DIR . "/__default_header.html" );
}
$header_html = ob_get_clean();
/** GENERATING HEADER HTML END */
 
 
 
 
 
 
/** GENERATING BODY HTML BEGIN */
 
ob_start();
 
if(file_exists( $FILE = BACKEND_GENERATED_BODIES_DIR . "/" .$app->getLegacyPageName() . ".html"))
{
  include($FILE );
}
else
{
  include( BACKEND_GENERATED_BODIES_DIR . "/__default_body.html" );
}
 
$body_html = ob_get_clean();
 
/** GENERATING BODY HTML END */
 
 
 
 
/** GENERATING BODY HTML BEGIN */
 
ob_start();
 
if(file_exists( $FILE = BACKEND_GENERATED_FOOTERS_DIR . "/" .$app->getLegacyPageName() . ".html"))
{
  include( $FILE );
}
else
{
  include( BACKEND_GENERATED_FOOTERS_DIR . "/__default_footer.html" );
}
 
$footer_html = ob_get_clean();
 
/** GENERATING BODY HTML END */
 
 
 
 
 
 
ob_start();
 
echo $header_html;
echo $body_html;
echo $footer_html;
echo "<!-- ". $app->getLegacyPageName() ." -->";
 
$html = ob_get_clean();
 
echo $html;
 
//echo \Helpers\Basic::getInstance()->sanitize_output($html);
#8include(/var/www/rc/drooble/trunk/projects/drooble_v1/services/web/private/pages_content/layout.php)
/var/www/rc/drooble/trunk/projects/drooble_v1/services/web/public/index.php (158)
<?php
 
error_reporting(E_ERROR | E_PARSE);
 
if (preg_match('~MSIE|Internet Explorer~i', $_SERVER['HTTP_USER_AGENT'])
  or (strpos($_SERVER['HTTP_USER_AGENT'], 'Trident/7.0; rv:11.0') !== false)
  or (strpos($_SERVER['HTTP_USER_AGENT'], 'Trident/7.0; Touch; rv:11.0') !== false)) {
  //is IE 11 or below
  header('Location: /no-support-browser.html');
  exit;
}
 
try {
  define('CURRENT_MODULE',"web");
 
  chdir(dirname(__DIR__));
 
  $_SERVER["REMOTE_ADDR"]=isset($_SERVER['HTTP_CF_CONNECTING_IP']) ? $_SERVER['HTTP_CF_CONNECTING_IP'] : $_SERVER["REMOTE_ADDR"]; //cloudflare IP address parsing;
 
 
  if ($_GET['rb']) {
    setcookie("rb", $_GET['rb'], time() + 2 * 3600, '/');
    $_COOKIE["rb"] = $_GET['rb'];
  }
    if (isset($_GET['rb']) && !$_GET['rb']) {
        setcookie("rb", $_GET['rb'], time() - 2 * 3600, '/');
    }
 
  if(file_exists(__DIR__ . '/../../../config/defines.php')){ include(__DIR__ . '/../../../config/defines.php'); }
 
  date_default_timezone_set('Etc/UTC');
 
  mb_internal_encoding("UTF-8");
 
  if(file_exists("private/config/common.php")) $_CONFIG = include("private/config/common.php");
 
  if(file_exists("/etc/drooble/configs/".CURRENT_MODULE."/config.php")) $_CONFIG = array_replace_recursive($_CONFIG, include("/etc/drooble/configs/".CURRENT_MODULE."/config.php"));
 
  require __DIR__ . '/../../../config/wrapper.php';
 
  $config = include __DIR__ . '/../../../config/config.php';
 
  $loader = require __DIR__ . '/../../../resources/lib/autoload.php';
 
  include __DIR__ . "/../../../config/services.php";
 
 
  $di['router'] = function () {
        return include __DIR__ . "/../private/config/router.php";
    };
 
//  include __DIR__ . "/../../../config/segment.php";
  
 
    $segment_options = [];
    if ($_GET['test_segment']) {
        $test_segment = ($_GET['test_segment'] == "true" || $_GET['test_segment'] == 1) ? 1 : 0;
        // need to overwrite segment ip rules when testing
        setcookie("test_segment", $test_segment, time() + 2 * 3600, '/');
        $segment_options['forward_anyway'] =  $test_segment ? true : false;
    }
    if (isset($_COOKIE['test_segment'])) {
        $segment_options['forward_anyway'] = $_COOKIE['test_segment'] == 1 ? true : false;
    }
    \Drooble\FW\DroobleSegment::init(\Config::segment_analytics()->PROJECT_KEYS->WRITE_KEY, $segment_options);
 
    $app = new \Drooble\FW\DroobleApp($di); //10ms
 
    $app->view->setViewsDir( __DIR__ . "/../private/views/" );
 
    $handle = $app->handle();
 
  $content = $handle->getContent();
 
    \Drooble\FW\DroobleSegment::flush();
 
  exit( $content );
 
} catch (\Phalcon\Exception $e) {
  echo $e->getMessage();
  exit;
} catch (\PDOException $e){
  echo $e->getMessage();
  exit;
} catch (\Drooble\Exceptions\InvalidParameters $e){
  exit(json_encode(array(false,["error"=>"InvalidParameters","hint"=>$e->getMessage()])));
} catch (\Drooble\Exceptions\NotFound $e){
  exit(json_encode(array(false,["error"=>"NotFound","hint"=>$e->getMessage()])));
} catch (\Drooble\Exceptions\AccessDenied $e){
  exit(json_encode(array(false,["error"=>"AccessDenied","hint"=>$e->getMessage()])));
} catch (\Drooble\Exceptions\GeneralError $e){
  exit(json_encode(array(false,["error"=>"GeneralError","hint"=>$e->getMessage()])));
} catch (\Exception $e){
 
  if($e->getMessage()=='nosess'){
    exit(json_encode(array(false,"nosess")));
  }
 
  if($e->getMessage()=='wrong_method'){
    exit(json_encode([false,"api error"]));
  }
 
  if($e->getMessage()=='action_not_alowed' || $e->getCode()==103){
    exit(json_encode([false,"not alowd"]));
  }
 
  if($e->getMessage()!='action_not_found' and $e->getCode()!=101 and $e->getCode() != 100){
    echo $e->getMessage();
  }
/* 
  if( $e->getMessage()=='not_implemented' AND ( $e->getCode() == 100 OR $e->getCode() == 101 ) ){
    //GO TO CONTENT BUILDER
  }else{
    exit(json_encode([false,"general error"]));
  } */
 
}
// END Phalcon
 
foreach(array( "a", "b", "c", "d", "e" ) as $get)
    ${"_" . $get} = isset($_GET[$get]) ? $_GET[$get] : "";
 
if (\Helpers\Basic::getAppMode() == DEVELOPMENT_MODE)
{
  (new \Phalcon\Debug)->listen();
}
 
//include(LIB . "cache.class.php"); // deprecated
include(LIB . "mysql.class.php");
include(LIB . "client.class.php");
 
if ($_POST['ajax'] == 'api1' || ($app->getRoutingType() == "ajaxAPI")){
  $c->api('api1'); //GO TO API AND EXIT
}
 
 
 
//handle action
if($_a=='a'){
  include(LIB."action_handler.php");
}
 
if($_a=='fbHandler'){
  include(LIB."facebook_handler.php");
}
 
 
$router = $di->getRouter();
if ($router->getClientPlatformType() == 'MB') {
//    $loadSmartBanner = false; TODO set to true if you want it back
}
 
 
 
 
//PREPARE HTML
 
include(BACKEND_GENERATED_CONTENT . "/layout.php");
 
 
\Drooble\FW\DroobleSegment::flush();
KeyValue
asearch
b
c
d
e
influencesArray ( [0] => Sid )
page2
KeyValue
USERwww-data
HOME/var/www
HTTP_CONNECTIONkeep-alive
HTTP_X_FORWARDED_PROTOhttp
HTTP_X_FORWARDED_PORT80
HTTP_X_FORWARDED_FOR18.219.112.111, 172.69.7.159
HTTP_USER_AGENTclaudebot
HTTP_CF_VISITOR{"scheme":"https"}
HTTP_CF_RAY87535567ec4e6058-ORD
HTTP_CF_IPCOUNTRYUS
HTTP_CF_CONNECTING_IP18.219.112.111
HTTP_CDN_LOOPcloudflare
HTTP_ACCEPT_ENCODINGgzip, br
HTTP_ACCEPT*/*
HTTP_HOSTrc.drooble.com
REDIRECT_STATUS200
SERVER_NAMErc.drooble.com
SERVER_PORT443
SERVER_ADDR172.31.25.120
REMOTE_PORT48844
REMOTE_ADDR18.219.112.111
SERVER_SOFTWAREnginx/1.14.0
GATEWAY_INTERFACECGI/1.1
HTTPSon
REQUEST_SCHEMEhttps
SERVER_PROTOCOLHTTP/1.1
DOCUMENT_ROOT/var/www/rc/drooble/trunk/projects/drooble_v1/services/web/public
DOCUMENT_URI/index.php
REQUEST_URI/search/?influences[]=Sid&page=2
SCRIPT_NAME/index.php
CONTENT_LENGTH
CONTENT_TYPE
REQUEST_METHODGET
QUERY_STRINGa=search&b=&c=&d=&e=&influences[]=Sid&page=2
SCRIPT_FILENAME/var/www/rc/drooble/trunk/projects/drooble_v1/services/web/public/index.php
PATH_INFO
FCGI_ROLERESPONDER
PHP_SELF/index.php
REQUEST_TIME_FLOAT1713261681.982
REQUEST_TIME1713261681
#Path
0/var/www/rc/drooble/trunk/projects/drooble_v1/services/web/public/index.php
1/var/www/rc/drooble/trunk/projects/drooble_v1/config/defines.php
2/var/www/rc/drooble/trunk/projects/drooble_v1/services/web/private/config/common.php
3/var/www/rc/drooble/trunk/projects/drooble_v1/config/wrapper.php
4/var/www/rc/drooble/trunk/projects/drooble_v1/config/config.php
5/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/autoload.php
6/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/composer/autoload_real.php
7/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/composer/ClassLoader.php
8/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/composer/autoload_static.php
9/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/ralouphie/getallheaders/src/getallheaders.php
10/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/guzzlehttp/promises/src/functions_include.php
11/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/guzzlehttp/promises/src/functions.php
12/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/guzzlehttp/psr7/src/functions_include.php
13/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/guzzlehttp/psr7/src/functions.php
14/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/guzzlehttp/guzzle/src/functions_include.php
15/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/guzzlehttp/guzzle/src/functions.php
16/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/react/promise/src/functions_include.php
17/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/react/promise/src/functions.php
18/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/mtdowling/jmespath.php/src/JmesPath.php
19/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/aws/aws-sdk-php/src/functions.php
20/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/facebook/php-sdk-v4/src/Facebook/polyfills.php
21/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/mixpanel/mixpanel-php/lib/Mixpanel.php
22/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/mixpanel/mixpanel-php/lib/Base/MixpanelBase.php
23/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/mixpanel/mixpanel-php/lib/Producers/MixpanelPeople.php
24/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/mixpanel/mixpanel-php/lib/Producers/MixpanelBaseProducer.php
25/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/mixpanel/mixpanel-php/lib/ConsumerStrategies/FileConsumer.php
26/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/mixpanel/mixpanel-php/lib/ConsumerStrategies/AbstractConsumer.php
27/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/mixpanel/mixpanel-php/lib/ConsumerStrategies/CurlConsumer.php
28/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/mixpanel/mixpanel-php/lib/ConsumerStrategies/SocketConsumer.php
29/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/mixpanel/mixpanel-php/lib/Producers/MixpanelEvents.php
30/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/mongodb/mongodb/src/functions.php
31/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/segmentio/analytics-php/lib/Segment.php
32/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/segmentio/analytics-php/lib/Segment/Client.php
33/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/segmentio/analytics-php/lib/Segment/Consumer.php
34/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/segmentio/analytics-php/lib/Segment/QueueConsumer.php
35/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/segmentio/analytics-php/lib/Segment/Consumer/File.php
36/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/segmentio/analytics-php/lib/Segment/Consumer/ForkCurl.php
37/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/segmentio/analytics-php/lib/Segment/Consumer/LibCurl.php
38/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/segmentio/analytics-php/lib/Segment/Consumer/Socket.php
39/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/segmentio/analytics-php/lib/Segment/Version.php
40/var/www/rc/drooble/trunk/projects/drooble_v1/config/services.php
41/var/www/rc/drooble/trunk/projects/drooble_v1/services/web/private/plugins/APIPrefetcher.php
42/var/www/rc/drooble/trunk/projects/drooble_v1/resources/library/Drooble/FW/DroobleSegment.php
43/var/www/rc/drooble/trunk/projects/drooble_v1/resources/helpers/DroobleApc.php
44/var/www/rc/drooble/trunk/projects/drooble_v1/resources/library/Drooble/FW/DroobleApp.php
45/var/www/rc/drooble/trunk/projects/drooble_v1/services/web/private/config/router.php
46/var/www/rc/drooble/trunk/projects/drooble_v1/resources/library/Drooble/FW/DroobleRouter.php
47/var/www/rc/drooble/trunk/projects/drooble_v1/resources/helpers/Basic.php
48/var/www/rc/drooble/trunk/projects/drooble_v1/resources/library/Drooble/FW/DroobleHelper.php
49/var/www/rc/drooble/trunk/projects/drooble_v1/resources/library/Drooble/FW/DroobleSessionManager.php
50/var/www/rc/drooble/trunk/projects/drooble_v1/resources/library/Drooble/FW/DroobleRedisSessionAdapter.php
51/var/www/rc/drooble/trunk/projects/drooble_v1/resources/library/Drooble/FW/DroobleRedisDriver.php
52/var/www/rc/drooble/trunk/projects/drooble_v1/resources/helpers/UserSessions.php
53/var/www/rc/drooble/trunk/projects/drooble_v1/services/web/private/lib/mysql.class.php
54/var/www/rc/drooble/trunk/projects/drooble_v1/services/web/private/lib/client.class.php
55/var/www/rc/drooble/trunk/projects/drooble_v1/services/web/private/pages_content/layout.php
56/var/www/rc/drooble/trunk/projects/drooble_v1/services/web/private/pages_content/0_php_scripting/search_result_page.php
57/var/www/rc/drooble/trunk/projects/drooble_v1/services/web/private/logic/search.class.php
58/var/www/rc/drooble/trunk/projects/drooble_v1/resources/library/Drooble/Search/DroobleSearchManager.php
59/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/elasticsearch/elasticsearch/src/Elasticsearch/ClientBuilder.php
60/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/psr/log/Psr/Log/NullLogger.php
61/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/psr/log/Psr/Log/AbstractLogger.php
62/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/psr/log/Psr/Log/LoggerInterface.php
63/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/guzzlehttp/ringphp/src/Client/CurlHandler.php
64/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/guzzlehttp/ringphp/src/Client/CurlFactory.php
65/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/guzzlehttp/ringphp/src/Client/CurlMultiHandler.php
66/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/guzzlehttp/ringphp/src/Client/Middleware.php
67/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/elasticsearch/elasticsearch/src/Elasticsearch/Serializers/SmartSerializer.php
68/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/elasticsearch/elasticsearch/src/Elasticsearch/Serializers/SerializerInterface.php
69/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/elasticsearch/elasticsearch/src/Elasticsearch/Connections/ConnectionFactory.php
70/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/elasticsearch/elasticsearch/src/Elasticsearch/Connections/ConnectionFactoryInterface.php
71/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/elasticsearch/elasticsearch/src/Elasticsearch/ConnectionPool/Selectors/RoundRobinSelector.php
72/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/elasticsearch/elasticsearch/src/Elasticsearch/ConnectionPool/Selectors/SelectorInterface.php
73/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/elasticsearch/elasticsearch/src/Elasticsearch/Connections/Connection.php
74/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/elasticsearch/elasticsearch/src/Elasticsearch/Connections/ConnectionInterface.php
75/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/elasticsearch/elasticsearch/src/Elasticsearch/ConnectionPool/StaticNoPingConnectionPool.php
76/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/elasticsearch/elasticsearch/src/Elasticsearch/ConnectionPool/AbstractConnectionPool.php
77/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/elasticsearch/elasticsearch/src/Elasticsearch/ConnectionPool/ConnectionPoolInterface.php
78/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/elasticsearch/elasticsearch/src/Elasticsearch/Transport.php
79/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/elasticsearch/elasticsearch/src/Elasticsearch/Client.php
80/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/elasticsearch/elasticsearch/src/Elasticsearch/Namespaces/IndicesNamespace.php
81/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/elasticsearch/elasticsearch/src/Elasticsearch/Namespaces/AbstractNamespace.php
82/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/elasticsearch/elasticsearch/src/Elasticsearch/Namespaces/ClusterNamespace.php
83/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/elasticsearch/elasticsearch/src/Elasticsearch/Namespaces/NodesNamespace.php
84/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/elasticsearch/elasticsearch/src/Elasticsearch/Namespaces/SnapshotNamespace.php
85/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/elasticsearch/elasticsearch/src/Elasticsearch/Namespaces/CatNamespace.php
86/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/elasticsearch/elasticsearch/src/Elasticsearch/Namespaces/IngestNamespace.php
87/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/elasticsearch/elasticsearch/src/Elasticsearch/Namespaces/TasksNamespace.php
88/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/elasticsearch/elasticsearch/src/Elasticsearch/Namespaces/RemoteNamespace.php
89/var/www/rc/drooble/trunk/projects/drooble_v1/resources/library/Drooble/Search/DroobleSearchClient.php
90/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/elasticsearch/elasticsearch/src/Elasticsearch/Endpoints/Search.php
91/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/elasticsearch/elasticsearch/src/Elasticsearch/Endpoints/AbstractEndpoint.php
92/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/guzzlehttp/ringphp/src/Core.php
93/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/guzzlehttp/ringphp/src/Future/CompletedFutureArray.php
94/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/guzzlehttp/ringphp/src/Future/CompletedFutureValue.php
95/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/guzzlehttp/ringphp/src/Future/FutureInterface.php
96/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/react/promise/src/PromiseInterface.php
97/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/react/promise/src/PromisorInterface.php
98/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/guzzlehttp/ringphp/src/Future/FutureArrayInterface.php
99/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/guzzlehttp/ringphp/src/Future/FutureArray.php
100/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/guzzlehttp/ringphp/src/Future/MagicFutureTrait.php
101/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/guzzlehttp/ringphp/src/Future/BaseFutureTrait.php
102/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/react/promise/src/FulfilledPromise.php
103/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/react/promise/src/ExtendedPromiseInterface.php
104/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/react/promise/src/CancellablePromiseInterface.php
105/var/www/rc/drooble/trunk/projects/drooble_v1/resources/lib/psr/log/Psr/Log/LogLevel.php
106/var/www/rc/drooble/trunk/projects/drooble_v1/services/web/private/controllers/API/ElementsController.php
107/var/www/rc/drooble/trunk/projects/drooble_v1/resources/library/Drooble/FW/DroobleAPIController.php
Memory
Usage4194304