ecto-query-patterns
Use when querying data with Ecto.Query DSL including where clauses, joins, aggregates, preloading, and query composition. Use for building flexible database queries in Elixir applications.
What this skill does
# Ecto Query Patterns
Master Ecto's powerful Query DSL to build efficient, composable database queries.
This skill covers the query syntax, filtering, joining, aggregation, preloading
associations, and advanced query composition patterns.
## Basic Query with from Macro
```elixir
import Ecto.Query, only: [from: 2]
# Basic query using keyword syntax
query = from u in "users",
where: u.age > 18,
select: u.name
# Execute the query
MyApp.Repo.all(query)
```
Queries are built using the `from/2` macro and only sent to the database when
passed to a `Repo` function like `all/1`, `one/1`, or `get/2`. The keyword syntax
provides a readable way to construct queries.
## Query with Schema Module
```elixir
query = from u in MyApp.User,
where: u.age > 18,
select: u.name
MyApp.Repo.all(query)
```
Using a schema module instead of a table name string provides better type safety
and allows Ecto to use the schema's field definitions for validation and casting.
## Bindingless Query Construction
```elixir
from MyApp.Post,
where: [category: "fresh and new"],
order_by: [desc: :published_at],
select: [:id, :title, :body]
```
Bindingless syntax allows building queries without explicit variable bindings.
This works well for simple queries and when using keyword list syntax for conditions.
## Query with Explicit Bindings
```elixir
query = from p in MyApp.Post,
where: p.category == "fresh and new",
order_by: [desc: p.published_at],
select: struct(p, [:id, :title, :body])
MyApp.Repo.all(query)
```
Explicit bindings (like `p` for posts) allow for more complex conditions and
selections. The `struct/2` function selects only specific fields from the schema.
## Dynamic Query Variables
```elixir
category = "fresh and new"
order_by = [desc: :published_at]
select_fields = [:id, :title, :body]
query = from MyApp.Post,
where: [category: ^category],
order_by: ^order_by,
select: ^select_fields
MyApp.Repo.all(query)
```
The pin operator `^` allows interpolating Elixir values into queries. This is
essential for parameterized queries and prevents SQL injection.
## Where Clause with Expressions
```elixir
query = from u in MyApp.User,
where: u.age > 0,
select: u.name
# Multiple where clauses are combined with AND
query = from u in MyApp.User,
where: u.age > 18,
where: u.confirmed == true,
select: u
MyApp.Repo.all(query)
```
Query expressions support field access, comparison operators, and literals.
Multiple `where` clauses are automatically combined with AND logic.
## Composable Queries
```elixir
# Create a base query
query = from u in MyApp.User, where: u.age > 18
# Extend the query
query = from u in query, select: u.name
MyApp.Repo.all(query)
```
Queries are composable - you can build on existing queries by using them in the
`in` clause. This enables powerful query abstraction and reusability.
## Query Composition Function Pattern
```elixir
def most_recent_from(query, minimum_date) do
from p in query,
where: p.published_at > ^minimum_date,
order_by: [desc: p.published_at]
end
# Usage
MyApp.Post
|> most_recent_from(~N[2024-01-01 00:00:00])
|> MyApp.Repo.all()
```
Extracting query logic into functions creates reusable, testable query components.
This pattern is fundamental to building maintainable query code.
## Or Where Conditions
```elixir
from p in MyApp.Post,
where: p.category == "elixir" or p.category == "phoenix",
select: p
```
Use the `or` keyword for alternative conditions. For more complex OR logic,
consider using `Ecto.Query.dynamic/2`.
## IN Query with List
```elixir
categories = ["elixir", "phoenix", "ecto"]
query = from p in MyApp.Post,
where: p.category in ^categories,
select: p
MyApp.Repo.all(query)
```
The `in` operator checks if a field value exists in a list of values. Use the
pin operator to interpolate the list variable.
## Like and ILike for Pattern Matching
```elixir
search_term = "%elixir%"
query = from p in MyApp.Post,
where: like(p.title, ^search_term),
select: p
# Case-insensitive version
query = from p in MyApp.Post,
where: ilike(p.title, ^search_term),
select: p
```
Use `like/2` for case-sensitive pattern matching and `ilike/2` for case-insensitive
matching. Wildcards `%` match any characters.
## Selecting Specific Fields
```elixir
# Select multiple fields
query = from p in MyApp.Post,
select: {p.id, p.title}
MyApp.Repo.all(query) # Returns [{1, "Title 1"}, {2, "Title 2"}]
# Select as map
query = from p in MyApp.Post,
select: %{id: p.id, title: p.title}
MyApp.Repo.all(query) # Returns [%{id: 1, title: "Title 1"}, ...]
# Select struct with specific fields
query = from p in MyApp.Post,
select: struct(p, [:id, :title, :body])
MyApp.Repo.all(query) # Returns Post structs with only selected fields loaded
```
Selecting specific fields instead of entire records improves query performance
by reducing data transfer and memory usage.
## Aggregation Functions
```elixir
# Count records
query = from p in MyApp.Post,
select: count(p.id)
MyApp.Repo.one(query) # Returns integer count
# Average
query = from p in MyApp.Post,
select: avg(p.rating)
# Sum
query = from o in MyApp.Order,
select: sum(o.total)
# Min and Max
query = from p in MyApp.Product,
select: {min(p.price), max(p.price)}
```
Ecto supports standard SQL aggregation functions including `count/1`, `avg/1`,
`sum/1`, `min/1`, and `max/1`.
## Group By and Having
```elixir
query = from p in MyApp.Post,
group_by: p.category,
select: {p.category, count(p.id)}
MyApp.Repo.all(query) # Returns [{"elixir", 10}, {"phoenix", 5}]
# With having clause
query = from p in MyApp.Post,
group_by: p.category,
having: count(p.id) > 5,
select: {p.category, count(p.id)}
```
Use `group_by` to group results by field values and `having` to filter groups
based on aggregate values.
## Order By
```elixir
# Single field ascending
query = from p in MyApp.Post,
order_by: p.published_at
# Single field descending
query = from p in MyApp.Post,
order_by: [desc: p.published_at]
# Multiple fields
query = from p in MyApp.Post,
order_by: [desc: p.published_at, asc: p.title]
# With nulls positioning
query = from p in MyApp.Post,
order_by: [desc_nulls_last: p.published_at]
```
The `order_by` option controls result ordering. You can specify ascending or
descending order, multiple fields, and null positioning.
## Limit and Offset for Pagination
```elixir
# Simple limit
query = from p in MyApp.Post,
limit: 10
# With offset for pagination
page = 2
per_page = 10
query = from p in MyApp.Post,
order_by: [desc: p.published_at],
limit: ^per_page,
offset: ^((page - 1) * per_page)
MyApp.Repo.all(query)
```
Use `limit` and `offset` for pagination. Always include an `order_by` clause
to ensure consistent pagination results.
## Inner Join
```elixir
query = from p in MyApp.Post,
join: c in MyApp.Comment,
on: c.post_id == p.id,
select: {p.title, c.body}
MyApp.Repo.all(query)
```
Inner joins return only records that have matching records in both tables. The
`on` clause specifies the join condition.
## Join with assoc Helper
```elixir
query = from p in MyApp.Post,
join: c in assoc(p, :comments),
select: {p, c}
MyApp.Repo.all(query)
```
The `assoc/2` helper uses the association definition from your schema, making
joins more maintainable and less error-prone than manually specifying foreign keys.
## Left Join
```elixir
query = from p in MyApp.Post,
left_join: c in assoc(p, :comments),
select: {p, c}
MyApp.Repo.all(query)
```
Left joins return all records from the left table (posts) even if there areRelated in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.