diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..03be3dc --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,8 @@ +version: 2 +updates: + - package-ecosystem: mix + directory: "/" + schedule: + interval: daily + time: "02:00" + open-pull-requests-limit: 10 diff --git a/.github/workflows/hex-deploy.yml b/.github/workflows/hex-deploy.yml index 1227ce6..681e966 100644 --- a/.github/workflows/hex-deploy.yml +++ b/.github/workflows/hex-deploy.yml @@ -39,8 +39,15 @@ jobs: run: mix local.hex --force && mix local.rebar --force - name: Install dependencies run: mix deps.get - - name: Set the new version - run: mix version.up + - name: Set the new patch version + if: "contains(github.event.head_commit.message, '[patch]')" + run: mix version.up patch + - name: Set the new minor version + if: "contains(github.event.head_commit.message, '[minor]')" + run: mix version.up minor + - name: Set the new major version + if: "contains(github.event.head_commit.message, '[major]')" + run: mix version.up major - name: Publish to hex.pm run: HEX_API_KEY=${{ secrets.HEX_API_KEY }} mix hex.publish --yes - name: Tag the version diff --git a/README.md b/README.md index e2ada70..e93683c 100644 --- a/README.md +++ b/README.md @@ -232,6 +232,10 @@ config :ravix, Ravix.Test.Store, certificate_file: "/opt/certs/cert.pfx" ``` +## Ecto + +What about querying your RavenDB using Ecto? [Ravix-Ecto](https://github.com/YgorCastor/ravix-ecto) + ## Current State * ~~Configuration Reading~~ diff --git a/lib/connection/commands/get_topology.ex b/lib/connection/commands/get_topology.ex index 0fa1003..6fc95e7 100644 --- a/lib/connection/commands/get_topology.ex +++ b/lib/connection/commands/get_topology.ex @@ -3,7 +3,7 @@ defmodule Ravix.Connection.Commands.GetTopology do Command to fetch the topology data from RavenDB ## Fields - - database_name: The name of the database + - database_name: The name of the database - force_url: Forces the informed url to the new topology """ use Ravix.Documents.Commands.RavenCommand, diff --git a/lib/connection/connection.ex b/lib/connection/connection.ex index 881a3c9..bb65773 100644 --- a/lib/connection/connection.ex +++ b/lib/connection/connection.ex @@ -1,7 +1,5 @@ defmodule Ravix.Connection do - @moduledoc """ - Service to manage the connection with a RavenDB database - """ + @moduledoc false use GenServer require OK @@ -16,7 +14,7 @@ defmodule Ravix.Connection do Receives the reference of a RavenDB store and a initial store state and starts a connection, the connection is registered in the :connections register under the naming StoreModule.Connection - Returns: + Returns: `{:ok, pid}` if the connecion is started `{:error, cause}` if the connection start failed """ @@ -52,7 +50,7 @@ defmodule Ravix.Connection do Triggers a topology update for the specified Ravix.Document.Store, this operation is asynchronous and will be done on background - Returns `:ok` + Returns `:ok` """ @spec update_topology(atom) :: :ok def update_topology(store) do diff --git a/lib/connection/connection_state_manager.ex b/lib/connection/connection_state_manager.ex index 15b55c5..54adc8a 100644 --- a/lib/connection/connection_state_manager.ex +++ b/lib/connection/connection_state_manager.ex @@ -1,7 +1,5 @@ defmodule Ravix.Connection.State.Manager do - @moduledoc """ - Manages the state of a RavenDB Store connection - """ + @moduledoc false require OK require Logger diff --git a/lib/connection/executor/request_executor.ex b/lib/connection/executor/request_executor.ex index 85efd18..31db918 100644 --- a/lib/connection/executor/request_executor.ex +++ b/lib/connection/executor/request_executor.ex @@ -1,11 +1,5 @@ defmodule Ravix.Connection.RequestExecutor do - @moduledoc """ - A process responsible for executing requests to the RavenDB API - - Each RavenDB cluster node has it's own stateful request executor, which - holds how many requests this node executed, the address and infos from the Node and - which requests are being executed in the moment. - """ + @moduledoc false use GenServer use Retry @@ -86,8 +80,8 @@ defmodule Ravix.Connection.RequestExecutor do headers = case conn_state.disable_topology_updates do - false -> headers - true -> [{"Topology-Etag", conn_state.topology_etag}] + true -> headers + false -> [{"Topology-Etag", conn_state.topology_etag}] end execute_for_node(command, node_pid, nil, headers, opts) diff --git a/lib/connection/executor/request_executor_option.ex b/lib/connection/executor/request_executor_option.ex index 5ffee75..30a41ff 100644 --- a/lib/connection/executor/request_executor_option.ex +++ b/lib/connection/executor/request_executor_option.ex @@ -1,4 +1,5 @@ defmodule Ravix.Connection.RequestExecutor.Options do + @moduledoc false alias Ravix.Connection.State, as: ConnectionState def from_connection_state(%ConnectionState{} = conn_state) do diff --git a/lib/connection/executor/supervisor.ex b/lib/connection/executor/supervisor.ex index c542c4b..e14f119 100644 --- a/lib/connection/executor/supervisor.ex +++ b/lib/connection/executor/supervisor.ex @@ -1,10 +1,5 @@ defmodule Ravix.Connection.RequestExecutor.Supervisor do - @moduledoc """ - Supervises the Requests Executors processes - - Each node connection has it own supervised process, so they are completely isolated - from each other. All executors are registered under the :request_executors Registry. - """ + @moduledoc false use DynamicSupervisor require Logger diff --git a/lib/connection/models/node_selector.ex b/lib/connection/models/node_selector.ex index 02b71ba..87c1109 100644 --- a/lib/connection/models/node_selector.ex +++ b/lib/connection/models/node_selector.ex @@ -1,7 +1,5 @@ defmodule Ravix.Connection.NodeSelector do - @moduledoc """ - Strategy to select nodes - """ + @moduledoc false defstruct current_node_index: 0 alias Ravix.Connection.State, as: ConnectionState diff --git a/lib/connection/supervisor.ex b/lib/connection/supervisor.ex index f5c14a6..e2bfe46 100644 --- a/lib/connection/supervisor.ex +++ b/lib/connection/supervisor.ex @@ -1,7 +1,5 @@ defmodule Ravix.Connection.Supervisor do - @moduledoc """ - Supervises and triggers the initialization of a Raven Store - """ + @moduledoc false use Supervisor alias Ravix.Connection diff --git a/lib/documents/commands/data/delete_documents.ex b/lib/documents/commands/data/delete_documents.ex index a1b9b3a..f5e2365 100644 --- a/lib/documents/commands/data/delete_documents.ex +++ b/lib/documents/commands/data/delete_documents.ex @@ -1,4 +1,5 @@ defmodule Ravix.Documents.Commands.Data.DeleteDocument do + @moduledoc false @derive {Jason.Encoder, only: [:Id, :ChangeVector, :Type]} defstruct Id: nil, ChangeVector: nil, diff --git a/lib/documents/commands/data/put_documents.ex b/lib/documents/commands/data/put_documents.ex index 79cba02..eaf7f7b 100644 --- a/lib/documents/commands/data/put_documents.ex +++ b/lib/documents/commands/data/put_documents.ex @@ -1,4 +1,5 @@ defmodule Ravix.Documents.Commands.Data.PutDocument do + @moduledoc false @derive {Jason.Encoder, only: [:Id, :Document, :ChangeVector, :Type]} defstruct Id: nil, Document: nil, diff --git a/lib/documents/protocols/create_request.ex b/lib/documents/protocols/create_request.ex index 4de0072..3acf077 100644 --- a/lib/documents/protocols/create_request.ex +++ b/lib/documents/protocols/create_request.ex @@ -1,7 +1,5 @@ defprotocol Ravix.Documents.Protocols.CreateRequest do - @moduledoc """ - Protocol to define how commands are converted to requests - """ + @moduledoc false @doc """ Creates a request based on the command diff --git a/lib/documents/protocols/to_json.ex b/lib/documents/protocols/to_json.ex index 641d840..477633d 100644 --- a/lib/documents/protocols/to_json.ex +++ b/lib/documents/protocols/to_json.ex @@ -1,4 +1,6 @@ defprotocol Ravix.Documents.Protocols.ToJson do + @moduledoc false + @spec to_json(t) :: any() @fallback_to_any true def to_json(command) diff --git a/lib/documents/session/session.ex b/lib/documents/session/session.ex index 52a40e1..fe7a136 100644 --- a/lib/documents/session/session.ex +++ b/lib/documents/session/session.ex @@ -1,6 +1,6 @@ defmodule Ravix.Documents.Session do @moduledoc """ - A stateful session to execute ravendb commands + A stateful session to execute RavenDB commands """ use GenServer @@ -36,6 +36,25 @@ defmodule Ravix.Documents.Session do ## Returns - `{:ok, results}` - `{:errors, cause}` + + ## Examples + iex> Session.load(session_id, "entity_id") + {:ok, + %{ + "Includes" => %{}, + "Results" => [ + %{ + "@metadata" => %{ + "@change-vector" => "A:6450-HJrwf2z3c0G/FHJPm3zK3w", + "@id" => "f13ffb17-ed7d-43b6-a483-23993db70958", + "@last-modified" => "2022-04-23T11:14:16.4277047Z" + }, + "cat_name" => "Coco", + "id" => "f13ffb17-ed7d-43b6-a483-23993db70958" + } + ], + "already_loaded_ids" => [] + }} """ @spec load(binary(), list() | bitstring(), any, keyword() | nil) :: any def load(session_id, ids, includes \\ nil, opts \\ nil) @@ -58,13 +77,13 @@ defmodule Ravix.Documents.Session do ## Parameters - session_id: the session id - - entity: the document to be deleted + - entity/entity_id: the document to be deleted ## Returns - - `{:ok, updated_state}` + - `{:ok, Ravix.Documents.Session.State}` - `{:error, cause}` """ - @spec delete(binary, map()) :: any + @spec delete(binary, map() | binary()) :: any def delete(session_id, entity) when is_map_key(entity, :id) do delete(session_id, entity.id) end @@ -85,7 +104,7 @@ defmodule Ravix.Documents.Session do - change_vector: the concurrency change vector ## Returns - - `{:ok, updated_session}` + - `{:ok, Ravix.Documents.Session.State}` - `{:error, cause}` """ @spec store(binary(), map(), binary() | nil, binary() | nil) :: any @@ -102,6 +121,22 @@ defmodule Ravix.Documents.Session do @doc """ Persists the session changes to the RavenDB database + + Returns a [RavenDB batch response](https://ravendb.net/docs/article-page/5.3/csharp/client-api/rest-api/document-commands/batch-commands#response-format) + + ## Examples + iex> Session.save_changes(session_id) + {:ok, + %{ + "Results" => [ + %{ + "ChangeVector" => nil, + "Deleted" => true, + "Id" => "3421125e-416a-4bce-bb56-56cb4a7991ae", + "Type" => "DELETE" + } + ] + }} """ @spec save_changes(binary) :: any def save_changes(session_id) do @@ -112,6 +147,8 @@ defmodule Ravix.Documents.Session do @doc """ Fetches the current session state + + Returns a `{:ok, Ravix.Documents.Session.State}` """ @spec fetch_state(binary()) :: {:error, :session_not_found} | {:ok, SessionState.t()} def fetch_state(session_id) do @@ -132,6 +169,8 @@ defmodule Ravix.Documents.Session do - query: The `Ravix.RQL.Query` to be executed - session_id: the session_id - method: The http method + + Returns a RavenDB query response """ @spec execute_query(any, binary, any) :: any def execute_query(query, session_id, method) do @@ -204,7 +243,7 @@ defmodule Ravix.Documents.Session do def handle_call({:delete, id}, _from, %SessionState{} = state) do case SessionManager.delete_document(state, id) do - {:ok, updated_state} -> {:reply, {:ok, id}, updated_state} + {:ok, updated_state} -> {:reply, {:ok, updated_state}, updated_state} {:error, err} -> {:reply, {:error, err}, state} end end diff --git a/lib/documents/store.ex b/lib/documents/store.ex index 3668b43..7543644 100644 --- a/lib/documents/store.ex +++ b/lib/documents/store.ex @@ -58,7 +58,27 @@ defmodule Ravix.Documents.Store do | {:error, {:already_started, pid}} | {:error, term} + @doc """ + Opens a RavenDB local session + + Returns a tuple with `{:ok, uuid}` if successful or `{:error, :not_found}` if the store + is not initialized + + ## Examples + iex> Ravix.Test.Store.open_session + {:ok, "8945c215-dd67-44da-9a64-2916e0a328d9"} + """ @callback open_session() :: {:ok, binary()} + @doc """ + Closes a RavenDB local session + + Returns `:ok` if successful or `{:error, :not_found}` if the session + is not found + + ## Examples + iex> Ravix.Test.Store.close_session("8945c215-dd67-44da-9a64-2916e0a328d9") + :ok + """ @callback close_session(session_id :: binary()) :: :ok | {:error, :not_found} end diff --git a/lib/operations/database_maintenance.ex b/lib/operations/database_maintenance.ex index 01c9b3c..0e4e31d 100644 --- a/lib/operations/database_maintenance.ex +++ b/lib/operations/database_maintenance.ex @@ -9,6 +9,34 @@ defmodule Ravix.Operations.Database.Maintenance do @doc """ Creates a database using the informed request executor + + Options: + - :encrypted = true/false + - :disabled = true/false + - :replication_factor = 1-N + + ## Examples + iex> Ravix.Operations.Database.Maintenance.create_database(Ravix.Test.Store, "test_db") + {:ok, + %{ + "Name" => "test_db", + "NodesAddedTo" => ["http://4e0373cbf5d0:8080"], + "RaftCommandIndex" => 443, + "Topology" => %{ + "ClusterTransactionIdBase64" => "mdO7gPZsMEeslGOxxNfpjA", + "DatabaseTopologyIdBase64" => "0FHV8Uc0jEi94uZQiT00mA", + "DemotionReasons" => %{}, + "DynamicNodesDistribution" => false, + "Members" => ["A"], + "NodesModifiedAt" => "2022-04-23T11:00:06.9470373Z", + "PriorityOrder" => [], + "Promotables" => [], + "PromotablesStatus" => %{}, + "Rehabs" => [], + "ReplicationFactor" => 1, + "Stamp" => %{"Index" => 443, "LeadersTicks" => -2, "Term" => 4} + } + }} """ @spec create_database(atom | pid, nil | binary, keyword) :: {:error, any} | {:ok, any} def create_database(store_or_pid, database_name, opts \\ []) diff --git a/lib/rql/query.ex b/lib/rql/query.ex index 7460189..620de7d 100644 --- a/lib/rql/query.ex +++ b/lib/rql/query.ex @@ -56,7 +56,12 @@ defmodule Ravix.RQL.Query do } @doc """ - Adds a `Ravix.RQL.Tokens.From` to the query + Creates a new query for the informed collection or index + + Returns a `Ravix.RQL.Query` or an `{:error, :query_document_must_be_informed}` if no collection/index was informed + + ## Examples + iex> Ravix.RQL.Query.from("test") """ @spec from(nil | String.t()) :: {:error, :query_document_must_be_informed} | Query.t() def from(nil), do: {:error, :query_document_must_be_informed} @@ -68,9 +73,18 @@ defmodule Ravix.RQL.Query do end @doc """ - Adds a `Ravix.RQL.Tokens.From` to the query with an alias + Creates a new query with an alias for the informed collection or index + + Returns a `Ravix.RQL.Query` or an `{:error, :query_document_must_be_informed}` if no collection/index was informed + + ## Examples + iex> Ravix.RQL.Query.from("test", "t") """ - def from(document, as_alias) do + @spec from(nil | String.t(), String.t()) :: + {:error, :query_document_must_be_informed} | Query.t() + def from(nil, _), do: {:error, :query_document_must_be_informed} + + def from(document, as_alias) when not is_nil(as_alias) do %Query{ from_token: From.from(document), aliases: Map.put(%{}, document, as_alias) @@ -78,16 +92,20 @@ defmodule Ravix.RQL.Query do end @doc """ - Adds a `Ravix.RQL.Tokens.Update` to the query - """ - @spec update(Query.t(), list(map()) | Update.t()) :: Query.t() - def update(%Query{} = query, document_updates) when is_list(document_updates) do - %Query{ - query - | update_token: Update.fields(document_updates) - } - end + Adds an update operation to the informed query, it supports a + `Ravix.RQL.Tokens.Update` token. The token can be created using the following functions: + + `Ravix.RQL.Tokens.Update.set(%Update{}, field, new_value)` to set values + `Ravix.RQL.Tokens.Update.inc(%Update{}, field, value_to_inc)` to inc values + `Ravix.RQL.Tokens.Update.dec(%Update{}, field, value_to_dec)` to dec values + + Returns a `Ravix.RQL.Query` with the update operation + ## Examples + iex> from = Ravix.RQL.Query.from("cats", "c") + iex> update = Ravix.RQL.Query.update(from, set(%Update{}, :cat_name, "Fluffer, the hand-ripper")) + """ + @spec update(Query.t(), Ravix.RQL.Tokens.Update.t()) :: Query.t() def update(%Query{} = query, update) do %Query{ query @@ -96,7 +114,13 @@ defmodule Ravix.RQL.Query do end @doc """ - Adds a `Ravix.RQL.Tokens.Where` to the query + Adds a where operation with a `Ravix.RQL.Tokens.Condition` to the query + + Returns a `Ravix.RQL.Query` with the where condition + + ## Examples + iex> from = Ravix.RQL.Query.from("cats", "c") + iex> where = Ravix.RQL.Query.where(from, equal_to("cat_name", "Meowvius")) """ @spec where(Query.t(), Condition.t()) :: Query.t() def where(%Query{} = query, %Condition{} = condition) do @@ -107,7 +131,13 @@ defmodule Ravix.RQL.Query do end @doc """ - Adds a `Ravix.RQL.Tokens.Select` to the query + Adds a select operation to project fields + + Returns a `Ravix.RQL.Query` with the select condition + + ## Examples + iex> from = Ravix.RQL.Query.from("cats", "c") + iex> select = Ravix.RQL.Query.select(from, ["name", "breed"]) """ @spec select(Query.t(), Select.allowed_select_params()) :: Query.t() def select(%Query{} = query, fields) do @@ -117,6 +147,15 @@ defmodule Ravix.RQL.Query do } end + @doc """ + Adds a select operation to project fields, leveraging the use of RavenDB Functions + + Returns a `Ravix.RQL.Query` with the select condition + + ## Examples + iex> from = Ravix.RQL.Query.from("cats", "c") + iex> select = Ravix.RQL.Query.select_function(from, ooga: "c.name") + """ @spec select_function(Query.t(), Keyword.t()) :: Query.t() def select_function(%Query{} = query, fields) do %Query{ @@ -126,7 +165,14 @@ defmodule Ravix.RQL.Query do end @doc """ - Adds a `Ravix.RQL.Tokens.And` to the query + Adds an `Ravix.RQL.Tokens.And` operation with a `Ravix.RQL.Tokens.Condition` to the query + + Returns a `Ravix.RQL.Query` with the and condition + + ## Examples + iex> from = Ravix.RQL.Query.from("cats", "c") + iex> where = Ravix.RQL.Query.where(from, equal_to("cat_name", "Meowvius")) + iex> and_v = Ravix.RQL.Query.and?(where, equal_to("breed", "Fatto")) """ @spec and?(Query.t(), Condition.t()) :: Query.t() def and?(%Query{} = query, %Condition{} = condition) do @@ -136,6 +182,16 @@ defmodule Ravix.RQL.Query do } end + @doc """ + Adds an negated `Ravix.RQL.Tokens.And` operation with a `Ravix.RQL.Tokens.Condition` to the query + + Returns a `Ravix.RQL.Query` with the and condition + + ## Examples + iex> from = Ravix.RQL.Query.from("cats", "c") + iex> where = Ravix.RQL.Query.where(from, equal_to("cat_name", "Meowvius")) + iex> and_v = Ravix.RQL.Query.and_not(where, equal_to("breed", "Fatto")) + """ @spec and_not(Query.t(), Condition.t()) :: Query.t() def and_not(%Query{} = query, %Condition{} = condition) do %Query{ @@ -144,6 +200,34 @@ defmodule Ravix.RQL.Query do } end + @doc """ + Adds an `Ravix.RQL.Tokens.Or` operation with a `Ravix.RQL.Tokens.Condition` to the query + + Returns a `Ravix.RQL.Query` with the and condition + + ## Examples + iex> from = Ravix.RQL.Query.from("cats", "c") + iex> where = Ravix.RQL.Query.where(from, equal_to("cat_name", "Meowvius")) + iex> or_v = Ravix.RQL.Query.or?(where, equal_to("breed", "Fatto")) + """ + @spec or?(Query.t(), Condition.t()) :: Query.t() + def or?(%Query{} = query, %Condition{} = condition) do + %Query{ + query + | or_tokens: query.or_tokens ++ [Or.condition(condition)] + } + end + + @doc """ + Adds a negated `Ravix.RQL.Tokens.Or` operation with a `Ravix.RQL.Tokens.Condition` to the query + + Returns a `Ravix.RQL.Query` with the and condition + + ## Examples + iex> from = Ravix.RQL.Query.from("cats", "c") + iex> where = Ravix.RQL.Query.where(from, equal_to("cat_name", "Meowvius")) + iex> or_v = Ravix.RQL.Query.or_not(where, equal_to("breed", "Fatto")) + """ @spec or_not(Query.t(), Condition.t()) :: Query.t() def or_not(%Query{} = query, %Condition{} = condition) do %Query{ @@ -152,6 +236,15 @@ defmodule Ravix.RQL.Query do } end + @doc """ + Adds a `Ravix.RQL.Tokens.Group` operation to the query + + Returns a `Ravix.RQL.Query` with the group_by condition + + ## Examples + iex> from = Ravix.RQL.Query.from("cats", "c") + iex> grouped = Ravix.RQL.Query.group_by(from, "breed") + """ @spec group_by(Query.t(), String.t() | [String.t()]) :: Query.t() def group_by(%Query{} = query, fields) do %Query{ @@ -161,16 +254,14 @@ defmodule Ravix.RQL.Query do end @doc """ - Adds a `Ravix.RQL.Tokens.Or` to the query - """ - @spec or?(Query.t(), Condition.t()) :: Query.t() - def or?(%Query{} = query, %Condition{} = condition) do - %Query{ - query - | or_tokens: query.or_tokens ++ [Or.condition(condition)] - } - end + Adds a `Ravix.RQL.Tokens.Limit` operation to the query + + Returns a `Ravix.RQL.Query` with the limit condition + ## Examples + iex> from = Ravix.RQL.Query.from("cats", "c") + iex> limit = Ravix.RQL.Query.limit(from, 5, 10) + """ @spec limit(Query.t(), non_neg_integer, non_neg_integer) :: Query.t() def limit(%Query{} = query, skip, next) do %Query{ @@ -179,6 +270,15 @@ defmodule Ravix.RQL.Query do } end + @doc """ + Adds a `Ravix.RQL.Tokens.Order` operation to the query + + Returns a `Ravix.RQL.Query` with the ordering condition + + ## Examples + iex> from = Ravix.RQL.Query.from("cats", "c") + iex> ordered = Ravix.RQL.Query.order_by(from, [{"@metadata.@last-modified", :desc}, {"name", :asc}]) + """ @spec order_by( Query.t(), [{:asc, String.t()} | {:desc, String.t()}, ...] @@ -194,6 +294,11 @@ defmodule Ravix.RQL.Query do @doc """ Create a Query using a raw RQL string + + Returns a `Ravix.RQL.Query` with the raw query + + ## Examples + iex> raw = Ravix.RQL.Query.raw("from @all_docs where cat_name = \"Fluffers\"") """ @spec raw(String.t()) :: Query.t() def raw(raw_query) do @@ -204,7 +309,12 @@ defmodule Ravix.RQL.Query do end @doc """ - Create a Query using a raw RQL string with parameters + Create a Query using a raw RQL string with replaceable placeholders + + Returns a `Ravix.RQL.Query` with the raw query and parameters + + ## Examples + iex> raw = Ravix.RQL.Query.raw("from @all_docs where cat_name = $p1", %{p1: "Fluffers"}) """ @spec raw(String.t(), map()) :: Query.t() def raw(raw_query, params) do @@ -216,7 +326,43 @@ defmodule Ravix.RQL.Query do end @doc """ - Executes a list query in the informed session + Executes the query in the informed session and returns the matched documents + + Returns a [RavenDB response](https://ravendb.net/docs/article-page/4.2/java/client-api/rest-api/queries/query-the-database#response-format) map + + ## Examples + iex> from("Cats") + |> select("name") + |> where(equal_to("name", cat.name)) + |> list_all(session_id) + {:ok, %{ + "DurationInMs" => 62, + "IncludedPaths" => nil, + "Includes" => %{}, + "IndexName" => "Auto/Cats/By@metadata.@last-modifiedAndidAndname", + "IndexTimestamp" => "2022-04-22T20:03:03.8373804", + "IsStale" => false, + "LastQueryTime" => "2022-04-22T20:03:04.3475275", + "LongTotalResults" => 1, + "NodeTag" => "A", + "ResultEtag" => 6489530344045176783, + "Results" => [ + %{ + "@metadata" => %{ + "@change-vector" => "A:6445-HJrwf2z3c0G/FHJPm3zK3w", + "@id" => "beee79e2-2560-408c-a680-253e9bd7d12e", + "@index-score" => 3.079441547393799, + "@last-modified" => "2022-04-22T20:03:03.7477980Z", + "@projection" => true + }, + "name" => "Lily" + } + ], + "ScannedResults" => 0, + "SkippedResults" => 0, + "TotalResults" => 1 + } + } """ @spec list_all(Query.t(), binary) :: {:error, any} | {:ok, any} def list_all(%Query{} = query, session_id) do @@ -224,7 +370,15 @@ defmodule Ravix.RQL.Query do end @doc """ - Delete all the documents that matches the informed query + Executes the delete query in the informed session + + Returns a [RavenDB response](https://ravendb.net/docs/article-page/5.3/java/client-api/rest-api/queries/delete-by-query#response-format) map + + ## Examples + iex> from("@all_docs") + |> where(equal_to("cat_name", any_entity.cat_name)) + |> delete_for(session_id) + {:ok, %{"OperationId" => 2480, "OperationNodeTag" => "A"}} """ @spec delete_for(Query.t(), binary) :: {:error, any} | {:ok, any} def delete_for(%Query{} = query, session_id) do @@ -232,7 +386,16 @@ defmodule Ravix.RQL.Query do end @doc """ - Updates all the documents that matches the informed query + Executes the patch query in the informed session + + Returns a [RavenDB response](https://ravendb.net/docs/article-page/5.3/java/client-api/rest-api/queries/patch-by-query#response-format) map + + ## Examples + iex> from("@all_docs", "a") + |> update(set(%Update{}, :cat_name, "Fluffer, the hand-ripper")) + |> where(equal_to("cat_name", any_entity.cat_name)) + |> update_for(session_id) + {:ok, %{"OperationId" => 2480, "OperationNodeTag" => "A"}} """ @spec update_for(Query.t(), binary) :: {:error, any} | {:ok, any} def update_for(%Query{} = query, session_id) do diff --git a/lib/rql/query_parser.ex b/lib/rql/query_parser.ex index 7a3ff0f..5ffbd7c 100644 --- a/lib/rql/query_parser.ex +++ b/lib/rql/query_parser.ex @@ -1,7 +1,5 @@ defmodule Ravix.RQL.QueryParser do - @moduledoc """ - Parsing tokens to RQL - """ + @moduledoc false require OK alias Ravix.RQL.Query diff --git a/lib/rql/tokens/and.ex b/lib/rql/tokens/and.ex index 0742e46..f491bcf 100644 --- a/lib/rql/tokens/and.ex +++ b/lib/rql/tokens/and.ex @@ -1,4 +1,5 @@ defmodule Ravix.RQL.Tokens.And do + @moduledoc false defstruct [ :token, :condition diff --git a/lib/rql/tokens/conditions.ex b/lib/rql/tokens/conditions.ex index e390808..6506b09 100644 --- a/lib/rql/tokens/conditions.ex +++ b/lib/rql/tokens/conditions.ex @@ -1,4 +1,7 @@ defmodule Ravix.RQL.Tokens.Condition do + @moduledoc """ + Supported RQL Conditions + """ defstruct [ :token, :field, @@ -13,7 +16,16 @@ defmodule Ravix.RQL.Tokens.Condition do params: list() } - @spec greater_than(String.t(), any) :: Condition.t() + @doc """ + Greater than condition + + Returns a `Ravix.RQL.Tokens.Condition` + + ## Examples + iex> import Ravix.RQL.Tokens.Condition + iex> greater_than("field", 10) + """ + @spec greater_than(atom() | String.t(), number()) :: Condition.t() def greater_than(field_name, value) do %Condition{ token: :greater_than, @@ -22,7 +34,16 @@ defmodule Ravix.RQL.Tokens.Condition do } end - @spec greater_than_or_equal_to(String.t(), any) :: Condition.t() + @doc """ + Greater than or equal to condition + + Returns a `Ravix.RQL.Tokens.Condition` + + ## Examples + iex> import Ravix.RQL.Tokens.Condition + iex> greater_than_or_equal_to("field", 10) + """ + @spec greater_than_or_equal_to(atom() | String.t(), number()) :: Condition.t() def greater_than_or_equal_to(field_name, value) do %Condition{ token: :greater_than_or_eq, @@ -31,7 +52,16 @@ defmodule Ravix.RQL.Tokens.Condition do } end - @spec lower_than(String.t(), any) :: Condition.t() + @doc """ + Lower than condition + + Returns a `Ravix.RQL.Tokens.Condition` + + ## Examples + iex> import Ravix.RQL.Tokens.Condition + iex> lower_than("field", 10) + """ + @spec lower_than(atom() | String.t(), number()) :: Condition.t() def lower_than(field_name, value) do %Condition{ token: :lower_than, @@ -40,7 +70,16 @@ defmodule Ravix.RQL.Tokens.Condition do } end - @spec lower_than_or_equal_to(String.t(), any) :: Condition.t() + @doc """ + Lower than or equal to condition + + Returns a `Ravix.RQL.Tokens.Condition` + + ## Examples + iex> import Ravix.RQL.Tokens.Condition + iex> lower_than_or_equal_to("field", 10) + """ + @spec lower_than_or_equal_to(atom() | String.t(), number()) :: Condition.t() def lower_than_or_equal_to(field_name, value) do %Condition{ token: :lower_than_or_eq, @@ -49,7 +88,16 @@ defmodule Ravix.RQL.Tokens.Condition do } end - @spec equal_to(String.t(), any) :: Condition.t() + @doc """ + Equal to condition + + Returns a `Ravix.RQL.Tokens.Condition` + + ## Examples + iex> import Ravix.RQL.Tokens.Condition + iex> equal_to("field", "value") + """ + @spec equal_to(atom() | String.t(), any) :: Condition.t() def equal_to(field_name, value) do %Condition{ token: :eq, @@ -58,7 +106,16 @@ defmodule Ravix.RQL.Tokens.Condition do } end - @spec not_equal_to(String.t(), any) :: Condition.t() + @doc """ + Not Equal to condition + + Returns a `Ravix.RQL.Tokens.Condition` + + ## Examples + iex> import Ravix.RQL.Tokens.Condition + iex> not_equal_to("field", "value") + """ + @spec not_equal_to(atom() | String.t(), any) :: Condition.t() def not_equal_to(field_name, value) do %Condition{ token: :ne, @@ -67,7 +124,16 @@ defmodule Ravix.RQL.Tokens.Condition do } end - @spec in?(String.t(), any) :: Condition.t() + @doc """ + Specifies that the value is in a list + + Returns a `Ravix.RQL.Tokens.Condition` + + ## Examples + iex> import Ravix.RQL.Tokens.Condition + iex> in?("field", [1,2,3]) + """ + @spec in?(atom() | String.t(), list()) :: Condition.t() def in?(field_name, values) do %Condition{ token: :in, @@ -76,7 +142,16 @@ defmodule Ravix.RQL.Tokens.Condition do } end - @spec not_in(String.t(), any) :: Condition.t() + @doc """ + Specifies that the value is not in a list + + Returns a `Ravix.RQL.Tokens.Condition` + + ## Examples + iex> import Ravix.RQL.Tokens.Condition + iex> not_in("field", ["a", "b", "c"]) + """ + @spec not_in(atom() | String.t(), list()) :: Condition.t() def not_in(field_name, values) do %Condition{ token: :nin, @@ -85,7 +160,16 @@ defmodule Ravix.RQL.Tokens.Condition do } end - @spec between(String.t(), any) :: Condition.t() + @doc """ + Specifies that the value is between two values + + Returns a `Ravix.RQL.Tokens.Condition` + + ## Examples + iex> import Ravix.RQL.Tokens.Condition + iex> between("field", [15,25]) + """ + @spec between(atom() | String.t(), list()) :: Condition.t() def between(field_name, values) do %Condition{ token: :between, diff --git a/lib/rql/tokens/from.ex b/lib/rql/tokens/from.ex index 466860a..8df9ffb 100644 --- a/lib/rql/tokens/from.ex +++ b/lib/rql/tokens/from.ex @@ -1,4 +1,5 @@ defmodule Ravix.RQL.Tokens.From do + @moduledoc false defstruct [ :token, :as_alias, diff --git a/lib/rql/tokens/group_by.ex b/lib/rql/tokens/group_by.ex index 8ba0ff8..c1faf07 100644 --- a/lib/rql/tokens/group_by.ex +++ b/lib/rql/tokens/group_by.ex @@ -1,4 +1,5 @@ defmodule Ravix.RQL.Tokens.Group do + @moduledoc false defstruct [ :token, :fields diff --git a/lib/rql/tokens/limit.ex b/lib/rql/tokens/limit.ex index ba70812..0e7e6af 100644 --- a/lib/rql/tokens/limit.ex +++ b/lib/rql/tokens/limit.ex @@ -1,4 +1,5 @@ defmodule Ravix.RQL.Tokens.Limit do + @moduledoc false defstruct [ :token, :skip, diff --git a/lib/rql/tokens/not.ex b/lib/rql/tokens/not.ex index 2d8dfb0..955a202 100644 --- a/lib/rql/tokens/not.ex +++ b/lib/rql/tokens/not.ex @@ -1,4 +1,5 @@ defmodule Ravix.RQL.Tokens.Not do + @moduledoc false defstruct [ :token, :condition diff --git a/lib/rql/tokens/or.ex b/lib/rql/tokens/or.ex index 4999af2..5442ba5 100644 --- a/lib/rql/tokens/or.ex +++ b/lib/rql/tokens/or.ex @@ -1,4 +1,5 @@ defmodule Ravix.RQL.Tokens.Or do + @moduledoc false defstruct [ :token, :condition diff --git a/lib/rql/tokens/order.ex b/lib/rql/tokens/order.ex index c7a91a5..20d05cd 100644 --- a/lib/rql/tokens/order.ex +++ b/lib/rql/tokens/order.ex @@ -1,4 +1,5 @@ defmodule Ravix.RQL.Tokens.Order do + @moduledoc false defstruct [ :token, :fields diff --git a/lib/rql/tokens/select.ex b/lib/rql/tokens/select.ex index 4a5c0e3..cd7e6d2 100644 --- a/lib/rql/tokens/select.ex +++ b/lib/rql/tokens/select.ex @@ -1,4 +1,5 @@ defmodule Ravix.RQL.Tokens.Select do + @moduledoc false defstruct [ :token, :fields @@ -6,7 +7,7 @@ defmodule Ravix.RQL.Tokens.Select do alias Ravix.RQL.Tokens.Select - @type field_name :: String.t() + @type field_name :: atom() | String.t() @type field_alias :: String.t() @type allowed_select_params :: list(field_name() | {field_name(), field_alias()}) diff --git a/lib/rql/tokens/update.ex b/lib/rql/tokens/update.ex index 236c738..037a2e9 100644 --- a/lib/rql/tokens/update.ex +++ b/lib/rql/tokens/update.ex @@ -1,4 +1,7 @@ defmodule Ravix.RQL.Tokens.Update do + @moduledoc """ + RQL Update tokens + """ defstruct token: :update, fields: [] @@ -9,32 +12,51 @@ defmodule Ravix.RQL.Tokens.Update do fields: list(map()) } - @spec fields(list(map())) :: Update.t() - def fields(fields) do - %Update{ - token: :update, - fields: fields - } - end + @doc """ + Creates a new "set" update operation + + Returns a `Ravix.RQL.Tokens.Update` - @spec set(Update.t(), String.t(), any) :: Ravix.RQL.Tokens.Update.t() - def set(update, field, value) do + ## Examples + iex> import alias Ravix.RQL.Tokens.Update + iex> set("field1", 10) |> set("field2", "a") + """ + @spec set(Update.t(), atom() | String.t(), any) :: Ravix.RQL.Tokens.Update.t() + def set(update \\ %Update{}, field, value) do %Update{ update | fields: update.fields ++ [%{name: field, value: value, operation: :set}] } end - @spec inc(Update.t(), String.t(), any) :: Ravix.RQL.Tokens.Update.t() - def inc(update, field, value) do + @doc """ + Creates a new "increment" update operation + + Returns a `Ravix.RQL.Tokens.Update` + + ## Examples + iex> import alias Ravix.RQL.Tokens.Update + iex> inc("field1", 10) + """ + @spec inc(Update.t(), atom() | String.t(), number()) :: Ravix.RQL.Tokens.Update.t() + def inc(update \\ %Update{}, field, value) when is_number(value) do %Update{ update | fields: update.fields ++ [%{name: field, value: value, operation: :inc}] } end - @spec dec(Update.t(), String.t(), any) :: Ravix.RQL.Tokens.Update.t() - def dec(update, field, value) do + @doc """ + Creates a new "decrement" update operation + + Returns a `Ravix.RQL.Tokens.Update` + + ## Examples + iex> import alias Ravix.RQL.Tokens.Update + iex> dec("field1", 10) + """ + @spec dec(Update.t(), atom() | String.t(), number()) :: Ravix.RQL.Tokens.Update.t() + def dec(update \\ %Update{}, field, value) when is_number(value) do %Update{ update | fields: update.fields ++ [%{name: field, value: value, operation: :dec}] diff --git a/lib/rql/tokens/where.ex b/lib/rql/tokens/where.ex index ceafe0e..e5b6ceb 100644 --- a/lib/rql/tokens/where.ex +++ b/lib/rql/tokens/where.ex @@ -1,4 +1,5 @@ defmodule Ravix.RQL.Tokens.Where do + @moduledoc false defstruct [ :token, :condition diff --git a/test/documents/session/session_test.exs b/test/documents/session/session_test.exs index 466e513..a921137 100644 --- a/test/documents/session/session_test.exs +++ b/test/documents/session/session_test.exs @@ -375,7 +375,7 @@ defmodule Ravix.Documents.SessionTest do response = result[:response] state = result[:state] - assert response == any_entity.id + assert length(response.deleted_entities) == 1 assert state.documents_by_id == %{} end diff --git a/test/rql/query_parser_test.exs b/test/rql/query_parser_test.exs index 303b6e1..804b61f 100644 --- a/test/rql/query_parser_test.exs +++ b/test/rql/query_parser_test.exs @@ -3,11 +3,10 @@ defmodule Ravix.RQL.QueryParserTest do import Ravix.RQL.Query import Ravix.RQL.Tokens.Condition + import Ravix.RQL.Tokens.Update alias Ravix.RQL.QueryParser alias Ravix.RQL.Tokens.Condition - alias Ravix.RQL.Tokens.Update - describe "parse/1" do test "It should parse the tokens succesfully" do @@ -43,17 +42,13 @@ defmodule Ravix.RQL.QueryParserTest do end test "Should parse an update succesfully" do - updates = [ - %{name: "field", value: "new_value", operation: :set}, - %{name: "field2", value: 1, operation: :inc}, - %{name: "field3", value: 2, operation: :dec} - ] + updates = set("field", "new_value") |> inc("field2", 1) |> dec("field3", 2) {:ok, query_result} = from("test", "t") |> where(greater_than("field", 10)) |> and?(equal_to("field2", "asdf")) - |> update(Update.fields(updates)) + |> update(updates) |> QueryParser.parse() assert query_result.query_string == diff --git a/test/rql/query_test.exs b/test/rql/query_test.exs index 47afd3c..6d8bc7f 100644 --- a/test/rql/query_test.exs +++ b/test/rql/query_test.exs @@ -8,7 +8,6 @@ defmodule Ravix.RQL.QueryTest do import Ravix.Factory alias Ravix.Documents.Session - alias Ravix.RQL.Tokens.Update alias Ravix.Test.Store, as: Store alias Ravix.Test.NonRetryableStore @@ -464,7 +463,7 @@ defmodule Ravix.RQL.QueryTest do update_response <- from("@all_docs", "a") - |> update(set(%Update{}, :cat_name, "Fluffer, the hand-ripper")) + |> update(set(:cat_name, "Fluffer, the hand-ripper")) |> where(equal_to("cat_name", any_entity.cat_name)) |> update_for(session_id) diff --git a/test/support/cat.ex b/test/support/cat.ex index 4679bab..70f3ebd 100644 --- a/test/support/cat.ex +++ b/test/support/cat.ex @@ -1,3 +1,4 @@ defmodule Ravix.SampleModel.Cat do + @moduledoc false use Ravix.Document, fields: [:id, :name, :breed] end diff --git a/test/support/factory.ex b/test/support/factory.ex index 28c5503..b05e1c0 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -1,4 +1,5 @@ defmodule Ravix.Factory do + @moduledoc false use ExMachina alias Ravix.Documents.Session diff --git a/test/support/non_retryable_store.ex b/test/support/non_retryable_store.ex index c57c80b..2ecacba 100644 --- a/test/support/non_retryable_store.ex +++ b/test/support/non_retryable_store.ex @@ -1,3 +1,4 @@ defmodule Ravix.Test.NonRetryableStore do + @moduledoc false use Ravix.Documents.Store, otp_app: :ravix end diff --git a/test/support/optimistic_lock_store.ex b/test/support/optimistic_lock_store.ex index a279538..2b83b7c 100644 --- a/test/support/optimistic_lock_store.ex +++ b/test/support/optimistic_lock_store.ex @@ -1,3 +1,4 @@ defmodule Ravix.Test.OptimisticLockStore do + @moduledoc false use Ravix.Documents.Store, otp_app: :ravix end diff --git a/test/support/random.ex b/test/support/random.ex index c0b33eb..18ec7ca 100644 --- a/test/support/random.ex +++ b/test/support/random.ex @@ -1,4 +1,5 @@ defmodule Ravix.Test.Random do + @moduledoc false def safe_random_string(size) do :crypto.strong_rand_bytes(size) |> Base.url_encode64() |> binary_part(0, size) end diff --git a/test/support/test_application.ex b/test/support/test_application.ex index d87d562..4c11754 100644 --- a/test/support/test_application.ex +++ b/test/support/test_application.ex @@ -1,4 +1,5 @@ defmodule Ravix.TestApplication do + @moduledoc false use Supervisor def init(_opts) do diff --git a/test/support/test_store.ex b/test/support/test_store.ex index 4151fe8..92a8fb4 100644 --- a/test/support/test_store.ex +++ b/test/support/test_store.ex @@ -1,3 +1,4 @@ defmodule Ravix.Test.Store do + @moduledoc false use Ravix.Documents.Store, otp_app: :ravix end diff --git a/test/support/test_store_invalid.ex b/test/support/test_store_invalid.ex index f993ea3..2c56a71 100644 --- a/test/support/test_store_invalid.ex +++ b/test/support/test_store_invalid.ex @@ -1,3 +1,4 @@ defmodule Ravix.TestStoreInvalid do + @moduledoc false use Ravix.Documents.Store, otp_app: :ravix end