public function addQuery($query, $index = '*', $comment = '')
{
// mbstring workaround
$this->mbPush();
// build request
$req = pack('NNNN', $this->offset, $this->limit, $this->mode, $this->ranker);
if ($this->ranker === self::SPH_RANK_EXPR) {
$req .= pack('N', strlen($this->rankexpr)) . $this->rankexpr;
}
// (deprecated) sort mode
$req .= pack('N', $this->sort);
$req .= pack('N', strlen($this->sortby)) . $this->sortby;
$req .= pack('N', strlen($query)) . $query;
$req .= pack('N', count($this->weights));
foreach ($this->weights as $weight) {
$req .= pack('N', (int) $weight);
}
$req .= pack('N', strlen($index)) . $index;
// id64 range marker
$req .= pack('N', 1);
// id64 range
$req .= $this->packU64($this->minid) . $this->packU64($this->maxid);
// filters
$req .= pack('N', count($this->filters));
foreach ($this->filters as $filter) {
$req .= pack('N', strlen($filter['attr'])) . $filter['attr'];
$req .= pack('N', $filter['type']);
switch ($filter['type']) {
case self::SPH_FILTER_VALUES:
$req .= pack('N', count($filter['values']));
foreach ($filter['values'] as $value) {
$req .= $this->packI64($value);
}
break;
case self::SPH_FILTER_RANGE:
$req .= $this->packI64($filter['min']) . $this->packI64($filter['max']);
break;
case self::SPH_FILTER_FLOATRANGE:
$req .= $this->packFloat($filter['min']) . $this->packFloat($filter['max']);
break;
default:
throw new \InvalidArgumentException('internal error: unhandled filter type');
}
$req .= pack('N', $filter['exclude']);
}
// group-by clause, max-matches count, group-sort clause, cutoff count
$req .= pack('NN', $this->groupfunc, strlen($this->groupby)) . $this->groupby;
$req .= pack('N', $this->maxmatches);
$req .= pack('N', strlen($this->groupsort)) . $this->groupsort;
$req .= pack('NNN', $this->cutoff, $this->retrycount, $this->retrydelay);
$req .= pack('N', strlen($this->groupdistinct)) . $this->groupdistinct;
// anchor point
if (empty($this->anchor)) {
$req .= pack('N', 0);
} else {
$a =& $this->anchor;
$req .= pack('N', 1);
$req .= pack('N', strlen($a['attrlat'])) . $a['attrlat'];
$req .= pack('N', strlen($a['attrlong'])) . $a['attrlong'];
$req .= $this->packFloat($a['lat']) . $this->packFloat($a['long']);
}
// per-index weights
$req .= pack('N', count($this->indexweights));
foreach ($this->indexweights as $idx => $weight) {
$req .= pack('N', strlen($idx)) . $idx . pack('N', $weight);
}
// max query time
$req .= pack('N', $this->maxquerytime);
// per-field weights
$req .= pack('N', count($this->fieldweights));
foreach ($this->fieldweights as $field => $weight) {
$req .= pack('N', strlen($field)) . $field . pack('N', $weight);
}
// comment
$req .= pack('N', strlen($comment)) . $comment;
// attribute overrides
$req .= pack('N', count($this->overrides));
foreach ($this->overrides as $key => $entry) {
$req .= pack('N', strlen($entry['attr'])) . $entry['attr'];
$req .= pack('NN', $entry['type'], count($entry['values']));
foreach ($entry['values'] as $id => $val) {
if (!is_numeric($id)) {
throw new \InvalidArgumentException('Document ID must be numeric.');
}
if (!is_numeric($val)) {
throw new \InvalidArgumentException('Attribute value must be numeric.');
}
$req .= $this->packU64($id);
switch ($entry['type']) {
case self::SPH_ATTR_FLOAT:
$req .= $this->packFloat($val);
break;
case self::SPH_ATTR_BIGINT:
$req .= $this->packI64($val);
break;
default:
$req .= pack('N', $val);
break;
}
}
}
// select-list
$req .= pack('N', strlen($this->select)) . $this->select;
// mbstring workaround
$this->mbPop();
// store request to requests array
$this->reqs[] = $req;
return count($this->reqs) - 1;
}