Packages
nous
0.15.6
0.17.0
0.16.6
0.16.5
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.8
0.15.7
0.15.6
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.17
0.12.16
0.12.15
0.12.14
0.12.13
0.12.12
0.12.11
0.12.9
0.12.7
0.12.6
0.12.5
0.12.3
0.12.2
0.12.0
0.11.3
0.11.0
0.10.1
0.10.0
0.9.0
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.5.0
AI agent framework for Elixir with multi-provider LLM support
Current section
Files
Jump to
Current section
Files
lib/nous/skills/ecto_patterns.ex
defmodule Nous.Skills.EctoPatterns do
@moduledoc "Built-in skill for Ecto query composition, changesets, and data patterns."
use Nous.Skill, tags: [:elixir, :ecto, :database, :query], group: :coding
@impl true
def name, do: "ecto_patterns"
@impl true
def description, do: "Ecto query composition, N+1 prevention, changesets, and context design"
@impl true
def instructions(_agent, _ctx) do
"""
You are an Ecto specialist. Follow these patterns:
1. **Query composition**: Build queries functionally, chain before executing:
```elixir
Post
|> filter_by_status(status)
|> filter_by_author(author_id)
|> preload(:comments)
|> Repo.all()
defp filter_by_status(query, nil), do: query
defp filter_by_status(query, status), do: from(p in query, where: p.status == ^status)
```
2. **Prevent N+1 queries**:
- `belongs_to`: Use JOIN preloading: `from(p in Post, join: a in assoc(p, :author), preload: [author: a])`
- `has_many`: Use separate query preloading: `preload: [comments: ^comments_query]`
- Never JOIN-preload `has_many` — it replicates parent rows over the wire
3. **Separate changesets by context**: `user_changeset/2` (name, email) vs `admin_changeset/2` (name, email, role, active) to prevent mass assignment.
4. **Context module design**: One public API module per business domain. Expose clean functions, hide Repo calls:
```elixir
defmodule MyApp.Accounts do
def create_user(attrs), do: %User{} |> User.changeset(attrs) |> Repo.insert()
def get_user!(id), do: Repo.get!(User, id)
end
```
5. **Use Ecto.Multi for transactions**: Chain operations that must succeed or fail together.
6. **Never reference schemas in migrations**: Migrations must be self-contained — schemas change over time.
7. **Always use parameterized queries**: Never interpolate user input into query strings.
"""
end
@impl true
def match?(input) do
input = String.downcase(input)
String.contains?(input, [
"ecto",
"query",
"changeset",
"repo",
"preload",
"migration",
"schema",
"n+1",
"context module"
])
end
end