2021-08-15 18:53:04 +00:00
|
|
|
# Pleroma: A lightweight social networking server
|
|
|
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
|
|
|
# SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
|
|
|
|
defmodule Mix.Tasks.Pleroma.Search.Meilisearch do
|
2021-08-16 19:24:31 +00:00
|
|
|
require Pleroma.Constants
|
2021-08-15 18:53:04 +00:00
|
|
|
|
2021-08-16 07:18:01 +00:00
|
|
|
import Mix.Pleroma
|
2021-08-15 18:53:04 +00:00
|
|
|
import Ecto.Query
|
|
|
|
|
2021-11-22 18:39:54 +00:00
|
|
|
import Pleroma.Search.Meilisearch,
|
|
|
|
only: [meili_post: 2, meili_put: 2, meili_get: 1, meili_delete!: 1]
|
2021-08-23 16:35:21 +00:00
|
|
|
|
2021-11-22 18:39:54 +00:00
|
|
|
def run(["index"]) do
|
2021-08-15 18:53:04 +00:00
|
|
|
start_pleroma()
|
|
|
|
|
2021-12-20 16:05:59 +00:00
|
|
|
meili_version =
|
|
|
|
(
|
|
|
|
{:ok, result} = meili_get("/version")
|
|
|
|
|
|
|
|
result["pkgVersion"]
|
|
|
|
)
|
|
|
|
|
|
|
|
# The ranking rule syntax was changed but nothing about that is mentioned in the changelog
|
2022-03-22 17:29:17 +00:00
|
|
|
if not Version.match?(meili_version, ">= 0.25.0") do
|
2021-12-20 16:05:59 +00:00
|
|
|
raise "Meilisearch <0.24.0 not supported"
|
|
|
|
end
|
|
|
|
|
2021-11-22 18:39:54 +00:00
|
|
|
{:ok, _} =
|
|
|
|
meili_post(
|
|
|
|
"/indexes/objects/settings/ranking-rules",
|
|
|
|
[
|
2021-12-20 16:05:59 +00:00
|
|
|
"published:desc",
|
2021-11-22 18:39:54 +00:00
|
|
|
"words",
|
|
|
|
"exactness",
|
|
|
|
"proximity",
|
|
|
|
"typo",
|
2021-12-20 16:05:59 +00:00
|
|
|
"attribute",
|
|
|
|
"sort"
|
2021-11-22 18:39:54 +00:00
|
|
|
]
|
|
|
|
)
|
2021-08-16 07:18:01 +00:00
|
|
|
|
2021-11-22 18:39:54 +00:00
|
|
|
{:ok, _} =
|
|
|
|
meili_post(
|
|
|
|
"/indexes/objects/settings/searchable-attributes",
|
|
|
|
[
|
|
|
|
"content"
|
|
|
|
]
|
|
|
|
)
|
|
|
|
|
|
|
|
IO.puts("Created indices. Starting to insert posts.")
|
2021-08-22 15:47:41 +00:00
|
|
|
|
2021-12-20 15:48:52 +00:00
|
|
|
chunk_size = Pleroma.Config.get([Pleroma.Search.Meilisearch, :initial_indexing_chunk_size])
|
2021-08-16 19:30:56 +00:00
|
|
|
|
|
|
|
Pleroma.Repo.transaction(
|
|
|
|
fn ->
|
2021-08-23 17:02:34 +00:00
|
|
|
query =
|
2021-08-16 19:30:56 +00:00
|
|
|
from(Pleroma.Object,
|
2021-11-22 18:39:54 +00:00
|
|
|
# Only index public and unlisted posts which are notes and have some text
|
2021-08-16 19:30:56 +00:00
|
|
|
where:
|
|
|
|
fragment("data->>'type' = 'Note'") and
|
2021-11-22 18:39:54 +00:00
|
|
|
(fragment("data->'to' \\? ?", ^Pleroma.Constants.as_public()) or
|
|
|
|
fragment("data->'cc' \\? ?", ^Pleroma.Constants.as_public())),
|
2021-08-16 21:30:14 +00:00
|
|
|
order_by: [desc: fragment("data->'published'")]
|
2021-08-23 17:02:34 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
count = query |> Pleroma.Repo.aggregate(:count, :data)
|
|
|
|
IO.puts("Entries to index: #{count}")
|
|
|
|
|
|
|
|
Pleroma.Repo.stream(
|
|
|
|
query,
|
2021-08-16 19:30:56 +00:00
|
|
|
timeout: :infinity
|
2021-08-15 18:53:04 +00:00
|
|
|
)
|
2021-08-22 19:53:18 +00:00
|
|
|
|> Stream.map(&Pleroma.Search.Meilisearch.object_to_search_data/1)
|
|
|
|
|> Stream.filter(fn o -> not is_nil(o) end)
|
2021-08-16 19:30:56 +00:00
|
|
|
|> Stream.chunk_every(chunk_size)
|
2021-08-22 16:38:03 +00:00
|
|
|
|> Stream.transform(0, fn objects, acc ->
|
|
|
|
new_acc = acc + Enum.count(objects)
|
|
|
|
|
2021-08-22 20:47:43 +00:00
|
|
|
# Reset to the beginning of the line and rewrite it
|
|
|
|
IO.write("\r")
|
|
|
|
IO.write("Indexed #{new_acc} entries")
|
2021-08-22 16:38:03 +00:00
|
|
|
|
|
|
|
{[objects], new_acc}
|
2021-08-16 19:30:56 +00:00
|
|
|
end)
|
|
|
|
|> Stream.each(fn objects ->
|
2021-08-23 16:35:21 +00:00
|
|
|
result =
|
2021-11-22 18:39:54 +00:00
|
|
|
meili_put(
|
2021-08-23 16:35:21 +00:00
|
|
|
"/indexes/objects/documents",
|
|
|
|
objects
|
2021-08-16 19:30:56 +00:00
|
|
|
)
|
2021-08-16 21:30:14 +00:00
|
|
|
|
2021-11-22 18:39:54 +00:00
|
|
|
with {:ok, res} <- result do
|
2022-03-22 17:45:49 +00:00
|
|
|
if not Map.has_key?(res, "uid") do
|
2021-11-22 18:39:54 +00:00
|
|
|
IO.puts("\nFailed to index: #{inspect(result)}")
|
|
|
|
end
|
|
|
|
else
|
|
|
|
e -> IO.puts("\nFailed to index due to network error: #{inspect(e)}")
|
2021-08-16 21:30:14 +00:00
|
|
|
end
|
2021-08-16 19:30:56 +00:00
|
|
|
end)
|
|
|
|
|> Stream.run()
|
|
|
|
end,
|
|
|
|
timeout: :infinity
|
|
|
|
)
|
2021-08-22 20:47:43 +00:00
|
|
|
|
|
|
|
IO.write("\n")
|
2021-08-15 18:53:04 +00:00
|
|
|
end
|
2021-08-16 19:24:31 +00:00
|
|
|
|
|
|
|
def run(["clear"]) do
|
|
|
|
start_pleroma()
|
|
|
|
|
2021-08-23 16:35:21 +00:00
|
|
|
meili_delete!("/indexes/objects/documents")
|
|
|
|
end
|
|
|
|
|
2022-03-22 17:29:17 +00:00
|
|
|
def run(["show-keys", master_key]) do
|
2021-08-23 16:35:21 +00:00
|
|
|
start_pleroma()
|
|
|
|
|
2021-08-16 19:24:31 +00:00
|
|
|
endpoint = Pleroma.Config.get([Pleroma.Search.Meilisearch, :url])
|
|
|
|
|
2021-08-23 16:35:21 +00:00
|
|
|
{:ok, result} =
|
|
|
|
Pleroma.HTTP.get(
|
|
|
|
Path.join(endpoint, "/keys"),
|
2022-03-22 17:29:17 +00:00
|
|
|
[{"Authorization", "Bearer #{master_key}"}]
|
2021-08-22 16:38:03 +00:00
|
|
|
)
|
2021-08-23 16:35:21 +00:00
|
|
|
|
|
|
|
decoded = Jason.decode!(result.body)
|
|
|
|
|
2022-03-22 17:29:17 +00:00
|
|
|
if decoded["results"] do
|
|
|
|
Enum.each(decoded["results"], fn %{"description" => desc, "key" => key} ->
|
|
|
|
IO.puts("#{desc}: #{key}")
|
|
|
|
end)
|
2021-08-23 16:35:21 +00:00
|
|
|
else
|
2022-03-22 17:29:17 +00:00
|
|
|
IO.puts("Error fetching the keys, check the master key is correct: #{inspect(decoded)}")
|
2021-08-23 16:35:21 +00:00
|
|
|
end
|
2021-08-16 19:24:31 +00:00
|
|
|
end
|
2021-08-23 17:21:46 +00:00
|
|
|
|
|
|
|
def run(["stats"]) do
|
|
|
|
start_pleroma()
|
|
|
|
|
2021-11-22 18:39:54 +00:00
|
|
|
{:ok, result} = meili_get("/indexes/objects/stats")
|
2021-08-23 17:21:46 +00:00
|
|
|
IO.puts("Number of entries: #{result["numberOfDocuments"]}")
|
|
|
|
IO.puts("Indexing? #{result["isIndexing"]}")
|
|
|
|
end
|
2021-08-15 18:53:04 +00:00
|
|
|
end
|