total rebase
[anni] / lib / mix / tasks / pleroma / search / meilisearch.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Mix.Tasks.Pleroma.Search.Meilisearch do
6   require Pleroma.Constants
7
8   import Mix.Pleroma
9   import Ecto.Query
10
11   import Pleroma.Search.Meilisearch,
12     only: [meili_post: 2, meili_put: 2, meili_get: 1, meili_delete: 1]
13
14   def run(["index"]) do
15     start_pleroma()
16     Pleroma.HTML.compile_scrubbers()
17
18     meili_version =
19       (
20         {:ok, result} = meili_get("/version")
21
22         result["pkgVersion"]
23       )
24
25     # The ranking rule syntax was changed but nothing about that is mentioned in the changelog
26     if not Version.match?(meili_version, ">= 0.25.0") do
27       raise "Meilisearch <0.24.0 not supported"
28     end
29
30     {:ok, _} =
31       meili_post(
32         "/indexes/objects/settings/ranking-rules",
33         [
34           "published:desc",
35           "words",
36           "exactness",
37           "proximity",
38           "typo",
39           "attribute",
40           "sort"
41         ]
42       )
43
44     {:ok, _} =
45       meili_post(
46         "/indexes/objects/settings/searchable-attributes",
47         [
48           "content"
49         ]
50       )
51
52     IO.puts("Created indices. Starting to insert posts.")
53
54     chunk_size = Pleroma.Config.get([Pleroma.Search.Meilisearch, :initial_indexing_chunk_size])
55
56     Pleroma.Repo.transaction(
57       fn ->
58         query =
59           from(Pleroma.Object,
60             # Only index public and unlisted posts which are notes and have some text
61             where:
62               fragment("data->>'type' = 'Note'") and
63                 (fragment("data->'to' \\? ?", ^Pleroma.Constants.as_public()) or
64                    fragment("data->'cc' \\? ?", ^Pleroma.Constants.as_public())),
65             order_by: [desc: fragment("data->'published'")]
66           )
67
68         count = query |> Pleroma.Repo.aggregate(:count, :data)
69         IO.puts("Entries to index: #{count}")
70
71         Pleroma.Repo.stream(
72           query,
73           timeout: :infinity
74         )
75         |> Stream.map(&Pleroma.Search.Meilisearch.object_to_search_data/1)
76         |> Stream.filter(fn o -> not is_nil(o) end)
77         |> Stream.chunk_every(chunk_size)
78         |> Stream.transform(0, fn objects, acc ->
79           new_acc = acc + Enum.count(objects)
80
81           # Reset to the beginning of the line and rewrite it
82           IO.write("\r")
83           IO.write("Indexed #{new_acc} entries")
84
85           {[objects], new_acc}
86         end)
87         |> Stream.each(fn objects ->
88           result =
89             meili_put(
90               "/indexes/objects/documents",
91               objects
92             )
93
94           with {:ok, res} <- result do
95             if not Map.has_key?(res, "uid") do
96               IO.puts("\nFailed to index: #{inspect(result)}")
97             end
98           else
99             e -> IO.puts("\nFailed to index due to network error: #{inspect(e)}")
100           end
101         end)
102         |> Stream.run()
103       end,
104       timeout: :infinity
105     )
106
107     IO.write("\n")
108   end
109
110   def run(["clear"]) do
111     start_pleroma()
112
113     meili_delete("/indexes/objects/documents")
114   end
115
116   def run(["show-keys", master_key]) do
117     start_pleroma()
118
119     endpoint = Pleroma.Config.get([Pleroma.Search.Meilisearch, :url])
120
121     {:ok, result} =
122       Pleroma.HTTP.get(
123         Path.join(endpoint, "/keys"),
124         [{"Authorization", "Bearer #{master_key}"}]
125       )
126
127     decoded = Jason.decode!(result.body)
128
129     if decoded["results"] do
130       Enum.each(decoded["results"], fn %{"description" => desc, "key" => key} ->
131         IO.puts("#{desc}: #{key}")
132       end)
133     else
134       IO.puts("Error fetching the keys, check the master key is correct: #{inspect(decoded)}")
135     end
136   end
137
138   def run(["stats"]) do
139     start_pleroma()
140
141     {:ok, result} = meili_get("/indexes/objects/stats")
142     IO.puts("Number of entries: #{result["numberOfDocuments"]}")
143     IO.puts("Indexing? #{result["isIndexing"]}")
144   end
145 end