diff --git a/config/config.exs b/config/config.exs
index 6861abc5..2a17cfd8 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -25,6 +25,8 @@ if Mix.env() == :test do
 
   config :ash_postgres, :ash_domains, [AshPostgres.Test.Domain]
 
+  config :ash, :custom_expressions, [AshPostgres.Expressions.TrigramWordSimilarity]
+
   config :ash_postgres, AshPostgres.TestRepo,
     username: "postgres",
     database: "ash_postgres_test",
diff --git a/mix.lock b/mix.lock
index 73c0f0c5..b6cfe550 100644
--- a/mix.lock
+++ b/mix.lock
@@ -20,7 +20,7 @@
   "git_cli": {:hex, :git_cli, "0.3.0", "a5422f9b95c99483385b976f5d43f7e8233283a47cda13533d7c16131cb14df5", [:mix], [], "hexpm", "78cb952f4c86a41f4d3511f1d3ecb28edb268e3a7df278de2faa1bd4672eaf9b"},
   "git_ops": {:hex, :git_ops, "2.6.3", "38c6e381b8281b86e2911fa39bea4eab2d171c86d7428786566891efb73b68c3", [:mix], [{:git_cli, "~> 0.2", [hex: :git_cli, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "a81cb6c6a2a026a4d48cb9a2e1dfca203f9283a3a70aa0c7bc171970c44f23f8"},
   "glob_ex": {:hex, :glob_ex, "0.1.10", "d819a368637495a5c1962ef34f48fe4e9a09032410b96ade5758f2cd1cc5fcde", [:mix], [], "hexpm", "c75357e57d71c85ef8ef7269b6e787dce3f0ff71e585f79a90e4d5477c532b90"},
-  "igniter": {:hex, :igniter, "0.3.76", "ff283416402f4d1ef3f79ab57d38aac08389b3768fc81da03795ce5347f1167f", [:mix], [{:glob_ex, "~> 0.1.7", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:rewrite, "~> 0.9", [hex: :rewrite, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.4", [hex: :sourceror, repo: "hexpm", optional: false]}, {:spitfire, ">= 0.1.3 and < 1.0.0-0", [hex: :spitfire, repo: "hexpm", optional: false]}], "hexpm", "4692f874f969dc4856167469a3415a5a57a362314f37e1cdb14431316a74e896"},
+  "igniter": {:hex, :igniter, "0.4.1", "74a6a376340755120a4c48949014ac09e95d1ffd918e73aea25fe8a4e405243e", [:mix], [{:glob_ex, "~> 0.1.7", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:rewrite, "~> 0.9", [hex: :rewrite, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.4", [hex: :sourceror, repo: "hexpm", optional: false]}, {:spitfire, ">= 0.1.3 and < 1.0.0-0", [hex: :spitfire, repo: "hexpm", optional: false]}], "hexpm", "c25a41acb4bb719ef1f0f847e9b999c995e3edf85ec631a404d5e035fea9c335"},
   "inflex": {:hex, :inflex, "2.1.0", "a365cf0821a9dacb65067abd95008ca1b0bb7dcdd85ae59965deef2aa062924c", [:mix], [], "hexpm", "14c17d05db4ee9b6d319b0bff1bdf22aa389a25398d1952c7a0b5f3d93162dd8"},
   "iterex": {:hex, :iterex, "0.1.2", "58f9b9b9a22a55cbfc7b5234a9c9c63eaac26d276b3db80936c0e1c60355a5a6", [:mix], [], "hexpm", "2e103b8bcc81757a9af121f6dc0df312c9a17220f302b1193ef720460d03029d"},
   "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
@@ -36,7 +36,7 @@
   "rewrite": {:hex, :rewrite, "0.10.5", "6afadeae0b9d843b27ac6225e88e165884875e0aed333ef4ad3bf36f9c101bed", [:mix], [{:glob_ex, "~> 0.1", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "51cc347a4269ad3a1e7a2c4122dbac9198302b082f5615964358b4635ebf3d4f"},
   "simple_sat": {:hex, :simple_sat, "0.1.3", "f650fc3c184a5fe741868b5ac56dc77fdbb428468f6dbf1978e14d0334497578", [:mix], [], "hexpm", "a54305066a356b7194dc81db2a89232bacdc0b3edaef68ed9aba28dcbc34887b"},
   "sobelow": {:hex, :sobelow, "0.13.0", "218afe9075904793f5c64b8837cc356e493d88fddde126a463839351870b8d1e", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cd6e9026b85fc35d7529da14f95e85a078d9dd1907a9097b3ba6ac7ebbe34a0d"},
-  "sourceror": {:hex, :sourceror, "1.7.0", "62c34f4e3a109d837edd652730219b6332745e0ec7db34d5d350a5cdf30b376a", [:mix], [], "hexpm", "3dd2b1bd780fd0df48089f48480a54fd065bf815b63ef8046219d7784e7435f3"},
+  "sourceror": {:hex, :sourceror, "1.7.1", "599d78f4cc2be7d55c9c4fd0a8d772fd0478e3a50e726697c20d13d02aa056d4", [:mix], [], "hexpm", "cd6f268fe29fa00afbc535e215158680a0662b357dc784646d7dff28ac65a0fc"},
   "spark": {:hex, :spark, "2.2.35", "1c0bb30f340151eca24164885935de39e6ada4010555f444c813d0488990f8f3", [:mix], [{:igniter, ">= 0.3.64 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.2", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "f242d6385c287389034a0e146d8f025b5c9ab777f1ae5cf0fdfc9209db6ae748"},
   "spitfire": {:hex, :spitfire, "0.1.3", "7ea0f544005dfbe48e615ed90250c9a271bfe126914012023fd5e4b6b82b7ec7", [:mix], [], "hexpm", "d53b5107bcff526a05c5bb54c95e77b36834550affd5830c9f58760e8c543657"},
   "splode": {:hex, :splode, "0.2.7", "ed042fa9bd8fe7b66dd0a0faabdb97352058420d90cd1c7c1537f609deb7ef6d", [:mix], [], "hexpm", "267f1f51d5a5ac988cda0649498294844988c5086916fed5a8aff297d69a2059"},
diff --git a/test/custom_expression_test.exs b/test/custom_expression_test.exs
new file mode 100644
index 00000000..e88737ad
--- /dev/null
+++ b/test/custom_expression_test.exs
@@ -0,0 +1,17 @@
+defmodule AshPostgres.Test.CustomExpressionTest do
+  use AshPostgres.RepoCase, async: false
+
+  test "unique constraint errors are properly caught" do
+    Ash.create!(AshPostgres.Test.Profile, %{description: "foo"})
+
+    assert [_] =
+             AshPostgres.Test.Profile
+             |> Ash.Query.for_read(:by_indirectly_matching_description, %{term: "fop"})
+             |> Ash.read!()
+
+    assert [_] =
+             AshPostgres.Test.Profile
+             |> Ash.Query.for_read(:by_directly_matching_description, %{term: "fop"})
+             |> Ash.read!()
+  end
+end
diff --git a/test/load_test.exs b/test/load_test.exs
index dd9b0e9d..9261e084 100644
--- a/test/load_test.exs
+++ b/test/load_test.exs
@@ -1,6 +1,16 @@
 defmodule AshPostgres.Test.LoadTest do
   use AshPostgres.RepoCase, async: false
-  alias AshPostgres.Test.{Author, Comment, Post, Record, StatefulPostFollower, TempEntity, User}
+
+  alias AshPostgres.Test.{
+    Author,
+    Comment,
+    Post,
+    PostFollower,
+    Record,
+    StatefulPostFollower,
+    TempEntity,
+    User
+  }
 
   require Ash.Query
 
@@ -130,6 +140,32 @@ defmodule AshPostgres.Test.LoadTest do
     assert length(post.active_followers) == 2
   end
 
+  test "many_to_many loads work with filter on the join relationship via the parent" do
+    post =
+      Post
+      |> Ash.Changeset.for_create(:create, %{title: "a"})
+      |> Ash.create!()
+
+    for i <- 1..2 do
+      user =
+        User
+        |> Ash.Changeset.for_create(:create, %{name: "user#{i}", is_active: true})
+        |> Ash.create!()
+
+      PostFollower
+      |> Ash.Changeset.for_create(:create, %{order: i, post_id: post.id, follower_id: user.id})
+      |> Ash.create!()
+    end
+
+    [post] =
+      Post
+      |> Ash.Query.for_read(:read, %{})
+      |> Ash.Query.load(:first_3_followers)
+      |> Ash.read!()
+
+    assert length(post.first_3_followers) == 2
+  end
+
   test "many_to_many loads work when nested" do
     source_post =
       Post
diff --git a/test/support/resources/post.ex b/test/support/resources/post.ex
index 7d2764cb..db11f897 100644
--- a/test/support/resources/post.ex
+++ b/test/support/resources/post.ex
@@ -524,6 +524,15 @@ defmodule AshPostgres.Test.Post do
       destination_attribute_on_join_resource: :follower_id
     )
 
+    many_to_many(:first_3_followers, AshPostgres.Test.User,
+      public?: true,
+      through: AshPostgres.Test.PostFollower,
+      join_relationship: :first_three_followers_assoc,
+      source_attribute_on_join_resource: :post_id,
+      destination_attribute_on_join_resource: :follower_id,
+      filter: expr(parent(first_three_followers_assoc.order) <= 3)
+    )
+
     many_to_many(:stateful_followers, AshPostgres.Test.User,
       public?: true,
       through: AshPostgres.Test.StatefulPostFollower,
diff --git a/test/support/resources/profile.ex b/test/support/resources/profile.ex
index 0455f9b9..862887c4 100644
--- a/test/support/resources/profile.ex
+++ b/test/support/resources/profile.ex
@@ -19,6 +19,30 @@ defmodule AshPostgres.Test.Profile do
     default_accept(:*)
 
     defaults([:create, :read, :update, :destroy])
+
+    read :by_indirectly_matching_description do
+      argument :term, :string do
+        allow_nil?(false)
+      end
+
+      filter(expr(calc_word_similarity(term: ^arg(:term)) > 0.2))
+    end
+
+    read :by_directly_matching_description do
+      argument :term, :string do
+        allow_nil?(false)
+      end
+
+      filter(expr(trigram_word_similarity(description, ^arg(:term)) > 0.2))
+    end
+  end
+
+  calculations do
+    calculate :calc_word_similarity,
+              :float,
+              expr(trigram_word_similarity(description, ^arg(:term))) do
+      argument(:term, :string, allow_nil?: false)
+    end
   end
 
   relationships do
diff --git a/test/support/trigram_word_similarity.ex b/test/support/trigram_word_similarity.ex
new file mode 100644
index 00000000..4b7d0bfb
--- /dev/null
+++ b/test/support/trigram_word_similarity.ex
@@ -0,0 +1,14 @@
+defmodule AshPostgres.Expressions.TrigramWordSimilarity do
+  @moduledoc false
+  use Ash.CustomExpression,
+    name: :trigram_word_similarity,
+    arguments: [[:string, :string]],
+    # setting to true does not seem to change the behaviour observed in this ticket
+    predicate?: false
+
+  def expression(data_layer, [left, right]) when data_layer in [AshPostgres.DataLayer] do
+    {:ok, expr(fragment("word_similarity(?, ?)", ^left, ^right))}
+  end
+
+  def expression(_data_layer, _args), do: :unknown
+end