public function search($options) { // we query the listings table which is sharded by month. // because it is sharded by month, we have to do a few more tricks // to get the data-set we want. // first, we try to narrow it down as much as possible by the criteria specified. $clauses = array(); // are we looking for auctions that are still in progress or already finished? $clauses[] = $this->db->prep('closed = ?', $options->closed ? 1 : 0); // if we have a specific item id we are looking for, query for that. if (isset($options->item_id)) { $clauses[] = $this->db->prep('item_id IN (%i)', $options->item_id); } // do we know the seller? if (isset($options->seller)) { $clauses[] = $this->db->prep('seller IN (%i)', $options->seller); } // sometimes, rarely we are looking for an auction purchased by a specific buyer. // obviously these auctions are already closed. should i sanity check the closed param? if (isset($options->buyer)) { $clauses[] = $this->db->prep('buyer IN (%i)', $options->buyer); } // we can narrow by the person who is currently the leading bidder. can't search by past // bidders since that is more of a bid history search. // haven't written that yet. if (isset($options->bidder)) { $clauses[] = $this->db->prep('bidder IN (%i)', $options->bidder); } // are we looking for a bid-only auction? if ($options->only == 'bid') { $clauses[] = 'price = 0'; } // how about a buy-now only auction? if ($options->only == 'buy') { $clauses[] = 'step = 0'; } // look for items only above a given price range. if ($options->floor && ctype_digit($options->floor)) { $clauses[] = $this->db->prep('pricesort >= %i', $options->floor); } // look for items only below a given price range. if ($options->ceiling && ctype_digit($options->ceiling)) { $clauses[] = $this->db->prep('pricesort <= %i', $options->ceiling); } // how do we want the result set sorted? $sort = $options->sort; $order = ''; switch ($sort) { case 'low_price': $order = 'pricesort ASC'; break; case 'high_price': $order = 'pricesort DESC'; break; case 'just_added': $order = 'created DESC'; break; case 'expires_soon': $clauses[] = $this->db->prep('expires > %i', Souk\Util::now()); $order = 'expires ASC'; break; case 'expires_soon_delay': $clauses[] = $this->db->prep('expires > %i', Souk\Util::now() + Souk\UTIL::MIN_EXPIRE); $order = 'expires ASC'; break; default: $key = $row['expires'] . '.' . $id; break; } $ds = Souk\Util::dateshard(); // start with the shard a few weeks out from now, which is where the new listings are. $ds->setTimestamp(Souk\Util::now() + Souk\UTIL::MAX_EXPIRE); // if the auction is still active, we don't have to search the really old shards. // stop at the shard for this week. if (!$options->closed) { $ds->setCutoff(1); } // start looping throw the shards and querying. $ids = array(); $where = $clauses ? 'WHERE ' . implode(' AND ', $clauses) : ''; if ($order) { $order = ' ORDER BY ' . $order; } // don't return more rows than the hard search limit imposed by souk. // after more than about 1000 rows, more results become meaningless. who paginates through all of that? // need them to somehow narrow their search more. $limit = 'LIMIT ' . Souk\Util::SEARCH_LIMIT; $ds = Souk\Util::dateshard(); foreach ($ds as $shard) { $table = $this->table($shard); if (\Gaia\Souk\Storage::isAutoSchemaEnabled()) { $this->create($table); } $sql = "SELECT row_id, pricesort, created, expires FROM {$table} {$where} {$order} {$limit}"; //print "\n" . $sql; // run the query $rs = $this->execute($sql); //print_r( $rs ); // pull out all the rows matched by the query. // we are making the key of the id list contain the // value of what we sort by, so we can do a keysort later, and order the // result in php since we have to span many shards. while ($row = $rs->fetch()) { $id = Souk\Util::composeId($shard, $row['row_id']); switch ($sort) { case 'low_price': case 'high_price': $key = $row['pricesort'] . '.' . $id; break; case 'just_added': $key = $row['created'] . '.' . $id; break; case 'expires_soon': case 'expires_soon_delay': default: $key = $row['expires'] . '.' . $id; break; } $ids[$key] = $id; } $rs->free(); } // now that we are all done fetching the rows, sort. switch ($sort) { case 'low_price': case 'expires_soon': case 'expires_soon_delay': ksort($ids, SORT_NUMERIC); break; default: krsort($ids, SORT_NUMERIC); break; } // since we queried many shards we could potentially have quite a few more // ids than the max limit. slice off only the top most rows. // those are the ones we care about. // we only need the other values so we can sort in php across all the shards. // a little bit inefficient, but it is just a list of numbers, and we are gonna // cache it for a long time in the caching layer. return array_values(array_slice($ids, 0, Souk\Util::SEARCH_LIMIT)); }