142 lines
3.9 KiB
Elixir
142 lines
3.9 KiB
Elixir
|
defmodule Mobilizon.Service.Search.SearchIndex do
|
||
|
@moduledoc """
|
||
|
Search provider for https://search.joinmobilizon.org
|
||
|
"""
|
||
|
|
||
|
alias Mobilizon.Addresses.Address
|
||
|
alias Mobilizon.Actors.Actor
|
||
|
alias Mobilizon.Events.{Categories, Event, EventOptions}
|
||
|
alias Mobilizon.Service.HTTP.GeospatialClient
|
||
|
alias Mobilizon.Service.Search.Provider
|
||
|
alias Mobilizon.Storage.Page
|
||
|
import Plug.Conn.Query, only: [encode: 1]
|
||
|
|
||
|
@behaviour Provider
|
||
|
|
||
|
@default_endpoint "https://search.joinmobilizon.org"
|
||
|
|
||
|
@events_api_endpoint "/api/v1/search/events"
|
||
|
@groups_api_endpoint "/api/v1/search/groups"
|
||
|
|
||
|
@default_options [
|
||
|
start: 0,
|
||
|
count: 10,
|
||
|
distance: "10_km"
|
||
|
]
|
||
|
|
||
|
# "?search=test&startDateMin=2022-04-21T16:08:32.675Z&boostLanguages[]=fr&boostLanguages[]=en&distance=10_km&sort=-match&start=0&count=5"
|
||
|
|
||
|
@doc """
|
||
|
Returns a paginated list of events
|
||
|
"""
|
||
|
@impl Provider
|
||
|
def search(options) do
|
||
|
case fetch(options) do
|
||
|
{:ok, %Tesla.Env{body: body, status: 200}} ->
|
||
|
{:ok, transform_results(body, Keyword.get(options, :type, :events))}
|
||
|
|
||
|
err ->
|
||
|
require Logger
|
||
|
Logger.error(inspect(err))
|
||
|
{:error, :http_error}
|
||
|
end
|
||
|
end
|
||
|
|
||
|
defp fetch(options) do
|
||
|
@default_options
|
||
|
|> Keyword.merge(options)
|
||
|
|> build_url()
|
||
|
|> GeospatialClient.get()
|
||
|
end
|
||
|
|
||
|
@spec transform_results(%{String.t() => list(map()) | non_neg_integer()}, atom()) ::
|
||
|
Page.t(Event.t() | Actor.t())
|
||
|
defp transform_results(%{"data" => data, "total" => total}, type) do
|
||
|
%Page{
|
||
|
total: total,
|
||
|
elements:
|
||
|
data
|
||
|
|> Enum.sort(&(&1["score"] >= &2["score"]))
|
||
|
|> Enum.map(fn element -> transform_result(element, type) end)
|
||
|
}
|
||
|
end
|
||
|
|
||
|
@spec transform_result(map(), :events | :groups) :: Event.t() | Actor.t()
|
||
|
defp transform_result(event, :events) do
|
||
|
%Event{
|
||
|
category: Categories.get_category(event["category"]),
|
||
|
organizer_actor: transform_actor(event["creator"]),
|
||
|
ends_on: event["endTime"],
|
||
|
attributed_to: transform_actor(event["group"], :Group),
|
||
|
id: event["id"],
|
||
|
join_options: event["joinMode"],
|
||
|
options: %EventOptions{
|
||
|
is_online: event["isOnline"],
|
||
|
maximum_attendee_capacity: event["maximumAttendeeCapacity"]
|
||
|
},
|
||
|
physical_address: transform_address(event["location"]),
|
||
|
title: event["name"],
|
||
|
publish_at: event["published"],
|
||
|
begins_on: event["startTime"],
|
||
|
status: String.downcase(event["status"]),
|
||
|
tags: event["tags"],
|
||
|
uuid: event["uuid"],
|
||
|
url: event["url"]
|
||
|
}
|
||
|
end
|
||
|
|
||
|
defp transform_result(group, :groups) do
|
||
|
group = transform_actor(group)
|
||
|
|
||
|
%Actor{
|
||
|
group
|
||
|
| type: :Group,
|
||
|
physical_address: transform_address(group["location"])
|
||
|
}
|
||
|
end
|
||
|
|
||
|
defp transform_actor(actor, type \\ :Person) do
|
||
|
%Actor{
|
||
|
type: type,
|
||
|
id: actor["id"],
|
||
|
url: actor["url"],
|
||
|
summary: actor["description"],
|
||
|
preferred_username: actor["name"],
|
||
|
name: actor["displayName"],
|
||
|
domain: actor["host"]
|
||
|
}
|
||
|
end
|
||
|
|
||
|
defp transform_address(address) when is_map(address) do
|
||
|
%Address{
|
||
|
description: address["name"],
|
||
|
geom: %Geo.Point{
|
||
|
coordinates: {address["location"]["lon"], address["location"]["lat"]},
|
||
|
srid: 4326
|
||
|
},
|
||
|
country: address["address"]["addressCountry"],
|
||
|
locality: address["address"]["addressLocality"],
|
||
|
region: address["address"]["addressRegion"],
|
||
|
postal_code: address["address"]["postalCode"],
|
||
|
street: address["address"]["streetAddress"]
|
||
|
}
|
||
|
end
|
||
|
|
||
|
defp transform_address(_), do: nil
|
||
|
|
||
|
@spec endpoint :: String.t()
|
||
|
defp endpoint do
|
||
|
:mobilizon
|
||
|
|> Application.get_env(__MODULE__, [])
|
||
|
|> Keyword.get(:endpoint, @default_endpoint)
|
||
|
end
|
||
|
|
||
|
defp build_url(options) do
|
||
|
{type, options} = Keyword.pop(options, :type, :events)
|
||
|
"#{endpoint()}#{api_endpoint(type)}?#{encode(options)}"
|
||
|
end
|
||
|
|
||
|
defp api_endpoint(:events), do: @events_api_endpoint
|
||
|
defp api_endpoint(:groups), do: @groups_api_endpoint
|
||
|
end
|