From 4a5d988d8c15cfe9263b23458a8f0a129beee014 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Mon, 18 Dec 2017 02:59:11 +0100 Subject: [PATCH 1/5] Implement search API (fixes #929) --- doc/api.md | 18 ++++++++++++++ include/api.php | 65 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/doc/api.md b/doc/api.md index 159bc69914..3c5cd0b7be 100644 --- a/doc/api.md +++ b/doc/api.md @@ -449,6 +449,24 @@ It shows all direct answers (excluding the original post) to a given id. Friendica doesn't allow showing followers of other users. +--- +### search (*; AUTH) +#### Parameters +* q: search query +* page: the page number (starting at 1) to return +* rpp: the number of statuses to return per page +* count: alias for the rpp parameter +* since_id: returns statuses with ids greater than the given id +* max_id: returns statuses with ids lower or equal to the given id + +#### Unsupported parameters +* geocode +* lang +* locale +* result_type +* until +* include_entities + --- ### users/search (*) #### Parameters diff --git a/include/api.php b/include/api.php index 2aa8fc645c..9cc82560d2 100644 --- a/include/api.php +++ b/include/api.php @@ -1485,6 +1485,71 @@ function api_users_search($type) /// @TODO move to top of file or somewhere better api_register_func('api/users/search', 'api_users_search'); +/** + * Returns statuses that match a specified query. + * + * @see https://developer.twitter.com/en/docs/tweets/search/api-reference/get-search-tweets + * + * @param string $type Return format: json, xml, atom, rss + * + * @return array|string + * @throws UnauthorizedException + * @throws BadRequestException + */ +function api_search($type) +{ + $data = array(); + + if (x($_REQUEST, 'q')) { + if (x($_REQUEST, 'rpp')) { + $count = $_REQUEST['rpp']; + } elseif (x($_REQUEST, 'count')) { + $count = $_REQUEST['count']; + } else { + $count = 15; + } + + $since_id = (x($_REQUEST, 'since_id') ? $_REQUEST['since_id'] : 0); + $max_id = (x($_REQUEST, 'max_id') ? $_REQUEST['max_id'] : 0); + $page = (x($_REQUEST, 'page') ? $_REQUEST['page'] - 1 : 0); + + $start = $page * $count; + + if ($max_id > 0) { + $sql_extra .= ' AND `item`.`id` <= ' . intval($max_id); + } + + $r = q( + "SELECT %s + FROM `item` %s + WHERE %s AND (`item`.`uid` = 0 OR (`item`.`uid` = %s AND NOT `item`.`global`)) + AND `item`.`body` REGEXP '%s' + $sql_extra + AND `item`.`id`>%d + GROUP BY `item`.`uri`, `item`.`id` + ORDER BY `item`.`id` DESC LIMIT %d ,%d ", + item_fieldlists(), + item_joins(), + item_condition(), + intval(local_user()), + dbesc(protect_sprintf(preg_quote($_REQUEST['q']))), + intval($since_id), + intval($start), + intval($count) + ); + + $data['status'] = api_format_items($r, api_get_user(get_app())); + } else { + throw new BadRequestException("q parameter is required."); + } + + return api_format_data("statuses", $type, $data); +} + +/// @TODO move to top of file or somewhere better +api_register_func('api/search/tweets', 'api_search', true); +api_register_func('api/search', 'api_search', true); + /** * * http://developer.twitter.com/doc/get/statuses/home_timeline From ee8468affef27864f5f9798942653b33989e3a9e Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Mon, 18 Dec 2017 13:35:36 +0100 Subject: [PATCH 2/5] Improve api_search() Use dba::p() instead of q() Move exception to the beginning Remove useless GROUP BY Remove useless protect_sprintf() --- include/api.php | 79 ++++++++++++++++++++++++------------------------- 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/include/api.php b/include/api.php index 9cc82560d2..2cf79fa5aa 100644 --- a/include/api.php +++ b/include/api.php @@ -1500,49 +1500,48 @@ function api_search($type) { $data = array(); - if (x($_REQUEST, 'q')) { - if (x($_REQUEST, 'rpp')) { - $count = $_REQUEST['rpp']; - } elseif (x($_REQUEST, 'count')) { - $count = $_REQUEST['count']; - } else { - $count = 15; - } - - $since_id = (x($_REQUEST, 'since_id') ? $_REQUEST['since_id'] : 0); - $max_id = (x($_REQUEST, 'max_id') ? $_REQUEST['max_id'] : 0); - $page = (x($_REQUEST, 'page') ? $_REQUEST['page'] - 1 : 0); - - $start = $page * $count; - - if ($max_id > 0) { - $sql_extra .= ' AND `item`.`id` <= ' . intval($max_id); - } - - $r = q( - "SELECT %s - FROM `item` %s - WHERE %s AND (`item`.`uid` = 0 OR (`item`.`uid` = %s AND NOT `item`.`global`)) - AND `item`.`body` REGEXP '%s' - $sql_extra - AND `item`.`id`>%d - GROUP BY `item`.`uri`, `item`.`id` - ORDER BY `item`.`id` DESC LIMIT %d ,%d ", - item_fieldlists(), - item_joins(), - item_condition(), - intval(local_user()), - dbesc(protect_sprintf(preg_quote($_REQUEST['q']))), - intval($since_id), - intval($start), - intval($count) - ); - - $data['status'] = api_format_items($r, api_get_user(get_app())); - } else { + if (!x($_REQUEST, 'q')) { throw new BadRequestException("q parameter is required."); } + if (x($_REQUEST, 'rpp')) { + $count = $_REQUEST['rpp']; + } elseif (x($_REQUEST, 'count')) { + $count = $_REQUEST['count']; + } else { + $count = 15; + } + + $since_id = (x($_REQUEST, 'since_id') ? $_REQUEST['since_id'] : 0); + $max_id = (x($_REQUEST, 'max_id') ? $_REQUEST['max_id'] : 0); + $page = (x($_REQUEST, 'page') ? $_REQUEST['page'] - 1 : 0); + + $start = $page * $count; + + if ($max_id > 0) { + $sql_extra .= ' AND `item`.`id` <= ' . intval($max_id); + } + + $r = dba::p( + "SELECT ".item_fieldlists()." + FROM `item` ".item_joins()." + WHERE ".item_condition()." AND (`item`.`uid` = 0 OR (`item`.`uid` = ? AND NOT `item`.`global`)) + AND `item`.`body` REGEXP ? + $sql_extra + AND `item`.`id`>? + ORDER BY `item`.`id` DESC LIMIT ".intval($start)." ,".intval($count)." ", + intval(api_user()), + $_REQUEST['q'], + intval($since_id) + ); + + $statuses = array(); + while ($row = dba::fetch($r)) { + $statuses[] = $row; + } + + $data['status'] = api_format_items($statuses, api_get_user(get_app())); + return api_format_data("statuses", $type, $data); } From 31d63d929dab4df07fff19ba6ea7a948036bdc8e Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Mon, 18 Dec 2017 14:28:04 +0100 Subject: [PATCH 3/5] Use dba::inArray() instead of dba::fetch() in api_search --- include/api.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/include/api.php b/include/api.php index 2cf79fa5aa..b247a738d0 100644 --- a/include/api.php +++ b/include/api.php @@ -1535,12 +1535,7 @@ function api_search($type) intval($since_id) ); - $statuses = array(); - while ($row = dba::fetch($r)) { - $statuses[] = $row; - } - - $data['status'] = api_format_items($statuses, api_get_user(get_app())); + $data['status'] = api_format_items(dba::inArray($r), api_get_user(get_app())); return api_format_data("statuses", $type, $data); } From 5899d821efb669ffbd8730bfbb634b96c1650333 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Mon, 18 Dec 2017 14:36:06 +0100 Subject: [PATCH 4/5] Remove useless intval() in api_search --- include/api.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/api.php b/include/api.php index b247a738d0..39a1f03185 100644 --- a/include/api.php +++ b/include/api.php @@ -1530,9 +1530,9 @@ function api_search($type) $sql_extra AND `item`.`id`>? ORDER BY `item`.`id` DESC LIMIT ".intval($start)." ,".intval($count)." ", - intval(api_user()), + api_user(), $_REQUEST['q'], - intval($since_id) + $since_id ); $data['status'] = api_format_items(dba::inArray($r), api_get_user(get_app())); From 02733c66f393caa031e1c3ec87cc1456cd0a9ff1 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Mon, 18 Dec 2017 15:23:15 +0100 Subject: [PATCH 5/5] Use LIKE instead of REGEXP in api_search --- include/api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/api.php b/include/api.php index 39a1f03185..c359c65f1a 100644 --- a/include/api.php +++ b/include/api.php @@ -1526,7 +1526,7 @@ function api_search($type) "SELECT ".item_fieldlists()." FROM `item` ".item_joins()." WHERE ".item_condition()." AND (`item`.`uid` = 0 OR (`item`.`uid` = ? AND NOT `item`.`global`)) - AND `item`.`body` REGEXP ? + AND `item`.`body` LIKE CONCAT('%',?,'%') $sql_extra AND `item`.`id`>? ORDER BY `item`.`id` DESC LIMIT ".intval($start)." ,".intval($count)." ",