前言
GraphQL 是一种新兴的 API 查询语言,它可以帮助我们更加灵活和高效地查询数据。而 MongoDB 是一种流行的 NoSQL 数据库,它的查询语言也非常强大。本文将介绍如何使用 GraphQL 和 MongoDB 的高级查询功能,以实现更加复杂和高效的数据查询。
GraphQL 简介
GraphQL 是一种由 Facebook 开发的 API 查询语言。它允许客户端指定需要的数据,从而避免了传统 RESTful API 中的 over-fetching 和 under-fetching 问题。GraphQL 支持查询、变更和订阅三种操作,可以轻松地实现复杂的数据查询和数据更新。
GraphQL 的查询语言非常灵活,可以通过嵌套查询、别名、变量等方式来实现数据的高效查询。例如,下面是一个简单的 GraphQL 查询:
// www.javascriptcn.com code example
query {
user(id: "123") {
id
name
email
posts(limit: 10) {
id
title
content
comments {
id
content
}
}
}
}这个查询将会返回一个用户的信息,包括用户的 ID、姓名、邮箱以及最近的 10 篇文章和每篇文章的评论。
MongoDB 简介
MongoDB 是一种流行的 NoSQL 数据库,它的查询语言也非常强大。MongoDB 支持复杂的查询操作,包括过滤、排序、分组、聚合等。MongoDB 还支持地理位置查询、文本搜索等高级功能。
下面是一个简单的 MongoDB 查询:
db.users.find({ age: { $gt: 18 } }).sort({ name: 1 }).limit(10)这个查询将会返回年龄大于 18 岁的前 10 个用户,并按照姓名升序排列。
GraphQL 和 MongoDB 集成
GraphQL 和 MongoDB 可以非常方便地集成在一起。我们可以使用 GraphQL 来定义数据模型和查询语言,然后使用 MongoDB 来存储和查询数据。下面是一个简单的 GraphQL 数据模型:
// www.javascriptcn.com code example
type User {
id: ID!
name: String!
email: String!
age: Int!
posts(limit: Int): [Post!]!
}
type Post {
id: ID!
title: String!
content: String!
author: User!
comments(limit: Int): [Comment!]!
}
type Comment {
id: ID!
content: String!
author: User!
post: Post!
}这个数据模型定义了三种类型:User、Post 和 Comment。每个类型都有一些字段,例如 User 有 id、name、email 和 age 字段,Post 有 id、title、content 和 author 字段等等。注意到 User 和 Post 类型之间存在双向关联,即 User 可以查询它的 posts,Post 可以查询它的 author 和 comments,而 Comment 可以查询它的 author 和 post。
我们可以使用 GraphQL 的查询语言来查询这个数据模型。例如,下面是一个查询用户和他的文章的 GraphQL 查询:
// www.javascriptcn.com code example
query {
user(id: "123") {
id
name
email
age
posts(limit: 10) {
id
title
content
comments(limit: 5) {
id
content
author {
id
name
}
}
}
}
}这个查询将会返回一个用户的信息,包括用户的 ID、姓名、邮箱、年龄以及最近的 10 篇文章和每篇文章的前 5 条评论和评论作者的信息。
我们可以使用 MongoDB 来实现这个数据模型的存储和查询。例如,下面是一个保存用户信息的 MongoDB 文档:
// www.javascriptcn.com code example
{
"_id": ObjectId("5f8f6d4f6c1a6a0007b8c4d4"),
"name": "Alice",
"email": "alice@example.com",
"age": 25,
"posts": [
{
"title": "Hello World",
"content": "This is my first post.",
"comments": [
{
"content": "Great post!",
"author": ObjectId("5f8f6d4f6c1a6a0007b8c4d5")
},
{
"content": "I like it!",
"author": ObjectId("5f8f6d4f6c1a6a0007b8c4d6")
}
]
},
{
"title": "GraphQL vs REST",
"content": "Which one is better?",
"comments": [
{
"content": "GraphQL is the future!",
"author": ObjectId("5f8f6d4f6c1a6a0007b8c4d7")
},
{
"content": "REST is still useful.",
"author": ObjectId("5f8f6d4f6c1a6a0007b8c4d8")
}
]
}
]
}这个文档包括用户的姓名、邮箱、年龄以及两篇文章和每篇文章的两条评论和评论作者的 ID。我们可以使用 MongoDB 的查询语言来查询这个文档。例如,下面是一个查询年龄大于 18 岁的用户和他们的文章和评论的 MongoDB 查询:
// www.javascriptcn.com code example
db.users.find({ age: { $gt: 18 } }).limit(10).map(function(user) {
user.posts = db.posts.find({ _id: { $in: user.posts } }).limit(10).map(function(post) {
post.author = db.users.findOne({ _id: post.author });
post.comments = db.comments.find({ _id: { $in: post.comments } }).limit(5).map(function(comment) {
comment.author = db.users.findOne({ _id: comment.author });
comment.post = post;
return comment;
});
return post;
});
return user;
});这个查询将会返回年龄大于 18 岁的前 10 个用户,并查询他们的文章和评论,并将作者和文章信息添加到评论中。
高级查询功能
GraphQL 和 MongoDB 都支持非常强大的查询功能。下面是一些常用的高级查询功能。
过滤
过滤是最常用的查询功能之一。它可以根据条件筛选出符合要求的数据。GraphQL 和 MongoDB 都支持过滤功能。
在 GraphQL 中,我们可以使用参数来指定过滤条件。例如,下面是一个查询年龄大于等于 18 岁的用户的 GraphQL 查询:
query {
users(age: { gte: 18 }) {
id
name
email
}
}在 MongoDB 中,我们可以使用 $gt、$gte、$lt、$lte、$eq、$ne、$in、$nin 等操作符来指定过滤条件。例如,下面是一个查询年龄大于等于 18 岁的用户的 MongoDB 查询:
db.users.find({ age: { $gte: 18 } })排序
排序是另一个常用的查询功能。它可以按照指定的字段对数据进行排序。GraphQL 和 MongoDB 都支持排序功能。
在 GraphQL 中,我们可以使用参数来指定排序方式。例如,下面是一个按照年龄升序排列的用户列表的 GraphQL 查询:
query {
users(orderBy: { field: age, direction: ASC }) {
id
name
email
age
}
}在 MongoDB 中,我们可以使用 sort() 方法来指定排序方式。例如,下面是一个按照年龄升序排列的用户列表的 MongoDB 查询:
db.users.find().sort({ age: 1 })分页
分页是处理大量数据的必要手段。它可以将数据分成多个页面,从而提高查询效率。GraphQL 和 MongoDB 都支持分页功能。
在 GraphQL 中,我们可以使用参数来指定分页条件。例如,下面是一个查询前 10 个用户的 GraphQL 查询:
query {
users(limit: 10) {
id
name
email
}
}在 MongoDB 中,我们可以使用 limit() 和 skip() 方法来指定分页条件。例如,下面是一个查询前 10 个用户的 MongoDB 查询:
db.users.find().limit(10)
聚合
聚合是处理数据统计的一种方式。它可以对数据进行分组、计数、求和、平均值等操作。MongoDB 支持非常强大的聚合功能,可以轻松地实现复杂的数据统计。
例如,下面是一个查询每个用户的文章数量和评论数量的 MongoDB 聚合查询:
db.users.aggregate([
{ $lookup: { from: "posts", localField: "_id", foreignField: "author", as: "posts" } },
{ $unwind: "$posts" },
{ $lookup: { from: "comments", localField: "posts._id", foreignField: "post", as: "posts.comments" } },
{ $group: { _id: "$_id", name: { $first: "$name" }, email: { $first: "$email" }, age: { $first: "$age" }, postCount: { $sum: 1 }, commentCount: { $sum: { $size: "$posts.comments" } } } }
])这个查询将会返回每个用户的姓名、邮箱、年龄、文章数量和评论数量。
示例代码
下面是一个使用 GraphQL 和 MongoDB 实现高级查询功能的示例代码:
// www.javascriptcn.com code example
const { ApolloServer, gql } = require('apollo-server');
const { MongoClient, ObjectId } = require('mongodb');
const uri = 'mongodb://localhost:27017';
const client = new MongoClient(uri, { useNewUrlParser: true, useUnifiedTopology: true });
const typeDefs = gql`
type User {
id: ID!
name: String!
email: String!
age: Int!
posts(limit: Int, orderBy: OrderByInput): [Post!]!
postCount: Int!
commentCount: Int!
}
type Post {
id: ID!
title: String!
content: String!
author: User!
comments(limit: Int, orderBy: OrderByInput): [Comment!]!
commentCount: Int!
}
type Comment {
id: ID!
content: String!
author: User!
post: Post!
}
input OrderByInput {
field: String!
direction: String!
}
type Query {
user(id: ID!): User
users(limit: Int, orderBy: OrderByInput): [User!]!
post(id: ID!): Post
posts(limit: Int, orderBy: OrderByInput): [Post!]!
comment(id: ID!): Comment
comments(limit: Int, orderBy: OrderByInput): [Comment!]!
}
`;
const resolvers = {
Query: {
user: async (_, { id }, { db }) => {
return db.collection('users').findOne({ _id: new ObjectId(id) });
},
users: async (_, { limit = 10, orderBy = { field: 'name', direction: 'ASC' } }, { db }) => {
const users = await db.collection('users').find().sort({ [orderBy.field]: orderBy.direction === 'ASC' ? 1 : -1 }).limit(limit).toArray();
return users.map(user => ({ ...user, postCount: user.posts.length, commentCount: user.posts.reduce((count, post) => count + post.comments.length, 0) }));
},
post: async (_, { id }, { db }) => {
return db.collection('posts').findOne({ _id: new ObjectId(id) });
},
posts: async (_, { limit = 10, orderBy = { field: 'title', direction: 'ASC' } }, { db }) => {
const posts = await db.collection('posts').find().sort({ [orderBy.field]: orderBy.direction === 'ASC' ? 1 : -1 }).limit(limit).toArray();
return posts.map(post => ({ ...post, commentCount: post.comments.length }));
},
comment: async (_, { id }, { db }) => {
return db.collection('comments').findOne({ _id: new ObjectId(id) });
},
comments: async (_, { limit = 10, orderBy = { field: 'content', direction: 'ASC' } }, { db }) => {
const comments = await db.collection('comments').find().sort({ [orderBy.field]: orderBy.direction === 'ASC' ? 1 : -1 }).limit(limit).toArray();
return comments;
},
},
User: {
posts: async ({ _id }, { limit = 10, orderBy = { field: 'title', direction: 'ASC' } }, { db }) => {
const posts = await db.collection('posts').find({ author: _id }).sort({ [orderBy.field]: orderBy.direction === 'ASC' ? 1 : -1 }).limit(limit).toArray();
return posts.map(post => ({ ...post, commentCount: post.comments.length }));
},
},
Post: {
author: async ({ author }, _, { db }) => {
return db.collection('users').findOne({ _id: author });
},
comments: async ({ _id }, { limit = 10, orderBy = { field: 'content', direction: 'ASC' } }, { db }) => {
const comments = await db.collection('comments').find({ post: _id }).sort({ [orderBy.field]: orderBy.direction === 'ASC' ? 1 : -1 }).limit(limit).toArray();
return comments;
},
},
Comment: {
author: async ({ author }, _, { db }) => {
return db.collection('users').findOne({ _id: author });
},
post: async ({ post }, _, { db }) => {
return db.collection('posts').findOne({ _id: post });
},
},
};
const server = new ApolloServer({
typeDefs,
resolvers,
context: async () => {
if (!client.isConnected()) await client.connect();
const db = client.db('mydb');
return { db };
},
});
server.listen().then(({ url }) => {
console.log(`Server ready at ${url}`);
});这个示例代码实现了一个简单的 GraphQL 服务器,它可以查询用户、文章和评论,并支持过滤、排序、分页和聚合等高级查询功能。它使用 MongoDB 存储数据,并使用 MongoDB 的查询语言实现数据查询。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67d3cd37a941bf713472dc6d