首页 > 解决方案 > graphql.GraphQLSchema:使用什么类型的参数来获取查询以传递给 mongo db.collection.find 来解析查询

问题描述

我正在学习如何使用expressexpress-graphql、**graphql、mongoose开发 GraphQL 服务,

db.collection.find 有一个可选的查询参数,它使用查询运算符指定选择过滤器。

我想知道是否可以定义一个模式,在该模式中为查询字段定义一个参数,最终将其按原样传递给集合查找方法。

例如,我希望 graphql 查询:

{ todosQuerable(query: {title: "Andare a Novellara"}) 
  { _id, title, completed } 
}

回应:

{
  "data": {
    "todos": [
      {
        "title": "Andare a Novellara",
        "completed": false
      }
    ]
  }
}

自从在 mongo

> db.Todo.find({title: 'Andare a Novellara'})
{ "_id" : ObjectId("600d95d2e506988bc4430bb7"), "title" : "Andare a Novellara", "completed" : false }

我在想类似的事情:

   todosQuerable: {
        type: new graphql.GraphQLList(TodoType),
        args: {
          query: { type: <???????????????> },
        },
        resolve: (source, { query }) => {
          return new Promise((resolve, reject) => {
            TODO.find(query, (err, todos) => {
              if (err) reject(err)
              else resolve(todos)
            })
          })
        }
      }

我做了一些尝试,但无法知道在这种情况下我应该使用哪种类型

ho帮助在这里重现问题我的测试的源存储库

请注意,这很好用:

  todosByTitle: {
    type: new graphql.GraphQLList(TodoType),
    args: {
      title: { type: graphql.GraphQLString },
    },
    resolve: (source, { title }) => {
      return new Promise((resolve, reject) => {
        TODO.find({title: {$regex: '.*' + title + '.*', $options: 'i'}}, (err, todos) => {
          if (err) reject(err)
          else resolve(todos)
        })
      })
    }
  }

但我正在寻找更通用的东西:我想获取名为 graphql 的字段参数query并将其按原样传递给 mongo 集合查找的查询参数。

标签: mongodbmongoosegraphqlexpress-graphql

解决方案


所以好消息是你可以做任何你想做的事。坏消息是:

  1. 你必须自己做
  2. 您必须添加每个可搜索字段,因此您可能会在此处获得 Todo 对象的两个副本。

您正在寻找的类型只是一个自定义输入对象类型,如下所示:

请注意,GraphQLInputObjectType以下内容与GraphQLObjectType.

var TodoQueryType = new graphql.GraphQLInputObjectType({
  name: 'TodoQuery',
  fields: function () {
    return {
      _id: {
        type: graphql.GraphQLID
      },
      title: {
        type: graphql.GraphQLString
      },
      completed: {
        type: graphql.GraphQLBoolean
      }
    }
  }
});
      todosQuerable: {
        ...
        type: new graphql.GraphQLList(TodoType),
        ...
        args: {
          query: { type: TodoQueryType },
        },
        ...
      }

这两个查询效果很好!

(这是我使用别名,所以我可以一次调用两次相同的查询)

{
  titleSearch: todosQuerable(query:{ title:"Buy orange" }) {
    _id
    title
    completed
  }
  idSearch: todosQuerable(query:{ _id:"601c3f374b6dcc601890048d" }) {
    _id
    title
    completed
  }
}

脚注

简单地说,这通常是 GraphQL 反模式,因为这是基于您的数据库选择构建 API,而不是作为客户端驱动的 API。

正则表达式按要求编辑:

如果您尝试进行正则表达式查找,则必须弄清楚如何以编程方式将字符串转换为正则表达式。即你的输入是一个string"/Novellara/"),但猫鼬需要传递一个RegExp做通配符(/Novellara/,没有引号)。

您可以通过多种方式做到这一点,但我将展示一个示例。如果您将输入字段更改为使用 value 和 isExpression 的两个属性,如下所示,您可以这样做,但您必须专门制作您的查询,因为它不再只是一个传递。

var ExpressableStringInput = new graphql.GraphQLInputObjectType({
  name: 'ExpressableString',
  fields: {
    value: {
      type: graphql.GraphQLString
    },
    isExpression:{
      type: graphql.GraphQLBoolean,
      defaultValue: false,
    }
  }
})

var TodoQueryType = new graphql.GraphQLInputObjectType({
  name: 'TodoQuery',
  fields: function () {
    return {
      _id: {
        type: graphql.GraphQLID
      },
      title: {
        type: ExpressableStringInput
      },
      completed: {
        type: graphql.GraphQLBoolean
      }
    }
  }
});

// resolver
      todosQuerable: {
        type: new graphql.GraphQLList(TodoType),
        args: {
          query: { type: TodoQueryType },
        },
        resolve: async (source, { query }) => {
          const dbQuery = {};

          if (query.title.isExpression) {
            dbQuery.title = new RegExp(query.title.value);
          } else {
            dbQuery.title = query.title.value;
          }

          return new Promise((resolve, reject) => {
            TODO.find(dbQuery, (err, todos) => {
              if (err) reject(err)
              else resolve(todos)
            })
          })
        }
      }

你的查询看起来像

query {
  todosQuerable(query:{ title: { value: "Buy.*", isExpression: true }}) {
    _id
    title
    completed
  }
}

这个查询在我看来是有道理的。如果我考虑您将向用户显示的表单,可能会有一个输入框和一个复选框,上面写着“这是一个正则表达式吗?” 或其他东西,它将填充此查询。

或者,您可以进行类似字符串匹配:如果第一个和最后一个字符是“/”,则在将其传递给 mongoose 之前,您会自动将其转换为正则表达式。


推荐阅读