用连接实现graphql

graphjoiner的Python项目详细描述


在参考图ql实现中,resolve函数描述如何 为一个对象的每个实例完成请求数据的某些部分。 如果使用SQL后端实现,则会导致N+1问题。 例如,给定查询:

{
    books(genre: "comedy") {
        title
        author {
            name
        }
    }
}

天真的graphql实现将发出一个sql查询来获取所有 喜剧类型的书,然后n个查询以获取每本书的作者 (其中n是第一个查询返回的图书数)。

对于这个问题有各种各样的解决方案:Graphjoiner建议 使用连接对于许多用例来说是自然的。对于这种特殊情况,我们只 需要运行两个查询:一个查询喜剧类型的所有书籍的列表, 一个是让喜剧类书籍的作者。

示例

假设我们有一些由sqlalchemy定义的模型。一本书有身份证,书名, 一种类型和一个作者的身份证。一个作者有一个身份证和一个名字。

fromsqlalchemyimportColumn,Integer,Unicode,ForeignKeyfromsqlalchemy.ext.declarativeimportdeclarative_baseBase=declarative_base()classAuthor(Base):__tablename__="author"id=Column(Integer,primary_key=True)name=Column(Unicode,nullable=False)classBook(Base):__tablename__="book"id=Column(Integer,primary_key=True)title=Column(Unicode,nullable=False)genre=Column(Unicode,nullable=False)author_id=Column(Integer,ForeignKey(Author.id))

然后定义根、书籍和作者的对象类型:

fromgraphqlimportGraphQLInt,GraphQLString,GraphQLArgumentfromgraphjoinerimportJoinType,RootJoinType,single,many,fieldfromsqlalchemy.ormimportQuerydefcreate_root():deffields():return{"books":many(book_join_type,books_query,args={"genre":GraphQLArgument(type=GraphQLString)})}defbooks_query(args,_):query=Query([]).select_from(Book)if"genre"inargs:query=query.filter(Book.genre==args["genre"])returnqueryreturnRootJoinType(name="Root",fields=fields)root=create_root()deffetch_immediates_from_database(selections,query,context):query=query.with_entities(*(selection.field.column_nameforselectioninselections))keys=tuple(selection.keyforselectioninselections)return[dict(zip(keys,row))forrowinquery.with_session(context.session).all()]defcreate_book_join_type():deffields():return{"id":field(column_name="id",type=GraphQLInt),"title":field(column_name="title",type=GraphQLString),"genre":field(column_name="genre",type=GraphQLString),"authorId":field(column_name="author_id",type=GraphQLInt),"author":single(author_join_type,author_query,join={"authorId":"id"}),}defauthor_query(args,book_query):books=book_query.with_entities(Book.author_id).distinct().subquery()returnQuery([]) \
            .select_from(Author) \
            .join(books,books.c.author_id==Author.id)returnJoinType(name="Book",fields=fields,fetch_immediates=fetch_immediates_from_database,)book_join_type=create_book_join_type()defcreate_author_join_type():deffields():return{"id":field(column_name="id",type=GraphQLInt),"name":field(column_name="name",type=GraphQLString),}returnJoinType(name="Author",fields=fields,fetch_immediates=fetch_immediates_from_database,)author_join_type=create_author_join_type()

我们可以通过调用execute

fromgraphjoinerimportexecutequery="""
    {
        books(genre: "comedy") {
            title
            author {
                name
            }
        }
    }
"""classContext(object):def__init__(self,session):self.session=sessionexecute(root,query,context=Context(session))

产生:

{
    "books": [
        {
            "title": "Leave It to Psmith",
            "author": {
                "name": "PG Wodehouse"
            }
        },
        {
            "title": "Right Ho, Jeeves",
            "author": {
                "name": "PG Wodehouse"
            }
        },
        {
            "title": "Catch-22",
            "author": {
                "name": "Joseph Heller"
            }
        },
    ]
}

让我们从根对象的定义开始,对其进行一些分解:

defcreate_root():deffields():return{"books":many(book_join_type,books_query,args={"genre":GraphQLArgument(type=GraphQLString)})}defbooks_query(args,_):query=Query([]).select_from(Book)if"genre"inargs:query=query.filter(Book.genre==args["genre"])returnqueryreturnRootJoinType(name="Root",fields=fields)root=create_root()

对于每个对象类型,我们需要定义其字段。 根目录只有一个字段,books,一对多关系, 我们用many()定义。 第一个参数,book_join_type, 是我们定义关系的类型。 第二个参数描述如何创建表示所有 相关书籍:在这种情况下,所有的书籍,可能被一个流派的论点过滤掉。

这意味着我们需要定义book_join_type

defcreate_book_join_type():deffields():return{"id":field(column_name="id",type=GraphQLInt),"title":field(column_name="title",type=GraphQLString),"genre":field(column_name="genre",type=GraphQLString),"authorId":field(column_name="author_id",type=GraphQLInt),"author":single(author_join_type,author_query,join={"authorId":"id"}),}defauthor_query(args,book_query):books=book_query.with_entities(Book.author_id).distinct().subquery()returnQuery([]) \
            .select_from(Author) \
            .join(books,books.c.author_id==Author.id)returnJoinType(name="Book",fields=fields,fetch_immediates=fetch_immediates_from_database,)book_join_type=create_book_join_type()

author字段定义为从书本到作者的一对一映射。 和前面一样,我们定义了一个函数,它为请求的作者生成一个查询。 我们还为single()提供了一个join参数,以便graphjoiner知道 如何将author查询和book查询的结果连接在一起: 在本例中,books上的authorId字段对应于id字段 关于作者。 (如果省略join参数,那么graphjoiner将执行cross 加入笛卡尔积。因为总是有一个根实例, 对于根目录中定义的关系,这是很好的。)

其余字段定义从graphql字段到数据库的映射 列。此映射由fetch_immediates_from_database处理。 中selections的值 fetch_immediates()是未定义为关系的字段的选择 (使用在 原始graphql查询,或作为连接的一部分是必需的。

deffetch_immediates_from_database(selections,query,context):query=query.with_entities(*(fields[selection.field_name].column_nameforselectioninselections))keys=tuple(selection.keyforselectioninselections)return[dict(zip(keys,row))forrowinquery.with_session(context.session).all()]

为了完整起见,我们可以调整author_join_typeso的定义 我们可以向作者索取书籍:

defcreate_author_join_type():deffields():return{"id":field(column_name="id",type=GraphQLInt),"name":field(column_name="name",type=GraphQLString),"author":many(book_join_type,book_query,join={"id":"authorId"}),}defbook_query(args,author_query):authors=author_query.with_entities(Author.id).distinct().subquery()returnQuery([]) \
            .select_from(Book) \
            .join(authors,authors.c.id==Book.author_id)returnJoinType(name="Author",fields=fields,fetch_immediates=fetch_immediates_from_database,)author_join_type=create_author_join_type()

安装

pip install graphjoiner

欢迎加入QQ群-->: 979659372 Python中文网_新手群

推荐PyPI第三方库


热门话题
swing Java按钮/网格布局   java列出Google日历中的所有事件   java无效:单击API publisher test按钮后连接到后端时出错   带有内部赋值的java While循环导致checkstyle错误   java为什么trimToSize/ensureCapacity方法提供“公共”级访问?   文件输出流的java问题   ListIterator和并发修改异常的java问题   java如何使用两个URL映射   无法识别使用“./../”构造的字符串java相对路径,为什么?   首次写入remotelyclosedsocket不会触发异常,对吗?JAVA   java OneDrive REST API为文件上载提供了400个无效谓词   Java泛型、集合接口和对象类的问题   OpenSSL Java安全提供程序   jmeter java运行jmx禁用操作