Merge remote-tracking branch 'upstream/develop' into develop
[akkoma] / lib / pleroma / search / elasticsearch.ex
1 defmodule Pleroma.Search.Elasticsearch do
2 @behaviour Pleroma.Search
3
4 alias Pleroma.Activity
5 alias Pleroma.Object.Fetcher
6 alias Pleroma.Web.MastodonAPI.StatusView
7 alias Pleroma.Web.MastodonAPI.AccountView
8 alias Pleroma.Web.ActivityPub.Visibility
9 alias Pleroma.Search.Elasticsearch.Parsers
10 alias Pleroma.Web.Endpoint
11
12 def es_query(:activity, query) do
13 must = Parsers.Activity.parse(query)
14
15 if must == [] do
16 :skip
17 else
18 %{
19 size: 50,
20 terminate_after: 50,
21 timeout: "5s",
22 sort: [
23 "_score",
24 %{_timestamp: %{order: "desc", format: "basic_date_time"}}
25 ],
26 query: %{
27 bool: %{
28 must: must
29 }
30 }
31 }
32 end
33 end
34
35 def es_query(:user, query) do
36 must = Parsers.User.parse(query)
37
38 if must == [] do
39 :skip
40 else
41 %{
42 size: 50,
43 terminate_after: 50,
44 timeout: "5s",
45 sort: [
46 "_score"
47 ],
48 query: %{
49 bool: %{
50 must: must
51 }
52 }
53 }
54 end
55 end
56
57 def es_query(:hashtag, query) do
58 must = Parsers.Hashtag.parse(query)
59
60 if must == [] do
61 :skip
62 else
63 %{
64 size: 50,
65 terminate_after: 50,
66 timeout: "5s",
67 sort: [
68 "_score"
69 ],
70 query: %{
71 bool: %{
72 must: Parsers.Hashtag.parse(query)
73 }
74 }
75 }
76 end
77 end
78
79 defp maybe_fetch(:activity, search_query) do
80 with true <- Regex.match?(~r/https?:/, search_query),
81 {:ok, object} <- Fetcher.fetch_object_from_id(search_query),
82 %Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]) do
83 activity
84 else
85 _ -> nil
86 end
87 end
88
89 @impl Pleroma.Search
90 def search(%{assigns: %{user: user}} = _conn, %{q: query} = _params, _options) do
91 parsed_query =
92 query
93 |> String.trim()
94 |> SearchParser.parse!()
95
96 activity_fetch_task =
97 Task.async(fn ->
98 maybe_fetch(:activity, String.trim(query))
99 end)
100
101 activity_task =
102 Task.async(fn ->
103 q = es_query(:activity, parsed_query)
104
105 Pleroma.Elasticsearch.search(:activities, q)
106 |> Enum.filter(fn x -> Visibility.visible_for_user?(x, user) end)
107 end)
108
109 user_task =
110 Task.async(fn ->
111 q = es_query(:user, parsed_query)
112
113 Pleroma.Elasticsearch.search(:users, q)
114 |> Enum.filter(fn x -> Pleroma.User.visible_for(x, user) == :visible end)
115 end)
116
117 hashtag_task =
118 Task.async(fn ->
119 q = es_query(:hashtag, parsed_query)
120
121 Pleroma.Elasticsearch.search(:hashtags, q)
122 end)
123
124 activity_results = Task.await(activity_task)
125 user_results = Task.await(user_task)
126 hashtag_results = Task.await(hashtag_task)
127 direct_activity = Task.await(activity_fetch_task)
128
129 activity_results =
130 if direct_activity == nil do
131 activity_results
132 else
133 [direct_activity | activity_results]
134 end
135
136 %{
137 "accounts" =>
138 AccountView.render("index.json",
139 users: user_results,
140 for: user
141 ),
142 "hashtags" =>
143 Enum.map(hashtag_results, fn x ->
144 %{
145 url: Endpoint.url() <> "/tag/" <> x,
146 name: x
147 }
148 end),
149 "statuses" =>
150 StatusView.render("index.json",
151 activities: activity_results,
152 for: user,
153 as: :activity
154 )
155 }
156 end
157 end