sbennett33

sbennett33

I’m working on creating a DataLayer for the Sanity CMS. So far I have support for reading from the API and basic attributes on a resource.

AshSanity

Right now I’m trying to figure out how to support relationships on resources. When I call Post |> Ash.Query.load(:comments) |> Api.read!() I was expecting something to be called in the DataLayer module to either run another query or load the related resources but nothing is happening that I can tell. It looks like the query is run to load the Post resource but that’s it.

What should I expect to see happen at the DataLayer level when loading a related resource?

Testing Loading Relationships

Showing Posts 1 to 7

zachdaniel

zachdaniel

Creator of Ash

:wave: For loading related data, data layers are not made aware that is loading a relationship, but rather a filter is passed down to the relationship based on the source data. So if there are no posts, no query is made to fetch the comments. You’d get a filter like post_id in [....] to your data layer for the comments query.

sbennett33

sbennett33 OP

Thanks Zach! That makes sense. In my test I wasn’t returning any posts in the response which is why I wasn’t seeing the subsequent request for the comments.

So, this way of handling relationships makes total sense for a relational database model where the resource on the “belongs to” side of a “has many” relationship has a foreign key pointing back at the parent record. Unfortunately Sanity stores their data in a NoSQL data lake where the parent resource keeps a reference to all the related resources so when you query for a list of posts you get something back like the following:

[
  {
    _id: "7d10669d-7e9f-4c8c-a7db-e1ea369e7055",
    _type: "post",
    title: "My First Post",
    body: "A very thought provoking post",
    comments: [
      {
        _key: "13ad3eba8563",
        _type: "reference",
        _ref: "1ccb9a37-c0f2-4aa7-82cb-3e16e6ecab99"
      },
      {
        _key: "d8c1e6a53174",
        _type: "reference",
        _ref: "fff10398-7cb1-4d9f-8dfd-42d0f8aca798"
      },
      {
        _key: "6faf4ddeef52",
        _type: "reference",
        _ref: "a7e06b12-561e-4ad8-9756-cd1979e754be"
      },
    ]
  },
  … (more posts)
]

If you want to load/dereference the comments you would do so when you query for the Posts or you’d have to look at the IDs in the comments array then do a query like id in [...] on the comment resource. I’ve been trying to figure out what the best way would be to model something like that using Ash resources and at this point I have two thoughts:

  1. Create a custom ref type for the comments attribute. Something like:
attributes do
  attribute :comments, {:array, ref: Comment}
end

I’d then need to handle that attribute type when casting attributes in my DataLayer which probably isn’t ideal.

  1. Create a new references block on resources that use AshSanity which could look something like:
references do
  has_many :comments, Comment
end

This approach feels like the more idiomatic way to do it but may involve touching a lot of plumbing and figuring out how to add support to Ash.Query so it knows how to load those references.

It may also just be the case that the Ash DataLayer model isn’t a good fit for modeling a NoSQL data store and I’m just totally barking up the wrong tree.

Would love to get your thoughts.

zachdaniel

zachdaniel

Creator of Ash

Interesting. So you could do this today with calculations or manual relationships (where your tool provides the implementatiton). However, it would be good to generalize this. The goal in Ash is to act as a superset of multiple kinds of storage layers. I think we should consider adding new ttype sof relationships. Specifically references_many, where the list of ids are stored somewhere on the source record. Thoughts?

sbennett33

sbennett33 OP

Nice. I like the idea of adding something like references_many to handle cases like this. I wouldn’t want the implementation to be tied to the shape of the data coming back from Sanity though. We’d probably want to pass the responsibility of managing the relationship down to the DataLayer somehow. For example Sanity returns what is basically an array of reference objects which contain the ID of the referenced document under the _ref key whereas other databases may return just a list of IDs or have the ID under a different key.

I’d be more than happy to help contribute to adding support for this. In the meantime I’ll take a look at how this might look using calculations or manual relationships as a stopgap until there’s builtin support at the framework level.

zachdaniel

zachdaniel

Creator of Ash

Yep, I think references many would point to an attribute or calculation that is expected to contain the list of keys used to look up the destination records, so could be generalized to any data layer. Then you’d provide a “references” calculation.

sbennett33

sbennett33 OP

I think that makes sense. I have two thoughts:

  1. It would be nice if we could somehow define that calculation at the DataLayer level and then also override it in the references_many. For AshSanity, the path to the ID in the reference would be the same for all references_many relationships and it would be a bummer to have to define that on every relationship. I know there are some info callbacks that are used with the DataLayers, I wonder if that mechanism could be used to supply the default calculation/lookup function or if there is a better mechanism to do that.

  2. We should also implement a corresponding references_one relationship for completeness.

zachdaniel

zachdaniel

Creator of Ash

Well, technically belongs_to is references_one. We could potentially make it part of the data layer callbacks to provide a default implementation of references_many, or something along those lines. But we’d also want to allow it to be overridden.

— All posts loaded —

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
kpanic
Hi everyone, I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding. I sta...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New
apz
I’m new to elixir and just tried to install the elixirLS extension for VScode(ium) and it is throwing some errors that I would like help ...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
georgeguimaraes
Just published claude-code-elixir, a plugin marketplace for Claude Code with Elixir support. These are the plugins I’ve been using for my...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews