2

タグ付けによるカテゴリと多くのタグを含む投稿があります。タグとカテゴリにはname、キーの翻訳を含むhstoreファイルが含まれています。

純粋なEctoクエリで、たとえば「pl」キーを使用して関連post.tagsするJSONをプリロードして選択するにはどうすればよいですか? tag.nameこの場合、「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")}))
4

3 に答える 3

1

これを行う 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"]

于 2016-01-15T12:23:57.700 に答える
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")
) 
于 2016-01-15T14:16:48.647 に答える
1

Ecto.Association(ie)をオーバーライドできるかどうかはわかりませんが:tags、プリロードをフィルタリングして値をマップすることはできます。

Repo.get!(Post, id) |> Repo.preload(tags: from(t in Tag, where: t.name == 'pl'))

詳細については、ドキュメントを確認してください。

于 2016-01-15T16:29:20.380 に答える