Showcase
This section illustrates how EdgeDB and EdgeQL can be used
in an implementation of an issue tracking system.
abstract concept Text:
# This is an abstract object containing text.
required link body -> str:
constraint maxlength:
# Maximum length of text is 10000
# characters.
10000
concept User is builtins.NamedObject
# NamedObject is a standard abstract base class,
# that provides a name link.
abstract concept OwnedObject:
# By default links are optional.
required link owner -> User
concept Status is builtins.Dictionary
# Dictionary is a NamedObject variant, that enforces
# name uniqueness across all instances if its subclass.
concept Priority is builtins.Dictionary
concept LogEntry is OwnedObject, Text:
# LogEntry is an OwnedObject and a Text, so it
# will have all of their links and attributes,
# in particular, owner and text links.
required link spent_time -> int
atom issue_num_t is builtins.sequence
# issue_num_t is defined as a concrete sequence type,
# used to generate sequential issue numbers.
concept Comment is Text, OwnedObject:
required link issue -> Issue
link parent -> Comment
concept Issue is builtins.NamedObject, OwnedObject, Text:
required link number -> issue_num_t:
readonly: true
# The number values are automatically generated,
# and are not supposed to be directly writable.
required link status -> Status
link priority -> Priority
link watchers -> User:
mapping: **
# The watchers link is mapped to User concept in
# many-to-many relation. The default mapping is
# *1 -- many-to-one.
link time_estimate -> int
link time_spent_log -> LogEntry:
mapping: 1*
# 1* -- one-to-many mapping.
link start_date -> datetime:
default := SELECT datetime::current_datetime()
# The default value of start_date will be a
# result of the EdgeQL expression above.
link due_date -> datetime
link related_to -> Issue:
mapping: **
# EdgeQL query to extract all relevant issue details
# traversing the hierarchy of Issue concept tree.
SELECT
Issue[
number,
name,
owner[
name
# Extract the related User object and
# include the value of the name link.
],
watchers[
name
],
priority[
name
],
status[
name
],
total_time_spent := (
SELECT agg::sum(
Issue.time_spent_log.spent_time
)
# The value of total_time_spent attribute
# is computed dynamically as a sum of all
# spen_time values in all LogEntries
# subordinate to this issue.
),
start_date,
due_date,
related_to[
number,
name,
priority[
name
]
]
]
WHERE
Issue.number = $number
# $number is a named query argument.
# An example JSON result of the query.
[
{
"id": "feae8382-5966-11e5-b3b1-b776f4595104",
"number": 163,
"name": "Boxes don't have round corners in IE6",
"owner": {
"id": "40dd6534-5967-11e5-9bda-3f5ef6a8f9be",
"name": "Joe Doe"
},
"watchers": [
{
"id": "5b643fcc-5967-11e5-a4e1-5311fa226efc",
"name": "Anna Smith"
},
{
"id": "6cb70818-5967-11e5-8a20-170ea3d3b6a3",
"name": "Peter Mill"
}
],
"priority": {
"id": "92bcd74a-5967-11e5-9ffa-2f6b459e5b3f",
"name": "URGENT"
},
"status": {
"id": "a6a56de4-5967-11e5-8abd-1b738a8d5e0c",
"name": "WON'T FIX"
},
"total_time_spent": 0,
"start_date": "2015-08-11T20:23:06+00:00",
"due_date": "2015-08-11T22:00:06+00:00",
"related_to": [
{
"id": "2ff12728-5968-11e5-8cd1-8f6dd62ac490",
"number": 121,
"name": "Images have strange background in IE7",
"priority": {
"id": "92bcd74a-5967-11e5-9ffa-2f6b459e5b3f",
"name": "URGENT"
}
}
]
}
]
# Perform full-text search on all Text objects
# in the database containing word "spam".
SELECT
Text[
type := __type__.name,
body,
# If a matched Text object is an Issue,
# extract extra information.
Issue.name,
Issue.number,
Issue.priority[
name
],
Issue.status[
name
],
# If a matched Text object is a LogEntry,
# extract 'spent_time'.
LogEntry.spent_time
]
WHERE
Text.body @@ "spam"
# An example JSON result of the query.
[
{
"id": "aba63e76-596d-11e5-b000-4b902bd4d4bc",
"type": "LogEntry",
"body": "Fixed spammy email title",
"spent_time": 1.2
},
{
"id": "0efd1f36-596d-11e5-b260-cfbc3fe107cf",
"type": "Issue",
"body": "Looks like spam to me.",
"name": "Too many notifications sent",
"number": 102,
"priority": {
"id": "796400b0-596d-11e5-b489-ff55f9e47278",
"name": "LOW"
},
"status": {
"id": "9daec59a-596d-11e5-901b-3baab1c5ba88",
"name": "NEW"
}
}
]
# Extract a tree of specific issue comments and
# replies recursively.
SELECT
Comment[
owner[
name
],
body,
children := <parent*
# Extract comments tree into 'children'
# result attribute:
# '*' signifies link recursion;
# '<' requests backward traversal of
# parent links so that the ordering
# of objects is correct.
]
WHERE
Comment.issue.number = $number
ORDER BY
Comment.mtime
# An example JSON result of the query.
[
{
"id": "64d80c48-596a-11e5-bcef-23dcc79df376",
"owner": {
"id": "6cb70818-5967-11e5-8a20-170ea3d3b6a3"
"name": "Peter Mill"
},
"body": "Works now, thanks!",
"children": [
{
"id": "4aa31966-596b-11e5-ac48-a3cb996c225b",
"owner": {
"id": "5b643fcc-5967-11e5-a4e1-5311fa226efc",
"name": "Anna Smith"
},
"body": "In my IE6 too!",
"children": []
}
]
}
]
# An example of Python 3.5 code using the EdgeDB
# Python binding.
import edgedb
from acmetracker import schema
async def print_latest_activity(user, *, limit=10):
# Extract and print last *limit* items
# belonging to a *user*, excluding LogEntries.
my_activity = schema.OwnedObject.select([
(schema.Issue, [
schema.Issue.number,
schema.Issue.due_date,
(schema.Issue.priority, [
schema.Priority.name
])
]),
edgedb.builtins.NamedObject.name,
schema.Text.body
]).where(
schema.OwnedObject.owner == user,
edgedb.isnot(schema.OwnedObject, schema.LogEntry)
).orderby(
(schema.OwnedObject.mtime, 'desc')
)
# Iterate over results asynchronously
async for entry in my_activity[:limit]:
print(entry)
# Script output:
<Issue "075561da-906e..." at 0x105817070>
<Issue "0ed8a600-4ad6..." at 0x105817080>
<Comment "1942a1fc-ffad..." at 0x105817090>
<Comment "b2d0ec00-42fe..." at 0x1058170a0>