question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Upsert not working when querying for multiple fields?

See original GitHub issue

So I’ve been able to successfully use the upsert function to update data when querying for a single field, but when passing a has of multiple fields I get the following error:

Error: A model cannot be updated without a "where" clause or an idAttribute.

For example, this works:

return Models.Artist
      .upsert({ name: name }, { website: website })
      .then(model => model.toJSON())

Where Artist has 2 columns: name and website and I want to search existing artists by name, update their website field if the artist already exists, create if not found.

But this does not work:

return Models.Printing
        .upsert({ card: card, set: set, number: number }, { ...fields })
        .then(model =>  model.toJSON())

Where Printing has about a dozen fields (which I am using spread syntax here as the fields are irrelevant), and I want to search existing printings by card, set, and number, ie:

SELECT * FROM printing WHERE card = '3' AND set = '2' AND number = '5'

Of course this will work when a row does not already exist, because it will be created instead. The problem only arises when trying to update an existing row.

For reference, Printing does have an ID attribute, so if a selection is successfully made the result should have an ID attribute, so I don’t know why that would cause the error.

I’ve tried re-writing upsert in different ways, such as:

upsert: function (selectData, updateData, options) {
  return this.findOne(selectData, extend(options, { require: false }))
    .bind(this)
    .then(function (model) {
      return model
        ? model.where(selectData).save(
          updateData,
          extend({ patch: true, method: 'update' }, options)
        )
        : this.create(
          extend(selectData, updateData),
          extend(options, { method: 'insert' })
        )
    });
}

To ensure that if there was not an ID attribute, the where condition was met as per the error message. But doing so results in a model being returned with only the selectData attributes.

I’m not sure what to do from here short of dropping down into Knex and trying to be more explicit in constructing my queries than the methods provided by Bookshelf. That seems unnecessarily complicated for what should be a simple problem.

Issue Analytics

  • State:open
  • Created 6 years ago
  • Comments:5 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
Saeriscommented, May 17, 2017

I can try to put together an example for you this evening, but for now you can just refer to my project to see what’s going on:

https://github.com/Saeris/Scribe

I’ve had upsert work for me in one of two ways:

  1. For selectData, I pass a single field (ie: { name: "string" }), for which it returns the first result, which as you point out is what it should do.

  2. For selectData and updataData I pass in every field, which either matches the first result, but more often just creates an entirely new row.

According to your description of how findOne works, it should return to me the first match for the hash of fields I pass to it. Because I never want to have duplicate rows in this case, where what I have here is basically a composite key of card set, and number, that key should be unique in that table. I should never have to deal with collections because of that, and ideally I would like to avoid doing so.

Right now upsert works fine for most of my other tables, because most of them are uniquely named. I’m leaving out the context as that’s irrelevant to the issue, but you can gather that from looking at the project I linked above.

Let me know if that helps your understanding, and I’ll get back to you with more info.

0reactions
bsiddiquicommented, May 16, 2017

As I mentioned earlier, need some more info if you’re still having an issue with ModelBase.upsert, which finds the first match and updates it or creates

Read more comments on GitHub >

github_iconTop Results From Across the Web

Upsert on a table that has multiple fields and jsonb column ...
Have another look at the INSERT syntax. ③ Assuming you meant jsonb_build_object(r.thing_type, r.thing_count) . What you had there didn't work.
Read more >
Upsert on a table that has multiple fields and jsonb column ...
So I was trying to do an upsert based on a query result: /* Querying from this table: id | arrive_date | arrive_location...
Read more >
How to Update Multiple Columns in Single Update Statement ...
We can update multiple columns by specifying multiple columns after the SET command ... Query: CREATE DATABASE geeks;. Step 2: Use database.
Read more >
Set for multiple fields in update query - Elixir Forum
Yes, upsert doesn't support FROM . My full raw SQL query looks like: INSERT INTO table (field_a, field_b) VALUES ('', ''), ('', ''),...
Read more >
SQL UPDATE - Dofactory
UPDATE changes data values in a database. UPDATE can update one or more records in a table. Use the WHERE clause to UPDATE...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found