So I have the following resource that I want to run through tika and then save the generated tsvectors to the DB:
defmodule MyApp.Documents.Document do
use Ash.Resource,
otp_app: :my_app,
domain: MyApp.Documents,
data_layer: AshPostgres.DataLayer,
extensions: [AshOban]
import Ecto.Query
postgres do
table "documents"
repo MyApp.Repo
end
actions do
defaults [:read, :destroy]
update :index_file do
change MyApp.Changes.ExtractText
require_atomic? false
end
end
attributes do
uuid_primary_key :id
attribute :title, :string, allow_nil?: false
attribute :indexed?, :boolean, allow_nil?: false, default: false
attribute :tsvectors, {:array, AshPostgres.Tsvector}
timestamps()
end
end
defmodule MyApp.Changes.ExtractText do
@moduledoc """
Extracts the text from a file
"""
alias MyApp.TikaClient
use Ash.Resource.Change
import Ecto.Query
@impl true
def change(changeset, _opts, context) do
content = TikaClient.extract_text!(changeset.data)
title = Ash.Changeset.get_attribute(changeset, :title)
context |> dbg()
# 1) let Postgres do the vector math for us
tsvector =
MyApp.Repo.one(
from d in MyApp.Documents.Document,
select:
fragment(
"""
setweight(to_tsvector('simple', ?), 'A')
||
setweight(to_tsvector('simple', ?), 'B')
""",
^title,
^content
),
limit: 1
)
# the tsvector appears to be correct: [%Postgrex.Lexeme{}, %Postgrex....]
# However, even using those stilll fails in the change_attribute call below.
# It also fails for this hardcoded value.
# The only value that works is []
changeset
|> Ash.Changeset.change_attribute(:tsvectors, [
%Postgrex.Lexeme{word: "00.01", positions: [{1, :A}]}
])
|> Ash.Changeset.change_attribute(:indexed?, true)
end
end
My migration looks like this:
defmodule MyApp.Repo.Migrations.AddTsvectorsToDocuments do
@moduledoc """
Updates resources based on their most recent snapshots.
This file was autogenerated with `mix ash_postgres.generate_migrations`
"""
use Ecto.Migration
def up do
alter table(:documents) do
add :tsvectors, {:array, :tsvector}
end
end
def down do
alter table(:documents) do
remove :tsvectors
end
end
end
The tsvectors
appears to be correct but any time I want to save something other than []
i just get:
[error] Error occurred for MyApp.Documents.Document: %{"id" => "7ec154fe-7c3f-4f77-9b16-fb1ab9500157"}!
Error occurred on action: index_file.
"** (Ash.Error.Invalid) \nBread Crumbs:\n > Error returned from: MyApp.Documents.Document.index_file\n\nInvalid Error\n\n* Invalid filter value `[%Postgrex.Lexeme{word: \"00.01\", positions: [{1, :A}]}]` supplied in `#Ecto.Changeset<action: nil, changes: %{indexed?: true, tsvectors: [%Postgrex.Lexeme{word: \"00.01\", positions: [{1, :A}]}], updated_at: ~U[2025-05-24 06:42:02.256998Z]}, errors: [], data: #MyApp.Documents.Document<>, valid?: true, ...>`\n (elixir 1.18.3) lib/enum.ex:2546: Enum.\"-reduce/3-lists^foldl/2-0-\"/3\n (elixir 1.18.3) lib/enum.ex:1840: Enum.\"-map_reduce/3-lists^mapfoldl/2-0-\"/3\n (elixir 1.18.3) lib/enum.ex:2546: Enum.\"-reduce/3-lists^foldl/2-0-\"/3\n (ecto 3.12.5) lib/ecto/repo/queryable.ex:214: Ecto.Repo.Queryable.execute/4\n (ash_postgres 2.5.20) lib/data_layer.ex:1532: AshPostgres.DataLayer.update_query/4\n (ash_postgres 2.5.20) lib/data_layer.ex:3110: AshPostgres.DataLayer.update/2\n (ash 3.5.11) lib/ash/actions/update/update.ex:523: anonymous fn/5 in Ash.Actions.Update.commit/3\n (ash 3.5.11) lib/ash/changeset/changeset.ex:4226: Ash.Changeset.run_around_actions/2\n (ash 3.5.11) lib/ash/changeset/changeset.ex:3904: anonymous fn/2 in Ash.Changeset.transaction_hooks/2\n (ash 3.5.11) lib/ash/changeset/changeset.ex:3814: Ash.Changeset.with_hooks/3\n (my_app 0.1.0) deps/ash_oban/lib/transformers/define_schedulers.ex:1051: MyApp.Documents.Document.AshOban.Worker.IndexFile.perform/1\n (oban 2.19.4) lib/oban/queue/executor.ex:145: Oban.Queue.Executor.perform/1\n (oban 2.19.4) lib/oban/queue/executor.ex:77: Oban.Queue.Executor.call/1\n (elixir 1.18.3) lib/task/supervised.ex:101: Task.Supervised.invoke_mfa/2\n (elixir 1.18.3) lib/task/supervised.ex:36: Task.Supervised.reply/4\n"
Invalid filter value
I have no idea why?
ash error
{:error,
%Ash.Error.Invalid{
bread_crumbs: ["Error returned from: MyApp.Documents.Document.index_file"],
changeset: "#Changeset<>",
errors: [
%Ash.Error.Query.InvalidFilterValue{
message: nil,
value: [%Postgrex.Lexeme{word: "00.01", positions: [{1, :A}]}],
context: #Ecto.Changeset<
action: nil,
changes: %{
indexed?: true,
tsvectors: [%Postgrex.Lexeme{word: "00.01", positions: [{1, :A}]}],
updated_at: ~U[2025-05-24 06:50:28.869995Z]
},
errors: [],
data: #MyApp.Documents.Document<>,
valid?: true,
...
>,
splode: Ash.Error,
bread_crumbs: ["Error returned from: MyApp.Documents.Document.index_file"],
vars: [],
path: [],
stacktrace: #Splode.Stacktrace<>,
class: :invalid
}
]
}}
ecto select output
MyApp.Repo.one(
from(d in MyApp.Documents.Document,
select:
fragment(
"setweight(to_tsvector('simple', ?), 'A') || setweight(to_tsvector('simple', ?), 'B')",
^title,
^content
),
limit: 1
)
) #=> [
%Postgrex.Lexeme{word: "00.01", positions: [{1, :A}]},
%Postgrex.Lexeme{word: "document", positions: [{5, :B}]},
%Postgrex.Lexeme{word: "handbuch.pdf", positions: [{3, :A}]},
%Postgrex.Lexeme{word: "hello", positions: [{6, :B}]},
%Postgrex.Lexeme{word: "niels", positions: [{7, :B}]},
%Postgrex.Lexeme{word: "qm", positions: [{2, :A}]},
%Postgrex.Lexeme{word: "word", positions: [{4, :B}]}
]