Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
296 views
in Technique[技术] by (71.8m points)

mongodb - graphql.GraphQLSchema: what type of argument to use to grab the query to pass to mongo db.collection.find to resolve the query

I'm learning ho to develop GraphQL service with express, express-graphql, **graphql, mongoose,

db.collection.find has an optional query parameter that specifies selection filter using query operators.

I wonder if it is possible to define a schema in which to define an argument for a query field that ultimately it is passed as it is to the collection find methods.

for example I expect that the graphql query:

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

responds with:

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

since in mongo

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

I'm thinking something like:

   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)
            })
          })
        }
      }

I have made a few attempts but have not been able to get an idea of which type I should use in this case

ho help reproduce the problem here the source repository of my tests

Please note that this works fine:

  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)
        })
      })
    }
  }

but what I'm looking for is something more generic: I would like to grab graphql field argument named query and pass it as is to the the query parameter of the mongo collection find.

question from:https://stackoverflow.com/questions/65925883/graphql-graphqlschema-what-type-of-argument-to-use-to-grab-the-query-to-pass-to

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

So the good news is you can do whatever you want. The bad news is that:

  1. You have to do it yourself
  2. You have to add every searchable field, so you'll probably end up with two copies of the Todo object here.

The type you're looking for is just a custom input object type like this:

Notice the GraphQLInputObjectType below is different from 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 },
        },
        ...
      }

These two queries work great!

(this is me using aliases so I can make the same query twice in one call)

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

Footnote:

Just to have it said, this is generally a GraphQL anti-pattern, as this is building an API based on your database choices, rather than as a client-driven API.

Regex Edit as requested:

If you're trying to do regular expression lookups, you have to figure out how to programmatically convert your strings into regular expressions. i.e. your input is a string ("/Novellara/"), but mongoose requires passing a RegExp to do wildcards (/Novellara/, no quotes).

You can do that a number of ways, but I'll show one example. If you change your input fields to use two properties of value & isExpression, like below, you can do it, but you have to specifically craft your query, since it's no longer just a passthrough.

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)
            })
          })
        }
      }

your query would then look like

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

This query makes sense in my mind. If I think about the form you would show to a user, there is probably an input box and a checkbox that says "is this a regular expression?" or something, which would populate this query.

Alternatively, you could do like string matching: if the first and last characters are "/", you automagically make it into a regex before passing it into mongoose.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...