Page MenuHomePhabricator

D18492.diff
No OneTemporary

D18492.diff

diff --git a/src/search/PhutilSearchQueryCompiler.php b/src/search/PhutilSearchQueryCompiler.php
--- a/src/search/PhutilSearchQueryCompiler.php
+++ b/src/search/PhutilSearchQueryCompiler.php
@@ -6,9 +6,12 @@
private $operators = '+ -><()~*:""&|';
private $query;
private $stemmer;
+ private $enableFunctions = false;
const OPERATOR_NOT = 'not';
const OPERATOR_AND = 'and';
+ const OPERATOR_SUBSTRING = 'sub';
+ const OPERATOR_EXACT = 'exact';
public function setOperators($operators) {
$this->operators = $operators;
@@ -28,6 +31,15 @@
return $this->stemmer;
}
+ public function setEnableFunctions($enable_functions) {
+ $this->enableFunctions = $enable_functions;
+ return $this;
+ }
+
+ public function getEnableFunctions() {
+ return $this->enableFunctions;
+ }
+
public function compileQuery(array $tokens) {
assert_instances_of($tokens, 'PhutilSearchQueryToken');
@@ -102,11 +114,21 @@
$query = phutil_utf8v($query);
$length = count($query);
+ $enable_functions = $this->getEnableFunctions();
+
$mode = 'scan';
$current_operator = array();
$current_token = array();
+ $current_function = null;
$is_quoted = false;
$tokens = array();
+
+ if ($enable_functions) {
+ $operator_characters = '[~=+-]';
+ } else {
+ $operator_characters = '[+-]';
+ }
+
for ($ii = 0; $ii < $length; $ii++) {
$character = $query[$ii];
@@ -115,7 +137,36 @@
continue;
}
+ $mode = 'function';
+ }
+
+ if ($mode == 'function') {
$mode = 'operator';
+
+ if ($enable_functions) {
+ $found = false;
+ for ($jj = $ii; $jj < $length; $jj++) {
+ if (preg_match('/^[a-zA-Z]\z/u', $query[$jj])) {
+ continue;
+ }
+ if ($query[$jj] == ':') {
+ $found = $jj;
+ }
+ break;
+ }
+
+ if ($found !== false) {
+ $function = array_slice($query, $ii, ($jj - $ii));
+ $current_function = implode('', $function);
+
+ if (!strlen($current_function)) {
+ $current_function = null;
+ }
+
+ $ii = $jj;
+ continue;
+ }
+ }
}
if ($mode == 'operator') {
@@ -123,7 +174,7 @@
continue;
}
- if (preg_match('/^[+-]\z/', $character)) {
+ if (preg_match('/^'.$operator_characters.'\z/', $character)) {
$current_operator[] = $character;
continue;
}
@@ -164,13 +215,21 @@
}
if ($capture) {
- $tokens[] = array(
+ $token = array(
'operator' => $current_operator,
'quoted' => $was_quoted,
'value' => $current_token,
);
+
+ if ($enable_functions) {
+ $token['function'] = $current_function;
+ }
+
+ $tokens[] = $token;
+
$current_operator = array();
$current_token = array();
+ $current_function = null;
continue;
} else {
$current_token[] = $character;
@@ -191,12 +250,18 @@
implode('', $current_operator)));
}
- $tokens[] = array(
+ $token = array(
'operator' => $current_operator,
'quoted' => false,
'value' => $current_token,
);
+ if ($enable_functions) {
+ $token['function'] = $current_function;
+ }
+
+ $tokens[] = $token;
+
$results = array();
foreach ($tokens as $token) {
$value = implode('', $token['value']);
@@ -210,6 +275,12 @@
case '-':
$operator = self::OPERATOR_NOT;
break;
+ case '~':
+ $operator = self::OPERATOR_SUBSTRING;
+ break;
+ case '=':
+ $operator = self::OPERATOR_EXACT;
+ break;
case '':
case '+':
$operator = self::OPERATOR_AND;
@@ -221,11 +292,17 @@
$operator_string));
}
- $results[] = array(
+ $result = array(
'operator' => $operator,
'quoted' => $token['quoted'],
'value' => $value,
);
+
+ if ($enable_functions) {
+ $result['function'] = $token['function'];
+ }
+
+ $results[] = $result;
}
return $results;
diff --git a/src/search/PhutilSearchQueryToken.php b/src/search/PhutilSearchQueryToken.php
--- a/src/search/PhutilSearchQueryToken.php
+++ b/src/search/PhutilSearchQueryToken.php
@@ -5,6 +5,7 @@
private $isQuoted;
private $value;
private $operator;
+ private $function;
public static function newFromDictionary(array $dictionary) {
$token = new self();
@@ -12,6 +13,7 @@
$token->isQuoted = $dictionary['quoted'];
$token->operator = $dictionary['operator'];
$token->value = $dictionary['value'];
+ $token->function = idx($dictionary, 'function');
return $token;
}
@@ -28,4 +30,8 @@
return $this->operator;
}
+ public function getFunction() {
+ return $this->function;
+ }
+
}
diff --git a/src/search/__tests__/PhutilSearchQueryCompilerTestCase.php b/src/search/__tests__/PhutilSearchQueryCompilerTestCase.php
--- a/src/search/__tests__/PhutilSearchQueryCompilerTestCase.php
+++ b/src/search/__tests__/PhutilSearchQueryCompilerTestCase.php
@@ -87,6 +87,43 @@
$this->assertCompileQueries($stemming_tests, null, $stemmer);
}
+ public function testCompileQueriesWithFunctions() {
+ $op_and = PhutilSearchQueryCompiler::OPERATOR_AND;
+ $op_sub = PhutilSearchQueryCompiler::OPERATOR_SUBSTRING;
+ $op_exact = PhutilSearchQueryCompiler::OPERATOR_EXACT;
+
+ $function_tests = array(
+ 'cat' => array(
+ array(null, $op_and, 'cat'),
+ ),
+ ':cat' => array(
+ array(null, $op_and, 'cat'),
+ ),
+ 'title:cat' => array(
+ array('title', $op_and, 'cat'),
+ ),
+ 'title:cat:dog' => array(
+ array('title', $op_and, 'cat:dog'),
+ ),
+ 'title:~cat' => array(
+ array('title', $op_sub, 'cat'),
+ ),
+ 'cat title:="Meow Meow"' => array(
+ array(null, $op_and, 'cat'),
+ array('title', $op_exact, 'Meow Meow'),
+ ),
+ 'title:cat title:dog' => array(
+ array('title', $op_and, 'cat'),
+ array('title', $op_and, 'dog'),
+ ),
+ '~"core and seven years ag"' => array(
+ array(null, $op_sub, 'core and seven years ag'),
+ ),
+ );
+
+ $this->assertCompileFunctionQueries($function_tests);
+ }
+
private function assertCompileQueries(
array $tests,
$operators = null,
@@ -143,4 +180,27 @@
}
}
+ private function assertCompileFunctionQueries(array $tests) {
+ foreach ($tests as $input => $expect) {
+ $compiler = id(new PhutilSearchQueryCompiler())
+ ->setEnableFunctions(true);
+
+ $tokens = $compiler->newTokens($input);
+
+ $result = array();
+ foreach ($tokens as $token) {
+ $result[] = array(
+ $token->getFunction(),
+ $token->getOperator(),
+ $token->getValue(),
+ );
+ }
+
+ $this->assertEqual(
+ $expect,
+ $result,
+ pht('Function compilation of query: %s', $input));
+ }
+ }
+
}

File Metadata

Mime Type
text/plain
Expires
Wed, Nov 27, 10:12 PM (18 h, 24 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6796794
Default Alt Text
D18492.diff (7 KB)

Event Timeline