5

I have a model with a JSON field:

class Item(db.Model)
   ...
   data = db.Column(JSON, nullable=False)
   ...

The data contains some JSON such as:

{
    "cost": 10.00,
    "passengers": 2,
    "surcharge": 1.6
}

I want to be able to get a sum of the cost across all rows in the table with a filter. I tried the following but that didn't seem to work.

db.session.query(func.count(Item.data['cost'])).filter(
          Item.data["surcharge"].cast(Float) > 1
      ).scalar()

2 Answers 2

10

You're using the wrong aggregate. count(expression) counts the number of rows for which the expression is not null. If you want a sum, use sum(expression):

db.session.query(func.sum(Item.data['cost'].astext.cast(Numeric))).\
    filter(Item.data['surcharge'].astext.cast(Numeric) > 1).\
    scalar()

Note that monetary values and binary floating point math is a bad mixture due to binary floats not being able to represent all decimal values. Instead use a proper monetary type, or Numeric in which case SQLAlchemy uses Decimal to represent the results in Python.

Sign up to request clarification or add additional context in comments.

2 Comments

Though PostgreSQL JSONB stores numbers in numeric, and JSON as decimal strings... However, needs some special handling with Python ...
Great answer but I had to use as_string() instead of astext (SQLAlchemy 1.3.23)
0

You need to use Float instead of Integer as argument of cast

db.session.query(func.count(Item)).filter(
          Item.data['surcharge'].cast(Float) > 1
      ).all()

1 Comment

Fair point, I've updated the query but that still doesn't work. It just returns 0.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.