我通过标记发布了类别和标签,标签和类别包含hstore name
字段,其中包含密钥中的翻译。
如何通过纯Ecto查询预加载相关的post.tags
并选择带有例如“pl”键的JSON tag.name
?我想在这种情况下传递locale键“pl”作为参数,但我不知道如何插值,没有任何作用。
defmodule Myapp.Tag do
use Myapp.Web, :model
schema "tags" do
field :name, :map
has_many :taggings, Myapp.Tagging
has_many :posts, through: [:taggings, :post]
belongs_to :post, Myapp.Post
timestamps
end
end
defmodule Myapp.Tagging do
use Myapp.Web, :model
schema "taggings" do
belongs_to :post, Myapp.Post
belongs_to :tag, Myapp.Tag
end
end
defmodule Myapp.Post do
use Myapp.Web, :model
schema "posts" do
field :title, :string
belongs_to :category, Myapp.Category
has_many :taggings, Myapp.Tagging
has_many :tags, through: [:taggings, :tag]
timestamps
end
end
测试:
post = Repo.get!(Post, id) |> Repo.preload([:tags, :category])
post.tags # => "tags":[{"name":{"pl":"narty","en":"ski"}},{"name":{"pl":"wspinaczka","en":"climbing"}}]
#how preload all with selected key?
posts = Post
|> Repo.all
|> Repo.preload(tags: from(t in Myapp.Tag, select: fragment("?::json->?", t.name, "pl")))
错误:
** (BadMapError) expected a map, got: "narty"
(stdlib) :maps.find(:id, "narty")
(elixir) lib/map.ex:27: Map.fetch!/2
(elixir) lib/enum.ex:1387: Enum."-reduce/3-lists^foldl/2-0-"/3
(elixir) lib/enum.ex:1043: anonymous fn/3 in Enum.map/2
(elixir) lib/enum.ex:1387: Enum."-reduce/3-lists^foldl/2-0-"/3
(elixir) lib/enum.ex:1043: Enum.map/2
(elixir) lib/enum.ex:1043: anonymous fn/3 in Enum.map/2
(elixir) lib/enum.ex:1387: Enum."-reduce/3-lists^foldl/2-0-"/3
(elixir) lib/enum.ex:1043: Enum.map/2
(stdlib) erl_eval.erl:669: :erl_eval.do_apply/6
(stdlib) erl_eval.erl:438: :erl_eval.expr/5
(iex) lib/iex/evaluator.ex:117: IEx.Evaluator.handle_eval/5
我想:
post.tags # => "tags":["narty","wspinaczka"]
迁移:
defmodule Myapp.Repo.Migrations.CreateTag do
use Ecto.Migration
def change do
create table(:tags) do
add :name, :map
add :category_id, references(:categories)
timestamps
end
end
end
defmodule Myapp.Repo.Migrations.CreateJoinTableTaggings do
use Ecto.Migration
def change do
create table(:taggings) do
add :post_id, references(:posts)
add :tag_id, references(:tags)
end
create index(:taggings, [:post_id, :tag_id])
end
end
更新
Ecto 2.0支持预加载查询中的自定义选择字段,因此这就是答案:
|> Repo.preload(tags: from(t in Myapp.Tag, select: %{id: t.id, data: fragment("?::json->?", t.name, "pl")}))
答案 0 :(得分:1)
一种方法是使用Enum.map函数过滤标记。
Repo.get!(Post, id) |> Repo.preload([:tags])
|> Map.get(:tags)
|> Enum.map(&(&1.name.pl))
这是相同的,只是语法不同:
Repo.get!(Post, id) |> Repo.preload([:tags])
|> Map.get(:tags)
|> Enum.map(fn(tag) -> tag.name.pl end)
返回一个列表
["narty", "wspinaczka"]
答案 1 :(得分:1)
另一种方法是使用Ecto.Query
模块并使用Ecto查询。
虽然Repo.preload方法有点不同所以我不确定它是不是你正在寻找的东西。
基于之前的多个数据模型:
query = from(from t in MyApp.Tag, where: t.post_id == ^id, select: t)
Repo.all(query) |> Enum.map(&(&1.name.pl))
基于编辑的较新的多个数据模型,这应该更接近:
post_id = "some_id" # <-- enter your id here
query = Ecto.Query.from tagging in MyApp.Tagging,
join: tag in assoc(tagging, :tags),
where: tagging.post_id == ^post_id,
select: tag
Repo.all(query) |> Enum.map(&(&1.name.pl))
你可以沿着这些方向尝试一下...... 我不确定它是否会起作用,因为我无法运行它,可能需要修改json片段查询..
Repo.all(
from tagging in MyApp.Tagging,
join: tag in assoc(tagging, :tags),
where: tagging.post_id == ^post_id,
select: fragment("?::json#>'{?,?}'", tag, ^"name", ^"pl")
)
答案 2 :(得分:1)
不确定是否可以覆盖:tags
(即)Repo.get!(Post, id) |> Repo.preload(tags: from(t in Tag, where: t.name == 'pl'))
但是您可以过滤预加载并映射该值。
usort($council_members->d, function($a, $b){
return $a->PositionDisplayOrder > $b->PositionDisplayOrder;
});
查看documentation了解详情。