@@ -5,7 +5,7 @@ Package Search
--------------
Package searches can be performed by issuing HTTP GET requests of the form
-+/rpc/?v=5&type=search&by=_field_&arg=_keywords_+ where _keywords_ is the
++/rpc/?v=6&type=search&by[]=_field_&arg[]=_keywords_+ where _keywords_ is the
search argument and _field_ is one of the following values:
* `name` (search by package name only)
@@ -15,31 +15,39 @@ search argument and _field_ is one of the following values:
* `makedepends` (search for packages that makedepend on _keywords_)
* `optdepends` (search for packages that optdepend on _keywords_)
* `checkdepends` (search for packages that checkdepend on _keywords_)
+* `provides` (search by package provides; v6 multiple by[] only)
The _by_ parameter can be skipped and defaults to `name-desc`.
If a maintainer search is performed and the search argument is left empty, a
list of orphan packages is returned.
+Note: Legacy v5 support is still enabled for singular _by_ searches.
+
Package Details
---------------
Package information can be obtained by issuing HTTP GET requests of the form
-+/rpc/?v=5&type=info&arg[]=_pkg1_&arg[]=_pkg2_&...+ where _pkg1_, _pkg2_, ...
++/rpc/?v=6&type=info&arg[]=_pkg1_&arg[]=_pkg2_&...+ where _pkg1_, _pkg2_, ...
are the names of packages to retrieve package details for.
Examples
--------
`search`::
- `/rpc/?v=5&type=search&arg=foobar`
+ `/rpc/?v=6&type=search&arg=foobar`
`search` by maintainer::
- `/rpc/?v=5&type=search&by=maintainer&arg=john`
+ `/rpc/?v=6&type=search&by[]=maintainer&arg=john`
`search` packages that have _boost_ as `makedepends`::
- `/rpc/?v=5&type=search&by=makedepends&arg=boost`
+ `/rpc/?v=6&type=search&by[]=makedepends&arg=boost`
`search` with callback::
- `/rpc/?v=5&type=search&arg=foobar&callback=jsonp1192244621103`
+ `/rpc/?v=6&type=search&arg=foobar&callback=jsonp1192244621103`
+`search` by provides::
+ `/rpc/?v=6&type=search&by[]=provides&arg[]=gcc`
+`search` by provides and name::
+ `/rpc/?v=6&type=search&by[]=name&by[]=provides&arg[]=gcc`
`info`::
- `/rpc/?v=5&type=info&arg[]=foobar`
+ `/rpc/?v=6&type=info&arg[]=foobar`
`info` with multiple packages::
- `/rpc/?v=5&type=info&arg[]=foo&arg[]=bar`
+ `/rpc/?v=6&type=info&arg[]=foo&arg[]=bar`
+
@@ -80,7 +80,8 @@ class AurJSON {
if (isset($http_data['v'])) {
$this->version = intval($http_data['v']);
}
- if ($this->version < 1 || $this->version > 5) {
+
+ if ($this->version < 1 || $this->version > 6) {
return $this->json_error('Invalid version specified.');
}
@@ -94,8 +95,14 @@ class AurJSON {
if (isset($http_data['search_by']) && !isset($http_data['by'])) {
$http_data['by'] = $http_data['search_by'];
}
- if (isset($http_data['by']) && !in_array($http_data['by'], self::$exposed_fields)) {
- return $this->json_error('Incorrect by field specified.');
+
+ if (isset($http_data['by']) && !is_array($http_data['by'])) {
+ if ($http_data['by'] === 'provides') {
+ return $this->json_error("The 'provides' by field " .
+ "may only be used via multiple by[] search.");
+ } elseif (!in_array($http_data['by'], self::$exposed_fields)) {
+ return $this->json_error('Incorrect by field specified.');
+ }
}
$this->dbh = DB::connect();
@@ -360,7 +367,7 @@ class AurJSON {
} elseif ($this->version >= 2) {
if ($this->version == 2 || $this->version == 3) {
$fields = implode(',', self::$fields_v2);
- } else if ($this->version == 4 || $this->version == 5) {
+ } else if ($this->version == 4 || $this->version == 5 || $this->version == 6) {
$fields = implode(',', self::$fields_v4);
}
$query = "SELECT {$fields} " .
@@ -470,6 +477,10 @@ class AurJSON {
* @return mixed Returns an array of package matches.
*/
private function search($http_data) {
+ if ($this->version == 6 && is_array($http_data['by'])) {
+ return $this->search_v6($http_data);
+ }
+
$keyword_string = $http_data['arg'];
if (isset($http_data['by'])) {
@@ -497,6 +508,8 @@ class AurJSON {
$keyword_string = $this->dbh->quote($keyword_string);
$where_condition = "Users.Username = $keyword_string ";
}
+ } else if ($search_by === 'provides') {
+ return $this->json_error("The 'provides' by field is only available via multiple by[] search.");
} else if (in_array($search_by, self::$exposed_depfields)) {
if (empty($keyword_string)) {
return $this->json_error('Query arg is empty.');
@@ -515,6 +528,89 @@ class AurJSON {
return $this->process_query('search', $where_condition);
}
+ /*
+ * Returns multiple-by, multiple-arg and/or search information
+ * a feature not included in the v5 and before RPC API,
+ * which introduces the beginnings of the v6 API.
+ *
+ * @param array $http_data Query parameters.
+ *
+ * @return mixed Returns an array of results containing the package data
+ */
+ private function search_v6($http_data) {
+ if (!is_array($http_data['arg'])) {
+ $http_data['arg'] = array($http_data['arg']);
+ }
+
+ $max_results = config_get_int('options', 'max_rpc_results');
+ $fields = implode(',', self::$fields_v4);
+
+ $query = "SELECT {$fields} FROM " .
+ "Packages LEFT JOIN PackageBases " .
+ "ON PackageBases.ID = Packages.PackageBaseID " .
+ "LEFT JOIN Users " .
+ "ON PackageBases.MaintainerUID = Users.ID " .
+ "LEFT JOIN PackageRelations " .
+ "ON PackageRelations.PackageID = Packages.ID " .
+ "AND PackageRelations.RelTypeID = 2 ";
+
+ $where = array();
+
+ foreach($http_data['by'] as $by) {
+ if($by == 'provides') {
+ foreach ($http_data['arg'] as $provide) {
+ array_push($where,
+ "PackageRelations.RelName = " .
+ $this->dbh->quote($provide));
+ }
+ } elseif($by == 'name') {
+ foreach ($http_data['arg'] as $name) {
+ array_push($where, "Packages.NAME LIKE " .
+ $this->dbh->quote('%' . $name . '%_'));
+ array_push($where, "Packages.Name = " .
+ $this->dbh->quote($name));
+ }
+ } elseif($by == 'name-desc') {
+ foreach ($http_data['arg'] as $name) {
+ array_push($where, "Packages.NAME LIKE " .
+ $this->dbh->quote('%' . $name . '%_'));
+ array_push($where, "Packages.Name = " .
+ $this->dbh->quote($name));
+ array_push($where, "Packages.Description LIKE " .
+ $this->dbh->quote('%' . $name . '%_'));
+ }
+ } elseif (in_array($by, self::$exposed_depfields)) {
+ foreach ($http_data['arg'] as $dep) {
+ $subquery = "SELECT PackageDepends.DepName FROM PackageDepends ";
+ $subquery .= "LEFT JOIN DependencyTypes ";
+ $subquery .= "ON PackageDepends.DepTypeID = DependencyTypes.ID ";
+ $subquery .= "WHERE PackageDepends.PackageID = Packages.ID ";
+ $subquery .= "AND DependencyTypes.Name = " . $this->dbh->quote($by);
+ $sub_condition = $this->dbh->quote($dep) . " IN (${subquery})";
+ array_push($where, $sub_condition);
+ }
+ } else {
+ return $this->json_error(
+ "${by} is not supported in v6 multi-argument by search."
+ );
+ }
+ }
+
+ $query .= "WHERE " . implode(" OR ", $where) . " ";
+ "AND PackageBases.PackagerUID IS NOT NULL " .
+ "LIMIT ${max_results}";
+
+ $packages = array(); // Final list of packages
+ $result = $this->dbh->query($query);
+
+ if ($result) {
+ while ($row = $result->fetch(PDO::FETCH_ASSOC))
+ array_push($packages, $row);
+ }
+
+ return $this->json_results('search', count($packages), $packages, NULL);
+ }
+
/*
* Returns the info on a specific package.
*
@@ -569,7 +665,6 @@ class AurJSON {
return $this->process_query('multiinfo', $where_condition);
}
-
/*
* Returns all the packages for a specific maintainer.
*
Add new method of multiple by[] search in API v6. The 'provides' by field is also supported in the new multiple by[] method included with this patch. 'provides' may not be used in legacy (v5) singular by search. A helpful json error message is returned to the user to indicate so. This patch is a follow-up implementation to https://patchwork.archlinux.org/patch/479/ Signed-off-by: Kevin Morris <kevr.gtalk@gmail.com> --- doc/rpc.txt | 24 ++++++--- web/lib/aurjson.class.php | 105 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 116 insertions(+), 13 deletions(-)