021552f7b718d31f069984bca657ae3215722756
[akkoma] / 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
17 {:ok, _} =
18 meili_post(
19 "/indexes/objects/settings/ranking-rules",
20 [
21 "desc(published)",
22 "words",
23 "exactness",
24 "proximity",
25 "wordsPosition",
26 "typo",
27 "attribute"
28 ]
29 )
30
31 {:ok, _} =
32 meili_post(
33 "/indexes/objects/settings/searchable-attributes",
34 [
35 "content"
36 ]
37 )
38
39 IO.puts("Created indices. Starting to insert posts.")
40
41 chunk_size = Pleroma.Config.get([Pleroma.Search.Meilisearch, :initial_indexing_chunk_size])
42
43 Pleroma.Repo.transaction(
44 fn ->
45 query =
46 from(Pleroma.Object,
47 # Only index public and unlisted posts which are notes and have some text
48 where:
49 fragment("data->>'type' = 'Note'") and
50 (fragment("data->'to' \\? ?", ^Pleroma.Constants.as_public()) or
51 fragment("data->'cc' \\? ?", ^Pleroma.Constants.as_public())),
52 order_by: [desc: fragment("data->'published'")]
53 )
54
55 count = query |> Pleroma.Repo.aggregate(:count, :data)
56 IO.puts("Entries to index: #{count}")
57
58 Pleroma.Repo.stream(
59 query,
60 timeout: :infinity
61 )
62 |> Stream.map(&Pleroma.Search.Meilisearch.object_to_search_data/1)
63 |> Stream.filter(fn o -> not is_nil(o) end)
64 |> Stream.chunk_every(chunk_size)
65 |> Stream.transform(0, fn objects, acc ->
66 new_acc = acc + Enum.count(objects)
67
68 # Reset to the beginning of the line and rewrite it
69 IO.write("\r")
70 IO.write("Indexed #{new_acc} entries")
71
72 {[objects], new_acc}
73 end)
74 |> Stream.each(fn objects ->
75 result =
76 meili_put(
77 "/indexes/objects/documents",
78 objects
79 )
80
81 with {:ok, res} <- result do
82 if not Map.has_key?(res, "updateId") do
83 IO.puts("\nFailed to index: #{inspect(result)}")
84 end
85 else
86 e -> IO.puts("\nFailed to index due to network error: #{inspect(e)}")
87 end
88 end)
89 |> Stream.run()
90 end,
91 timeout: :infinity
92 )
93
94 IO.write("\n")
95 end
96
97 def run(["clear"]) do
98 start_pleroma()
99
100 meili_delete!("/indexes/objects/documents")
101 end
102
103 def run(["show-private-key", master_key]) do
104 start_pleroma()
105
106 endpoint = Pleroma.Config.get([Pleroma.Search.Meilisearch, :url])
107
108 {:ok, result} =
109 Pleroma.HTTP.get(
110 Path.join(endpoint, "/keys"),
111 [{"X-Meili-API-Key", master_key}]
112 )
113
114 decoded = Jason.decode!(result.body)
115
116 if decoded["private"] do
117 IO.puts(decoded["private"])
118 else
119 IO.puts("Error fetching the key, check the master key is correct: #{inspect(decoded)}")
120 end
121 end
122
123 def run(["stats"]) do
124 start_pleroma()
125
126 {:ok, result} = meili_get("/indexes/objects/stats")
127 IO.puts("Number of entries: #{result["numberOfDocuments"]}")
128 IO.puts("Indexing? #{result["isIndexing"]}")
129 end
130 end