Skip to content

Latest commit

 

History

History
2119 lines (1610 loc) · 53.7 KB

presentation.adoc

File metadata and controls

2119 lines (1610 loc) · 53.7 KB

Crux Tutorial

.slide-background-content { background: #353535; } .reveal h2 { font-size: 1.6em; color: #ffc85f; } .reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6, .slide-content { } .reveal img, .reveal video, .reveal iframe { height: 50vh; } .reveal section img { margin: unset; background: unset; border: unset; box-shadow: unset; } .reveal .comms blockquote { font-family: Courier; font-size: 2rem !important; color: rgba(255, 176, 25, 0.90); background: rgba(255, 255, 255, 0.05); box-shadow: 0px 0px 10px rgba(255, 176, 25, 0.36); font-style: unset; text-align: justify; width: unset; } .reveal code { font-family: Monaco; text-transform: none; color: orange; } .reveal pre { font-size: 2.5rem !important; color: rgba(255, 176, 25, 0.90); background: rgba(255, 255, 255, 0.05); line-height: unset; box-shadow: 0px 0px 10px rgba(255, 176, 25, 0.36); } .listingblock code { font-size: .5em; } .manual .slide-content { box-shadow: 0px 0px 10px rgba(255, 255, 255, 0.36); background: black; font-family: courier; font-size: 0.7em; overflow-y: auto; height: 18em; text-align: left; padding: 1em; width: unset; } .speech blockquote { width: unset; font-size: .75em; height: 18em; overflow: auto; } .language-clj { } .reveal pare code { } .manual { } .reveal blockquote { } .reveal blockquote p { } .comms { } .comms p { } .ticket { background: white; box-shadow: 10px 20px #F0000011; height: 13em; overflow: auto; } .ticket p, .ticket .hdlist1, .ticket .hdlist2, .ticket .title { font-family: times; font-size: 1.5rem; color: #370000 !important; }

Welcome

Introduction

Johanna Antonelli

[email protected]

  • Joined JUXT as intern, full time year later after masters

  • Tutorial was first task

Space Tutorial

  • The space tutorial is designed as a gentle introduction to Crux.

  • Using basic Clojure

  • Choose your own adventure.. (debatable)

  • made to be a welcoming introduction to Crux

  • gentle introduction to basic concepts

  • basic clojure used

Etiquette

Technical questions at the end

  • The tutorial aims to cover topics as standalone as can be. This means a lot of questions may be answered as we progress.

  • Make a note.

Any questions so far?

Mission

Welcome to the year 2115

Congratulations on your new position at Helios Banking Inc.

  • Require juxt/crux as a dependency in your project

  • It’s the year 2115.

  • You have been hired by an inter-planetary bank, Helios Banking Inc.

  • Your task is to travel around the solar system completing assignments using Crux.

  • You have been given a Crux manual which will help you along your way.

  • In preparation you will have set up a project with crux as a dependency

  • instructions in the posts.

Earth

earth

Space Port

A warm welcome from the Helios family.

For your first assignment we would like you to go to Pluto and help Tombaugh Resources Ltd. set up their stock reporting system.

Don’t forget to fill in your flight manifest, else you won’t be permitted entry to the planets.

Power up

Before you leave you must fill in your flight manifest. To do this, you must first set up a Crux node.

Crux Manual

For a Clojure in-process node, the start-node function accepts a module tree, a file, or a resource.

(require '[crux.api :as crux]
         '[clojure.java.io :as io])

(crux/start-node {...}) ; module tree
(crux/start-node (io/file ...))
(crux/start-node (io/resource ...))

Start node

(require '[crux.api :as crux]
         '[clojure.java.io :as io])

(def node
 (crux/start-node
  {:crux.node/topology '[crux.standalone/topology]
   :crux.kv/db-dir "data/db-dir"}))
  • You want to get started as quickly as possible so you decide to use Crux’s inbuilt standalone node topology.

  • There is no persistence with this, so it is perfect for testing and debug.

  • Even so, Crux needs a database directory specified

Flight manifest

You take a look around your ship and check the critical levels.

You read the manual entry for putting data into Crux.

Crux manual

Crux takes data in document form. Each document must be in Extensible Data Notation (edn) and each document must contain a unique :crux.db/id value. However, beyond those two requirements you have the flexibility to add whatever you like to your documents because Crux is schemaless.

Flight Manifest

(def manifest
  {:crux.db/id :manifest
   :pilot-name "Johanna"
   :id/rocket "SB002-sol"
   :id/employee "22910x2"
   :badges "SETUP"
   :cargo ["stereo" "gold fish" "slippers" "secret note"]})
  • Just as you’re about to write your manifest, one of the porters passes you a secret note and asks you to deliver it to a martian named Kaarlang.

  • They are certain you will meet Kaarlang on your travels and so you see no harm in delivering the note for them.

You put the manifest into Crux.

(crux/submit-tx node [[:crux.tx/put manifest]])
;;=>#:crux.tx{:tx-id 0, :tx-time #inst "2020-06-18T13:54:08.375-00:00"}

This is put, one of Crux’s four transaction operations.

Check entity history

Check that this was successful by asking Crux to show the entity history.

(crux/entity-history (crux/db node) :manifest :asc)
;;=> {:crux.tx/tx-time #inst "2020-06-18T13:54:08.375-00:00",
;;    :crux.tx/tx-id 0,
;;    :crux.db/valid-time #inst "2020-06-18T13:54:08.375-00:00",
;;    :crux.db/content-hash #crux/id "0ab888b62775eea2eb2fffe10c9f6bfbf661a792"}
  • Which returns the history of the provided entity id at this point in time.

  • You can use :asc or :desc flag with entity-history for the ascending or descending history respectively.

Liftoff!

Pluto

pluto
  • As you enter the Plutonian atmosphere, a message pops up on your communication panel:

Communications panel

Welcome to the dwarf planet Pluto. You are entering privately governed space. If you do not have the correct papers, entry will be denied. We hope you enjoy your stay.

Have a nice day.

— Anarchic Directorate of Pluto

The government of Pluto is asking to see your flight manifest.

Crux Manual

Transaction Description

put

Writes a version of a document

delete

Deletes a version of a document

match

Stops a transaction if the precondition is not met.

evict

Removes a document entirely

Put:

The put operation is used to write versions of a document (doc).

Each document must be in Extensible Data Notation (edn) and must contain a unique :crux.db/id value. However, beyond those two requirements you have the flexibility to add whatever you like to your documents because Crux is schemaless.

Along with the document (doc), put has two optional additional arguments:

start valid-time

The time at which the entry will be valid from.

end valid-time

The time at which the entry will be valid until.

This means that you can query back through Crux, you can use valid-time arguments to see the state of Crux at a different time.

If no valid-time is provided, start valid-time defaults to the transaction time and the document is valid either eternally or until the document is updated by a future transaction with the same eid.

Time in Crux is denoted

#inst "yyyy-MM-ddThh:mm:SS"

For example, 9:30 pm on January 2nd 1999 would be written:

#inst "1999-01-02T21:30:00".

A complete put transaction has the form

[:crux.tx/put doc valid-time-start valid-time-end]

  • Currently there are only four transaction operations in Crux: put, delete, match and evict.

Ticket

Task

Commodity Logging

Company

Tombaugh Resources Ltd.

Contact

R. Glogofloon

Submitted

2115-02-20T13:38:20

Additional information

We need help setting up a new recording system for our mine. I have enclosed a list of the commodities we deal with. Please send someone soon because we already have a week’s worth of unrecorded stock-takes.

Attachments

TOMBAUGH-COMMOD_00ASD776a

You land on the surface of the dwarf planet. As you do, the job ticket for this assignment is unlocked.

Assignment

(crux/submit-tx node
                [[:crux.tx/put
                  {:crux.db/id :commodity/Pu
                   :common-name "Plutonium"
                   :type :element/metal
                   :density 19.816
                   :radioactive true}]

                 [:crux.tx/put
                  {:crux.db/id :commodity/N
                   :common-name "Nitrogen"
                   :type :element/gas
                   :density 1.2506
                   :radioactive false}]

                 [:crux.tx/put
                  {:crux.db/id :commodity/CH4
                   :common-name "Methane"
                   :type :molecule/gas
                   :density 0.717
                   :radioactive false}]])
;;=> #:crux.tx{:tx-id 0, :tx-time #inst "2020-06-18T14:11:51.087-00:00"}
  • You make your way over to the mines on the next shuttle.

  • On your way you decide to get a head start and put the commodities into Crux.

  • Since it takes six hours for each transaction to reach your Crux node on Earth from here, it is a good idea to batch up all the commodities in a single transaction.

Stock take

Hello, I’m glad you’re here.

I would like you to fill in our last week’s worth of data on our commodities. We need to be able to look back at a given day and see what our stocks were for auditing purposes.

The stock for each day must be submitted at 6pm Earth time (UTC) for your banks records.

Are you able to do that for me?

— R. Glogofloon

  • Reginald is asking if we can fill in the last weeks worth of data for their stocks.

  • He’s given us the valid time it needs to go in at for auditing purposes

input

crux/submit-tx node
                [[:crux.tx/put
                  {:crux.db/id :stock/Pu
                   :commod :commodity/Pu
                   :weight-ton 21 }
                  #inst "2115-02-13T18"] ;; valid-time

                 [:crux.tx/put
                  {:crux.db/id :stock/Pu
                   :commod :commodity/Pu
                   :weight-ton 23 }
                  #inst "2115-02-14T18"]

                 [:crux.tx/put
                  {:crux.db/id :stock/Pu
                   :commod :commodity/Pu
                   :weight-ton 22.2 }
                  #inst "2115-02-15T18"]

                 [:crux.tx/put
                  {:crux.db/id :stock/Pu
                   :commod :commodity/Pu
                   :weight-ton 24 }
                  #inst "2115-02-18T18"]

                 [:crux.tx/put
                  {:crux.db/id :stock/Pu
                   :commod :commodity/Pu
                   :weight-ton 24.9 }
                  #inst "2115-02-19T18"]])
;;=> #:crux.tx{:tx-id 1, :tx-time #inst "2020-06-18T14:14:08.347-00:00"}

input

(crux/submit-tx node
                [[:crux.tx/put
                  {:crux.db/id :stock/N
                   :commod :commodity/N
                   :weight-ton 3 }
                  #inst "2115-02-13T18"  ;; start valid-time
                  #inst "2115-02-19T18"] ;; end valid-time

                 [:crux.tx/put
                  {:crux.db/id :stock/CH4
                   :commod :commodity/CH4
                   :weight-ton 92 }
                  #inst "2115-02-15T18"
                  #inst "2115-02-19T18"]])
;;=> #:crux.tx{:tx-id 2, :tx-time #inst "2020-06-18T14:15:19.716-00:00"}

You notice that the amount of Nitrogen and Methane has not changed in the last week which saves you some time

Result

(crux/entity (crux/db node #inst "2115-02-14") :stock/Pu)
;;=> {:crux.db/id :stock/Pu, :commod :commodity/Pu, :weight-ton 21}

(crux/entity (crux/db node #inst "2115-02-18") :stock/Pu)
;;=> {:crux.db/id :stock/Pu, :commod :commodity/Pu, :weight-ton 22.2}

The CEO is impressed with your speed, but a little skeptical that you have done it properly.

You gain their confidence by showing them the entries for Plutonium on two different days:

Easy ingest

(defn easy-ingest
  "Uses Crux put transaction to add a vector of documents to a node"
  [node docs]
  (crux/submit-tx node (mapv (fn [doc] [:crux.tx/put doc]) docs)))

As a parting gift to them you create an easy ingest function so that if they needed to add more commodities to their stock list they could do it fast.

Tombaugh Resources Ltd. are happy that this will be simple enough to use. They thank you for the extra help and you head back to your ship.

Space Port

Congratulations on completing your first assignment.

We would like you to go to Mercury, the hub of the trade world. Their main trade center has a new IT department and want you to show them how to query Crux.

— Helios Banking Inc.

You are back at your ship and check your communications panel. There is a new assignment waiting for you:

Manifest

(crux/submit-tx
 node
 [[:crux.tx/put
   (assoc manifest :badges ["SETUP" "PUT"])]])
;;=> #:crux.tx{:tx-id 3, :tx-time #inst "2020-06-18T14:20:31.602-00:00"}

It’s along flight so you refuel, and update your manifest.

You have been awarded a new badge.

The Clojure function assoc to save time.

Liftoff!

Mercury

mercury

Communications Panel

You come into range of the satellites orbiting Mecury.

Your communications panel lights up.

The government is asking to see your flight manifest.

You have permission to land

Greetings.

You have reached Mercury, home to the greatest stock market in the Solar System. Your ship has been flagged as a transport vessel for business related activities.

Please have your flight manifest ready and prepare to land.

— Mercury Commonwealth

Space Port

You read your Crux manual as you wait for an available landing pad.

Crux Manual

A Datalog query consists of a set of variables and a set of clauses. The result of running a query is a result set (or lazy sequence) of the possible combinations of values that satisfy all of the clauses at the same time. These combinations of values are referred to as "tuples".

The possible values within the result tuples are derived from your database of documents.

The documents themselves are represented in the database indexes as "entity–attribute–value" (EAV) facts. For example, a single document

{:crux.db/id :myid :color "blue" :age 12}

is transformed into two facts

[[:myid :color "blue"][:myid :age 12]]

In the most basic case, a Datalog query works by searching for "subgraphs" in the database that match the pattern defined by the clauses. The values within these subgraphs are then returned according to the list of return variables requested in the :find vector within the query.

Ticket

You land on the surface of the tidally locked planet. As you do, the job ticket for this assignment is issued.

Task

Find information on products for stock buyers

Company

Interplanetary Buyers & Sellers (IPBS)

Contact

Cosmina Sinnett

Submitted

2115-06-20T10:54:27

Additional information

We have some new starters in the sales team. They need to be trained on how to query Crux using Datalog to quickly find the information they need on a product. Traders must have access to up-to-date information when talking to their clients. We would also like you to create a function that can be used for the things we have to look up a lot. I will include example data so they can learn using relevant commodities.

Attachments

Attachments :: example_data.txt

Attachment

On your way over to the IPBS office you input the data in the attachment using the easy ingest function you created on Pluto. This means you are ready to give them a tutorial when you get there.

cosmina

Oh good, you’re here. I have a room reserved and we have five new starters ready and waiting to learn how to query Crux.

We are in the middle of our double sunrise. The workers take this time to rest, but in half an Earth hour the sun will rise again and the workers will start back.

You can take this time to prepare any training material if you wish.

— Cosmina Sinnett, Head of Training - IPBS

You have the opportunity to prepare examples for the lesson ahead.

Lesson plan

You put together examples and make notes so you can be confident in your lesson.

Lesson 1: Basic Query

(crux/q (crux/db node)
        '{:find [element]
          :where [[element :type :element/metal]]})
;;=> #{[:commodity/Pu] [:commodity/Au]}

This basic query is returning all the elements that are defined as :element/metal. The :find clause tells Crux what variables you want to return.

In this case we are returning the :crux.db/id due to our placement of element.

Lesson 2: Quoting

(=
 (crux/q (crux/db node)
         '{:find [element]
           :where [[element :type :element/metal]]})

 (crux/q (crux/db node)
         {:find '[element]
          :where '[[element :type :element/metal]]})

 (crux/q (crux/db node)
         (quote
          {:find [element]
           :where [[element :type :element/metal]]})))
;;=> true

The vectors given to the clauses should be quoted. How you do it at this stage is arbitrary, but it becomes more important if you are using :args, which we will cover momentarily.

Lesson 3: Return the name of metal elements

(crux/q (crux/db node)
        '{:find [name]
          :where [[e :type :element/metal]
                  [e :common-name name]]})
;;=> #{["Gold"] ["Plutonium"]}

To find all the names of the commodities that have a certain property, such as :type, you need to use a combination of clauses. Here we have bound the results of type :element/metal to e. Next, we can use the results bound to e and bind the :common-name of them to name. name is what has been specified to be returned and so our result is the common names of all the elements that are metals.

One way to think of this is that you are filtering to only get the results that satisfy all the clauses.

Lesson 4: More information

(crux/q (crux/db node)
        '{:find [name rho]
          :where [[e :density rho]
                  [e :common-name name]]})
;;=> #{["Nitrogen" 1.2506] ["Carbon" 2.267] ["Methane" 0.717] ["Borax" 1.73] ["Gold" 19.3] ["Plutonium" 19.816]}

You can pull out as much data as you want into your result tuples by adding additional variables to the :find clause.

The example above returns the :density and the :common-name values for all entities in Crux that have values of some kind for both :density and :common-name attributes.

Lesson 5: Arguments

(crux/q (crux/db node)
        {:find '[name]
         :where '[[e :type t]
                  [e :common-name name]]
         :args [{'t :element/metal}]})
;;=> #{["Gold"] ["Plutonium"]}

:args can be used to further filter the results. Lets break down what is going down here.

Lesson 5: Arguments

First, we are assigning all :crux.db/id that have a :type to e:

(crux/q (crux/db node)
        {:find '[name]
         :where '[[e :type t]
                  [e :common-name name]]
         :args [{'t :element/metal}]})
;;=> #{["Gold"] ["Plutonium"]}
e

#{[:commodity/Pu] [:commodity/borax] [:commodity/CH4] [:commodity/Au] [:commodity/C] [:commodity/N]}

Lesson 5: Arguments

At the same time we are assigning all the :types to t:

(crux/q (crux/db node)
        {:find '[name]
         :where '[[e :type t]
                  [e :common-name name]]
         :args [{'t :element/metal}]})
;;=> #{["Gold"] ["Plutonium"]}
t

#{[:element/gas] [:element/metal] [:element/non-metal] [:mineral/solid] [:molecule/gas]}

Lesson 5: Arguments

Then we assign all the names within e that have a :common-name to name:

(crux/q (crux/db node)
        {:find '[name]
         :where '[[e :type t]
                  [e :common-name name]]
         :args [{'t :element/metal}]})
;;=> #{["Gold"] ["Plutonium"]}
name

#{["Methane"] ["Carbon"] ["Gold"] ["Plutonium"] ["Nitrogen"] ["Borax"]}

Lesson 5: Arguments

(crux/q (crux/db node)
        {:find '[name]
         :where '[[e :type t]
                  [e :common-name name]]
         :args [{'t :element/metal}]})
;;=> #{["Gold"] ["Plutonium"]}

We have specified that we want to get the names out, but not before looking at :args

In :args we have further filtered the results to only show us the names of that have :type :element/metal.

We could have done that inside the :where clause, but using :args removes the need for hard-coding inside the query clauses.

Lesson

You give your lesson to the new starters when they return. They are a good audience and follow it well.

To check their understanding you set them a task to create a function to aid their daily queries. You are impressed with their efforts.

When you are finished, Cosmina thanks you and you head back to the space port.

(defn filter-type
  [type]
  (crux/q (crux/db node)
        {:find '[name]
         :where '[[e :type t]
                  [e :common-name name]]
         :args [{'t type}]}))

(defn filter-appearance
  [description]
  (crux/q (crux/db node)
        {:find '[name IUPAC]
         :where '[[e :common-name name]
                  [e :IUPAC-name IUPAC]
                  [e :appearance appearance]]
         :args [{'appearance description}]}))

(filter-type :element/metal)
;;=> #{["Gold"] ["Plutonium"]}

(filter-appearance "white solid")
;;=> #{["Borax" "Sodium tetraborate decahydrate"]}

Space Port

You are back at your spaceship. Seeing another light on your communications panel, you realize there is another assignment ready for you.

Congratulations on completing your assignment. You are getting the hang of things now, and we are impressed with your progress.

We would like you to go to Neptune. They have recently lost a lot of data in a flood so they have decided to digitize their entire system and archives. We told them you could do it in such a way that the data is still time ordered as it was with their previous filing system.

Good luck, and don’t forget to update your manifest.

— Banking Inc.

Manifest

You update your manifest with the latest badge.

(crux/submit-tx
 node [[:crux.tx/put (assoc manifest
                            :badges ["SETUP" "PUT" "DATALOG-QUERIES"])]])
;;=> #:crux.tx{:tx-id 1, :tx-time #inst "2020-06-18T14:31:46.148-00:00"}

Liftoff!

Neptune

neptune

Communications panel

You enter the Neptunian atmosphere and your communications panel lights up.

It is our honor to welcome you to the planet Neptune.

If you are visiting for business reasons, please present your manifest.

Otherwise, have your visa ready for inspection.

— Poseidon Republic of Wealth

The government is asking to see your flight manifest.

You have permission to land

Space Port

On your way down to the landing site you take the time to read the Crux manual.

Crux Manual

One or more documents can be inserted into Crux via a put transaction at a specific valid-time. The valid-time can be any time (past, future or present).

If no valid-time is provided, Crux will default to the transaction time, i.e. the present. Each document survives until it is deleted or a new version of it is added.

Assignment

Upon landing on the ice giant, your communications panel lights up indicating that the job ticket is available.

Ticket

Task

Back fill insurance documents

Company

Coast Insurance

Contact

Lyndon Mercia-York

Submitted

2115-02-22T13:38:20

Additional information

We have lost a lot of our records in a flood. I think it is prudent to start storing our data digitally. It is important to track policy holders' level of cover at the time of the incident. We have read a lot about the conveniences of Crux’s bitemporality in this situation. We need you to help us get up and running. Please send someone quickly though - the waters are rising.

Attachments

 — 

Lyndon

Thank goodness you’re here.

We need you to show us how to put our customers information into Crux in order of time. Working with insurance claims, we should be able to easily look back in time at what type of coverage the customer had at the time of the incident.

Are you able to help us?

— Lyndon Mercia-York

Outside your ship you are met by a panicked looking Lyndon.

Ingestion

Lyndon gives you some data for a client that you can use as an example. Coast Insurance need to know what kind of cover each customer has and if it was valid at a given time.

You show them how to ingest a document using a valid-time so that the information is backdated to when the customer took the cover out.

(crux/submit-tx
 node
 [[:crux.tx/put
   {:crux.db/id :consumer/RJ29sUU
    :consumer-id :RJ29sUU
    :first-name "Jay"
    :last-name "Rose"
    :cover? true
    :cover-type :Full}
   #inst "2114-12-03"]])
;;=> #:crux.tx{:tx-id 0, :tx-time #inst "2020-06-18T14:38:29.240-00:00"}

Bitemporality

The company needs to know the history of insurance for each cover. You show them how to use the bitemporality of Crux to do this.

(crux/submit-tx
 node
 [[:crux.tx/put (1)
   {:crux.db/id :consumer/RJ29sUU
    :consumer-id :RJ29sUU
    :first-name "Jay"
    :last-name "Rose"
    :cover? true
    :cover-type :Full}
   #inst "2113-12-03" ;; Valid time start
   #inst "2114-12-03"] ;; Valid time end

  [:crux.tx/put (2)
   {:crux.db/id :consumer/RJ29sUU
    :consumer-id :RJ29sUU
    :first-name "Jay"
    :last-name "Rose"
    :cover? true
    :cover-type :Full}
   #inst "2112-12-03"
   #inst "2113-12-03"]

  [:crux.tx/put (3)
   {:crux.db/id :consumer/RJ29sUU
    :consumer-id :RJ29sUU
    :first-name "Jay"
    :last-name "Rose"
    :cover? false}
   #inst "2112-06-03"
   #inst "2112-12-02"]

  [:crux.tx/put (4)
   {:crux.db/id :consumer/RJ29sUU
    :consumer-id :RJ29sUU
    :first-name "Jay"
    :last-name "Rose"
    :cover? true
    :cover-type :Promotional}
   #inst "2111-06-03"
   #inst "2112-06-03"]])
;;=> #:crux.tx{:tx-id 1, :tx-time #inst "2020-06-18T14:39:11.189-00:00"}
  1. This is the insurance that the customer had last year. Along with the start valid-time you use an end valid-time so as not to affect the most recent version of the document.

  2. This is the previous insurance plan. Again, you use a start and end valid-time.

  3. There was a period when the customer was not covered,

  4. and before that the customer was on a promotional plan.

Queries through time

You now show them a few queries. You know that you can query Crux as of a given valid-time. This shows the state of Crux at that time.

First you chose a date that the customer had full cover:

(crux/q (crux/db node #inst "2115-07-03")
        '{:find [cover type]
          :where [[e :consumer-id :RJ29sUU]
                  [e :cover? cover]
                  [e :cover-type type]]})
;;=> #{[true :Full]}

Queries through time

Next you show them a query for a the customer in a time when they had a different type of cover:

(crux/q (crux/db node #inst "2111-07-03")
        '{:find [cover type]
          :where [[e :consumer-id :RJ29sUU]
                  [e :cover? cover]
                  [e :cover-type type]]})
;;=> #{[true :Promotional]}

Queries through time

And finally you show them a time when the customer had no cover at all.

(crux/q (crux/db node #inst "2112-07-03")
        '{:find [cover type]
          :where [[e :consumer-id :RJ29sUU]
                  [e :cover? cover]
                  [e :cover-type type]]})
;;=> #{}

Lyndon

Confident in their ability to put the remainder of their records into Crux, Lyndon thanks you.

You say goodbye to Lyndon and head back to the space port.

I can’t believe we’ve not digitized sooner. There was a huge push to start using more paper as the Neptune tree population was getting out of control from the accelerated terraforming, but since all these floods I’m not sure paper was the right choice.

— Lyndon Mercia-York

Space Port

Back at your spaceship you check your communications panel. There is a new assignment waiting for you.

Communications Pannel

We have assigned you a quick task on Saturn helping a small company who are having some problems keeping their records in order.

This shouldn’t take long, but don’t forget they will still need to see your manifest.

— Helios Banking Inc.

Update Manifest

You add the new badge to your manifest

(crux/submit-tx
 node [[:crux.tx/put
        (assoc manifest
               :badges ["SETUP" "PUT" "DATALOG-QUERIES" "BITEMP"])]])
;;=> #:crux.tx{:tx-id 2, :tx-time #inst "2020-06-18T14:46:47.606-00:00"}

Liftoff!

Saturn

saturn

Communications Panel

As you pass through the innermost ring of Saturn, A warning light appears on your communications panel. You open the message. It’s from Space Customs and reads:

They are asking to see your flight manifest.

You have permission to land

We extend you the warmest welcome.

We must check papers before we can give you permission to land.

— Cronus Peaceful Nations

Space Port

As you prepare to land you open your Crux manual to the page on match.

Crux Manual

Transaction Description

put

Writes a version of a document

delete

Deletes a version of a document

match

Stops a transaction if the precondition is not met.

evict

Removes a document entirely

match:

match checks the current state of an entity - if the entity doesn’t match the provided doc, the transaction will not continue. You can also pass nil to check that the entity doesn’t exist prior to your transaction.

A match transaction takes the entity id, along with an expected document. Optionally you can provide a valid time.

Time in Crux is denoted

#inst "yyyy-MM-ddThh:mm:SS"

For example, 9:30 pm on January 2nd 1999 would be written:

#inst "1999-01-02T21:30:00".

A complete match transaction has the form

[:crux.tx/match entity-id expected-doc valid-time]

Assignment

As you land on the surface of Saturn the job ticket for this assignment is unlocked.

Ticket

Task

Secure trading system

Company

Cronus Market Technologies

Contact

Ubuku Eppimami

Submitted

2115-02-23T13:38:20

Additional information

We need to be shown how to ensure no trades are done without the buyer and seller having the necessary funds or stock respectively

Attachments

example_data

Ingest

The next shuttle to the CMT office leaves in 5 Earth minutes. While you wait you use your easy ingest function you created on Pluto to put the example data into your system. You also decide to make some Clojure functions so you can easily show Ubuku the stock and fund levels after the trades.

Just as you are finishing off your shuttle arrives.

After a short journey through the icy lower clouds of Saturn you are met by a friendly faced Ubuku.

(defn stock-check
  [company-id item]
  {:result (crux/q (crux/db node)
                   {:find '[name funds stock]
                    :where ['[e :company-name name]
                            '[e :credits funds]
                            ['e item 'stock]]
                    :args [{'e company-id}]})
   :item item})

(defn format-stock-check
  [{:keys [result item] :as stock-check}]
  (for [[name funds commod] result]
    (str "Name: " name ", Funds: " funds ", " item " " commod)))

Ubuku

You explain to Ubuku that all they need to do to solve their problem is to use the match operation along with put when they are processing their trades.

Hello friend.

We have been using Crux for a short time now and think it is great. The problem is a human one. Occasionally we process trades without checking that are enough funds in the buyers account.

I know there is a way that we can stop this happening in Crux.

I sent you some example data in the job ticket for you to use, did you find it alright?

— Ubuku Eppimami

Match

You show Ubuku the match operation for a valid transaction. You move 10 units of Methane (:units/CH4) each at the cost of 100 credits to Blue Energy:

You explain that because the provided doc is as expected for both the buyer and the seller that the transaction goes through.

(crux/submit-tx
 node
 [[:crux.tx/match
   :blue-energy
   {:crux.db/id :blue-energy
    :seller? false
    :buyer? true
    :company-name "Blue Energy"
    :credits 1000}]
  [:crux.tx/put
   {:crux.db/id :blue-energy
    :seller? false
    :buyer? true
    :company-name "Blue Energy"
    :credits 900
    :units/CH4 10}]

  [:crux.tx/match
   :tombaugh-resources
   {:crux.db/id :tombaugh-resources
    :company-name "Tombaugh Resources Ltd."
    :seller? true
    :buyer? false
    :units/Pu 50
    :units/N 3
    :units/CH4 92
    :credits 51}]
  [:crux.tx/put
   {:crux.db/id :tombaugh-resources
    :company-name "Tombaugh Resources Ltd."
    :seller? true
    :buyer? false
    :units/Pu 50
    :units/N 3
    :units/CH4 82
    :credits 151}]])
;;=> #:crux.tx{:tx-id 0, :tx-time #inst "2020-06-18T15:37:20.271-00:00"}

Result

You show Ubuku the result of the trade using the function you created earlier:

They are happy that this works as he sees the 1000 credits move from Blue energy to Tombaugh Resources Ltd. and 10 units of Methane the other way.

Ubuku asks if you can show them what would happen if there was not enough funds in the account of a buyer.

(format-stock-check (stock-check :tombaugh-resources :units/CH4))
;;=> ("Name: Tombaugh Resources Ltd., Funds: 151, :units/CH4 82")
(format-stock-check (stock-check :blue-energy :units/CH4))
;;=> ("Name: Blue Energy, Funds: 900, :units/CH4 10")

Match

You show him a trade where the old doc is not as expected for Encompass trade, to buy 10,000 units of Gold from Gold Harmony.

You explain to Ubuku that this time, because you have both match operations in the same transaction, the trade does not go through.

The accounts remain the same, even though the failing match was the second operation.

Ubuku thanks you. This is just what they are looking for. You head back to the space station to see if there is another assignment waiting for you.

(crux/submit-tx
 node
 [[:crux.tx/match
   :gold-harmony
   {:crux.db/id :gold-harmony
    :company-name "Gold Harmony"
    :seller? true
    :buyer? false
    :units/Au 10211
    :credits 51}]
  [:crux.tx/put
   {:crux.db/id :gold-harmony
    :company-name "Gold Harmony"
    :seller? true
    :buyer? false
    :units/Au 211
    :credits 51}]
  [:crux.tx/match
   :encompass-trade
   {:crux.db/id :encompass-trade
    :company-name "Encompass Trade"
    :seller? true
    :buyer? true
    :units/Au 10
    :units/Pu 5
    :units/CH4 211
    :credits 100002}]
  [:crux.tx/put
   {:crux.db/id :encompass-trade
    :company-name "Encompass Trade"
    :seller? true
    :buyer? true
    :units/Au 10010
    :units/Pu 5
    :units/CH4 211
    :credits 1002}]])
;;=> #:crux.tx{:tx-id 1, :tx-time #inst "2020-06-18T15:23:38.540-00:00"}

(format-stock-check (stock-check :gold-harmony :units/Au))
;;=> ("Name: Gold Harmony, Funds: 51, :units/Au 10211")

(format-stock-check (stock-check :encompass-trade :units/Au))
;;=> ("Name: Encompass Trade, Funds: 1002, :units/Au 10")

Space Port

Back at the spaceship there is a light waiting for you on your communications panel.

Well done, you’ve had a productive week. We have one final task for you to do before you finish for the week

You need to go to Jupiter and meet Kaarlang, it’s his last day working for us and he needs to delete his trade clients from his personal Crux node for data protection.

— Helios Banking Inc.

Update Manifest

You update your manifest with your most recent badge.

As you do so, you check to see if you still have the note that the porter gave you for Kaarlang back on Earth.

(crux/submit-tx
 node [[:crux.tx/put
        (assoc manifest
               :badges ["SETUP" "PUT" "DATALOG-QUERIES" "BITEMP" "MATCH"])]])
;;=> #:crux.tx{:tx-id 3, :tx-time #inst "2020-06-18T15:24:39.037-00:00"}

Update Manifest

(crux/q (crux/db node)
        {:find '[belongings]
         :where '[[e :cargo belongings]]
         :args [{'belongings "secret note"}]})
;;=> #{["secret note"]}

Liftoff!

Feeling a bit apprehensive, you enter countdown for lift off to Jupiter. See you soon.

Jupiter

jupiter

You approach Jupiter and marvel at its bands. You wish you could have seen it this close before the Great Red Spot dissipated.

As you enter the Jovian atmosphere your communications panel lights up with the now expected, but rather terse message from boundary control.

Communications Panel

The government is asking to see your flight manifest.

You have your manifest ready and so have permission to land

Jupiter’s boundary is controlled.

If you wish to enter, present your papers now.

— Zeus Confederacy

Space Port

It’s a turbulent ride down to the space port. To take your mind off the colossal storm outside, you check the Crux manual for the delete operation.

Crux Manual

Operation Description

put

Writes a version of a document

delete

Deletes a version of a document

match

Stops a transaction if the precondition is not met.

evict

Removes a document entirely

Delete:

The delete operation takes a valid eid with the option to include a start and end valid-time.

Time in Crux is denoted

#inst "yyyy-MM-ddThh:mm:SS"

For example, 9:30 pm on January 2nd 1999 would be written:

#inst "1999-01-02T21:30:00".

The document will be deleted as of the transaction time, or between the start and end valid-time if provided. Historical versions of the document that fall outside of the valid-time window will be preserved.

A complete delete transaction has the form

[:crux.tx/delete eid valid-time-start valid-time-end]

Assignment

You land on the raised platform and open your job ticket:

Ticket

Task

Remove assigned clients for leaver.

Company

Helios Banking Inc.

Contact

Kaarlang

Submitted

2115-02-10T13:38:20

Additional information

Help Kaarlang delete client history from his Crux node in accordance with Earth data protection laws.

Attachments

 — 

Kaarlang

As you leave your ship, you are met by the martian Kaarlang:

Yes, we’ll work together to do this.

Kaarlang gives you his client history so you can sync up your Crux node.

Hi there, I believe you’re here to help me.

I’ve been told I need to delete my client history with today being my last day.

Is this something you can help me with?

Ingest

You input the client history

(crux/submit-tx
 node [[:crux.tx/put {:crux.db/id :kaarlang/clients
                      :clients [:encompass-trade]}
        #inst "2110-01-01T09"
        #inst "2111-01-01T09"]

       [:crux.tx/put {:crux.db/id :kaarlang/clients
                      :clients [:encompass-trade :blue-energy]}
        #inst "2111-01-01T09"
        #inst "2113-01-01T09"]

       [:crux.tx/put {:crux.db/id :kaarlang/clients
                      :clients [:blue-energy]}
        #inst "2113-01-01T09"
        #inst "2114-01-01T09"]

       [:crux.tx/put {:crux.db/id :kaarlang/clients
                      :clients [:blue-energy :gold-harmony :tombaugh-resources]}
        #inst "2114-01-01T09"
        #inst "2115-01-01T09"]])
;;=> #:crux.tx{:tx-id 0, :tx-time #inst "2020-06-18T15:55:00.894-00:00"}

Delete

To get a good visual aid, you show Kaarlang how to view his client history. This way you both can see when the clients are deleted. You use the option :with-docs? to show the content of the doc.

The result shows the names of the clients that have been assigned to Kaarlang since he started at the company in 2110.

(crux/entity-history
 (crux/db node #inst "2116-01-01T09")
 :kaarlang/clients
 :desc
 {:with-docs? true})
#_#_
=> [{:crux.tx/tx-time #inst "2020-06-18T15:57:28.900-00:00",
    :crux.tx/tx-id 1,
    :crux.db/valid-time #inst "2114-01-01T09:00:00.000-00:00",
    :crux.db/content-hash #crux/id "d4bca6c78409d9d40ee42319a8aec32bffad9030",
    :crux.db/doc
    {:crux.db/id :kaarlang/clients,
     :clients [:blue-energy :gold-harmony :tombaugh-resources]}}
   {:crux.tx/tx-time #inst "2020-06-18T15:57:28.900-00:00",
    :crux.tx/tx-id 1,
    :crux.db/valid-time #inst "2113-01-01T09:00:00.000-00:00",
    :crux.db/content-hash #crux/id "000e5b775b55d06f0bddc77d736184284aa1e4e9",
    :crux.db/doc {:crux.db/id :kaarlang/clients, :clients [:blue-energy]}}
   {:crux.tx/tx-time #inst "2020-06-18T15:57:28.900-00:00",
    :crux.tx/tx-id 1,
    :crux.db/valid-time #inst "2111-01-01T09:00:00.000-00:00",
    :crux.db/content-hash #crux/id "cd71551fe21219db59067ce7483370fdebaae8b0",
    :crux.db/doc
    {:crux.db/id :kaarlang/clients, :clients [:encompass-trade :blue-energy]}}
   {:crux.tx/tx-time #inst "2020-06-18T15:57:28.900-00:00",
    :crux.tx/tx-id 1,
    :crux.db/valid-time #inst "2110-01-01T09:00:00.000-00:00",
    :crux.db/content-hash #crux/id "5ec42ea653288e01e1a9d7d2068b4658416177e0",
    :crux.db/doc {:crux.db/id :kaarlang/clients, :clients [:encompass-trade]}}]

Delete

Next you delete the whole history of clients buy choosing a start and end valid-time that spans his entire employment time.

(crux/submit-tx
 node [[:crux.tx/delete :kaarlang/clients #inst "2110-01-01" #inst "2116-01-01"]])
;;=> #:crux.tx{:tx-id 1, :tx-time #inst "2020-06-18T15:59:38.323-00:00"

Delete

Using the same method as before you show Kaarlang the effect of this operation.

Kaarlang is impressed it is that easy. You point out that there are no longer any documents attached to the transactions.

(crux/entity-history
 (crux/db node #inst "2116-01-01T09")
 :kaarlang/clients
 :desc
 {:with-docs? true})
#_#_
=> [{:crux.tx/tx-time #inst "2020-06-18T15:59:38.323-00:00",
    :crux.tx/tx-id 2,
    :crux.db/valid-time #inst "2115-01-01T09:00:00.000-00:00",
    :crux.db/content-hash #crux/id "0000000000000000000000000000000000000000",
    :crux.db/doc nil}
   {:crux.tx/tx-time #inst "2020-06-18T15:59:38.323-00:00",
    :crux.tx/tx-id 2,
    :crux.db/valid-time #inst "2114-01-01T09:00:00.000-00:00",
    :crux.db/content-hash #crux/id "0000000000000000000000000000000000000000",
    :crux.db/doc nil}
   {:crux.tx/tx-time #inst "2020-06-18T15:59:38.323-00:00",
    :crux.tx/tx-id 2,
    :crux.db/valid-time #inst "2113-01-01T09:00:00.000-00:00",
    :crux.db/content-hash #crux/id "0000000000000000000000000000000000000000",
    :crux.db/doc nil}
   {:crux.tx/tx-time #inst "2020-06-18T15:59:38.323-00:00",
    :crux.tx/tx-id 2,
    :crux.db/valid-time #inst "2111-01-01T09:00:00.000-00:00",
    :crux.db/content-hash #crux/id "0000000000000000000000000000000000000000",
    :crux.db/doc nil}
   {:crux.tx/tx-time #inst "2020-06-18T15:59:38.323-00:00",
    :crux.tx/tx-id 2,
    :crux.db/valid-time #inst "2110-01-01T09:00:00.000-00:00",
    :crux.db/content-hash #crux/id "0000000000000000000000000000000000000000",
    :crux.db/doc nil}
   {:crux.tx/tx-time #inst "2020-06-18T15:59:38.323-00:00",
    :crux.tx/tx-id 2,
    :crux.db/valid-time #inst "2110-01-01T00:00:00.000-00:00",
    :crux.db/content-hash #crux/id "0000000000000000000000000000000000000000",
    :crux.db/doc nil}]

Kaarlang

You remember the secret note in your pocket and pass it to Kaarlang.

I am grateful that you took the time to show me this.

Today is a sad day for me as I have very much enjoyed my time here.

Although, I was expecting to hear from a friend before I left. There is a very important message that I am waiting for.

The Secret Note

Kaarlang reads the note.

He looks at you with the a peculiar facial expression.

This is the note I was waiting for.

It has information about the a top secret stellar transport shuttle.

If you are interested in a great adventure, the shuttle comes once every hundred years or so to take a select few to a nearby star system. The system is home to a mysterious hyper-intelligent form of life.

I’m sure there would be a place for you if you were willing to help out. The passengers on the shuttle have the right to be forgotten. We need someone that can remove the passengers data from the solar system.

What do you think?

Secret location

oumuaua

You arrive at the comet 'Oumuamua and pull along side, asking for permission to land. A voice comes over the communications system

Arrival

How did you find us? Who sent you?? — Mysterious person

"Kaarlang sent me" You have permission to land

Space Port

You land on the space port and are ushered inside. The ships captain, Ilex, greets you.

You are excited by the prospect and agree to help. First you read the manual entry for evict as this will be the perfect tool.

Hello, it’s good to have you with us.

We are set to leave the solar system right away and as part of our service we offer people the right to be forgotten. Some are not worried that their information is kept here, however others want there to be no personal data left behind.

You may not have been told this yet, but this comet is actually a transportation vessel. It will take us to the star system Gilese 667C which is home to intelligent life far superior to our own. We all are hoping to find opportunities beyond our wildest dreams. All records of this transportation vessel and any life outside of the solar system are heavily monitored and wiped in the interest of preserving the normal technological advancement of the Human race. This means we know little of the beings we are going to meet.

Our task for you is to remove the records of the people who have chosen to be forgotten here.

— Ilex, Captain

Crux Manual

You are given the data for the people on the ship and sync up your Crux node. You decide that you are going to embark on this adventure along with them so you add your name to the list.

Operation Description

put

Writes a version of a document

delete

Deletes a version of a document

match

Stops a transaction if the precondition is not met.

evict

Removes a document entirely

Evict:

Crux supports eviction of active and historical data to assist with technical compliance for information privacy regulations.

The main transaction log contains only hashes and is immutable. All document content is stored in a dedicated document log that can be evicted by compaction.

A complete evict transaction has the form

[:crux.tx/evict eid]

Ingest

(crux/submit-tx node
                [[:crux.tx/put
                  {:crux.db/id :person/kaarlang
                   :full-name "Kaarlang"
                   :origin-planet "Mars"
                   :identity-tag :KA01299242093
                   :DOB #inst "2040-11-23"}]

                 [:crux.tx/put
                  {:crux.db/id :person/ilex
                   :full-name "Ilex Jefferson"
                   :origin-planet "Venus"
                   :identity-tag :IJ01222212454
                   :DOB #inst "2061-02-17"}]

                 [:crux.tx/put
                  {:crux.db/id :person/thadd
                   :full-name "Thad Christover"
                   :origin-moon "Titan"
                   :identity-tag :IJ01222212454
                   :DOB #inst "2101-01-01"}]

                 [:crux.tx/put
                  {:crux.db/id :person/johanna
                   :full-name "Johanna"
                   :origin-planet "Earth"
                   :identity-tag :JA012992129120
                   :DOB #inst "2090-12-07"}]])
;;=> #:crux.tx{:tx-id 0, :tx-time #inst "2020-06-18T16:11:03.410-00:00"}

Ingest

Before you start the eviction process you make a query function so you can see the full results of anything stored in Crux:

(defn full-query
  [node]
  (crux/q
   (crux/db node)
   '{:find [id]
     :where [[e :crux.db/id id]]
     :full-results? true}))

Results

You show the others the result:

(full-query node)
#_#_
=> #{[{:crux.db/id :person/ilex,
    :full-name "Ilex Jefferson",
    :origin-planet "Venus",
    :identity-tag :IJ01222212454,
    :DOB #inst "2061-02-17T00:00:00.000-00:00"}]
  [{:crux.db/id :person/thadd,
    :full-name "Thad Christover",
    :origin-moon "Titan",
    :identity-tag :IJ01222212454,
    :DOB #inst "2101-01-01T00:00:00.000-00:00"}]
  [{:crux.db/id :person/kaarlang,
    :full-name "Kaarlang",
    :origin-planet "Mars",
    :identity-tag :KA01299242093,
    :DOB #inst "2040-11-23T00:00:00.000-00:00"}]
  [{:crux.db/id :person/johanna,
    :full-name "Johanna",
    :origin-planet "Earth",
    :identity-tag :JA012992129120,
    :DOB #inst "2090-12-07T00:00:00.000-00:00"}]}

Evict

The Crux manual said that the evict operation will remove a document entirely. Ilex tells you the only person who whishes to exercise their right to be forgotten is Kaarlang.

(crux/submit-tx node [[:crux.tx/evict :person/kaarlang]])
;;=> #:crux.tx{:tx-id 3, :tx-time #inst "2020-06-18T16:13:03.276-00:00"}

Result

You use your function and see that the transaction was a success.

All the data associated with the specified :crux.db/id has been removed from the Crux along with the eid itself.

(full-query node)
#_#_
=> #{[{:crux.db/id :person/ilex,
       :full-name "Ilex Jefferson",
       :origin-planet "Venus",
       :identity-tag :IJ01222212454,
       :DOB #inst "2061-02-17T00:00:00.000-00:00"}]
     [{:crux.db/id :person/thadd,
       :full-name "Thad Christover",
       :origin-moon "Titan",
       :identity-tag :IJ01222212454,
       :DOB #inst "2101-01-01T00:00:00.000-00:00"}]
     [{:crux.db/id :person/johanna,
       :full-name "Johanna",
       :origin-planet "Earth",
       :identity-tag :JA012992129120,
       :DOB #inst "2090-12-07T00:00:00.000-00:00"}]}

Transaction History

This means the transactions will never be removed. You assure Ilex that the documents are completely removed from Crux, you can show this by looking at the history.

You show the results to Kaarlang who is happy that there his details are no longer a part of the ships logs.

The transaction history is immutable.

(crux/entity-history
 (crux/db node)
 :person/kaarlang
 :desc
 {:with-docs? true})
#_#_
=> [{:crux.tx/tx-time #inst "2020-06-18T16:11:03.410-00:00",
     :crux.tx/tx-id 2,
     :crux.db/valid-time #inst "2020-06-18T16:11:03.410-00:00",
     :crux.db/content-hash #crux/id "c3ad3191fff06083fedf3640b625566c02033a6b",
     :crux.db/doc
     #:crux.db{:id #crux/id "efe634523d6867a3c6e4089074adf29b07b45f43",
               :evicted? true}}]

Departure

ice

Ilex thanks you and takes you to the Cryogenics department. You must be put into stasis as the journey will take around 25 years, even at the near light speeds of the ship.

You are astonished with the amount that you have done in one short week. How did little old you end up with an opportunity as big as this?

Your eyes get heavy as the cryogenicist initiates the hibernation process. As they do, you wonder if you’ll ever come back to the solar system.

Questions