ZastrixArundell

ZastrixArundell

Indexes are ignored

So I have a bit of an issue on my application and I don’t know how it is caused. I’m not sure whether my code is fail-safe. I created a table with a context which has an an index on 2 elements but for some reason sometimes the elements are added so I have 2+ elements with the same index so I have an issue.

Here’s my migration file:

defmodule App.Repo.Migrations.CreateCounts do
  use Ecto.Migration

  def change do
    create table(:counts) do
      add :date, :string
      add :count, :integer
      add :owner_id, references(:users, on_delete: :nothing)

      timestamps()
    end

    create index(:counts, [:owner_id, :date])
  end
end

And then my count module:

defmodule App.Count do
  use Ecto.Schema
  import Ecto.Changeset
  import Ecto.Query
  alias App.Repo

  schema "counts" do
    field :count, :integer
    field :date, :string
    field :owner_id, :id

    timestamps()
  end

  def changeset(email_count, attrs) do
    email_count
    |> cast(attrs, [:date, :count, :owner_id])
    |> validate_required([:date, :count, :owner_id])
  end

  def increment(struct) do
    %__MODULE__{struct | count: struct.count + 1}
  end

  def create_new() do
    today = Timex.now
    %__MODULE__{date: Timex.format!(today, "{0D}/{0M}/{YYYY}"), count: 0}
  end

  def set_owner_id(struct, owner) do
    %__MODULE__{struct | owner_id: owner.id}
  end

  def update_count(owner) do
    id = owner.id
    date_now = Timex.format!(Timex.now, "{0D}/{0M}/{YYYY}")

    buffer =
      from(e in __MODULE__, where: e.owner_id == ^id, where: e.date == ^date_now)
      |> Repo.one()

    if buffer != nil do
      data = buffer |> increment() |> Map.from_struct()
      changeset(buffer, data)
      |> Repo.update()
    else
      data = create_new() |> set_owner_id(owner) |> increment() |> Map.from_struct()
      changeset(%__MODULE__{}, data)
      |> Repo.insert()
    end
  end
end

But then sometimes (I am unable to create it at will) I get an error:

%Ecto.MultipleResultsError{
   message: "expected at most one result but got 3 in query:\n\nfrom e0 in App.Count,\n  where: e0.owner_id == ^1,\n  where: e0.date == ^\"10/02/2020\"\n"
 }
``

It looks like the indexes are being ignored because it inserts values with the same `owner_id` and `date`?:

[
%App.Count{
meta: #Ecto.Schema.Metadata<:loaded, “counts”>,
count: 1,
date: “10/02/2020”,
id: 14,
inserted_at: ~N[2020-02-10 13:00:11],
owner_id: 1,
updated_at: ~N[2020-02-10 13:00:11]
},
%App.Count{
meta: #Ecto.Schema.Metadata<:loaded, “counts”>,
count: 1,
date: “10/02/2020”,
id: 15,
inserted_at: ~N[2020-02-10 13:00:11],
owner_id: 1,
updated_at: ~N[2020-02-10 13:00:11]
},
%App.Count{
meta: #Ecto.Schema.Metadata<:loaded, “counts”>,
count: 1,
date: “10/02/2020”,
id: 16,
inserted_at: ~N[2020-02-10 13:00:11],
owner_id: 1,
updated_at: ~N[2020-02-10 13:00:11]
}
]```

Marked As Solved

NobbZ

NobbZ

Unless you put also a unique constraint over the index, it is allowed to have multiple entries.

An index by itself is just a catalog of some small data fragments to a list of table rows that contains that fragment.

As an analogy take a look at a technical book. At the back you usually find a list of words, each pointing to one or many pages in the book explaining the word. This is an index.

Also Liked

ZastrixArundell

ZastrixArundell

I just used unique_index/2 instead of index/2 in my migrations and I don’t have the issue anymore!

outlog

outlog

think what you want is unique_index: Ecto.Migration — Ecto SQL v3.14.0

then you must also handle the conflict case..

additionally I would default the count to 0, and then I would do the increment on the DB side eg. something like this (ecto-fied)

UPDATE counts 
  SET count = count + 1
WHERE owner_id = X and date = Y;

you might just want to bring it all together in an upsert..

Where Next?

Popular in Questions Top

nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 records...
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" =&gt; #BSON.ObjectId&lt;58eb1a7a9ad169198c3dXXXX&gt;, "email" =&gt; ...
New
Emily
I have VueJS GUIs with the project generated using Webpack. I have Elixir modules that will need to be used by the VueJS GUIs. I forese...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
greenz1
I have a phoenix application from which a user can download multiple(5-6) files of size 1MB. I couldn’t find anything related to sending ...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New

Other popular topics Top

Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New
sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
shijith.k
I am trying to start a new phoenix project with elixir 1.9, but mix phx.new does not work. It says that ** (Mix) The task "phx.new" could...
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New

We're in Beta

About us Mission Statement