giant massive dep upgrade and dialyxir-found error emporium (#371)
[akkoma] / lib / pleroma / search / elasticsearch.ex
index af2e13e48be4640691032d9252aabdc184e33944..20e03e1f0d7c40b6a7e1c44c54fd05cccb7c038f 100644 (file)
@@ -1,78 +1,91 @@
-defmodule Pleroma.Search.Elasticsearch do
-  @behaviour Pleroma.Search
+# Akkoma: A lightweight social networking server
+# Copyright © 2022-2022 Akkoma Authors <https://git.ihatebeinga.live/IHBAGang/akkoma/>
+# SPDX-License-Identifier: AGPL-3.0-only
 
-  alias Pleroma.Web.MastodonAPI.StatusView
+defmodule Pleroma.Search.Elasticsearch do
+  @behaviour Pleroma.Search.SearchBackend
 
-  defp to_es(term) when is_binary(term) do
-    %{
-      match: %{
-        content: %{
-          query: term,
-          operator: "AND"
-        }
-      }
-    }
-  end
+  alias Pleroma.Activity
+  alias Pleroma.Object.Fetcher
+  alias Pleroma.Web.ActivityPub.Visibility
+  alias Pleroma.Search.Elasticsearch.Parsers
 
-  defp to_es({:quoted, term}), do: to_es(term)
+  def es_query(:activity, query, offset, limit) do
+    must = Parsers.Activity.parse(query)
 
-  defp to_es({:filter, ["hashtag", query]}) do
     %{
-      term: %{
-        hashtags: %{
-          value: query
+      size: limit,
+      from: offset,
+      terminate_after: 50,
+      timeout: "5s",
+      sort: [
+        "_score",
+        %{"_timestamp" => %{order: "desc", format: "basic_date_time"}}
+      ],
+      query: %{
+        bool: %{
+          must: must
         }
       }
     }
   end
 
-  defp to_es({:filter, [field, query]}) do
-    %{
-      term: %{
-        field => %{
-          value: query
-        }
-      }
-    }
+  defp maybe_fetch(:activity, search_query) do
+    with true <- Regex.match?(~r/https?:/, search_query),
+         {:ok, object} <- Fetcher.fetch_object_from_id(search_query),
+         %Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]) do
+      activity
+    else
+      _ -> nil
+    end
   end
 
-  defp parse(query) do
-    query
-    |> SearchParser.parse!()
-    |> Enum.map(&to_es/1)
-  end
+  def search(user, query, options) do
+    limit = Enum.min([Keyword.get(options, :limit), 40])
+    offset = Keyword.get(options, :offset, 0)
 
-  @impl Pleroma.Search
-  def search(%{assigns: %{user: user}} = _conn, %{q: query} = _params, _options) do
-    q = %{
-      query: %{
-        bool: %{
-          must: parse(query)
-        }
-      }
-    }
+    parsed_query =
+      query
+      |> String.trim()
+      |> SearchParser.parse!()
 
-    out = Pleroma.Elasticsearch.search_activities(q)
+    activity_fetch_task =
+      Task.async(fn ->
+        maybe_fetch(:activity, String.trim(query))
+      end)
 
-    with {:ok, raw_results} <- out 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()
+    activity_task =
+      Task.async(fn ->
+        q = es_query(:activity, parsed_query, offset, limit)
 
-      %{
-        "accounts" => [],
-        "hashtags" => [],
-        "statuses" =>
-          StatusView.render("index.json",
-            activities: results,
-            for: user,
-            as: :activity
-          )
-      }
-    end
+        :activities
+        |> Pleroma.Search.Elasticsearch.Store.search(q)
+        |> Enum.filter(fn x ->
+          x.data["type"] == "Create" && x.object.data["type"] == "Note" &&
+            Visibility.visible_for_user?(x, user)
+        end)
+      end)
+
+    activity_results = Task.await(activity_task)
+    direct_activity = Task.await(activity_fetch_task)
+
+    activity_results =
+      if direct_activity == nil do
+        activity_results
+      else
+        [direct_activity | activity_results]
+      end
+
+    activity_results
+  end
+
+  @impl true
+  def add_to_index(activity) do
+    Elasticsearch.put_document(Pleroma.Search.Elasticsearch.Cluster, activity, "activities")
+  end
+
+  @impl true
+  def remove_from_index(object) do
+    Elasticsearch.delete_document(Pleroma.Search.Elasticsearch.Cluster, object, "activities")
   end
 end