# The Select Pattern

Derive multiple views from one cached query with SelectTransform and use_query_select

TanStack Query's `select` option transforms cached data into a derived shape. It re-runs only when the underlying data changes. gpui-query provides the same pattern through [`SelectTransform`] and the [`use_query_select`] hook. One cached query can feed several derived views without duplicating the cache entry.

[`SelectTransform`]: /docs/api/queries
[`use_query_select`]: /docs/api/queries

## Why a separate hook?

In Rust the transform output type `U` has to be known at compile time. The `QueryOptions` struct is not generic over a transform output, so instead of making the whole options type generic, `select` lives on a dedicated hook: `use_query_select`. It accepts the transform as a separate parameter and returns a `MappedQueryResource<T, U, E>` entity.

The trade-off is ergonomic: `select` is a distinct hook rather than a field on options, but the type system stays simple.

## SelectTransform

A `SelectTransform<T, U>` wraps an `Fn(&T) -> U` closure. Build one with `SelectTransform::new`:

```rust
use gpui_query::core::SelectTransform;

// Count the users in a cached Vec<User>.
let count = SelectTransform::new(|users: &Vec<String>| users.len());

// Project a single field.
let names = SelectTransform::new(|users: &Vec<String>| {
    users.iter().cloned().collect::<Vec<_>>()
});

assert_eq!(count.apply(&vec!["a".to_string(), "b".to_string()]), 2);
```

`SelectTransform` is `Clone + Send + Sync` (the closure is stored in an `Arc`), so you can share one transform across several mapped resources.

## use_query_select

The hook wires a `SelectTransform` into the `use_query` lifecycle. It returns a triple: the mapped view entity, the underlying query entity, and the pair of subscriptions that keep both observations alive. **Store both subscriptions**. Dropping either breaks the observation.

```rust
use gpui_query::hook::{use_query_select, QueryOptions};
use gpui_query::core::SelectTransform;
# #[derive(Clone, PartialEq)]
# struct User { name: String }
# #[derive(Clone, Debug)]
# struct MyError;

struct UserCountView {
    /// Derived view: just the count.
    count: gpui::Entity<gpui_query::core::MappedQueryResource<Vec<User>, usize, MyError>>,
    _subs: (gpui::Subscription, gpui::Subscription),
}

impl UserCountView {
    fn new(cx: &mut gpui::Context<Self>) -> Self {
        let transform = SelectTransform::new(|users: &Vec<User>| users.len());

        let (count, _query, subs) = use_query_select(
            QueryOptions::new("users"),
            transform,
            |_signal| async move { Ok::<_, MyError>(vec![]) },
            cx,
        );

        Self { count, _subs: subs }
    }
}
```

Reading the derived value applies the transform lazily:

```rust
# // (inside a render / event handler that has cx)
# let count: &gpui::Entity<gpui_query::core::MappedQueryResource<Vec<String>, usize, ()>> = unimplemented!();
# let cx: &gpui::App = unimplemented!();
let n: Option<usize> = count.read(cx).data();
```

## How it stays in sync

`use_query_select` does three things:

1. Calls `use_query` to create and subscribe to the underlying `QueryResource<T, E>`.
2. Seeds a `MappedQueryResource<T, U, E>` with the query's current data (one clone into an `Arc<T>`).
3. Observes the source entity. Every time it changes, the mapped resource compares the new data against the cached source **by reference** and only re-stores + notifies when the content actually changed.

The upshot: unchanged notifications (the common case, such as a retry tick or a status flip with the same data) pay a cheap `Arc::clone` instead of a full `T` clone. The transform itself is applied lazily, on every `data()` call.

## Many views from one cache

Because the source data is shared, you can attach several mapped resources to the same underlying query, each with its own transform:

```rust
use gpui_query::core::SelectTransform;

let total  = SelectTransform::new(|users: &Vec<String>| users.len());
let names  = SelectTransform::new(|users: &Vec<String>| users.clone());
let first  = SelectTransform::new(|users: &Vec<String>| users.first().cloned());
```

All three read from the **same** cached `Vec<User>`. There is no duplication. Each `MappedQueryResource` stores only an `Arc<T>` to the shared source plus its own transform closure.

## Cost notes

- The transform closure runs **every time** `MappedQueryResource::data()` is called, and there is no output cache. For lightweight transforms (a field access, a `.len()`) the cost is negligible.
- For expensive transforms called more than once in a render pass, cache the result in a local:

  ```rust
  let derived = mapped.read(cx).data(); // transform runs once
  // reuse `derived` below instead of calling .data() again
  ```

- The mapped entity notifies observers only when the **source content** changes, so derived views re-render at the same cadence as the underlying query. Retry counts or status flips that leave data untouched cause no extra churn.

## Next steps

- [Query keys](/docs/guides/query-keys): keys the underlying query is cached under.
- [Caching](/docs/guides/caching): how the source data's `CachePolicy` controls refetch.
- [Observers](/docs/advanced/observers): the status-deduplication primitive the mapped resource relies on.
