代码之家  ›  专栏  ›  技术社区  ›  edbras

如何使用sqlacalchemy根据计算的json值选择postgresql记录?

  •  1
  • edbras  · 技术社区  · 6 年前

    我有一个Postgres表,其中有两列用于查询:

    start_date_detail: date 
    flex: jsonb
    

    flex字段示例:

    {
        "communication": [
            {
                "remind_on": "mxmw@cxla.nl",
                "type": "email",
                "remind_date": -150
            },
            {
                "remind_on": "+31612345678",
                "type": "sms",
                "remind_date": -360
            }
        ]
    }
    

    我需要选择上周有提醒日期的所有记录,所以(伪代码):

    (now() - 1-week) < (start_date_detail + remind_date) < now()
    

    我怎么能用sqlacalchemy意识到这一点呢?

    因为它是查询中的一个计算值,所以我不知道怎么做。 在《Postgres》中,我提到了这一点:

    SELECT * FROM time_item 
    WHERE 
        (start_date_detail + INTERVAL '1 second' * (flex->'communication'->0->>'remind_date')::numeric <= NOW())
        OR (start_date_detail + INTERVAL '1 second' * (flex->'communication'->1->>'remind_date')::numeric <= NOW())
    

    如何将其放入sqlacalchemy中?

    还有一件事: 在上面的查询中,我将每个通信项添加到WHERE子句中。我怎样才能使这个更灵活呢?也就是说,我不需要为每个通信项都添加WHERE子句。

    1 回复  |  直到 6 年前
        1
  •  2
  •   Ilja Everilä    6 年前

    你可以用 jsonb_array_elements() 将数组扩展到一组 jsonb 然后可以在谓词中使用的元素。通过使用 alias . 使用诸如

    In [4]: class TimeItem(Base):
       ...:     __tablename__ = 'time_item'
       ...:     id = Column(Integer, primary_key=True)
       ...:     start_date_detail = Column(Date)
       ...:     flex = Column(JSONB)
       ...:     
    

    查询可能看起来像

    In [39]: session.query(TimeItem).\
        ...:     select_from(TimeItem,
        ...:                 func.jsonb_array_elements(TimeItem.flex['communication']).
        ...:                     alias('comm')).\
        ...:     filter((TimeItem.start_date_detail +
        ...:             timedelta(seconds=1) *
        ...:             column('comm', type_=JSONB)['remind_date'].
        ...:                 astext.
        ...:                 cast(Integer)).
        ...:                     between(func.now() - timedelta(weeks=1),
        ...:                             func.now())).\
        ...:     all()
    

    然后你可以根据你的需要调整谓词,我试着用你的例子来说明。 remind_date 被解释为偏移秒到 start_date_detail 与前一周相比。查询实体时,例如 TimeItem SQLAlchemy根据对象标识执行自己的重复数据消除,因此查询可以省略SQL端distinct,移动现有子查询表达式中的数组元素,或者类似的操作。