1

For example:

const update = ({title, price, imageUrl, description, whereId, whereTitle }) => {
    return db.query(
        `UPDATE products 
            SET title=$1, 
                price=$2,
                "imageUrl"=$3,
                description=$4 
            WHERE 
                id=$5,
                title ILIKE '%$6%'
            `,
        [title, price, imageUrl, description, whereId, whereTitle],
    )
}

update({
    whereId: 1,
    title: 'Aliens 2: Electric Boogaloo',
    description: 'Best sequel ever',
})
// shall execute this query:
// UPDATE products SET title='Aliens 2: Electric Boogaloo', description'Best sequel ever' WHERE id=1
// and keeping the price, "imageUrl" untouched / previous value

update({
    whereTitle: 'godfather',
    price: 20,
})
// shall execute this query:
// UPDATE products SET price=20 WHERE title ILIKE '%godfather%'
// or set the price of every products that contain the word 'godfather' as $20
// and keeping the title, "imageUrl", description, id untouched / previous value

Right now, I have to create a query for every possible query

  • SET title WHERE id

  • SET title, price WHERE id

  • SET price, "imageUrl" WHERE title

  • ...

Which is not very dry, therefore I think there must be a better way of doing it

Edit 1:

This is what I imagine the code would look like.

db.query(
    `UPDATE products 
        SET 
            title !== null? "title=$1," : skipToNextLine() 
            price !== null? "price=$2," : skipToNextLine()
            "imageUrl" !== null? "\"imageUrl\"=$3," : skipToNextLine()
            description !== null? "description=$4"  : skipToNextLine()
        whereId !== null && whereTitle !== null? "WHERE" : skipToNextLine()
            whereId !== null? "id=$5," : skipToNextLine()
            whereTitle !== null? "title ILIKE '%$6%'" : skipToNextLine()
        `,
    [title, price, imageUrl, description, whereId, whereTitle],
)

I do it in a style of ternary expression but there are plenty of other styles. I think the idea is similar to html's templating language like pug.js, ejs, handlebars, etc. There must be a way other than writing dozens of static html files for your html.

Edit 2:

I used string concatenation for quick and dirty solution, but I wonder if it is vulnerable to SQL injection. The code also becomes very long and code reliability / ease of comprehension drops significantly.

Here is my current solution:

const update = ({
    title,
    price,
    imageUrl,
    description,
    whereId,
    whereTitle,
}) => {
    let queryText = 'UPDATE products SET'
    let queryValue = []
    let dollarCounter = 1
    if (!!title) {
        queryText = queryText.concat(` title=$${dollarCounter},`)
        queryValue.push(title)
        dollarCounter++
    }
    if (!!price) {
        queryText = queryText.concat(` price=$${dollarCounter},`)
        queryValue.push(price)
        dollarCounter++
    }
    if (!!imageUrl) {
        queryText = queryText.concat(` "imageUrl"=$${dollarCounter},`)
        queryValue.push(imageUrl)
        dollarCounter++
    }
    if (!!description) {
        queryText = queryText.concat(` description=$${dollarCounter},`)
        queryValue.push(description)
        dollarCounter++
    }
    queryText = queryText.slice(0, -1)
    if (!!whereId || !!whereTitle) queryText = queryText.concat(' WHERE')
    if (!!whereId) {
        queryText = queryText.concat(` id=$${dollarCounter},`)
        queryValue.push(whereId)
        dollarCounter++
    }
    if (!!whereTitle) {
        queryText = queryText.concat(` title ILIKE '%$${dollarCounter}%',`)
        queryValue.push(whereTitle)
        dollarCounter++
    }
    queryText = queryText.slice(0, -1)
    const query = {
        name: 'update',
        text: queryText,
        values: queryValue,
    }
    return db.query(query)
}
calvindio
  • 347
  • 3
  • 11
  • If you re interested in an ORM solution try with [sequelize](https://sequelize.org/) – Vanojx1 Jan 30 '20 at 07:37
  • It would be easy enough to do this but any functionality created for this would be too specific to your particular query here to be able to be used elsewhere. For example, what kind of logic could you code to determine that "whereTitle" needs an `ilike`, but "whereId" requires `=`? There's nothing about the data to make that determination without hardcoding for your specific use case. If you're concerned with DRY, you should restructure your data so this code can actually be used elsewhere, which means this should be able to be determined based on some other (currently nonexistent) attributes. – 404 Jan 30 '20 at 12:54
  • @Vanojx1 I was advised not to use sequelize because: - I'm quite comfortable with postgresql's syntax - I heard complicated query can be hard / impossible with orm - I heard orm is less performant than vanila pg – calvindio Jan 31 '20 at 05:54
  • @404 I added the pseudocode of what I imagine the code would look like. Basically it is like an html templating language where you can generate a dynamic html file, but there must be a way to do that too with psql query. It is like ternary expression: if true add this line else go to next line – calvindio Jan 31 '20 at 06:12

0 Answers0