Python Sqlalchemy:使用两个数组类型列的筛选操作
我有两个类:问题和用户。问题类有一个名为Python Sqlalchemy:使用两个数组类型列的筛选操作,python,arrays,sqlalchemy,Python,Arrays,Sqlalchemy,我有两个类:问题和用户。问题类有一个名为category\u id的字段,类型为Column(数组(整数)),而用户类有一个名为preferences的对象,该对象有一个名为ignored\u categories的字段,类型也为Column(数组(整数)) 我试图从用户的首选项中查询出现在忽略的\u类别字段中的类别ID字段中至少没有一个值的所有问题,或者如果前面的值为真,在category\u id字段中有用户首选项字段ignore\u categories之外的任何其他值。因此,以下情况应该
category\u id
的字段,类型为Column(数组(整数))
,而用户类有一个名为preferences
的对象,该对象有一个名为ignored\u categories
的字段,类型也为Column(数组(整数))
我试图从用户的首选项中查询出现在忽略的\u类别
字段中的类别ID
字段中至少没有一个值的所有问题,或者如果前面的值为真,在category\u id
字段中有用户首选项字段ignore\u categories
之外的任何其他值。因此,以下情况应该是正确的:
=[]和category\u id
=[1,2,3]=>应该通过ignored\u categories
=[1,2,4]和category\u id
=[1,2,3]=>应该通过ignored\u categories
=[1]和category\u id
=[1,2,3]=>应该忽略ignored\u categories
=[1,2,3]和category_id
=[1,2,3]=>应该忽略忽略的_分类
=[1,2,3,4]和category\u id
=[1,2,3]=>应该通过ignored\u categories
=[5,7]和category\u id
=[1,2,3]=>应该通过ignored\u categories
user = DBSession.query(User).filter_by(id=user_id).one() # this gets the user object
query = DBSession.query(Question).order_by(Question.created_at.desc())
query = query.filter(
or_(
not_(
Question.category_ids.overlap(user.preferences.ignored_categories)
),
Question.category_ids.contains(user.preferences.ignored_categories)
)
)
这方面的问题是,如果类别ID
是忽略的类别
的超集,则仅包含
测试,这无法为以下数据集提供正确的结果:
=[1]和category\u id
=[1,2,3]=>应该忽略ignored\u categories
overlap
功能仅测试两个数组中是否至少存在一个值,但这会使测试失败,例如:
=[1,2,4]和category\u id
=[1,2,3]=>应该通过ignored\u categories
class User(Base, DictSerializable):
__tablename__ = 'users'
__table_args__ = dict(schema='user')
id = Column(types.Id, primary_key=True)
# other fields
class UserPreferences(Base, DictSerializable):
__tablename__ = 'user_preferences'
__table_args__ = dict(schema='user')
id = Column(types.Id, primary_key=True)
user_id = Column(types.Id, ForeignKey(User.id))
ignored_categories = Column(types.ARRAY(types.Number), default=[])
# other fields
user = relationship("User",
backref=backref("preferences", single_parent=True, cascade="all, delete-orphan",
passive_deletes=True, uselist=False),
)
class Question(Base, DictSerializable):
__tablename__ = 'questions'
__table_args__ = dict(schema='question')
id = Column(types.Id, primary_key=True)
user_id = Column(types.Id, ForeignKey(User.id, ondelete="CASCADE", onupdate="CASCADE"))
# other fields
category_ids = Column(types.ARRAY(types.Integer))
user = relationship("User", foreign_keys=user_id,
backref=backref("questions", order_by=id, single_parent=True, uselist=True,
cascade="all, delete-orphan", passive_deletes=True)
)
question_categories = Table('question_categories', Base.metadata,
Column('question_id', types.Integer, ForeignKey(Question.id)),
Column('category_id', types.Integer, ForeignKey(Category.id)) )
Question.categories = relationship(Category, secondary=question_categories, backref=backref('questions'))
这个问题相当于在
category\u id
中找到一个元素,该元素在ignored\u categories
中不存在,在特殊情况下,如果category\u id
为空,则不会忽略该元素
这可以通过连接表实现,如下所示:
SELECT DISTINCT questions.*
FROM questions
LEFT JOIN question_categories ON questions.id = question_categories.question_id
WHERE (
question_categories.category_id IS NULL -- special case where question has no categories
OR question_categories.category_id NOT IN (
SELECT category_id FROM user_ignored_categories WHERE user_id = 123 -- for this user
) -- find a category that isn't ignored
)
要对阵列执行此操作,可以执行以下操作:
SELECT questions.*
FROM questions
WHERE (
cardinality(categories) = 0 -- special case where question has no categories
OR NOT ((SELECT ignored_categories FROM users WHERE id = 123) @> categories)
);
您可以根据您的数据测试这些设置。您需要的是设置差异操作。不能使用交集(“重叠”)和子集(“包含”)模拟集差异。如何实现设置差异取决于您的数据库。由于这个原因,数组通常被认为不如联接表灵活。@univerio我用表结构更新了这个问题。你能给我推荐一下我使用联接静默的方式吗?我所说的“联接表”是指
创建表问题\类别(问题\ id int,类别\ id int,主键(问题\ id,类别\ id)
。对于你的特定情况,你可能可以忽略忽略\类别。包含(类别\ id)
,说明了类别ID
为空的情况。您使用的数据库是什么?我怀疑Postgresql使用了架构和数组,如果是这样的话,@univerio这样的表已经存在。我已经用它更新了问题(问题类别
)。我需要category\u id
列,以便更快地访问类别,而不必将所有类别作为对象数组发送。但我仍然无法看到如何使用该表筛选我想要的问题。