search_engines.php 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. <?php
  2. // This file is part of GNU social - https://www.gnu.org/software/social
  3. //
  4. // GNU social is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Affero General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // GNU social is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Affero General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Affero General Public License
  15. // along with GNU social. If not, see <http://www.gnu.org/licenses/>.
  16. defined('GNUSOCIAL') || die();
  17. class SearchEngine
  18. {
  19. protected $target;
  20. protected $table;
  21. public function __construct($target, $table)
  22. {
  23. $this->target = $target;
  24. $this->table = $table;
  25. }
  26. public function query($q)
  27. {
  28. }
  29. public function limit($offset, $count, $rss = false)
  30. {
  31. return $this->target->limit($offset, $count);
  32. }
  33. public function set_sort_mode($mode)
  34. {
  35. switch ($mode) {
  36. case 'chron':
  37. return $this->target->orderBy('created DESC');
  38. break;
  39. case 'reverse_chron':
  40. return $this->target->orderBy('created ASC');
  41. break;
  42. case 'nickname_desc':
  43. if ($this->table != 'profile') {
  44. throw new Exception(
  45. 'nickname_desc sort mode can only be use when searching profile.'
  46. );
  47. } else {
  48. return $this->target->orderBy(sprintf('%1$s.nickname DESC', $this->table));
  49. }
  50. break;
  51. case 'nickname_asc':
  52. if ($this->table != 'profile') {
  53. throw new Exception(
  54. 'nickname_desc sort mode can only be use when searching profile.'
  55. );
  56. } else {
  57. return $this->target->orderBy(sprintf('%1$s.nickname ASC', $this->table));
  58. }
  59. break;
  60. default:
  61. return $this->target->orderBy('created DESC');
  62. break;
  63. }
  64. }
  65. }
  66. class PostgreSQLSearch extends SearchEngine
  67. {
  68. public function query($q)
  69. {
  70. if ($this->table === 'profile') {
  71. $cols = implode(" || ' ' || ", array_map(
  72. function ($col) {
  73. return sprintf(
  74. "COALESCE(%s.%s, '')",
  75. common_database_tablename($this->table),
  76. $col
  77. );
  78. },
  79. ['nickname', 'fullname', 'location', 'bio', 'homepage']
  80. ));
  81. $this->target->whereAdd(sprintf(
  82. 'to_tsvector(\'english\', %2$s) @@ plainto_tsquery(\'%1$s\')',
  83. $this->target->escape($q, true),
  84. $cols
  85. ));
  86. return true;
  87. } elseif ($this->table === 'notice') {
  88. // Don't show imported notices
  89. $this->target->whereAdd('notice.is_local <> ' . Notice::GATEWAY);
  90. $this->target->whereAdd(sprintf(
  91. 'to_tsvector(\'english\', content) @@ plainto_tsquery(\'%1$s\')',
  92. $this->target->escape($q, true)
  93. ));
  94. return true;
  95. } else {
  96. throw new ServerException('Unknown table: ' . $this->table);
  97. }
  98. }
  99. }
  100. class MySQLSearch extends SearchEngine
  101. {
  102. public function query($q)
  103. {
  104. if ($this->table === 'profile') {
  105. $this->target->whereAdd(sprintf(
  106. 'MATCH (%2$s.nickname, %2$s.fullname, %2$s.location, %2$s.bio, %2$s.homepage) ' .
  107. 'AGAINST (\'%1$s\' IN BOOLEAN MODE)',
  108. $this->target->escape($q, true),
  109. $this->table
  110. ));
  111. if (strtolower($q) != $q) {
  112. $this->target->whereAdd(
  113. sprintf(
  114. 'MATCH (%2$s.nickname, %2$s.fullname, %2$s.location, %2$s.bio, %2$s.homepage) ' .
  115. 'AGAINST (\'%1$s\' IN BOOLEAN MODE)',
  116. $this->target->escape(strtolower($q), true),
  117. $this->table
  118. ),
  119. 'OR'
  120. );
  121. }
  122. return true;
  123. } elseif ($this->table === 'notice') {
  124. // Don't show imported notices
  125. $this->target->whereAdd('notice.is_local <> ' . Notice::GATEWAY);
  126. $this->target->whereAdd(sprintf(
  127. 'MATCH (%2$s.content) AGAINST (\'%1$s\' IN BOOLEAN MODE)',
  128. $this->target->escape($q, true),
  129. $this->table
  130. ));
  131. if (strtolower($q) != $q) {
  132. $this->target->whereAdd(
  133. sprintf(
  134. 'MATCH (%2$s.content) AGAINST (\'%1$s\' IN BOOLEAN MODE)',
  135. $this->target->escape(strtolower($q), true),
  136. $this->table
  137. ),
  138. 'OR'
  139. );
  140. }
  141. return true;
  142. } else {
  143. throw new ServerException('Unknown table: ' . $this->table);
  144. }
  145. }
  146. }
  147. class SQLLikeSearch extends SearchEngine
  148. {
  149. public function query($q)
  150. {
  151. if ($this->table === 'profile') {
  152. $qry = sprintf(
  153. '( %2$s.nickname LIKE \'%%%1$s%%\' ' .
  154. ' OR %2$s.fullname LIKE \'%%%1$s%%\' ' .
  155. ' OR %2$s.location LIKE \'%%%1$s%%\' ' .
  156. ' OR %2$s.bio LIKE \'%%%1$s%%\' ' .
  157. ' OR %2$s.homepage LIKE \'%%%1$s%%\')',
  158. $this->target->escape($q, true),
  159. $this->table
  160. );
  161. } elseif ($this->table === 'notice') {
  162. $qry = sprintf('content LIKE \'%%%1$s%%\'', $this->target->escape($q, true));
  163. } else {
  164. throw new ServerException('Unknown table: ' . $this->table);
  165. }
  166. $this->target->whereAdd($qry);
  167. return true;
  168. }
  169. }