Efficient query planning and data fetching for SQL.Use JOIN
s and/or batched requests to retrieve all your data.It takes a GraphQL query and your schema and automatically generates the SQL.Send that SQL to your database and get back all the data needed to resolve with only one or a few round-trips to the database.
Translate a GraphQL query like this:
{
user(id: 2) {
fullName
email
posts {
id
body
comments {
body
author { fullName }
}
}
}
}
...into a couple SQL queries like this:
SELECT
"user"."id" AS "id",
"user"."email_address" AS "email_address",
"posts"."id" AS "posts__id",
"posts"."body" AS "posts__body",
"user"."first_name" AS "first_name",
"user"."last_name" AS "last_name"
FROM accounts AS "user"
LEFT JOIN posts AS "posts" ON "user".id = "posts".author_id
WHERE "user".id = 2
-- then get the right comments for each post
SELECT
"comments"."id" AS "id",
"comments"."body" AS "body",
"author"."id" AS "author__id",
"author"."first_name" AS "author__first_name",
"author"."last_name" AS "author__last_name",
"comments"."post_id" AS "post_id"
FROM comments AS "comments"
LEFT JOIN accounts AS "author" ON "comments".author_id = "author".id
WHERE "comments".archived = FALSE AND "comments"."post_id" IN (2,8,11,12) -- the post IDs from the previous query
...and get back correctly hydrated data.
{
"user": {
"fullName": "Yasmine Rolfson",
"email": "Earl.Koss41@yahoo.com",
"posts": [
{
"id": 2,
"body": "Harum unde maiores est quasi totam consequuntur. Necessitatibus doloribus ut totam dolore omnis quos error eos. Rem nostrum assumenda eius veniam fugit dicta in consequuntur. Ut porro dolorem aliquid qui magnam a.",
"comments": [
{
"body": "The AI driver is down, program the multi-byte sensor so we can parse the SAS bandwidth!",
"author": { "fullName": "Yasmine Rolfson" }
},
{
"body": "Try to program the SMS transmitter, maybe it will synthesize the optical firewall!",
"author": { "fullName": "Ole Barrows" }
},
]
},
// other posts omitted for clarity...
]
}
}
It works on top of Facebook's graphql-js reference implementation.All you have to do is add a few properties to the objects in your schema and call the joinMonster
function.A SQL query is "compiled" for you to send to the DBMS.The data-fetching is efficiently batched.The data is then hydrated into the right shape for your GraphQL schema.
More details on the "round-trip" (a.k.a. N+1) problem are here.
Since it works with the reference implementation, the API is all very familiar. Join Monster is a tool built on top to add batch data fetching. You add some special properties along-side the schema definition that Join Monster knows to look for. The use of graphql-js does not change. You still define your types the same way. You can write resolve functions to manipulate the data from Join Monster, or incorporate data from elsewhere without breaking out of your "join-monsterized" schema.
Join Monster has support for several different implementations of pagination, all based on the interface in the Relay Connection Specification. Using Relay on the client is totally optional!
Great helpers for the Node Interface and automatic pagination for Connection Types. See docs.
$ npm install join-monster
GraphQLObjectType
from graphql-js and add the SQL table name.sqlColumn
, sqlDeps
, or sqlExpr
to the fields. Join Monster will look at these when analyzing the query.JOIN
your tables and it will hydrate hierarchies of data.joinMonster
in its resolver. All it needs is the resolveInfo
and a callback to send the (one) SQL query to the database. Voila! All your data is returned to the resolver.import joinMonster from 'join-monster'
import {
GraphQLObjectType,
GraphQLList,
GraphQLString,
GraphQLInt
// and some other stuff
} from 'graphql'
const User = new GraphQLObjectType({
name: 'User',
sqlTable: 'accounts', // the SQL table for this object type is called "accounts"
uniqueKey: 'id', // the id in each row is unique for this table
fields: () => ({
id: {
// the column name is assumed to be the same as the field name
type: GraphQLInt
},
email: {
type: GraphQLString,
// if the column name is different, it must be specified specified
sqlColumn: 'email_address'
},
idEncoded: {
description: 'The ID base-64 encoded',
type: GraphQLString,
// this field uses a sqlColumn and applies a resolver function on the value
// if a resolver is present, the `sqlColumn` MUST be specified even if it is the same name as the field
sqlColumn: 'id',
resolve: user => toBase64(user.idEncoded)
},
fullName: {
description: "A user's first and last name",
type: GraphQLString,
// perhaps there is no 1-to-1 mapping of field to column
// this field depends on multiple columns
sqlDeps: [ 'first_name', 'last_name' ],
// compute the value with a resolver
resolve: user => `${user.first_name} ${user.last_name}`
},
capitalizedLastName: {
type: GraphQLString,
// do a computed column in SQL with raw expression
sqlExpr: (table, args) => `UPPER(${table}.last_name)`
},
// got tables inside tables??
// get it with a JOIN!
posts: {
description: "A List of posts this user has written.",
type: new GraphQLList(Post),
// a function to generate the join condition from the table aliases
sqlJoin(userTable, postTable) {
return `${userTable}.id = ${postTable}.author_id`
}
},
// got a relationship but don't want to add another JOIN?
// get this in a second batch request
comments: {
description: "The comment they have written",
type: new GraphQLList(Comment),
// specify which columns to match up the values
sqlBatch: {
thisKey: 'author_id',
parentKey: 'id'
}
},
// many-to-many relations are supported too
following: {
description: "Other users that this user is following.",
type: new GraphQLList(User),
// name the table that holds the two foreign keys
junction: {
sqlTable: 'relationships',
sqlJoins: [
// first the parent table to the junction
(followerTable, junctionTable, args) => `${followerTable}.id = ${junctionTable}.follower_id`,
// then the junction to the child
(junctionTable, followeeTable, args) => `${junctionTable}.followee_id = ${followeeTable}.id`
]
}
},
numLegs: {
description: 'Number of legs this user has.',
type: GraphQLInt,
// data isn't coming from the SQL table? no problem! joinMonster will ignore this field
resolve: () => 2
}
})
})
const Comment = new GraphQLObjectType({
name: 'Comment',
sqlTable: 'comments',
uniqueKey: 'id',
fields: () => ({
// id and body column names are the same
id: {
type: GraphQLInt
},
body: {
type: GraphQLString
}
})
})
const Post = new GraphQLObjectType({
name: 'Post',
sqlTable: 'posts',
uniqueKey: 'id',
fields: () => ({
id: {
type: GraphQLInt
},
body: {
type: GraphQLString
}
})
})
export const QueryRoot = new GraphQLObjectType({
name: 'Query',
fields: () => ({
// place this user type in the schema
user: {
type: User,
// let client search for users by `id`
args: {
id: { type: GraphQLInt }
},
// how to write the WHERE condition
where: (usersTable, args, context) => {
if (args.id) return `${usersTable}.id = ${args.id}`
},
resolve: (parent, args, context, resolveInfo) => {
// resolve the user and the comments and any other descendants in a single request and return the data!
// all you need to pass is the `resolveInfo` and a callback for querying the database
return joinMonster(resolveInfo, {}, sql => {
// knex is a query library for SQL databases
return knex.raw(sql)
})
}
}
})
})
Detailed instructions for set up are found in the docs.
graphql-tools
The GraphQL schema language doesn't let you add arbitrary properties to the type definitions. If you're using something like the Apollo graphql-tools package to write your code with the schema language, you'll need an adapter. See the join-monster-graphql-tools-adapter if you want to use this with graphql-tools
.
$ git clone https://github.com/stems/join-monster-demo.git
$ cd join-monster-demo
$ npm install
$ npm start
# go to http://localhost:3000/graphql
# if you also want to run the paginated version, create postgres database from the dump provided
psql $YOUR_DATABASE < data/paginated-demo-dump.sql
DATABASE_URL=postgres://$USER:$PASS@$HOST/$YOUR_DATABASE npm start
# go to http://localhost:3000/graphql-relay
Explore the schema, try out some queries, and see what the resulting SQL queries and responses look like in our custom version of GraphiQL!
There's still a lot of work to do. Please feel free to fork and submit a Pull Request!
ORDER BY
expressions #138.关于inner join 与 left join 之间的区别,以前以为自己搞懂了,今天从前端取参数的时候发现不是预想中的结果,才知道问题出在inner join 上了。 需求是从数据库查数据,在前端以柱形图的形式展现出来,查到的数据按行业分组,显示每个行业的户数及户数占比,涉及到的字段有A表的用户数、总用户数和B表的行业名称。本来是不管查不查的到数据,在X轴都应该显示行业名称的,结果是X、Y轴都
内连接 内连接(INNER JOIN)主要通过设置连接条件的方式,来移除查询结果中某些数据行的交叉连接。简单来说,就是利用条件表达式来消除交叉连接的某些数据行。 内连接使用 INNER JOIN 关键字连接两张表,并使用 ON 子句来设置连接条件。如果没有连接条件,INNER JOIN 和 CROSS JOIN 在语法上是等同的,两者可以互换。 内连接的语法格式如下: SELECT <字段名> F
For Example: Table A have 12( 8+4) entries, 8 entries have valid relation with B Table B have 80(77+3) entries , 77 entries have valid relation with A. then the return amount of join is : cross join :
join和join in有什么区别么?怎样使用他们?请详细说明,谢谢 join和join in的区别为:意思不同、用法不同、侧重点不同。 一、意思不同 1、join:成为…的一员,参加。 2、join in:加入,参加(活动)。 二、用法不同 1、join:join作“参加”解时,其含义是以非发起人和非主办人的身份加入到业已存在的组织(如军队、党团、社团协会等)或正在进行的某种集体活动(如游戏、比
String.Join 在指定 String 数组的每个元素之间串联指定的分隔符 String,从而产生单个串联的字符串。 有两个重载函数: public static string Join( string separator, string[] value ); public static string Join( string separator, string[]
1、left join(左链接) 作用:以左表为主,查询左表的全部数据,右表展示与左表相关联的部分,也就是有交集的部分。 select * from staff s left join company c on s.company_id = c.company_id; 2、right join (右链接) 作用:以右表为主,查询右表的全部数据,左表展示与右表相关联的部分,有
mongodb提供ref和populate的方法,支持类似join的SQL操作。本文给出一个实际的例子: 1. 数据1: var daob = new Schema({ user: { type: String }, title: { type: String }, tag: [{ type: String
SQL语句的并集UNION,交集JOIN(内连接,外连接),交叉汇总 2010-07-28 09:05:23 1. a. 并集UNION SELECT column1, column2 FROM table1 UNION SELECT column1, column2 FROM table2 b. 交集JOIN SELECT * FROM table1 AS a JOIN table2 b
概述: 将序列中的元素以指定的字符连接生成一个新的字符串。 语法: ‘delimiter’.join(seq) delimiter:分隔符。可以为空 delimiter:要连接的元素序列、字符串、元组、字典 返回通过指定字符连接序列中元素后生成的新字符串。 上代码: while True: try: print(''.join(sorted(input()))) e
quoted from: https://docs.oracle.com/en/database/oracle/oracle-database/12.2/tgsql/joins.html#GUID-8E7760A6-48D6-4794-BF2F-290349C019B9 A join type is determined by the type of join condition. This se
JOIN 表运算符对两个输入表进行操作。联接的类型有交叉联接、内部联接和外部联接,它们的区别在于如何应用逻辑查询处理阶段。交叉联接仅应用一个阶段——笛卡尔乘积,内部联接应用两个阶段——笛卡尔乘积和筛选,外部联接应用三个阶段——笛卡尔乘积、筛选和添加外部行。 交叉联接(CROSS JOIN) 交叉联接仅执行一个逻辑查询处理阶段——笛卡尔乘积,这一阶段对提供的两个输入表进行操作,联接并生成两个表的笛卡
Inner join:内连接,也叫等值连接,inner join产生同时符合A和B的一组数据。 Cross join:交叉连接,得到的结果是两个表的乘积,即笛卡尔积 笛卡尔(Descartes)乘积又叫直积。假设集合A={a,b},集合B={0,1,2},则两个集合的笛卡尔积为{(a,0),(a,1),(a,2),(b,0),(b,1), (b,2)}。可以扩展到多个集合的情况。类似的例子有,如果
join()方法:将数组所有元素拼接成一个数组,相当于java的toString方法 默认逗号连接 var joinArr = ["hello","world"]; var joinArr1 = joinArr.join(); // joinArr1: "hello,world" var joinArr2 = joinArr.join(""); // joinArr2: "helloworld
join(separator) join是数组中的方法,将数组中的每个元素用分隔符连接起来并转换为字符串。即将数组元素连接成字符串。返回的是字符串哈,不在是数组了。 let arr = [1,2,3,4,5] let str = arr.join('aa') console.log(str); // 输出:1aa2aa3aa4aa5 console.log(typeof str); // 输
JOIN:表示连接组合两个表中的列字段记录,一共有三种用法 第一种:内连接,即单纯的使用join ... on select d.dname,e.ename,e.sal from emp e join dept d on e.deptno=d.deptno; 第二种:左连接,left join ... on(会检查左边表的数据是否都包含在新生成的表中,若是,则和join一样;若不是,则用NULL和
join 方法 把数组的元素放入一个字符串,元素通过指定的分隔符进行分隔。 语法: arrayObject.join( separator ); 参数说明: separator - 可选。指定要使用的分隔符。如果省略该参数,则使用逗号作为分隔符。 返回值: 返回一个字符串。该字符串是通过把 arrayObject 的每个元素转换为字符串,然后把这些字符串连接起来,在两个元素之间插入 sepa
描述 (Description) Javascript数组join()方法将数组的所有元素连接成一个字符串。 语法 (Syntax) 其语法如下 - array.join(separator); 参数细节 (Parameter Details) separator - 指定用于分隔数组的每个元素的字符串。 如果省略,则使用逗号分隔数组元素。 返回值 (Return Value) 连接所有数组元素
描述 (Description) 此函数使用EXPR的值将LIST的元素组合成单个字符串,以分隔每个元素。 它实际上与分裂相反。 注意,EXPR仅在LIST中的元素对之间进行插值; 它不会放在字符串中的第一个元素之前或之后。 要将没有分隔符的字符串连接在一起,请提供空字符串而不是undef。 语法 (Syntax) 以下是此函数的简单语法 - join EXPR, LIST 返回值 (Retur
一个Function,它返回一个字符串,该字符串包含数组中指定数量的子字符串。 这与Split Method完全相反。 语法 (Syntax) Join(List[,delimiter]) 参数描述 (Parameter Description) List - 必需参数。 包含要连接的子字符串的数组。 Delimiter - 可选参数。 该字符,在返回字符串时用作分隔符。 默认分隔符是Spac
join()方法将数组的所有元素连接成一个字符串。 语法 (Syntax) array.join(separator); 参数 (Parameters) separator - 指定用于分隔数组的每个元素的字符串。 如果省略,则使用逗号分隔数组元素。 返回值 (Return Value) 连接所有数组元素后返回一个字符串。 例子 (Example) var arr = new Array(
此方法返回一个字符串,其中各个字符由指定的分隔符连接。 import numpy as np print np.char.join(':','dmy') print np.char.join([':','-'],['dmy','ymd']) 其输出如下 - d:m:y ['d:m:y' 'y-m-d']