Does something like this work?

query = db.session.query(PostsModel).join(pn, PostsModel.id ==
pn.c.post_id).filter(pn.c.pn_nodes == db.session.query(n.c.n_nodes))

I can't help feeling this is a very inefficient query though. I still
don't really understand your data model (I don't understand where
topics come into this). But if you've got a many-to-many relationship
between posts and nodes, and you want to find posts that are linked to
*all* of a set of nodes, perhaps you could start with something like
this:

SELECT post_id
FROM pn
WHERE node_id IN (...)
GROUP BY post_id
HAVING count(node_id) = <number of distinct node IDs>

Without the HAVING clause, that query would return all the posts that
match at least one of the nodes. The HAVING clause ensures that the
posts match *all* of the nodes.

Simon

On Tue, Jun 11, 2019 at 3:51 AM Desmond Lim <limwen...@gmail.com> wrote:
>
> Okay, I have ORMed my sql statement:
>
> pn = db.session.query(PNModel.post_id, 
> db.func.array_agg(PNModel.node_id.distinct()).label('pn_nodes')).group_by(PNModel.post_id)
> n = 
> db.session.query(db.func.array_agg(NodesModel.id.distinct()).label('n_nodes')).filter(NodesModel.project_uuid
>  ==
>     project_uuid).filter(NodesModel.topic.in_(topic_list)).subquery()
> query = db.session.query(pn.c.post_id).filter(pn.c.pn_nodes == 
> db.session.query(n.c.n_nodes))
> results = query.all()
>
> This returns what my original SQL returned. My next step is to get all the 
> posts of the post_id in the list
>
> post_id_list = [result[0] for result in results]
> posts = PostsModel.find_by_id_list(id_list=post_id_list)
>
> I'm just wondering, if there is an easy way to get the post object from the 
> query instead of doing the above.
>
> Desmond
>
>
> On Tue, 11 Jun 2019 at 08:30, Desmond Lim <limwen...@gmail.com> wrote:
>>
>> Hi Simon,
>>
>> Sorry I think I really didn't make what I'm asking for clear.
>>
>> I have these codes
>>
>> nodes = 
>> NodesModel.find_by_project_uuid_and_topic_list(project_uuid=project_uuid, 
>> topic_list=node_dict['topics'])
>> node_id_list = [node.id for node in nodes]
>>
>> select_sql = text('WITH pn_o AS (SELECT post_id, ARRAY_AGG(DISTINCT node_id) 
>> AS pn_nodes FROM pn GROUP BY post_id), '
>>                   'n_o AS (SELECT ARRAY_AGG(DISTINCT node_id) as n_nodes 
>> FROM pn WHERE node_id IN :input_tuple) '
>>                   'SELECT post_id FROM pn_o WHERE pn_nodes = (SELECT n_nodes 
>> FROM n_o)')
>>
>> params = {"input_tuple": tuple(node_id_list)}
>> results = db.session.execute(select_sql, params).fetchall()
>>
>> Which does what I want. In the end, in results, I get a list of posts that 
>> have nodes that contain all the topics I need, in other words, the posts 
>> contain all the topic words.
>>
>> I'm just wondering, if there is a way to ORM the select_sql as I would 
>> prefer to work with the models than the raw SQL. And I'm doing it this way 
>> because I want the server to do the heavy lifting.
>>
>> Desmond
>>
>> On 10 Jun 2019, 22:41 +0800, Simon King <si...@simonking.org.uk>, wrote:
>>
>> I'm still not sure I understand. Here's an example that does what I
>> *think* you're asking for:
>>
>> import sqlalchemy as sa
>> import sqlalchemy.orm as saorm
>> from sqlalchemy.ext.declarative import declarative_base
>>
>> Base = declarative_base()
>>
>>
>> pn = sa.Table(
>> "pn", Base.metadata,
>> sa.Column("post_id", sa.Integer, sa.ForeignKey("posts.id")),
>> sa.Column("node_id", sa.Integer, sa.ForeignKey("nodes.id")),
>> )
>>
>>
>> class Node(Base):
>> __tablename__ = "nodes"
>> id = sa.Column(sa.Integer, autoincrement=True, primary_key=True)
>> project_uuid = sa.Column(sa.Text, nullable=False)
>> topic = sa.Column(sa.Text(20), nullable=False)
>>
>> posts = saorm.relationship("Post", secondary=pn, backref="nodes")
>>
>>
>> class Post(Base):
>> __tablename__ = "posts"
>> id = sa.Column(sa.Integer, autoincrement=True, primary_key=True)
>> project_uuid = sa.Column(sa.Text, nullable=False)
>>
>>
>> if __name__ == "__main__":
>> engine = sa.create_engine("sqlite://")
>> Base.metadata.create_all(bind=engine)
>> Session = saorm.sessionmaker(bind=engine)
>>
>> projectid = "42a8e275-f153-452b-99d3-645bdd513ca9"
>> topics = ['a', 'b', 'c', 'd']
>> nodes = [Node(project_uuid=projectid, topic=t) for t in topics]
>> posts = [Post(project_uuid=projectid) for i in range(11)]
>>
>> import random
>> random.seed(0)
>> for node in nodes:
>> node.posts = random.sample(posts, 4)
>>
>> session = Session()
>> session.add_all(nodes + posts)
>> session.flush()
>>
>> node_ids = [1, 2]
>> print("Looking for posts with node IDs: %s" % node_ids)
>> posts = (
>> session.query(Post)
>> .join("nodes")
>> .filter(Node.id.in_(node_ids))
>> .all()
>> )
>> for p in posts:
>> print("Post %s: Node IDs %s" % (p.id, [n.id for n in p.nodes]))
>>
>> Simon
>>
>> On Mon, Jun 10, 2019 at 1:22 PM Desmond Lim <limwen...@gmail.com> wrote:
>>
>>
>> Hi Simon,
>>
>> The tables and sample data are below.
>>
>> So first I search the nodes table for a and b, getting the node_id of them 
>> (in this example, 1 and 2).
>>
>> Then using the SQL statement, I would get all posts that have 1 and 2 as the 
>> node_id. So in the example data, 100, 103, 108 will be returned as these 
>> posts contain the nodes 1 and 2.
>>
>> Hope this helps.
>>
>> Desmond
>>
>> -------------
>>
>> CREATE SEQUENCE IF NOT EXISTS nodes_id_seq;
>>
>> CREATE TABLE "public"."nodes" (
>> "id" int8 NOT NULL DEFAULT nextval('nodes_id_seq'::regclass),
>> "project_uuid" uuid NOT NULL,
>> "topic" varchar(20) NOT NULL,
>> PRIMARY KEY ("id")
>> );
>>
>> CREATE SEQUENCE IF NOT EXISTS posts_id_seq;
>>
>>
>> CREATE TABLE "public"."posts" (
>> "id" int8 NOT NULL DEFAULT nextval('posts_id_seq'::regclass),
>> "project_uuid" uuid NOT NULL,
>> PRIMARY KEY ("id")
>> );
>>
>> CREATE TABLE "public"."pn" (
>> "post_id" int8 NOT NULL,
>> "node_id" int8 NOT NULL,
>> PRIMARY KEY ("post_id","node_id")
>> );
>>
>> INSERT INTO "public"."nodes" ("id", "project_uuid", "topic", "size", 
>> "given_id") VALUES
>> ('1', '42a8e275-f153-452b-99d3-645bdd513ca9', 'a'),
>> ('2', '42a8e275-f153-452b-99d3-645bdd513ca9', 'b'),
>> ('3', '42a8e275-f153-452b-99d3-645bdd513ca9', 'c'),
>> ('4', '42a8e275-f153-452b-99d3-645bdd513ca9', 'd')
>>
>> INSERT INTO "public"."posts" ("id", "project_uuid") VALUES
>> ('100', '42a8e275-f153-452b-99d3-645bdd513ca9'),
>> ('101', '42a8e275-f153-452b-99d3-645bdd513ca9'),
>> ('102', '42a8e275-f153-452b-99d3-645bdd513ca9'),
>> ('103', '42a8e275-f153-452b-99d3-645bdd513ca9'),
>> ('104', '42a8e275-f153-452b-99d3-645bdd513ca9'),
>> ('105', '42a8e275-f153-452b-99d3-645bdd513ca9'),
>> ('106', '42a8e275-f153-452b-99d3-645bdd513ca9'),
>> ('107', '42a8e275-f153-452b-99d3-645bdd513ca9'),
>> ('108', '42a8e275-f153-452b-99d3-645bdd513ca9'),
>> ('109', '42a8e275-f153-452b-99d3-645bdd513ca9'),
>> ('110', '42a8e275-f153-452b-99d3-645bdd513ca9')
>>
>> INSERT INTO "public"."pn" ("post_id", "node_id") VALUES
>> ('1', '100'),
>> ('2', '100'),
>> ('3', '100'),
>> ('2', '101'),
>> ('3', '101'),
>> ('4', '101'),
>> ('5', '101'),
>> ('3', '102'),
>> ('4', '102'),
>> ('1', '103'),
>> ('2', '103'),
>> ('3', '103'),
>> ('2', '104'),
>> ('4', '104'),
>> ('1', '105'),
>> ('4', '105'),
>> ('3', '105'),
>> ('3', '106'),
>> ('4', '106'),
>> ('4', '107'),
>> ('1', '108'),
>> ('2', '108'),
>> ('3', '109'),
>> ('1', '109'),
>> ('4', '109'),
>> ('2', '110'),
>> ('3', '110')
>>
>> ALTER TABLE "public"."posts" ADD FOREIGN KEY ("project_uuid") REFERENCES 
>> "public"."projects"("uuid");
>> ALTER TABLE "public"."pn" ADD FOREIGN KEY ("post_id") REFERENCES 
>> "public"."posts"("id");
>> ALTER TABLE "public"."pn" ADD FOREIGN KEY ("node_id") REFERENCES 
>> "public"."nodes"("id");
>>
>> On Mon, 10 Jun 2019 at 17:08, Simon King <si...@simonking.org.uk> wrote:
>>
>>
>> Hi Desmond.
>>
>> I don't really understand your table structure. Could you present it
>> in the form of a standalone script that we can run (including sample
>> data)?
>>
>> Thanks,
>>
>> Simon
>>
>> On Mon, Jun 10, 2019 at 2:57 AM Desmond Lim <limwen...@gmail.com> wrote:
>>
>>
>> Hi there,
>>
>> I really have no idea how to do this via sqlalchemy. I have 2 tables:
>>
>> class NodesModel(db.Model):
>> __tablename__ = 'nodes'
>>
>> id = db.Column(db.BigInteger, primary_key=True)
>> topic = db.Column(db.String(20), nullable=False)
>>
>> pn = relationship("PNModel", backref="nodes")
>>
>> class PNModel(db.Model):
>> __tablename__ = 'pn'
>>
>> post_id = db.Column(db.BigInteger, db.ForeignKey('posts.id'), 
>> primary_key=True)
>> node_id = db.Column(db.BigInteger, db.ForeignKey('nodes.id'), 
>> primary_key=True)
>>
>>
>> I will get a list of nodes that have the topic (e.g. one, two). Then taking 
>> the nodes, I'll select the PN that have both nodes (it can be 1 or more) 
>> associated with it.
>>
>> e.g.
>>
>>
>>
>> I'm doing it this way at this point:
>>
>> nodes = NodesModel.find_by_topic_list(topic_list)
>> node_id_list = [node.id for node in nodes]
>>
>> Then running this sql statement:
>> WITH pn_1 AS (SELECT post_id, ARRAY_AGG(DISTINCT node_id) AS nodes
>> FROM pn GROUP BY post_id),
>> nodes_1 AS (SELECT ARRAY_AGG(DISTINCT id) AS s_nodes
>> FROM pn WHERE node_id IN (dict(node_id_list))
>> SELECT DISTINCT post_id FROM pn_1
>> WHERE nodes_1 = (SELECT s_nodes FROM select_nodes);
>>
>> There has to be a better and more elegant way to do this with SQLAlchemy and 
>> I can't seem to figure it out. I've tried using subqueries and then I 
>> realised that I'm actually calling the nodes table twice, which I shouldn't 
>> have to.
>>
>> Can anyone tell me how to use the nodes table to get the post_id form pn 
>> (after which getting the post should be easy via ORM should be easy, I 
>> think).
>>
>> Thanks.
>> Desmond
>>
>> --
>> SQLAlchemy -
>> The Python SQL Toolkit and Object Relational Mapper
>>
>> http://www.sqlalchemy.org/
>>
>> To post example code, please provide an MCVE: Minimal, Complete, and 
>> Verifiable Example. See http://stackoverflow.com/help/mcve for a full 
>> description.
>> ---
>> You received this message because you are subscribed to the Google Groups 
>> "sqlalchemy" group.
>> To unsubscribe from this group and stop receiving emails from it, send an 
>> email to sqlalchemy+unsubscr...@googlegroups.com.
>> To post to this group, send email to sqlalchemy@googlegroups.com.
>> Visit this group at https://groups.google.com/group/sqlalchemy.
>> To view this discussion on the web visit 
>> https://groups.google.com/d/msgid/sqlalchemy/CAM%2BCzahgN2TF88p4__5W76DiYO3THD1tV2BgekvX913Mv6nU7Q%40mail.gmail.com.
>> For more options, visit https://groups.google.com/d/optout.
>>
>>
>> --
>> SQLAlchemy -
>> The Python SQL Toolkit and Object Relational Mapper
>>
>> http://www.sqlalchemy.org/
>>
>> To post example code, please provide an MCVE: Minimal, Complete, and 
>> Verifiable Example. See http://stackoverflow.com/help/mcve for a full 
>> description.
>> ---
>> You received this message because you are subscribed to the Google Groups 
>> "sqlalchemy" group.
>> To unsubscribe from this group and stop receiving emails from it, send an 
>> email to sqlalchemy+unsubscr...@googlegroups.com.
>> To post to this group, send email to sqlalchemy@googlegroups.com.
>> Visit this group at https://groups.google.com/group/sqlalchemy.
>> To view this discussion on the web visit 
>> https://groups.google.com/d/msgid/sqlalchemy/CAFHwexcmpOTTms0-A9QbiT_DTQey8e-Wzze4W5HoDXZdZ7eJWA%40mail.gmail.com.
>> For more options, visit https://groups.google.com/d/optout.
>>
>>
>> --
>> SQLAlchemy -
>> The Python SQL Toolkit and Object Relational Mapper
>>
>> http://www.sqlalchemy.org/
>>
>> To post example code, please provide an MCVE: Minimal, Complete, and 
>> Verifiable Example. See http://stackoverflow.com/help/mcve for a full 
>> description.
>> ---
>> You received this message because you are subscribed to the Google Groups 
>> "sqlalchemy" group.
>> To unsubscribe from this group and stop receiving emails from it, send an 
>> email to sqlalchemy+unsubscr...@googlegroups.com.
>> To post to this group, send email to sqlalchemy@googlegroups.com.
>> Visit this group at https://groups.google.com/group/sqlalchemy.
>> To view this discussion on the web visit 
>> https://groups.google.com/d/msgid/sqlalchemy/CAM%2BCzahCwZ7_yKbFTPvYX4kXTz1bZ%3DoTXGVw1g0eO26qeGBGpg%40mail.gmail.com.
>> For more options, visit https://groups.google.com/d/optout.
>>
>>
>> --
>> SQLAlchemy -
>> The Python SQL Toolkit and Object Relational Mapper
>>
>> http://www.sqlalchemy.org/
>>
>> To post example code, please provide an MCVE: Minimal, Complete, and 
>> Verifiable Example. See http://stackoverflow.com/help/mcve for a full 
>> description.
>> ---
>> You received this message because you are subscribed to the Google Groups 
>> "sqlalchemy" group.
>> To unsubscribe from this group and stop receiving emails from it, send an 
>> email to sqlalchemy+unsubscr...@googlegroups.com.
>> To post to this group, send email to sqlalchemy@googlegroups.com.
>> Visit this group at https://groups.google.com/group/sqlalchemy.
>> To view this discussion on the web visit 
>> https://groups.google.com/d/msgid/sqlalchemy/CAFHwexeWqM1m4aXWpuJnzfrUvgzBpq1JOVd_PJ1w-08TDTwzig%40mail.gmail.com.
>> For more options, visit https://groups.google.com/d/optout.
>
> --
> SQLAlchemy -
> The Python SQL Toolkit and Object Relational Mapper
>
> http://www.sqlalchemy.org/
>
> To post example code, please provide an MCVE: Minimal, Complete, and 
> Verifiable Example. See http://stackoverflow.com/help/mcve for a full 
> description.
> ---
> You received this message because you are subscribed to the Google Groups 
> "sqlalchemy" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to sqlalchemy+unsubscr...@googlegroups.com.
> To post to this group, send email to sqlalchemy@googlegroups.com.
> Visit this group at https://groups.google.com/group/sqlalchemy.
> To view this discussion on the web visit 
> https://groups.google.com/d/msgid/sqlalchemy/CAM%2BCzagfcZmCPDHPYUWXTVV%3DzBqq96KSu30JJnO0GbAGt8wn9g%40mail.gmail.com.
> For more options, visit https://groups.google.com/d/optout.

-- 
SQLAlchemy - 
The Python SQL Toolkit and Object Relational Mapper

http://www.sqlalchemy.org/

To post example code, please provide an MCVE: Minimal, Complete, and Verifiable 
Example.  See  http://stackoverflow.com/help/mcve for a full description.
--- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at https://groups.google.com/group/sqlalchemy.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/sqlalchemy/CAFHwexd%3DqLtDuQCTw5uR3%3Dphn9JC%2BkxckENYGw73%3DAj9ChOJZA%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to