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

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

问题描述

我的 graphQL 架构如下所示,

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

此查询有效.

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

谁能指出我如何在 graphql 中创建大于(或小于)时间查询(使用 faunaDB).

解决方案

不支持 GraphQL 范围查询(但 .. 他们来了!)

FaunaDB 没有为其 GraphQL 提供开箱即用的范围查询,我们正在研究这些功能.

...但有一个解决方法.

这并不意味着它不能执行范围查询,因为 FQL 支持范围查询,并且您始终可以通过编写用户定义函数 (UDF).

...使用解析器

通过在架构中使用

首先,我们的架构将使用解析器进行扩展:

type Todo {名称:字符串!created_at:时间}类型查询{allTodos:[Todo!]!todosByCreatedAtFlag(created_at: Time!): [Todo!]!todosByCreatedRange(before: Time, after:Time): [Todo!]!@解析器}

所有这一切只是添加一个函数供我们实现:

如果我们通过 GraphQL 调用,我们会得到我们之前在屏幕截图中看到的 Abort 消息,因为它尚未实现.但是我们可以看到GraphQL语句实际上调用了函数.

...UDF 实现

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

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

我们将添加一个索引来支持我们的查询.值用于范围(以及用于返回值和排序).我们将添加 created_at 以覆盖它并添加 ref 因为我们需要返回值来获取索引后面的实际文档.

然后我们可以开始编写一个简单的函数(目前还不能工作)

查询(拉姆达([之前",之后"],分页(范围(匹配(索引(todosByCreatedAtRange")),变量(之前"),变量(之后")))))

并且可以通过 shell 手动调用函数来测试这一点.

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

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

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

查询(拉姆达([之前",之后"],地图(分页(范围(匹配(索引(todosByCreatedAtRange")),变量(之前"),变量(之后"))),Lambda([created_at", ref"], Var(ref")))))

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

让我们得到实际的文件.我知道 FQL 很冗长(并且有充分的理由,尽管将来它会变得不那么冗长)所以我开始添加注释以澄清事情

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

我们的函数现在返回数据..哇哦!

我们仍然需要确保这些数据符合 GraphQL 的预期,并且从 schema 中我们可以看到它需要一个 [Todo!]!(请参阅文档选项卡)和 Todo 的外观(请参阅架构选项卡):

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

您还可以从该文档选项卡中看到,非解析器"查询会自动更改为返回 TodoPages.到目前为止,我们编写的函数实际上是返回页面.

选项 1,更改架构并将其转换为分页解析器.

我们可以通过向解析器添加 paginated: true 选项来解决这个问题.您必须考虑将添加到解析器的额外参数,如

我们得到了结果.

完整的查询如下所示:

查询(拉姆达([之前",之后"],选择([数据"],地图(分页(范围(匹配(索引(todosByCreatedAtRange")),变量(之前"),变量(之后"))),拉姆达([created_at",ref"],Let({ todo: Get(Var(ref")) }, Var(todo")))))))

免责声明

一旦你自定义了,分页也成为你的责任(例如传递一个额外的参数).您不能再像通常那样通过请求 GraphQL 主体中的关系来立即获取关系.

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

在您回避 FQL 之前(是的,我们确实必须添加范围查询并正在努力),这里有一些关于 UDF 方法的一般解释以及为什么无论如何考虑它是有意义的.

>

在某个时刻,您会遇到 GraphQL 中不可能发生的事情(复杂的条件事务,例如更新文档并仅在上次更新的结果为真时才更新其他文档).使用其他 GraphQL 实现的用户通常会通过编写无服务器函数来解决这个问题,以防您必须实现高级逻辑或事务.

FaunaDB 对此的回答是使用他们的用户定义函数 (UDF).这不是无服务器功能,它是在 FQL 中实现的 FaunaDB 功能,乍一看似乎很麻烦,但重要的是要意识到它为您提供了相同的好处(多区域/强一致性/可扩展性/免费层/即付即用)You-go) 由 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 中按时间过滤(使用 faunaDB 服务)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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