<?php
namespace Elastica;
use Elastica\Exception\InvalidException;
use Elastica\Filter\AbstractFilter;
use Elastica\ResultSet\BuilderInterface;
use Elastica\ResultSet\DefaultBuilder;
/**
* Elastica search object.
*
* @author Nicolas Ruflin <spam@ruflin.com>
*/
class Search
{
const OPTION_SEARCH_TYPE = 'search_type';
const OPTION_ROUTING = 'routing';
const OPTION_PREFERENCE = 'preference';
const OPTION_VERSION = 'version';
const OPTION_TIMEOUT = 'timeout';
const OPTION_FROM = 'from';
const OPTION_SIZE = 'size';
const OPTION_SCROLL = 'scroll';
const OPTION_SCROLL_ID = 'scroll_id';
const OPTION_QUERY_CACHE = 'query_cache';
const OPTION_SEARCH_TYPE_COUNT = 'count';
const OPTION_SEARCH_TYPE_SCAN = 'scan';
const OPTION_SEARCH_TYPE_DFS_QUERY_THEN_FETCH = 'dfs_query_then_fetch';
const OPTION_SEARCH_TYPE_DFS_QUERY_AND_FETCH = 'dfs_query_and_fetch';
const OPTION_SEARCH_TYPE_QUERY_THEN_FETCH = 'query_then_fetch';
const OPTION_SEARCH_TYPE_QUERY_AND_FETCH = 'query_and_fetch';
const OPTION_SEARCH_TYPE_SUGGEST = 'suggest';
const OPTION_SEARCH_TYPE_SCROLL = 'scroll';
const OPTION_SEARCH_IGNORE_UNAVAILABLE = 'ignore_unavailable';
private $_builder;
protected $_indices = array();
protected $_types = array();
protected $_query;
protected $_options = array();
protected $_client;
public function __construct(Client $client, BuilderInterface $builder = null)
{
$this->_builder = $builder ?: new DefaultBuilder();
$this->_client = $client;
}
public function addIndex($index)
{
if ($index instanceof Index) {
$index = $index->getName();
}
if (!is_scalar($index)) {
throw new InvalidException('Invalid param type');
}
$this->_indices[] = (string) $index;
return $this;
}
public function addIndices(array $indices = array())
{
foreach ($indices as $index) {
$this->addIndex($index);
}
return $this;
}
public function addType($type)
{
if ($type instanceof Type) {
$type = $type->getName();
}
if (!is_string($type)) {
throw new InvalidException('Invalid type type');
}
$this->_types[] = $type;
return $this;
}
public function addTypes(array $types = array())
{
foreach ($types as $type) {
$this->addType($type);
}
return $this;
}
public function setQuery($query)
{
if ($query instanceof AbstractFilter) {
trigger_error('Deprecated: Elastica\Search::setQuery() passing AbstractFilter is deprecated. Create query and use setPostFilter with AbstractQuery instead.', E_USER_DEPRECATED);
}
$this->_query = Query::create($query);
return $this;
}
public function setOption($key, $value)
{
$this->_validateOption($key);
$this->_options[$key] = $value;
return $this;
}
public function setOptions(array $options)
{
$this->clearOptions();
foreach ($options as $key => $value) {
$this->setOption($key, $value);
}
return $this;
}
public function clearOptions()
{
$this->_options = array();
return $this;
}
public function addOption($key, $value)
{
$this->_validateOption($key);
if (!isset($this->_options[$key])) {
$this->_options[$key] = array();
}
$this->_options[$key][] = $value;
return $this;
}
public function hasOption($key)
{
return isset($this->_options[$key]);
}
public function getOption($key)
{
if (!$this->hasOption($key)) {
throw new InvalidException('Option '.$key.' does not exist');
}
return $this->_options[$key];
}
public function getOptions()
{
return $this->_options;
}
protected function _validateOption($key)
{
switch ($key) {
case self::OPTION_SEARCH_TYPE:
case self::OPTION_ROUTING:
case self::OPTION_PREFERENCE:
case self::OPTION_VERSION:
case self::OPTION_TIMEOUT:
case self::OPTION_FROM:
case self::OPTION_SIZE:
case self::OPTION_SCROLL:
case self::OPTION_SCROLL_ID:
case self::OPTION_SEARCH_TYPE_SUGGEST:
case self::OPTION_SEARCH_IGNORE_UNAVAILABLE:
case self::OPTION_QUERY_CACHE:
return true;
}
throw new InvalidException('Invalid option '.$key);
}
public function getClient()
{
return $this->_client;
}
public function getIndices()
{
return $this->_indices;
}
public function hasIndices()
{
return count($this->_indices) > 0;
}
public function hasIndex($index)
{
if ($index instanceof Index) {
$index = $index->getName();
}
return in_array($index, $this->_indices);
}
public function getTypes()
{
return $this->_types;
}
public function hasTypes()
{
return count($this->_types) > 0;
}
public function hasType($type)
{
if ($type instanceof Type) {
$type = $type->getName();
}
return in_array($type, $this->_types);
}
public function getQuery()
{
if (null === $this->_query) {
$this->_query = Query::create('');
}
return $this->_query;
}
public static function create(SearchableInterface $searchObject)
{
return $searchObject->createSearch();
}
public function getPath()
{
if (isset($this->_options[self::OPTION_SCROLL_ID])) {
return '_search/scroll';
}
$indices = $this->getIndices();
$path = '';
$types = $this->getTypes();
if (empty($indices)) {
if (!empty($types)) {
$path .= '_all';
}
} else {
$path .= implode(',', $indices);
}
if (!empty($types)) {
$path .= '/'.implode(',', $types);
}
return $path.'/_search';
}
public function search($query = '', $options = null)
{
$this->setOptionsAndQuery($options, $query);
$query = $this->getQuery();
$path = $this->getPath();
$params = $this->getOptions();
if ('_search/scroll' == $path) {
$data = $params[self::OPTION_SCROLL_ID];
unset($params[self::OPTION_SCROLL_ID]);
} else {
$data = $query->toArray();
}
$response = $this->getClient()->request(
$path,
Request::GET,
$data,
$params
);
return $this->_builder->buildResultSet($response, $query);
}
public function count($query = '', $fullResult = false)
{
$this->setOptionsAndQuery(null, $query);
$query = $this->getQuery();
$path = $this->getPath();
$response = $this->getClient()->request(
$path,
Request::GET,
$query->toArray(),
array(self::OPTION_SEARCH_TYPE => self::OPTION_SEARCH_TYPE_COUNT)
);
$resultSet = $this->_builder->buildResultSet($response, $query);
return $fullResult ? $resultSet : $resultSet->getTotalHits();
}
public function setOptionsAndQuery($options = null, $query = '')
{
if ('' != $query) {
$this->setQuery($query);
}
if (is_int($options)) {
$this->getQuery()->setSize($options);
} elseif (is_array($options)) {
if (isset($options['limit'])) {
$this->getQuery()->setSize($options['limit']);
unset($options['limit']);
}
if (isset($options['explain'])) {
$this->getQuery()->setExplain($options['explain']);
unset($options['explain']);
}
$this->setOptions($options);
}
return $this;
}
public function setSuggest(Suggest $suggest)
{
return $this->setOptionsAndQuery(array(self::OPTION_SEARCH_TYPE_SUGGEST => 'suggest'), $suggest);
}
public function scroll($expiryTime = '1m')
{
return new Scroll($this, $expiryTime);
}
public function scanAndScroll($expiryTime = '1m', $sizePerShard = 1000)
{
return new ScanAndScroll($this, $expiryTime, $sizePerShard);
}
public function getResultSetBuilder()
{
return $this->_builder;
}
}