integrate search endpoint with ES
authorFloatingGhost <hannah@coffee-and-dreams.uk>
Sat, 11 Dec 2021 18:48:46 +0000 (18:48 +0000)
committerFloatingGhost <hannah@coffee-and-dreams.uk>
Sat, 11 Dec 2021 18:48:46 +0000 (18:48 +0000)
config/config.exs
lib/pleroma/elasticsearch/document_mappings/note.ex
lib/pleroma/elasticsearch/store.ex
lib/pleroma/web/common_api.ex
lib/pleroma/web/mastodon_api/controllers/search_controller.ex

index 681b498275d90bdc80ebb5bd28b6100075136a54..dea05d276d1a2c1e9f8aba945cddc0e2ad7e64b7 100644 (file)
@@ -851,6 +851,9 @@ config :pleroma, ConcurrentLimiter, [
   {Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy, [max_running: 5, max_waiting: 5]}
 ]
 
+config :pleroma, :search,
+  provider: :builtin
+
 # Import environment specific config. This must remain at the bottom
 # of this file so it overrides the configuration defined above.
 import_config "#{Mix.env()}.exs"
index f4e3307fe547eb47e14fbd1fb66dbfd41e67c6ac..60efde599f93f4de40e509f6136b301bfbbea761 100644 (file)
@@ -4,6 +4,7 @@ defmodule Pleroma.Elasticsearch.DocumentMappings.Activity do
   def id(obj), do: obj.id
   def encode(%{object: %{data: %{ "type" => "Note" }}} = activity) do
     %{
+        _timestamp: activity.inserted_at,
         user: activity.user_actor.nickname,
         content: activity.object.data["content"],
         instance: URI.parse(activity.user_actor.ap_id).host,
index 2ff4bf889eab3670a9c41d4bd95789f6d139383f..d9e9ed1a73057ee21db2e19a36929cf3cc47b422 100644 (file)
@@ -26,11 +26,20 @@ defmodule Pleroma.Elasticsearch do
     end)
     |> List.flatten()
 
-    IO.inspect Elastix.Bulk.post(
+    Elastix.Bulk.post(
         url(),
         d,
         index: "activities",
         type: "activity"
     )
   end
+
+  def search(query) do
+    Elastix.Search.search(
+        url(),
+        "activities",
+        ["activity"],
+        %{query: %{term: %{content: query}}}
+    )
+  end
 end
index 6f685cb7ba570b117d768f34f79a1d1be8441e6c..95ac7b71a8a5394a0cf42b5d70f94b3d071b76c3 100644 (file)
@@ -16,6 +16,8 @@ defmodule Pleroma.Web.CommonAPI do
   alias Pleroma.Web.ActivityPub.Utils
   alias Pleroma.Web.ActivityPub.Visibility
   alias Pleroma.Web.CommonAPI.ActivityDraft
+  alias Pleroma.Elasticsearch
+  alias Pleroma.Config
 
   import Pleroma.Web.Gettext
   import Pleroma.Web.CommonAPI.Utils
@@ -395,9 +397,24 @@ defmodule Pleroma.Web.CommonAPI do
     end
   end
 
+  def maybe_put_into_elasticsearch({:ok, activity}) do
+    if Config.get([:search, :provider]) == :elasticsearch do
+      actor = Pleroma.Activity.user_actor(activity)
+      activity
+      |> Map.put(:user_actor, actor)
+      |> Elasticsearch.put()
+    end
+  end
+
+  def maybe_put_into_elasticsearch(_) do
+    {:ok, :skipped}
+  end
+
   def post(user, %{status: _} = data) do
     with {:ok, draft} <- ActivityDraft.create(user, data) do
-      ActivityPub.create(draft.changes, draft.preview?)
+      activity = ActivityPub.create(draft.changes, draft.preview?)
+      maybe_put_into_elasticsearch(activity)
+      activity
     end
   end
 
index 64b177eb3b11439e143fb4f40e1170c6bfa0d6f8..484a959afcbc4ef7b8d7876631d0a7b9c5e25e9e 100644 (file)
@@ -45,6 +45,43 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
 
   defp do_search(version, %{assigns: %{user: user}} = conn, %{q: query} = params) do
     query = String.trim(query)
+    options = search_options(params, user)
+    if Pleroma.Config.get([:search, :provider]) == :elasticsearch do
+      elasticsearch_search(conn, query, options)
+    else
+      builtin_search(version, conn, params)
+    end
+  end
+
+  defp elasticsearch_search(%{assigns: %{user: user}} = conn, query, options) do
+    with {:ok, raw_results} <- Pleroma.Elasticsearch.search(query) do
+      results = raw_results
+      |> Map.get(:body)
+      |> Map.get("hits")
+      |> Map.get("hits")
+      |> Enum.map(fn result -> result["_id"] end)
+      |> Pleroma.Activity.all_by_ids_with_object()
+      
+      json(
+        conn,
+        %{
+          accounts: [],
+          hashtags: [],
+          statuses: StatusView.render("index.json",
+            activities: results,
+            for: user,
+            as: :activity
+        )}
+      )
+    else
+      {:error, _} ->
+        conn
+        |> put_status(:internal_server_error)
+        |> json(%{error: "Search failed"})
+    end
+  end
+
+  defp builtin_search(version, %{assigns: %{user: user}} = conn, %{q: query} = params) do
     options = search_options(params, user)
     timeout = Keyword.get(Repo.config(), :timeout, 15_000)
     default_values = %{"statuses" => [], "accounts" => [], "hashtags" => []}