Fork of Pleroma with site-specific changes and feature branches https://git.pleroma.social/pleroma/pleroma
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

100 lines
2.8KB

  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. defmodule Pleroma.Repo do
  5. use Ecto.Repo,
  6. otp_app: :pleroma,
  7. adapter: Ecto.Adapters.Postgres,
  8. migration_timestamps: [type: :naive_datetime_usec]
  9. use Ecto.Explain
  10. import Ecto.Query
  11. require Logger
  12. defmodule Instrumenter, do: use(Prometheus.EctoInstrumenter)
  13. @doc """
  14. Dynamically loads the repository url from the
  15. DATABASE_URL environment variable.
  16. """
  17. def init(_, opts) do
  18. {:ok, Keyword.put(opts, :url, System.get_env("DATABASE_URL"))}
  19. end
  20. @doc "find resource based on prepared query"
  21. @spec find_resource(Ecto.Query.t()) :: {:ok, struct()} | {:error, :not_found}
  22. def find_resource(%Ecto.Query{} = query) do
  23. case __MODULE__.one(query) do
  24. nil -> {:error, :not_found}
  25. resource -> {:ok, resource}
  26. end
  27. end
  28. def find_resource(_query), do: {:error, :not_found}
  29. @doc """
  30. Gets association from cache or loads if need
  31. ## Examples
  32. iex> Repo.get_assoc(token, :user)
  33. %User{}
  34. """
  35. @spec get_assoc(struct(), atom()) :: {:ok, struct()} | {:error, :not_found}
  36. def get_assoc(resource, association) do
  37. case __MODULE__.preload(resource, association) do
  38. %{^association => assoc} when not is_nil(assoc) -> {:ok, assoc}
  39. _ -> {:error, :not_found}
  40. end
  41. end
  42. @doc """
  43. Returns a lazy enumerable that emits all entries from the data store matching the given query.
  44. `returns_as` use to group records. use the `batches` option to fetch records in bulk.
  45. ## Examples
  46. # fetch records one-by-one
  47. iex> Pleroma.Repo.chunk_stream(Pleroma.Activity.Queries.by_actor(ap_id), 500)
  48. # fetch records in bulk
  49. iex> Pleroma.Repo.chunk_stream(Pleroma.Activity.Queries.by_actor(ap_id), 500, :batches)
  50. """
  51. @spec chunk_stream(Ecto.Query.t(), integer(), atom()) :: Enumerable.t()
  52. def chunk_stream(query, chunk_size, returns_as \\ :one, query_options \\ []) do
  53. # We don't actually need start and end functions of resource streaming,
  54. # but it seems to be the only way to not fetch records one-by-one and
  55. # have individual records be the elements of the stream, instead of
  56. # lists of records
  57. Stream.resource(
  58. fn -> 0 end,
  59. fn
  60. last_id ->
  61. query
  62. |> order_by(asc: :id)
  63. |> where([r], r.id > ^last_id)
  64. |> limit(^chunk_size)
  65. |> all(query_options)
  66. |> case do
  67. [] ->
  68. {:halt, last_id}
  69. records ->
  70. last_id = List.last(records).id
  71. if returns_as == :one do
  72. {records, last_id}
  73. else
  74. {[records], last_id}
  75. end
  76. end
  77. end,
  78. fn _ -> :ok end
  79. )
  80. end
  81. end