DEV Community

Cover image for How to Add Records to the PicoLisp Database
Mia
Mia

Posted on • Edited on • Originally published at picolisp-explored.com

2 1

How to Add Records to the PicoLisp Database

In the last post, we have created a simple entity/relation-model using the PicoLisp E/R framework. You can find the file with the definitions here.

Now let's create a new database and fill it with some values.


Create a new database

First of all, let's start the file from the REPL and open a new database file.

$ pil family-dbmodel.l +

: (pool "myFamily.db")
-> T 
Enter fullscreen mode Exit fullscreen mode

The database is empty:

: (show *DB)
{1} NIL
-> {1}
Enter fullscreen mode Exit fullscreen mode

Inserting some data using the request function

From the previous posts you might remember that we can insert new items with the new function. However, for many cases there is a better alternative: the request function. What's the difference? request returns an object from the database if it exists, otherwise it creates a new one. This avoids that duplicates are inserted by mistake.

Syntax and example from the documentation:

(request 'typ 'sym ['hook] 'val ..) -> obj

Example:

: (request '(+Item) 'nr 2)
-> {B2}


Let's create an object of the class +Man called Adam.

: (request '(+Man) 'name "Adam")
-> {2}
Enter fullscreen mode Exit fullscreen mode

This seems to have worked. Let's insert Eve as well.

: (request '(+Woman) 'name "Eve")
-> {6}
Enter fullscreen mode Exit fullscreen mode

Querying the data

Let's see what we have in our database now:

: (show *DB)
{1} NIL
   +Person {3}
   +Woman {7}
   +Man {4}
-> {1}
Enter fullscreen mode Exit fullscreen mode

Well, it's not empty anymore, but we don't see anything like "Adam" or "Eve", only the object class names. In order to find our entries, we need to query the database. One of the most useful functions for that is collect.

collect has several options, for now let's only consider the most easy one: return all objects of a certain class. Again from the documentation:

(collect 'sym 'cls (...) )

Returns a list of all database objects of class cls, where the values for the sym arguments correspond to the any arguments (...)

: (collect 'nr '+Item)
-> ({B1} {B2} {B3} {B4} {B5} {B6} {B8})

So, applied to our example: We know we have inserted one +Woman and one +Man, both of them are +Person. We can query it like this:

: (collect 'name '+Person)
-> ({2} {6})
Enter fullscreen mode Exit fullscreen mode

This looks better! We can confirm that we have the right return values with show:

: (show '{2})
{2} (+Man)
   name "Adam"
-> {2}
Enter fullscreen mode Exit fullscreen mode

However it would be nice to receive this directly in one query. For this we can use the more function, which accepts a list and a function as arguments.

(more 'lst ['fun]) -> flg

Displays the elements of lst (first form). After each step, more waits for console input, and terminates when a non-empty line is entered. In that case, T is returned, otherwise (when end of data is reached) NIL.

Applied to our example:

: (more (collect 'name '+Person) show)
{2} (+Man)
   name "Adam"

{6} (+Woman)
   name "Eve"
Enter fullscreen mode Exit fullscreen mode

Let's commit the content up to now to the database by commit:

: (commit)
T
Enter fullscreen mode Exit fullscreen mode

Defining the relationships

What other fields do we have? About Adam and Eve we don't have much information about birthday, death day and job. But we can define the relationship between them.

Adam and Eve are partners. Since the partner attribute is a +Joint connection, it's enough to define it on one side to automatically set it on the other side as well. We can set the relation using the function put> and (commit). As a shortcut, we could also do put!> in one step.

Note: put> is a method of the +Entity class and should not be confused with put for "normal" objects.

Since we know that Adam is {2} and Eve is {6}, we can address them directly. This is OK for debugging mode - in a real application, it should't be addressed directly because it could corrupt the debugger.

So, we set Eve as partner of Adam:

: (put!> '{2} 'partner '{6})
-> {6}
Enter fullscreen mode Exit fullscreen mode

Let's check both of them: They should now be partners of each other.

: (show '{2})
{2} (+Man)
   partner {6}
   name "Adam"
-> {2}

: (show '{6})
{6} (+Woman)
   partner {2}
   name "Eve"
-> {6}
Enter fullscreen mode Exit fullscreen mode

Some more entries

Let's also add the children: Kain, Abel and Set. We can add their jobs and parents as well.

: (request '(+Man) 'name "Kain" 'job "farmer" 'father '{2} 'mother '{6})
-> {12}
: (request '(+Man) 'name "Abel" 'job "shepherd" 'father '{2} 'mother '{6})
-> {14}
: (request '(+Man) 'name "Seth" 'father '{2} 'mother '{6})
-> {15}

: (commit)
-> T
Enter fullscreen mode Exit fullscreen mode

Now both Adam and Eve have another attribute "kids" which contains three items:

: (show '{2})
{2} (+Man)
   kids ({15} {14} {12})
   partner {6}
   name "Adam"
-> {2}
Enter fullscreen mode Exit fullscreen mode

Again, we could step through Adam's kids using a combination of more and show. Instead of show we could also use get:

: (more (get '{2} 'kids) show)
{15} (+Man)
   mother {6}
   father {2}
   name "Seth"

{14} (+Man)
   mother {6}
   father {2}
   job "shepherd"
   name "Abel"

{12} (+Man)
   mother {6}
   father {2}
   job "farmer"
   name "Kain"
Enter fullscreen mode Exit fullscreen mode

Validation checks

As you might remember, for some values we have defined the types. For example, the birthday needs to be of class +Date:

(rel birthday (+Ref +Date))
Enter fullscreen mode Exit fullscreen mode

Let's see what happens if we define a string instead.

: (put> '{2} 'birthday "Genesis")
-> "Genesis"
Enter fullscreen mode Exit fullscreen mode

Well, it seems it was accepted although "Genesis" is clearly not a date type.

: (show '{2})
{2} (+Man)
   birthday "Genesis"
   kids ({15} {14} {12})
   partner {6}
   name "Adam"
-> {2}
Enter fullscreen mode Exit fullscreen mode

However, we can check if the data is correct using the mis> function.

: (mis> '{2} 'birthday "Genesis")
-> "Numeric input expected"
Enter fullscreen mode Exit fullscreen mode

Therefore you should always run mis> before you commit any input into a database, for consistency and also for security reasons.


In the next post we will expand this little example to a larger database which already has some values inside.


Sources

Do your career a big favor. Join DEV. (The website you're on right now)

It takes one minute, it's free, and is worth it for your career.

Get started

Community matters

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Dive into an ocean of knowledge with this thought-provoking post, revered deeply within the supportive DEV Community. Developers of all levels are welcome to join and enhance our collective intelligence.

Saying a simple "thank you" can brighten someone's day. Share your gratitude in the comments below!

On DEV, sharing ideas eases our path and fortifies our community connections. Found this helpful? Sending a quick thanks to the author can be profoundly valued.

Okay