mirror of
https://github.com/ash-project/ash_hq.git
synced 2024-09-19 21:03:30 +12:00
improvement: flatten search results
This commit is contained in:
parent
48bd736190
commit
3535896420
4 changed files with 95 additions and 188 deletions
|
@ -7,6 +7,13 @@ defmodule AshHq.Docs.Extensions.RenderMarkdown.Changes.RenderMarkdown do
|
|||
source = Ash.Changeset.get_attribute(changeset, opts[:source])
|
||||
text = remove_ash_hq_hidden_content(source)
|
||||
|
||||
changeset =
|
||||
if text != source do
|
||||
Ash.Changeset.force_change_attribute(changeset, opts[:source], text)
|
||||
else
|
||||
changeset
|
||||
end
|
||||
|
||||
case AshHq.Docs.Extensions.RenderMarkdown.as_html(
|
||||
text,
|
||||
AshHq.Docs.Extensions.RenderMarkdown.header_ids?(changeset.resource)
|
||||
|
|
|
@ -6,6 +6,7 @@ defmodule AshHq.Docs.Search do
|
|||
|
||||
argument :query, :string do
|
||||
allow_nil?(false)
|
||||
constraints trim?: false, allow_empty?: true
|
||||
end
|
||||
|
||||
argument :library_versions, {:array, :uuid} do
|
||||
|
|
|
@ -2,120 +2,16 @@ defmodule AshHq.Docs.Search.Steps.BuildResults do
|
|||
use Ash.Flow.Step
|
||||
|
||||
def run(input, _opts, _context) do
|
||||
search_results =
|
||||
input
|
||||
|> Map.values()
|
||||
|> List.flatten()
|
||||
|> Enum.sort_by(
|
||||
# false comes first, and we want all things where the name matches to go first
|
||||
&{name_match_rank(&1), -&1.match_rank, Map.get(&1, :extension_order, -1),
|
||||
Enum.count(Map.get(&1, :path, []))}
|
||||
)
|
||||
|
||||
sort_rank =
|
||||
search_results
|
||||
|> Enum.with_index()
|
||||
|> Map.new(fn {item, i} ->
|
||||
{item.id, i}
|
||||
end)
|
||||
|
||||
results =
|
||||
search_results
|
||||
|> Enum.group_by(fn
|
||||
%{extension_type: type} ->
|
||||
type
|
||||
|
||||
%AshHq.Docs.Function{module_name: module_name} ->
|
||||
module_name
|
||||
|
||||
%AshHq.Docs.Module{name: name} ->
|
||||
name
|
||||
|
||||
%AshHq.Docs.Guide{
|
||||
library_version: %{version: version, library_display_name: library_display_name}
|
||||
} ->
|
||||
"#{library_display_name} #{version}"
|
||||
|
||||
%AshHq.Docs.Extension{
|
||||
library_version: %{version: version, library_display_name: library_display_name}
|
||||
} ->
|
||||
"#{library_display_name} #{version}"
|
||||
|
||||
%AshHq.Docs.LibraryVersion{library_display_name: library_display_name, version: version} ->
|
||||
"#{library_display_name} #{version}"
|
||||
|
||||
other ->
|
||||
raise "Nothing matching #{inspect(other)}"
|
||||
end)
|
||||
|> Enum.sort_by(fn {_type, items} ->
|
||||
items
|
||||
|> Enum.map(&Map.get(sort_rank, &1.id))
|
||||
|> Enum.min()
|
||||
end)
|
||||
|> Enum.map(fn {type, items} ->
|
||||
{type, group_by_paths(items, sort_rank)}
|
||||
end)
|
||||
|
||||
item_list = item_list(results)
|
||||
|
||||
{:ok, %{item_list: item_list, results: results}}
|
||||
end
|
||||
|
||||
defp item_list(results) do
|
||||
List.flatten(do_item_list(results))
|
||||
end
|
||||
|
||||
defp do_item_list({_key, %{items: items, further: further}}) do
|
||||
do_item_list(items) ++ do_item_list(further)
|
||||
end
|
||||
|
||||
defp do_item_list(items) when is_list(items) do
|
||||
Enum.map(items, &do_item_list/1)
|
||||
end
|
||||
|
||||
defp do_item_list(item), do: item
|
||||
|
||||
defp group_by_paths(items, sort_rank) do
|
||||
items
|
||||
|> Enum.map(&{Map.get(&1, :path, []), &1})
|
||||
|> do_group_by_paths(sort_rank)
|
||||
end
|
||||
|
||||
defp do_group_by_paths(items, sort_rank, path_acc \\ []) do
|
||||
{items_for_group, further} =
|
||||
Enum.split_with(items, fn
|
||||
{[], _} ->
|
||||
true
|
||||
|
||||
_ ->
|
||||
false
|
||||
end)
|
||||
|
||||
further_items =
|
||||
further
|
||||
|> Enum.group_by(
|
||||
fn {[next | _rest], _item} ->
|
||||
next
|
||||
end,
|
||||
fn {[_next | rest], item} ->
|
||||
{rest, item}
|
||||
end
|
||||
)
|
||||
|> Enum.sort_by(fn {_nested, items} ->
|
||||
items
|
||||
|> Enum.map(&elem(&1, 1))
|
||||
|> Enum.sort_by(&Map.get(sort_rank, &1.id))
|
||||
end)
|
||||
|> Enum.map(fn {nested, items} ->
|
||||
{nested, do_group_by_paths(items, sort_rank, path_acc ++ [nested])}
|
||||
end)
|
||||
|
||||
items =
|
||||
items_for_group
|
||||
|> Enum.map(&elem(&1, 1))
|
||||
|> Enum.sort_by(&Map.get(sort_rank, &1.id))
|
||||
|
||||
%{path: path_acc, items: items, further: further_items}
|
||||
# search_results =
|
||||
{:ok,
|
||||
input
|
||||
|> Map.values()
|
||||
|> List.flatten()
|
||||
|> Enum.sort_by(
|
||||
# false comes first, and we want all things where the name matches to go first
|
||||
&{name_match_rank(&1), -&1.match_rank, Map.get(&1, :extension_order, -1),
|
||||
Enum.count(Map.get(&1, :path, []))}
|
||||
)}
|
||||
end
|
||||
|
||||
defp name_match_rank(record) do
|
||||
|
|
|
@ -4,22 +4,23 @@ defmodule AshHqWeb.Components.Search do
|
|||
require Ash.Query
|
||||
|
||||
alias AshHqWeb.Routes
|
||||
alias AshHqWeb.Components.CalloutText
|
||||
alias Surface.Components.{Form, LiveRedirect}
|
||||
alias Surface.Components.Form.{Checkbox, Label, Select}
|
||||
|
||||
prop(open, :boolean, default: false)
|
||||
prop(close, :event, required: true)
|
||||
prop(libraries, :list, required: true)
|
||||
prop(selected_versions, :map, required: true)
|
||||
prop(change_versions, :event, required: true)
|
||||
prop(selected_types, :list, required: true)
|
||||
prop(change_types, :event, required: true)
|
||||
prop(uri, :string, required: true)
|
||||
prop open, :boolean, default: false
|
||||
prop close, :event, required: true
|
||||
prop libraries, :list, required: true
|
||||
prop selected_versions, :map, required: true
|
||||
prop change_versions, :event, required: true
|
||||
prop selected_types, :list, required: true
|
||||
prop change_types, :event, required: true
|
||||
prop uri, :string, required: true
|
||||
|
||||
data(versions, :map, default: %{})
|
||||
data(search, :string, default: "")
|
||||
data(results, :map, default: %{})
|
||||
data(selected_item, :string)
|
||||
data versions, :map, default: %{}
|
||||
data search, :string, default: ""
|
||||
# data(results, :map, default: %{})
|
||||
data selected_item, :string
|
||||
|
||||
def render(assigns) do
|
||||
~F"""
|
||||
|
@ -94,7 +95,7 @@ defmodule AshHqWeb.Components.Search do
|
|||
</Form>
|
||||
</div>
|
||||
<div class="pl-4 overflow-y-auto col-span-6 md:col-span-7 xl:col-span-8">
|
||||
{render_groups(assigns, @results)}
|
||||
{render_items(assigns, @item_list)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -103,72 +104,73 @@ defmodule AshHqWeb.Components.Search do
|
|||
"""
|
||||
end
|
||||
|
||||
defp render_groups(assigns, results, first? \\ true) do
|
||||
defp render_items(assigns, items) do
|
||||
~F"""
|
||||
{#for {group, results} <- results}
|
||||
<div class={"ml-4": !first?}>
|
||||
{#if first?}
|
||||
<div class="font-medium text-lg">
|
||||
{group}
|
||||
{#for item <- items}
|
||||
<LiveRedirect to={Routes.doc_link(item, @selected_versions)} opts={id: item.id}>
|
||||
<div class={
|
||||
"rounded-lg mb-4 py-2 px-2 hover:bg-gray-400 dark:hover:bg-gray-600",
|
||||
"bg-gray-400 dark:bg-gray-600": @selected_item.id == item.id,
|
||||
"bg-gray-200 dark:bg-gray-800": @selected_item.id != item.id
|
||||
}>
|
||||
<div class="flex justify-between pb-2">
|
||||
<div class="flex flex-row">
|
||||
{#for path_item <- item_path(item)}
|
||||
<Heroicons.Solid.ChevronRightIcon class="h-6 w-6" />
|
||||
<div>
|
||||
{path_item}
|
||||
</div>
|
||||
{/for}
|
||||
<Heroicons.Solid.ChevronRightIcon class="h-6 w-6" />
|
||||
<div class="font-bold text-lg">
|
||||
{#if item.name_matches}
|
||||
<CalloutText>{item_name(item)}</CalloutText>
|
||||
{#else}
|
||||
{item_name(item)}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{item_type(item)}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if Enum.empty?(results.items)}
|
||||
{render_results(assigns, results)}
|
||||
{#else}
|
||||
<div class={"mt-4", "border-l border-gray-700 pl-2": !first?}>
|
||||
{render_results(assigns, results)}
|
||||
<div class="text-gray-700 dark:text-gray-400">
|
||||
{raw(item.search_headline)}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</LiveRedirect>
|
||||
{/for}
|
||||
"""
|
||||
end
|
||||
|
||||
defp render_results(assigns, results) do
|
||||
~F"""
|
||||
<div>
|
||||
<div class="font-medium mb-1">
|
||||
{#if Map.get(results, :path, []) != []}
|
||||
<div class="flex flex-row justify-start align-middle items-center text-center">
|
||||
{#for path_item <- Map.get(results, :path, [])}
|
||||
<Heroicons.Solid.ChevronRightIcon class="h-6 w-6" />
|
||||
<div>
|
||||
{path_item}
|
||||
</div>
|
||||
{/for}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#for item <- results.items}
|
||||
<LiveRedirect to={Routes.doc_link(item, @selected_versions)} opts={id: item.id}>
|
||||
<div class={
|
||||
"rounded-lg mb-4 py-2 px-2 hover:bg-gray-400 dark:hover:bg-gray-600",
|
||||
"bg-gray-400 dark:bg-gray-600": @selected_item.id == item.id,
|
||||
"bg-gray-200 dark:bg-gray-800": @selected_item.id != item.id
|
||||
}>
|
||||
<div class="flex justify-between pb-2">
|
||||
<div>
|
||||
{#if item.__struct__ != AshHq.Docs.LibraryVersion &&
|
||||
item.name != List.last(Map.get(results, :path, []))}
|
||||
{item.name}
|
||||
{/if}
|
||||
{#if item.__struct__ == AshHq.Docs.LibraryVersion}
|
||||
{item.version}
|
||||
{/if}
|
||||
</div>
|
||||
<div>
|
||||
{item_type(item)}
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-gray-700 dark:text-gray-400">
|
||||
{raw(item.search_headline)}
|
||||
</div>
|
||||
</div>
|
||||
</LiveRedirect>
|
||||
{/for}
|
||||
{render_groups(assigns, results.further, false)}
|
||||
</div>
|
||||
"""
|
||||
defp item_name(%{name: name}), do: name
|
||||
defp item_name(%{version: version}), do: version
|
||||
|
||||
defp item_path(%{
|
||||
library_name: library_name,
|
||||
extension_name: extension_name,
|
||||
path: path
|
||||
}) do
|
||||
[library_name, extension_name, path] |> List.flatten()
|
||||
end
|
||||
|
||||
defp item_path(%{
|
||||
library_name: library_name,
|
||||
module_name: module_name
|
||||
}) do
|
||||
[library_name, module_name]
|
||||
end
|
||||
|
||||
defp item_path(%{library_name: library_name}) do
|
||||
[library_name]
|
||||
end
|
||||
|
||||
defp item_path(%{library_version: %{library_name: library_name}}) do
|
||||
[library_name]
|
||||
end
|
||||
|
||||
defp item_path(_) do
|
||||
[]
|
||||
end
|
||||
|
||||
def mount(socket) do
|
||||
|
@ -252,7 +254,8 @@ defmodule AshHqWeb.Components.Search do
|
|||
end)
|
||||
|> Enum.reject(&is_nil/1)
|
||||
|
||||
%{results: results, item_list: item_list} =
|
||||
# %{results: results, item_list: item_list} =
|
||||
item_list =
|
||||
AshHq.Docs.Search.run!(
|
||||
socket.assigns.search,
|
||||
versions,
|
||||
|
@ -262,7 +265,7 @@ defmodule AshHqWeb.Components.Search do
|
|||
selected_item = Enum.at(item_list, 0)
|
||||
|
||||
socket
|
||||
|> assign(:results, results)
|
||||
# |> assign(:results, results)
|
||||
|> assign(:item_list, item_list)
|
||||
|> set_selected_item(selected_item)
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue