在graphql中按时间过滤(使用floralDB服务) [英] filter by Time in graphql (using faunaDB service)

查看:101
本文介绍了在graphql中按时间过滤(使用floralDB服务)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的graphQL模式如下:

  type Todo {名称:字符串!created_at:时间}输入查询{allTodos:[Todo!]!todosByCreatedAtFlag(created_at:Time!):[Todo!]!} 

此查询有效.

 查询{todosByCreatedAtFlag(created_at:" 2017-02-08T16:10:3​​3Z"){数据 {_ID名称created_at}}} 

谁能指出我如何在graphql中创建大于(或小于)时间查询(使用动物群数据库).

解决方案

不支持GraphQL范围查询(但它们即将来临!)

FaunaDB不提供现成的 GraphQL 的范围查询,我们正在研究这些功能.

..但有一种解决方法.

这并不意味着它不能进行范围查询,因为 FQL 支持范围查询,并且您始终可以通过从GraphQL到FQL的转义"来编写更高级的查询用户定义功能(UDF).

..使用解析器

通过在架构中使用

第一件事是,我们的架构将使用解析器进行扩展:

  type Todo {名称:字符串!created_at:时间}输入查询{allTodos:[Todo!]!todosByCreatedAtFlag(created_at:Time!):[Todo!]!todosByCreatedRange(之前:时间,之后:时间):[Todo!]!@解析器} 

这一切都是为我们添加一个要实现的功能:

如果我们通过GraphQL进行调用,则会准确地给出我们之前在屏幕快照中看到的Abort消息,因为该消息尚未实现.但是我们可以看到GraphQL语句实际上调用了该函数.

.. UDF实施

我们要做的第一件事是添加只是写一个名字作为lambda的第一个参数的参数:

如果您需要传递多个参数(在我在架构中定义的解析器中执行此操作),这还会采用一个数组:

我们将添加索引以支持我们的查询.值用于范围(以及返回值和排序).我们将添加created_at以覆盖其范围,并添加 ref ,因为我们需要返回值才能将实际文档置于索引之后.

然后我们可以通过编写一个简单的函数(尚不可用)开始

  Query(Lambda([之前",之后"],分页范围(Match(Index("todosByCreatedAtRange")),Var("before"),Var("after")))))) 

并可以通过外壳手动调用该函数来进行测试.

这确实返回了两个对象(范围包括在内).当然,这有一个问题,它不会返回GraphQL期望的结构中的数据,因此我们将得到以下奇怪的错误:

我们现在可以做两件事,要么在Schema中定义一个适合这些类型的类型,和/或我们可以调整返回数据.我们将进行后者,并使我们的结果适应预期的[Todo!]!结果显示给你.

第一步,将结果映射.我们在这里介绍的唯一一件事就是地图和Lambda.我们还没有做任何特别的事情,我们只是返回引用而不是ts和引用作为示例.

  Query(Lambda([之前",之后"],地图(分页范围(Match(Index("todosByCreatedAtRange"))),Var("before"),Var(之后"))),Lambda(["created_at","ref"],Var("ref"))))) 

调用它确实表明该函数现在仅返回引用.

让我们获取实际文件.我知道FQL是冗长的(并且有充分的理由,尽管将来它会变得不太冗长),所以我开始添加评论以澄清问题

  Query(Lambda([之前",之后"],地图(//这只是获取范围的查询分页范围(Match(Index("todosByCreatedAtRange"))),Var("before"),Var(之后"))),//这是一个将在每个结果上执行的函数(借助Map)Lambda(["created_at","ref"],//我们将使用Let来构造查询(允许我们使用变量)让({待办事项:Get(Var("ref")))},//然后我们返回一些东西Var("todo")))))) 

我们的函数现在返回数据.

我们仍然需要确保该数据符合GraphQL的期望,并且从模式中我们可以看到它期望的是[Todo!]!(请参阅"docs"标签)和一个Todo外观(请参见"schema"标签):

  type Todo {_id:ID!_ts:长!名称:字符串!created_at:时间} 

您还可以从docs选项卡中看到,"non-resolver"查询会自动更改为返回TodoPages.到目前为止,我们编写的函数实际上返回页面.

选项1,更改架构并将其变成分页的解析器.

我们可以通过在解析器中添加paginated:true选项来解决此问题.您将必须考虑将要添加到解析器的其他参数,如

我们得到了结果.

完整的查询如下:

  Query(Lambda([之前",之后"],选择([数据"],地图(分页范围(Match(Index("todosByCreatedAtRange"))),Var("before"),Var(之后"))),Lambda(["created_at","ref"],Let({todo:Get(Var("ref")))},Var("todo")))))))) 

免责声明

自定义后,分页也将成为您的责任(例如,传递额外的参数).您再也无法像通常那样仅在GraphQL主体中请求关系了,才可以直接获取关系.

关于UDF和GraphQL/FQL混合的好处的一些话

在您回避FQL(是的,我们确实必须添加范围查询并进行此工作)之前,这里是对UDF方法的一些解释,以及为什么无论如何都要考虑一下.

您有时会在GraphQL中遇到不可能的事情(复杂的条件事务,例如,仅当从前一次更新得出的某些条件为真时才更新文档并更新另一个文档).使用其他GraphQL实现的用户通常通过编写无服务器功能来解决此问题,以防您必须实现高级逻辑或事务.

FaunaDB对此的答案是使用其用户定义函数(UDF).这不是一个无服务器功能,它是用FQL实现的FaunaDB函数,乍一看似乎很麻烦,但重要的是要意识到它给您带来了相同的好处(多区域/强一致性,可伸缩性/自由层/按需付费随便看看)FaunaDB所提供.

My graphQL schema looks like this,

type Todo {
  name: String!
  created_at: Time
}

type Query {
  allTodos: [Todo!]!
  todosByCreatedAtFlag(created_at: Time!): [Todo!]!
}

This query works.

query {  
  todosByCreatedAtFlag(created_at: "2017-02-08T16:10:33Z") {
    data {
      _id
      name
      created_at
    }
  }
}

Could anyone point out how i can create greater than (or less than) Time query in graphql (using faunaDB).

解决方案

GraphQL range queries are not supported (yet.. they're coming!)

FaunaDB does not provide range queries for their GraphQL out-of-the-box, we are working on these features.

.. but there is a workaround.

That doesn't mean though that it can't do range queries since range queries are supported in FQL and you can always 'escape' from GraphQL to FQL to implement more advanced queries by writing a User Defined Function (UDF).

.. using resolvers

By using the @resolver keyword in your schema you can implement GraphQL queries yourself by writing a User Defined Function in FaunaDB in FQL. There are some basic examples in the documentation bt I imagine you might need some help so I'll write you a simple example.

I added your schema and added two documents:

First thing is that our schema will be extended with the resolver:

type Todo {
  name: String!
  created_at: Time
}

type Query {
  allTodos: [Todo!]!
  todosByCreatedAtFlag(created_at: Time!): [Todo!]!
  todosByCreatedRange(before: Time, after:Time): [Todo!]! @resolver
}

All this does is add a function for us to implement:

Which if we call via GraphQL gives us exactly that Abort message we saw in the screenshot before since it has not been implemented yet. But we can see that the GraphQL statement actually calls the function.

.. UDF implementation

First thing we will do is add the parameter which is just writing a name as the first parameter of the lambda:

Which also takes an array in case you need to pass multiple parameters (which I do in the resolver that I defined in the schema):

We'll add an index to support our query. Values are for ranges (and for return values and sorting). We'll add created_at to range over it and also add ref since we'll need the return value to get the actual document behind the index.

We could then start off by just writing a simple function (that won't work yet)

Query(
      Lambda(
        ["before", "after"],
        Paginate(
          Range(Match(Index("todosByCreatedAtRange")), Var("before"), Var("after"))
        )
      )
    )

and could test this by calling the function manually via the shell.

This indeed returns the two objects (range is inclusive). Of course, there is one problem with this, it does not return the data in the structure that GraphQL expects it so we'll get these strange errors:

We can do two things now, either define a type in our Schema that fits these and/or we can adapt the data the returns. We'll do the latter and adapt our result to the expected [Todo!]! result to show you.

Step one, map over the result. The only thing we introduce here is the Map and the Lambda. We do not do anything special yet, we just return the reference instead of both the ts and the reference as an example.

Query(
  Lambda(
    ["before", "after"],
    Map(
      Paginate(
        Range(
          Match(Index("todosByCreatedAtRange")),
          Var("before"),
          Var("after")
        )
      ),
      Lambda(["created_at", "ref"], Var("ref"))
    )
  )
)

Calling it indeed shows that the function now only returns references.

Let's get the actual documents. I know that FQL is verbose (and with good reasons, although it should become less verbose in the future) so I started adding comments to clarify things

Query(
  Lambda(
    ["before", "after"],
    Map(
      // This is just the query to get your range
      Paginate(
        Range(
          Match(Index("todosByCreatedAtRange")),
          Var("before"),
          Var("after")
        )
      ),
      
      // This is a function that will be executed on each result (with the help of Map)
      Lambda(["created_at", "ref"], 
        // We'll use Let to structure our queries ( allowing us to use varaibles )
        Let({ 
          todo: Get(Var("ref")) 
        }, 
        // And then we return something
        Var("todo")))
    )
  )
)

Our function now returns data.. woohoo!

We still need to make sure this data is conforms to what GraphQL expects, and from the schema we can see that it expects a [Todo!]! (See docs tab) and a Todo looks like (see the schema tab):

type Todo {
  _id: ID!
  _ts: Long!
  name: String!
  created_at: Time
}

As you can also see from that docs tab is that 'non-resolver' queries are automatically changed to return TodoPages. The function we wrote so far actually return pages.

Option 1, change the schema and turn it into a paginated resolver.

We can fix this by adding the paginated: true option to the resolver. You will have to take into account for extra parameters that will be added to the resolver as explained here. I haven't tried that myself, so I'm not 100% certain how that would work. The advantage of a paginated resolve is that you can immediately take advantage of sane pagination in the GraphQL endpoint.

Option 2, turn it into a non-paginated result.

A paginated result is a result that looks as follows: { data: [ document1, document2, .. ], before: ... after: .. }

The result doesn't accept pages but an array so I'll change it and retrieve the data field:

And we have our result.

The complete query looks as follows:

Query(
  Lambda(
    ["before", "after"],
    Select(
      ["data"],
      Map(
        Paginate(
          Range(
            Match(Index("todosByCreatedAtRange")),
            Var("before"),
            Var("after")
          )
        ),
        Lambda(
          ["created_at", "ref"],
          Let({ todo: Get(Var("ref")) }, Var("todo"))
        )
      )
    )
  )
)

Disclaimers

Once you go custom, pagination also becomes your responsibility (e.g. pass an extra parameter). You can't fetch relations out of the box anymore as you would normally do by just requesting the relations in the GraphQL body.

Some words on the benefits of UDFs and the hybrid of GraphQL/FQL

Before you shy away from FQL (and yes, we do have to add range queries and are working on that), here is some explanation on the UDF approach in general and why it makes sense to think about it anyway.

You will at a certain moment encounter things in GraphQL that are just impossible (complex conditional transactions, e.g. update document and update this other document only if some condition that results form the previous update is true). Users that use other GraphQL implementations typically solve this by writing a serverless function in case you have to implement advanced logic or transactions.

FaunaDB's answer to this is to use their User Defined Functions (UDFs). This is not a serverless function, it's a FaunaDB function implemented in FQL which might seem cumbersome at first but it's important to realize that it gives you the same benefits ( multi-region/strong consistency/scalability/free-tier/pay-as-you-go) that FaunaDB provides.

这篇关于在graphql中按时间过滤(使用floralDB服务)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆