defmodule Ami.Search do use Ecto.Schema import Ecto.{ Query, Changeset } alias Ami.{ # Course, Community, # Tool, Toolkit, CourseSearch, Repo, User # Like } schema "search" do field(:term, :string, virtual: true) field(:types, :string, virtual: true) end def changeset(search, params \\ %{}) do cast(search, params, [:term]) end def kv_options do [ [key: "All", value: "all"], [key: "Courses", value: "courses"], [key: "Tools", value: "tools"], [key: "Toolkits", value: "toolkits"], [key: "Communities", value: "communities"] ] end def search_resources(%{"query" => term, "user_id" => user_id}) do user = Repo.get(User, user_id) courses = case CourseSearch.search_for_user_with_term(user, term) do {:ok, %{courses: course_periods, packages: _packages}} -> course_periods {:error, _} -> [] end tools = __MODULE__.search_tools(term) toolkits = __MODULE__.search_toolkits(term) communities = __MODULE__.search_communities_for_user_with_term(user, term) {:ok, %{courses: courses, tools: tools, toolkits: toolkits, communities: communities}} end def search_resources(%{"query" => term}) do courses = CourseSearch.search_for_user_with_term(term) tools = __MODULE__.search_tools(term) toolkits = __MODULE__.search_toolkits(term) communities = __MODULE__.search_public_universal_communities(term) {:ok, %{courses: courses, tools: tools, toolkits: toolkits, communities: communities}} end def search_tools(term) do Ecto.Adapters.SQL.query!(Ami.Repo, "SELECT * FROM tool_search($1)", [term]) |> result_to_tool_struct end def search_toolkits(term) do Ecto.Adapters.SQL.query!(Ami.Repo, "SELECT * FROM toolkit_search($1)", [term]) |> result_to_toolkit_struct |> Enum.map(&Toolkit.fetch_tools_count(&1)) end def search_public_universal_communities(term) do Ecto.Adapters.SQL.query!(Ami.Repo, "SELECT * FROM community_search($1)", [term]) |> result_to_community_struct |> Enum.filter(&Community.public_or_universal?(&1)) |> Enum.map(&Community.fetch_members_count(&1)) end def search_communities_for_user_with_term(user, term) do Ecto.Adapters.SQL.query!(Ami.Repo, "SELECT * FROM community_search($1)", [term]) |> result_to_community_struct |> keep_user_communities(user) |> Enum.map(&Community.fetch_members_count(&1)) end def search_my_resources(%{"query" => term}, user) do communities = __MODULE__.search_communities_for_user_with_term(user, term) tools = __MODULE__.search_my_tools(user, term) toolkits = __MODULE__.search_my_toolkits(user, term) {:ok, %{tools: tools, toolkits: toolkits, communities: communities}} end def search_my_tools(user, term) do search_tools(term) |> keep_user_tools(user) end def search_my_toolkits(user, term) do search_toolkits(term) |> keep_user_toolkits(user) end def search_my_community( %{"query" => term, "community_id" => community_id}, _user ) do community = Ami.Repo.get(Ami.Community, community_id) |> Ami.Repo.preload([:direct_tools, :direct_toolkits, :tools, toolkits: :tools]) courses = CourseSearch.search_for_community_with_term(community, term) tools = search_tools(term) |> keep_community_tools(community) toolkits = search_toolkits(term) |> keep_community_toolkits(community) {:ok, %{courses: courses, tools: tools, toolkits: toolkits}} end defp result_to_tool_struct(res) do cols = Enum.map(res.columns, &String.to_atom(&1)) Enum.map(res.rows, fn row -> struct(Ami.Tool, Enum.zip(cols, row)) end) end defp result_to_toolkit_struct(res) do cols = Enum.map(res.columns, &String.to_atom(&1)) Enum.map(res.rows, fn row -> struct(Ami.Toolkit, Enum.zip(cols, row)) end) end defp result_to_community_struct(res) do cols = Enum.map(res.columns, &String.to_atom(&1)) Enum.map(res.rows, fn row -> struct(Ami.Community, Enum.zip(cols, row)) end) end defp keep_user_communities(community_list, user) do all_ids_set = Enum.map(community_list, fn c -> c.id end) |> MapSet.new() universal_public_protected_ids_set = Enum.filter(community_list, &Community.universal_or_public_or_protected?(&1)) |> Enum.filter(fn community -> community.is_dormant == false end) |> Enum.map(fn c -> c.id end) |> MapSet.new() user_community_ids_set = Community.get_awaken_communities_for(user) |> Enum.map(fn c -> c.id end) |> MapSet.new() keep_ids = MapSet.intersection(all_ids_set, user_community_ids_set) |> MapSet.union(universal_public_protected_ids_set) |> MapSet.to_list() Repo.all(from(c in Community, where: c.id in ^keep_ids)) end defp keep_user_tools(tools, user) do fetched_tools_ids_set = Enum.map(tools, fn t -> t.id end) |> MapSet.new() community_tool_ids_set = Community.get_awaken_communities_for(user) |> Enum.map(fn comm -> Repo.preload(comm, :tools).tools end) |> List.flatten() |> Enum.uniq() |> Enum.map(fn t -> t.id end) |> MapSet.new() keep_ids = MapSet.intersection(fetched_tools_ids_set, community_tool_ids_set) |> MapSet.to_list() Repo.all(from(t in Ami.Tool, where: t.id in ^keep_ids)) end defp keep_user_toolkits(toolkits, user) do fetched_toolkit_ids_set = Enum.map(toolkits, fn tk -> tk.id end) |> MapSet.new() community_toolkit_ids_set = Community.get_awaken_communities_for(user) |> Enum.map(fn comm -> Repo.preload(comm, :toolkits).toolkits end) |> List.flatten() |> Enum.uniq() |> Enum.map(fn tk -> tk.id end) |> MapSet.new() keep_ids = MapSet.intersection(fetched_toolkit_ids_set, community_toolkit_ids_set) |> MapSet.to_list() Repo.all(from(tk in Ami.Toolkit, where: tk.id in ^keep_ids)) |> Enum.map(&Toolkit.fetch_tools_count(&1)) end defp keep_community_tools(tools, community) do fetched_tools_ids_set = Enum.map(tools, fn t -> t.id end) |> MapSet.new() community_tool_ids = community.tools |> Enum.concat(community.direct_tools) |> Enum.concat(List.flatten(Enum.map(community.toolkits, fn tk -> tk.tools end))) |> Enum.uniq() |> Enum.map(fn t -> t.id end) |> MapSet.new() keep_ids = MapSet.intersection(fetched_tools_ids_set, community_tool_ids) |> MapSet.to_list() Repo.all(from(t in Ami.Tool, where: t.id in ^keep_ids)) end defp keep_community_toolkits(toolkits, community) do fetched_toolkit_ids_set = Enum.map(toolkits, fn tk -> tk.id end) |> MapSet.new() community_toolkit_ids_set = community.direct_toolkits |> Enum.concat(community.toolkits) |> Enum.uniq() |> Enum.map(fn tk -> tk.id end) |> MapSet.new() keep_ids = MapSet.intersection(fetched_toolkit_ids_set, community_toolkit_ids_set) |> MapSet.to_list() Repo.all(from(tk in Ami.Toolkit, where: tk.id in ^keep_ids)) |> Enum.map(&Toolkit.fetch_tools_count(&1)) end end