以下是来自官方的一个user group pet 的查询demo

参考关系图

环境准备

  • docker-compose mysql 环境
version: "3"
services:
  mysql:
    image: mysql:5.7.16
    ports:
      - 3306:3306
    command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    environment:
      MYSQL_ROOT_PASSWORD: dalongrong
      MYSQL_DATABASE: gogs
      MYSQL_USER: gogs
      MYSQL_PASSWORD: dalongrong
      TZ: Asia/Shanghai
 
 
  • golang mod 项目
go mod init github.com/rongfengliang/ent-pet-user-group

生成实体模型

  • 生成schema
entc init Pet User Group
  • 配置schema 字段以及边
    pets
package schema
import (
  "github.com/facebookincubator/ent"
  "github.com/facebookincubator/ent/schema/edge"
  "github.com/facebookincubator/ent/schema/field"
)
// Pet holds the schema definition for the Pet entity.
type Pet struct {
  ent.Schema
}
// Fields of the Pet.
func (Pet) Fields() []ent.Field {
  return []ent.Field{
    field.String("name"),
  }
}
// Edges of the Pet.
func (Pet) Edges() []ent.Edge {
  return []ent.Edge{
    edge.To("friends", Pet.Type),
    edge.From("owner", User.Type).
      Ref("pets").
      Unique(),
  }
}
 
 

user

package schema
import (
  "github.com/facebookincubator/ent"
  "github.com/facebookincubator/ent/schema/edge"
  "github.com/facebookincubator/ent/schema/field"
)
// User holds the schema definition for the User entity.
type User struct {
  ent.Schema
}
// Fields of the User.
func (User) Fields() []ent.Field {
  return []ent.Field{
    field.Int("age"),
    field.String("name"),
  }
}
// Edges of the User.
func (User) Edges() []ent.Edge {
  return []ent.Edge{
    edge.To("pets", Pet.Type),
    edge.To("friends", User.Type),
    edge.From("groups", Group.Type).
      Ref("users"),
    edge.From("manage", Group.Type).
      Ref("admin"),
  }
}
 
 

group

package schema
import (
  "github.com/facebookincubator/ent"
  "github.com/facebookincubator/ent/schema/edge"
  "github.com/facebookincubator/ent/schema/field"
)
// Group holds the schema definition for the Group entity.
type Group struct {
  ent.Schema
}
// Fields of the Group.
func (Group) Fields() []ent.Field {
  return []ent.Field{
    field.String("name"),
  }
}
// Edges of the Group.
func (Group) Edges() []ent.Edge {
  return []ent.Edge{
    edge.To("users", User.Type),
    edge.To("admin", User.Type).
      Unique(),
  }
}
 

生成边以及定点数据

package main
import (
  "context"
  "fmt"
  "log"
  _ "github.com/go-sql-driver/mysql"
  "github.com/rongfengliang/ent-pet-user-group/ent"
)
func main() {
  client, err := ent.Open("mysql", "root:dalongrong@tcp(127.0.0.1)/gogs")
  if err != nil {
    log.Fatalf("failed opening connection to sqlite: %v", err)
  }
  defer client.Close()
  ctx := context.Background()
  Gen(ctx, client.Debug())
}
// Gen for generate demo datas
func Gen(ctx context.Context, client *ent.Client) error {
  hub, err := client.Group.
    Create().
    SetName("Github").
    Save(ctx)
  if err != nil {
    return fmt.Errorf("failed creating the group: %v", err)
  }
  // Create the admin of the group.
  // Unlike `Save`, `SaveX` panics if an error occurs.
  dan := client.User.
    Create().
    SetAge(29).
    SetName("Dan").
    AddManage(hub).
    SaveX(ctx)
  // Create "Ariel" and its pets.
  a8m := client.User.
    Create().
    SetAge(30).
    SetName("Ariel").
    AddGroups(hub).
    AddFriends(dan).
    SaveX(ctx)
  pedro := client.Pet.
    Create().
    SetName("Pedro").
    SetOwner(a8m).
    SaveX(ctx)
  xabi := client.Pet.
    Create().
    SetName("Xabi").
    SetOwner(a8m).
    SaveX(ctx)
  // Create "Alex" and its pets.
  alex := client.User.
    Create().
    SetAge(37).
    SetName("Alex").
    SaveX(ctx)
  coco := client.Pet.
    Create().
    SetName("Coco").
    SetOwner(alex).
    AddFriends(pedro).
    SaveX(ctx)
  fmt.Println("Pets created:", pedro, xabi, coco)
  // Output:
  // Pets created: Pet(id=1, name=Pedro) Pet(id=2, name=Xabi) Pet(id=3, name=Coco)
  return nil
}
 

一些查询

为了方便查看生成的sql 我使用了debug,同时也可以开启慢查询

set global slow_query_log=1;
set global long_query_time=0;
  • 一个demo
    上面的遍历从一个Group实体开始,继续到其admin(边),继续到其friends(边),获取其pets(边),获取每个宠物的
    friends(边),并请求其所有者
    参考图

    代码:

 
func Traverse(ctx context.Context, client *ent.Client) error {
    owner, err := client.Group. // GroupClient.
        Query(). // Query builder.
        Where(group.Name("Github")). // Filter only Github group (only 1).
        QueryAdmin(). // Getting Dan.
        QueryFriends(). // Getting Dan's friends: [Ariel].
        QueryPets(). // Their pets: [Pedro, Xabi].
        QueryFriends(). // Pedro's friends: [Coco], Xabi's friends: [].
        QueryOwner(). // Coco's owner: Alex.
        Only(ctx) // Expect only one entity to return in the query.
    if err != nil {
        return fmt.Errorf("failed querying the owner: %v", err)
    }
    fmt.Println(owner)
    // Output:
    // User(id=3, age=37, name=Alex)
    return nil
}
 

效果:

2019/10/15 13:10:12 driver.Query: query=SELECT DISTINCT `users`.`id`, `users`.`age`, `users`.`name` FROM `users` JOIN (SELECT `pets`.`owner_id` FROM
 `pets` JOIN (SELECT `pet_friends`.`friend_id` FROM `pet_friends` JOIN (SELECT `pets`.`id` FROM `pets` JOIN (SELECT `users`.`id` FROM `users` JOIN (
SELECT `user_friends`.`friend_id` FROM `user_friends` JOIN (SELECT `users`.`id` FROM `users` JOIN (SELECT `groups`.`admin_id` FROM `groups` WHERE `g
roups`.`name` = ?) AS `t1` ON `users`.`id` = `t1`.`admin_id`) AS `t1` ON `user_friends`.`user_id` = `t1`.`id`) AS `t1` ON `users`.`id` = `t1`.`frien
d_id`) AS `t1` ON `pets`.`owner_id` = `t1`.`id`) AS `t1` ON `pet_friends`.`pet_id` = `t1`.`id`) AS `t1` ON `pets`.`id` = `t1`.`friend_id`) AS `t1` O
N `users`.`id` = `t1`.`owner_id` LIMIT ? args=[Github 2]
User(id=3, age=37, name=Alex)
 
 

sql 查询计划

EXPLAIN SELECT
    DISTINCT `users`.`id`,
    `users`.`age`,
    `users`.`name`
FROM
    `users`
    JOIN (
        SELECT
            `pets`.`owner_id`
        FROM
            `pets`
            JOIN (
                SELECT
                    `pet_friends`.`friend_id`
                FROM
                    `pet_friends`
                    JOIN (
                        SELECT
                            `pets`.`id`
                        FROM
                            `pets`
                            JOIN (
                                SELECT
                                    `users`.`id`
                                FROM
                                    `users`
                                    JOIN (
                                        SELECT
                                            `user_friends`.`friend_id`
                                        FROM
                                            `user_friends`
                                            JOIN (
                                                SELECT
                                                    `users`.`id`
                                                FROM
                                                    `users`
                                                    JOIN (
                                                        SELECT
                                                            `groups`.`admin_id`
                                                        FROM
                                                            `groups`
                                                        WHERE
                                                            `groups`.`name` = 'Github'
                                                    ) AS `t1` ON `users`.`id` = `t1`.`admin_id`
                                            ) AS `t1` ON `user_friends`.`user_id` = `t1`.`id`
                                    ) AS `t1` ON `users`.`id` = `t1`.`friend_id`
                            ) AS `t1` ON `pets`.`owner_id` = `t1`.`id`
                    ) AS `t1` ON `pet_friends`.`pet_id` = `t1`.`id`
            ) AS `t1` ON `pets`.`id` = `t1`.`friend_id`
    ) AS `t1` ON `users`.`id` = `t1`.`owner_id`
 
 

效果:

  • 另外一个demo
    我们要获取所有owner(edge)是friend 某个组admin(边)的宠物(实体)。 
func Traverse(ctx context.Context, client *ent.Client) error {
    pets, err := client.Pet.
        Query().
        Where(
            pet.HasOwnerWith(
                user.HasFriendsWith(
                    user.HasManage(),
                ),
            ),
        ).
        All(ctx)
    if err != nil {
        return fmt.Errorf("failed querying the pets: %v", err)
    }
    fmt.Println(pets)
    // Output:
    // [Pet(id=1, name=Pedro) Pet(id=2, name=Xabi)]
    return nil
}
 
 

效果

go run cmd/query2/main.go 
2019/10/15 13:18:47 driver.Query: query=SELECT DISTINCT `pets`.`id`, `pets`.`name` FROM `pets` WHERE `pets`.`owner_id` IN (SELECT `id` FROM `users` 
WHERE `users`.`id` IN (SELECT `user_friends`.`user_id` FROM `user_friends` JOIN `users` AS `t0` ON `user_friends`.`friend_id` = `t0`.`id` WHERE `t0`
.`id` IN (SELECT `admin_id` FROM `groups` WHERE `admin_id` IS NOT NULL))) args=[]
[Pet(id=1, name=Pedro) Pet(id=2, name=Xabi)]
 

查询计划

EXPLAIN SELECT
    DISTINCT `pets`.`id`,
    `pets`.`name`
FROM
    `pets`
WHERE
    `pets`.`owner_id` IN (
        SELECT
            `id`
        FROM
            `users`
        WHERE
            `users`.`id` IN (
                SELECT
                    `user_friends`.`user_id`
                FROM
                    `user_friends`
                    JOIN `users` AS `t0` ON `user_friends`.`friend_id` = `t0`.`id`
                WHERE
                    `t0`.`id` IN (
                        SELECT
                            `admin_id`
                        FROM
                            `groups`
                        WHERE
                            `admin_id` IS NOT NULL
                    )
            )
    )
 
 

效果

参考资料

https://entgo.io/docs/traversals/
https://github.com/rongfengliang/ent-pet-user-group

ent 基本使用十五 一个图遍历的例子的更多相关文章

  1. PMP十五至尊图(第六版)

    PMP(Project Management Professinoal)项目经理专业资格认证,由美国项目管理学会PMI(Project Management Institute)发起并组织的一种资格认 ...

  2. 第七十五课 图的遍历(DFS)

    添加DFS函数: #ifndef GRAPH_H #define GRAPH_H #include "Object.h" #include "SharedPointer. ...

  3. 【PMP】十五至尊图

    以上是PMP的10大知识领域与5个过程组,在PMP考试中属于必须记忆的知识,该知识来源于PMBOK 第6版 附件为每日练习记忆模板,可以更好的记忆上图 点击下载附件

  4. ExpandoObject与DynamicObject的使用 RabbitMQ与.net core(一)安装 RabbitMQ与.net core(二)Producer与Exchange ASP.NET Core 2.1 : 十五.图解路由(2.1 or earler) .NET Core中的一个接口多种实现的依赖注入与动态选择看这篇就够了

    ExpandoObject与DynamicObject的使用   using ImpromptuInterface; using System; using System.Dynamic; names ...

  5. 通过Dapr实现一个简单的基于.net的微服务电商系统(十五)——集中式接口文档实现

    之前有小伙伴在评论区留言说如何集成swagger,最开始没有想透给了对方一个似是而非的回答.实际上后来下来想了一下,用.NET5 提供的Source Generator其实可以很方便的实现接口集成.今 ...

  6. 第三十五个知识点:给针对ECDLP问题的Pollard rho,Pollard "Kangaroo",parallel Pollard rho攻击的一个粗略的描述

    第三十五个知识点:给针对ECDLP问题的Pollard rho,Pollard "Kangaroo",parallel Pollard rho攻击的一个粗略的描述 我们的目标是对任 ...

  7. 使用Multiplayer Networking做一个简单的多人游戏例子-1/3(Unity3D开发之二十五)

    猴子原创,欢迎转载.转载请注明: 转载自Cocos2Der-CSDN,谢谢! 原文地址: http://blog.csdn.net/cocos2der/article/details/51006463 ...

  8. 第四百一十五节,python常用排序算法学习

    第四百一十五节,python常用排序算法学习 常用排序 名称 复杂度 说明 备注 冒泡排序Bubble Sort O(N*N) 将待排序的元素看作是竖着排列的“气泡”,较小的元素比较轻,从而要往上浮 ...

  9. 如鹏网学习笔记(十五)ASP.NET MVC核心基础笔记

    一.ASP.Net MVC简介 1,什么是ASP.NET MVC? HttpHandler是ASP.net的底层机制,如果直接使用HttpHandler进行开发难度比较大.工作量大.因此提供了ASP. ...

随机推荐

  1. windows上 nginx 配置代理服务,配置多域名,以及最简单实现跨域配置

    Nginx,不用多说啦,大家都熟悉的不能再熟悉了,它是一款轻量级的高性能Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,最近在本地研究将nginx和resin配合使用,使服务 ...

  2. java什么是构造方法

    构造方法 一.构造方法的特点 (1)每当创建给定类的实例时就调用的方法 (2)与类同名,但没有返回类型 (3)Java 为对象分配内存,初始化实例变量并调用构造方法 (4)两种构造方法 1.参数化构造 ...

  3. Django---Django的中间件

    Django---Django的中间件 一丶中间件介绍 什么是中间件 官方的说法:中间件是一个用来处理Django的请求和响应的框架级别的钩子.它是一个轻量.低级别的插件系统,用于在全局范围内改变Dj ...

  4. 【转载】Asp.Net生成图片验证码工具类

    在Asp.Net应用程序中,很多时候登陆页面以及其他安全重要操作的页面需要输入验证码,本文提供一个生成验证码图片的工具类,该工具类通过随机数生成验证码文本后,再通过C#中的图片处理类位图类,字体类,一 ...

  5. 封装axios,带请求头和响应头

    import axios from "axios"; import qs from "qs"; //处理参数 import router from '../ro ...

  6. map字典,储存cookie,切换账户,展示购物车不同商品

    1:首页 1,静态html5+css做好基本样式 html5,css,jQery, sass 2,jsonp的方式src引入模拟的json数据//这里用的jsonp方式拿数据,详情有使用ajax 遍历 ...

  7. 记录下Hbuilder 打包IOS发布时 总是提示错误:ios prifile文件与私钥证书匹配 的问题

    最近两天,新的APP准备要上线,然后打包正式发布版 时,总是提示不匹配 证书照hbuilder里面的文档 一样也不行,然后百度了N种方法,都是不行,而且也比较少搜索到相关问题. 后来都是谷歌了下,找到 ...

  8. Android源码分析(四)-----Android源码编译及刷机步骤

    一 : 获取源码: 每个公司服务器地址不同,以如下源码地址为例: http://10.1.14.6/android/Qualcomm/msm89xx/branches/msm89xx svn环境执行: ...

  9. Spark编译的三种方式

    有三种编译方式,此文采用make-distribution.sh编译 其余两种为maven 和SBT编译 1.配置jdk 配置maven 配置scala 2.修改spark下make-distribu ...

  10. ThinkPHP5中如何实现模板完全静态化

    模板完全静态化,也就是通过模板完全生成纯静态的网页,相比动态页面和伪静态页面更安全更利于SEO访问更快.相比前二者各有利弊吧,现在稍微对这三种形式的优缺点对比一下,以及在ThinkPHP5项目中实现完 ...