edgeDB入门使用记录(一)
概述: edgedb是一种新型的面向对象的数据库, 外表关联采用类似对象继承的方式(每张表都是一个类obj的set), 会自动隐式link, 每条数据都会自动创建uuid, 数据库采用迁移的模式, 查询语句, 命令语句更亲和于一般编程语法, 所以更加简洁方便! 同时: 前端可以直接发送请求url及查询体获得数据,无需后端服务!
需要注意的点: 1. 在 Edgedb 中,“无数据”会被显示为空集:{};2. ""(空字符串)实际上也是数据。 3. 注释使用符号#
-
安装好软件后,先执行初始化命令, 会自动创建一个数据库, 名称默认为edgedb; 找到default.esdl文件,配置定义数据库模块(类似创建数据库或表格)
edgedb project init
-
改动数据库之后,可以使用命令进行执行, 类似创建快照(会自动检测对比上一版,生成迁移文件, 保存所有版本快照, 用于记录变动或方便回溯)
edgedb migration create // 补充: 如果数据库type已有数据, 而迁移后有新增required字段,会导致异常 // 解决方案: 1. 删除type表中没有此字段的所有数据,再进行迁移 // 2. 如果不想删除,执行创建命令后会有提示, 给不含此字段的所有数据设一个默认值 // fill_expr> <std::str>{} // 在{}括号中间输入需要设定的默认值 // 补充,执行后会看不到列字段变化, 需要插入一条数据后才会更新 // fill_expr> 逃生舱函数 1. assert_exists 2. assert_single 3. type // casts (<bigint>.xp)
-
执行数据库更改, 自动找到最新生成的迁移文件, 根据文件内代码执行迁移, 避免误操作
edgedb migrate
-
查看数据库
edgedb list types
-
程序会自动创建一个强大的可视化web服务, 执行命令后会自动调用浏览器打开,类似在线的workbench
edgedb ui
-
稍有不同的操作符
:= 用于声明,类似赋值; = 用于判断是否相等; != 用于判断是否不相等
-
插入数据,
insert
+类名(表名)
+{}
// 一般赋值 insert User { name := "xzz" pwd := "123" } // 筛选 条件 插入 使用()包裹条件查询语句 insert Vampire { name := 'Count Dracula', places_visited := (select Place filter .name = 'Romania'), # .places_visited is the result of this select query. };
-
定义普通数组数据可以使用
array<str>
, 但如果有外链关联, 可以使用multi
链接其他对象type City { required name: str; modern_name: str; } // 普通数组 type NPC { required name: str; places_visited: array<str>; } // 关联外表的数组数据 type NPC { required name: str; multi places_visited: City }
-
插入关联数据
// 因为没有过滤筛选, 此处相当于把关联city的所有数据都进行链接 insert NPC { name := 'Jonathan Harker', places_visited := City, }; // 查看指定数据内部属性 select NPC { name, places_visited: { name } };
-
定义标量类型: 每次只保存一个值, 如枚举值, 语句结尾必须加上
;
作为结束scalar type Class extending enum<Rogue, Mystic, Merchant>;
-
定义抽象类, 抽象类只能被继承, 不能执行操作, 否则会抛出异常
abstract type Person { required name: str; multi places_visited: City; }
-
转换数据类型, 使用
<>
进行类型声明select <int32>'9' + 9; // 可以连续转换, is 表示判断, 会返回boolean select <str><int64><str><int32>50 is str;
-
过滤数据, 关键词:
filter
// .name 是 Person.name 的缩写 select Person { name, places_visited: {name}, } filter .name = 'Emil Sinclair';
-
其他关键词
- like 区分大小写
- ilike 不区分
- % 匹配部分符合的内容类似模糊搜索; 如
like Bistr%
可以匹配到“Bistritz”,不能匹配“bistritz” - [] 索引匹配
.name[0] = 'B'
匹配名称第一位是B的数据 - slice 返回切割的数据
-
数据约束, 关键词:
constraint
std::max_value(max: anytype)
// max_value() 作为具体限制 scalar type HumanAge extending int16 { constraint max_value(120); } // min(2,6,4,1) 获取其中最小值
-
删除数据, 关键词:
delete
, 如果数据有被引用, 会删除失败// 普通删除 delete Country; // 需要查看 删除的数据 select (delete Country) { name };
-
插入数据时, 希望只链接单条数据, 类似一对一关系映射, 关键词:
assert_single()
;detached
用于明确指向的是原始对象, 而非正在创建的对象本身// demo1 insert NPC { name := 'Mina Murray', lover := assert_single( ( select detached NPC filter .name = 'Jonathan Harker' ) ), places_visited := (select City filter .name = 'London'), }; // demo2 insert MinorVampire { name := 'Vampire Woman 1', master := assert_single( (select Vampire filter .name = 'Count Dracula') ), };
-
判断对应结果是否为空
{}
, 关键词:exists
select Person { name, is_single := not exists Person.lover, };
-
指定返回结果数量, 关键词:
limit
select Movie {*} limit 1 ;
-
时间类型:
std::datetime
带日期时间时区的精确数据cal::local_datetime
不带时区的日期时间数据cal::local_time
只返回时间cal::local_date
只返回日期
select <cal::local_time>('15:44:56'); // 定义module,也即定义新的表 type Time { required clock: str; property clock_time := <cal::local_time>.clock; property hour := .clock[0:2]; // 此处表示slice, 截取0到1两位 }
-
条件判断:
(result) if (condition) else
type Time { required clock: str; property clock_time := <cal::local_time>.clock; property hour := .clock[0:2]; property vampires_are := SleepState.Asleep if <int16>.hour > 7 and <int16>.hour < 19 else SleepState.Awake; }
-
插入数据的同时查询结果, 优先执行()内的语句, 并且可以对数据进行computed
select ( # Start a selection insert Time { # Put the insert inside it clock := '22:44:10' } ) # The bracket finishes the selection { # Now just choose the properties we want clock, hour, vampires_are, double_hour := <int16>.hour * 2 };
-
查看type(表格)的相关信息, 关键词:
describe
// 一般信息 describe type Time // 详细信息 describe type Time as text
-
批量插入
insert NPC { name := 'Jonathan Harker', places_visited := ( select Place filter .name in {'Munich', 'Buda-Pesth', 'Bistritz'} ) };
-
增量插入(更新),
+=
更接近原生js; 存量删除,-=
用于剔除某一条数据update NPC filter .name = 'Jonathan Harker' set { places_visited += (select Place filter .name = 'Slovakia') };
-
级联运算,
++
类似字符串拼接select 'A character from the book:' ++ (select NPC.name) ++ ',who is not ';
-
当使用了link链接其他对象时, 清除数据会失败, 可以改为使用单个插入对象
insert Vampire { name := 'Count Dracula', age := 800, slaves := { (insert MinorVampire { name := 'Vampire Woman 1', }), (insert MinorVampire { name := 'Vampire Woman 2', }), (insert MinorVampire { name := 'Vampire Woman 3', }), } };
-
想直接获取并返回json格式数据,
<json>
select <json> Person {*}
-
唯一值约束, 类似unique,
constraint exclusive
abstract type Person { required name: str { ## Add a block constraint exclusive; ## and the constraint } multi places_visited: Place; lover: Person; } // 批量定义 约束 的值 type BlogPost { title: str; author: User; constraint exclusive on ((.title, .author)); }
-
如果对于被继承者, 希望允许重名,可以使用委托
delegated
abstract type Person { required name: str { delegated constraint exclusive; } multi places_visited: Place; lover: Person; strength: int16; }
-
数组解包方法, array_unpack([3,6,8,4])
-
使用变量查询, 以$符号开头
// 此处$n为任意名字的变量, 使用变量后会自动弹窗要求输入变量值 select Person { name } filter .name = <str>$n
-
多重继承, 以实现继承多个属性(与java里类的单继承相反)
// 只有非抽象类才能继承(存疑) type Crewman extending HasNumber, Person { }
-
同一个type(表格)里插入自增数据, 可以使用
count()
, 此函数会自动计算type表内数据数量with next_number := count(Crewman) + 1, insert Crewman { number := next_number };
-
使用序列, 类似mysql里的自增主键,
sequence
// 需要继承,否则删除本体若干数据后, 数字会在减少若干后的基础上增加 scalar type TownspersonNumber extending sequence; type Townsperson extending Person { number: TownspersonNumber; }
-
使用
is
查询多类型数据, 翻译: 查询父类同时查询子类响应数据// 错误 因为 age 和 number 是 子类的属性 select Person { name, age, number, }; // 正确, 明确指定是哪一个 子类 select Person { name, [is NPC].age, [is Crewman].number, };
-
超类型
supertype
子类型subtypes
// 此处 Person 是 子类型, PC 是 超类型, 所以 会返回 true select Person { name, is_PC := Person is PC, };
-
multi
与 数组的使用// 数组适合小型数据, 且适合需要索引的 场合 type Castle extending Place { doors: array<int16>; } insert Castle { name := 'Castle Dracula', doors := [6, 19, 10], }; // multi 适合大型数据 type Castle extending Place { multi doors: int16; } insert Castle { name := 'Castle Dracula', doors := {6, 19, 10}, };
-
动态计算的link
type Movie { required title: str; multi actors: Person; # returns all movies with same title multi same_title := ( with t := .title select detached Movie filter .title = t ) }
-
反向link
type Movie { required title: str; multi actors: Person; } type Person { required name: str; multi acted_in := .<actors[is Movie]; }
-
多态 (暂未理解)
abstract type Content { required title: str; } type Movie extending Content { required release_year: int64; } type TVShow extending Content { required num_seasons: int64; } type Franchise { required name: str; multi entries: Content; }
-
给表中数据自动定义默认添加值,
overload
default
type NPC extending Person { age: HumanAge; overloaded multi places_visited: Place { default := (select City filter .name = 'London'); } } // 限定数据 type NPC extending Person { overloaded age: int16 { constraint max_value(120); } overloaded multi places_visited: Place { default := (select City filter .name = 'London'); } }
-
获取当前时间函数
datetime_current()
-
新增数据时, 自动插入当前时间
datetime_of_statement()
abstract type Place { required name: str { delegated constraint exclusive; } modern_name: str; post_date: datetime { default := datetime_of_statement() } }
-
批量插入新数据,
for in
合并数据:union
// 批量插入 for character_name in {'John Seward', 'Quincey Morris', 'Arthur Holmwood'} union ( insert NPC { name := character_name, lovers := (select Person filter .name = 'Lucy Westenra'), } ); // 循环 for n in {1, 2, 3, 4, 5} union ( insert Crewman { number := n, first_appearance := cal::to_local_date(1893, 7, 6), last_appearance := cal::to_local_date(1893, 7, 16), } );