Skip to content

Code Discussion

Before we move on to the front end, let's talk about the code you just pasted in. There's a lot!

We're assuming you're familiar with Express.js and therefore won't really need any explanation for the index.ts file in step 4. And the next file in that step is just a set of interfaces, which are pretty straightforward to read.

Now here's a rundown of the functions in step 3's dataService module.

dataService.ts rundown

sendSampleData

This function prepopulates the database with a set of sample data, and builds the initial indexes structure called searches.

Note that at this time, calling this function is required because it builds the searches structure. (Feel free to retool the code so the first call to create a media item creates it instead; if you want to try this, we recommend adding code that first checks if it already exists, and if not, create it; and then add on to it with the new data.)

Notice inside this function it loads the sample data that's in the variable called jsonData, and it steps through such data, calling converToCreateOrUpdate each time; that function (when called from here) creates a GolemBaseCreate structure for each media item in the sample data; each one contains all the data stored in the media item in a key-value pair fashion. Pairs with a string value are stored in a member called stringAnnotations, and those with a numeric value are stored in a member called numericAnnotations. Both members are arrays of a structure containing a key and a value.

About the entities and how we model them in JavaScript

Golem-Base allows you to create a set of entities in a single transaction, and that's what we do here. We call client.createEntities, passing all the "creates" that were put together in the previous step. They're passed in as a single array.

Note also that this step constructs the searches index we mentioned. Notice its structure:

let searchesTest:Searches = {
    directors: [],
    artists: [],
    authors: [],
    movie_genres: [],
    music_genres: [],
    book_genres: []
}

The idea is that every time a media item is added, this structure gets updated. If the media item is a book, the book's author gets added to the authors list (if he or she isn't already in the list), and the book's genre gets added to book_genres (again, if it doesn't already exist). And similarly with the movie and music types.

The idea here is to maintain an index of the people and genres for quick lookup. In this application, the main place this is used is to prepopulate the dropdown lists in the query builder. So for example, if you're searching for movies, you can choose which director in the query from the list provided. That list gets filled from this structure.

Golem-base doesn't store JavaScript objects as-is, however; instead, we convert it to the same structure we mentioned earlier regarding key-value pairs. In this case, all six of these members get their values converted to a string containing a comma-delimited list; for example, if the directors array contains strings "Christopher Nolan" and "Denis Villeneuve", then those names get converted to "Christopher Nolan,Denis Villeneuve". Then we store this key-value pair in the string annotations:

key: "directors" value: "Christopher Nolan,Denis Villeneuve"

And then the set of key value pairs gets pushed out as an entity in Golem-Base.

Upon retreival, we then reconstruct the original JavaScript object. We read the key and value, call "split" with a comma to get back a list of strings, and then we store those strings in the object with the member name being the key:

Key Value version: ( key: "directors", value: "Christopher Nolan,Denis Villeneuve" ) JavaScript Object version: { ... "directors": ["Christopher Nolan", "Denis Villeneuve"] ... }

The first is for storing inside Golem-Base; the second is for using within a JavaScript/TypeScript application.

As for the media entities, for those we use a slightly different approach. These are flat JavaScript objects, and they map neatly to a key-value pair system. Take this media item, for example, as a JavaScript object:

{
  "type": "movie",
  "title": "Inception",
  "description": "A mind-bending dream within a dream",
  "director": "Christopher Nolan",
  "genre": "sci-fi",
  "rating": 5,
  "watched": true,
  "year": 2010
}

For this, we build a sequence of key-value pairs and store them as string annotations within a Golem-Base entity, like so:

[(key="type", value="movie"),
(key="title", value="Inception"),
(key="description", value="A mind-bending dream within a dream"),
(key="director", value="Christopher Nolan"),
(key="genre", value="sci-fi"),
(key="watched", value=true)]

for the string annotations, and

[(key="rating", value=5),
(key="year", value=2010)]

for the numeric annotations.

When we read in the entity, we pass through all the keys and create a member with the same name in the JavaScript object, storing the values accordingly.

To store them, we simply do the reverse, building up the key-value annotations.

Tip: What we're doing here is effectively modeling SQL tables. Notice the first member is called "type". That represents the table name. The remaining items represent the column names and values.

Finally: Note that to keep the data separate from any other applications, every entity we create gets an "app" key in the string annotations. In this app, we have it stored in the constant called GOLEM_BASE_APP_NAME, which is in the media.ts file.

purge

This function simply deletes all the entities from Golem-base. Note that we don't have a corresponding function in the SDK, and so to do this we simply query for all the entities. The query function returns an array of keys representing each item. We store these in an array and call client.deleteEntities, passing the array to delete them all.

createOrUpdateMediaItem

This is where we build either a GolemBaseCreate structure or a GolemBaseUpdate struture depending on whether we're creating or updating the entity. (These two structures are defined in the SDK, and they're what we mentioned earlier; in the case of Create, you can send an array of GolemBaseCreate structures in a single call to create each entity.)

To build these, we simply go through the members of the object, and create a key-value pair for each member. And for that step, we've created a separate function called converToCreateOrUpdate, which we describe next.

But first, we must also update the searches index. To do so, we load it in using a specialty function we created called getSearchEntity. We then add on the new media item's information; this step takes place in a function called updateSearchesFromItem, where we pass both the current searches index and the new item being created.

We then construct an instance of GolemBaseUpdate based on the current searches object, which was just updated with the new information (if necessary).

We now have two GolemBase objects:

  • A GolemBaseCreate object for adding the new media item
  • A GolemBaseUpdate object for updating the searches index.

We then call client.sendTransaction, which pushes these two changes to the GolemBase system.

convertToCreateOrUpdate

This converts our plain old JavaScript object such as this:

{
  "type": "movie",
  "title": "Inception",
  "description": "A mind-bending dream within a dream",
  "director": "Christopher Nolan",
  "genre": "sci-fi",
  "rating": 5,
  "watched": true,
  "year": 2010
}

To the set of key-value pairs described earlier, and stores them as stringAnnotations and numericAnnotations.

The GolemBaseCreate and GolemBaseUpdate structures also include a few more items that we create here:

  • entityKey: Only for Update; this is the key for the entity we're updating

  • data: A data value. In "regular" Golem-Base apps, this holds the actual data, and the key-value pairs describe it. But in this app we're taking a slightly different approach, and storing the data itself in the key-value pairs. At present, we just generate a basic string based on the title and description.

  • btl: This stands for "blocks to live" and specifies how many blocks in the Ethereum chain the media item should live before being automatically deleted.

getItemByEntityKey

This function retrives the item from the Golem-Base node based on its key. That retreives the data as a set of key-value pairs; as such, we then call a function called transformAnnotationsToPOJO that reconstructs the JavaScript object with members based on the keys and values.

query

This function takes a query string (built in the express.js query route), and then send it to the query function in the SDK. This gives us back a list of keys.

From there we loop through the list, one by one calling the getItemByEntityKey function we just described to get the data as a JavaScript object.

searches.ts rundown

This file contains a couple functions for managing the searches index.

updateSearchesFromItem

This function takes as parameters the current value of the searches index (in plain JavaScript form), and a media item that was just added (also in plain JavaScript form) and rebuilds the searches object to include any additions needed to name or genre. If the name or genre isn't already present, it adds it, and then returns the updated searches object. (The caller converts it to key-value pairs and sends it to the Golem-Base node.)

transformSearchesToKeyValuePairs

This is a helper function that transforms the searches into a set of key-value pairs. Recall from earlier the two structures:

  • JavaScript Object: Where the values are lists
  • KeyValue pairs: Where the values are comma-delimited names inside a single string

There are six members or keys:

  • directors
  • artists
  • authors
  • movie_genres
  • music_genres
  • book_genres

getSearchEntity

This looks up the single searches index in the Golem-Base node, retreives it using a query combined with a call into the SDK's geetEntityMetaData, and then transforms the entire thing from key-value pairs back to the JavaScript object as described above.

That's it for the backend! Let's build the frontend next.

Head to Step 5.