summaryrefslogtreecommitdiff
path: root/app/models/project.rb
diff options
context:
space:
mode:
authorYorick Peterse <yorickpeterse@gmail.com>2016-06-08 17:03:51 +0200
committerYorick Peterse <yorickpeterse@gmail.com>2016-06-10 15:33:05 +0200
commit24920bc52a5658dd1d16d38ba3dc46f92dfe7675 (patch)
treee1bb3003b7df81dec147e831334011e5f41cd1c2 /app/models/project.rb
parentcea3cf177c68bb1fa9326d4e88631b7737ae8a98 (diff)
downloadgitlab-ce-finding-multiple-projects-by-paths.tar.gz
Add Project.where_paths_infinding-multiple-projects-by-paths
This method can be used to find multiple projects for multiple paths. For example, take this snippet: Project.where_paths_in(%w{gitlab-org/gitlab-ce gitlab-org/gitlab-ee}) This will return an ActiveRecord::Relation containing the GitLab CE and GitLab EE projects. This method takes care of matching rows both case-sensitively and case-insensitively where needed. Project.find_with_namespace in turn has been modified to use Project.where_paths_in without nuking any scoping (instead it uses reorder(nil)). This means that any default scopes (e.g. those used for "pending_delete" stay intact). The method Project.where_paths_in was added so the various Markdown filters can use a single query to grab all the projects referenced in a set of documents, something Project.find_with_namespace didn't allow.
Diffstat (limited to 'app/models/project.rb')
-rw-r--r--app/models/project.rb71
1 files changed, 60 insertions, 11 deletions
diff --git a/app/models/project.rb b/app/models/project.rb
index f47ef8a81de..7bee7550d6c 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -253,20 +253,69 @@ class Project < ActiveRecord::Base
non_archived.where(table[:name].matches(pattern))
end
- def find_with_namespace(id)
- namespace_path, project_path = id.split('/', 2)
+ # Finds a single project for the given path.
+ #
+ # path - The full project path (including namespace path).
+ #
+ # Returns a Project, or nil if no project could be found.
+ def find_with_namespace(path)
+ where_paths_in([path]).reorder(nil).take
+ end
- return nil if !namespace_path || !project_path
+ # Builds a relation to find multiple projects by their full paths.
+ #
+ # Each path must be in the following format:
+ #
+ # namespace_path/project_path
+ #
+ # For example:
+ #
+ # gitlab-org/gitlab-ce
+ #
+ # Usage:
+ #
+ # Project.where_paths_in(%w{gitlab-org/gitlab-ce gitlab-org/gitlab-ee})
+ #
+ # This would return the projects with the full paths matching the values
+ # given.
+ #
+ # paths - An Array of full paths (namespace path + project path) for which
+ # to find the projects.
+ #
+ # Returns an ActiveRecord::Relation.
+ def where_paths_in(paths)
+ wheres = []
+ cast_lower = Gitlab::Database.postgresql?
+
+ paths.each do |path|
+ namespace_path, project_path = path.split('/', 2)
+
+ next unless namespace_path && project_path
+
+ namespace_path = connection.quote(namespace_path)
+ project_path = connection.quote(project_path)
+
+ where = "(namespaces.path = #{namespace_path}
+ AND projects.path = #{project_path})"
+
+ if cast_lower
+ where = "(
+ #{where}
+ OR (
+ LOWER(namespaces.path) = LOWER(#{namespace_path})
+ AND LOWER(projects.path) = LOWER(#{project_path})
+ )
+ )"
+ end
- # Use of unscoped ensures we're not secretly adding any ORDER BYs, which
- # have a negative impact on performance (and aren't needed for this
- # query).
- projects = unscoped.
- joins(:namespace).
- iwhere('namespaces.path' => namespace_path)
+ wheres << where
+ end
- projects.find_by('projects.path' => project_path) ||
- projects.iwhere('projects.path' => project_path).take
+ if wheres.empty?
+ none
+ else
+ joins(:namespace).where(wheres.join(' OR '))
+ end
end
def visibility_levels