Single-table design in DynamoDB
Single-table design is a way to model your data so that all your entities and their relationships are in a single table in DynamoDB. This allows querying data in a single request and avoiding joins as they are not supported by DynamoDB, and has other cost and performance benefits.
To find an object in DB we need to query it using both its PK (partition key) and SK (sort key).
PK: "USER#123"
SK: "USER#123"
We must specify a sort key, so here both keys have the same value. This is the case for simle entities only.
The id of the user is prepended by the type of entity and a '#' to delimit them.
Now if we want to specify the relation that a user can have a house, we can do that in the same table like so:
PK: "USER#123"
SK: "HOUSE#456"
This enables us to find the house if we know both user id and house id. Also to find all houses owned by the user only using their id:
query conditions: PK = "USER#123" and SK beginsWith "HOUSE#"
We can expand the house entry to contain info on the state of the house:
PK: "USER#123"
SK: "HOUSE#IN_CONSTRUCTION#456"
This way we can query all houses of this user, or only those in a specific state.
query conditions: PK = "USER#123" and SK beginsWith "HOUSE#FINISHED#"
This stacking can be done multiple times to allow range queries on different attributes. A drawback here is that to get a house by user id and house id,
we also need to know its state. This is why we need to know our access patterns before making the schema.
Another technique to allow flexibility of queries is adding more indexes. DynamoDB supports multiple GSIs (global secondary indexes) per table. For example:
GSI1 PK: "CITY#Sofia"
GSI1 SK: "HOUSE#456"
We can now query all houses in a city without knowing the user id.
This copies the object and that takes up space, this is the case even if the data is duplicated.
The fields that are copied are specified on index creation: only PK and SK, some or all fields.
To demonstrate this design process we can go through the steps as I did for SpeedCook. The entities are:
- Users
- Recipes
- Unlocked recipes
And the properties and relations they have:
- Recipes have details
- Users have details
- User has many unlocked recipes
The rough functionality we want is:
- CRUD users and recipes
- Unlock recipe for user
- List unlocked recipes for user
Based on the above we need to come up with the access patterns for each entity:
- User
- Get user profile
- Recipe
- Get single recipe
- Get all recipes that can be cooked: open + unlocked
- Get all locked recipes in a dietary category when choosing what to unlock
- Unlocked recipe
- Unlock a locked recipe (unique, only once for a single user)
- Get all unlocked recipes for user
- Cooked meal
- Complete a cooking of a recipe by a user, non-unique - a user can cook the same recipe multiple times
- See history of completed meals
Now we have everything we need to specify our schema. For each access pattern we specify if it reads or writes or both, if the target is the main index or a global secondary one, what is the query and a note on how it will be used:
Access pattern | operation | target | PK / SK | Notes |
---|---|---|---|---|
User CRUD | R/W | main | PK USER#user_id SK USER#user_id |
Unique entry |
Get single recipe | R/W | main | PK RECIPE#recipe_id SK RECIPE#recipe_id |
Unique entry; non-key attribs: name, category, accessLevel cookingTime, imageName, ingredients, spices instructions |
Get unlocked recipe ids for user, order by time of unlocking | R | main | PK USER#user_id SK begins_with(UNLOCK_TIME#) |
|
Get all recipes that can be cooked in one or more categories: unlocked for user + open | R | GSI1 | PK ACCESS#access_type SK CATEGORY#category_id#recipe_id |
three steps:
|
Get all locked (for user) recipes in a dietary category when choosing what to unlock | R | step 1:GSI1, step 2:main | step 1:
PK ACCESS#access_type SK CATEGORY#category_id#recipe_id, step 2: PK USER#user_id SK begins_with(UNLOCK_TIME#) |
two steps: 1.get all locked (attainable) recipes 2. get all unlocked recipe ids for user and remove them from the list of locked ones |
Unlock recipe for user | W | main | PK USER#user_id SK UNLOCK_TIME#unlock_time |
References:
- Dynobase. “DynamoDB single table design made easy.” Accessed 25 Sept, 2024. https://dynobase.dev/dynamodb-single-table-design-tool
- AWS. “Data modelling foundations in DynamoDB.” Accessed 25 Sept, 2024. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/data-modeling-foundations.html
- AWS. “Best practices for using sort keys to organize data in DynamoDB.” Accessed 25 Sept, 2024. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-sort-keys.html
Animating SVG with simple JS
A nice looking effect can be achieved by gradually drawing a SVG. You can do this for an icon or some text that is made out of paths.
SVG files are just XML so you can use a SVG editor to make the paths and open the file with a text editor. When copying the tags to your HTML you should check if the code is also valid HTML, maybe you need to close the tags or delete some attributes.
You can fit this to your needs by having it also draw backwards by reducing the length
that is overwritten each tick on the strokeDasharray
.
You can have this run when the page opens or an element is hovered or a menu text/icon is rendered.
Learning foreign language vocabulary
Learning words in a new language is best done when connecting the concept of something directly to the word in the new language - as opposed to mapping the new word to the one in your mother tongue which is in turn mapped to the concept.
To help you and me form the habit of directly mapping the new word to the concept, I will show an example with words that makes this approach a necessity: the pronouns in the German language. German has multiple uses for 'sie' and in English we say 'you' for many different cases, so this missmatch and repeatedly using the same word for different meanings is a good example.
So first let's imagine or visualise the concepts:
Imagine talking to a friend. You are about to tell him something.
Imagine talking to multiple people.
Imagine speaking to the king. Address him formally.
Imagine talking about a group of people who are not present in the conversation.
Imagine talking about a woman who is not present in the conversation.
Nominativ:
Example sentence | Concept |
---|---|
Hast du das getan? |
|
Habt ihr das getan? |
|
Haben Sie das getan? |
|
Haben sie das getan? |
|
Hat sie das getan? |
Akkusativ:
Example sentence | Concept |
---|---|
Ich sehe dich. |
|
Ich sehe euch. |
|
Ich sehe Sie. |
|
Ich sehe sie. |
|
Ich sehe sie. |
Dativ:
Example sentence | Concept |
---|---|
Ich gebe dir etwas. |
|
Ich gebe euch etwas. |
|
Ich gebe Ihnen etwas. |
|
Ich gebe ihnen etwas. |
|
Ich gebe ihr etwas. |
sources:
https://en.wikibooks.org/wiki/German/Grammar/Pronouns
https://german.stackexchange.com/a/48144
Informaton science fathers

In the time of high attention to AI and data science we talk about the high value of data and how large amounts of it are precious. Governments try to regulate how it is collected, shared and protected. But do you know who are the fathers of information science?
I stumbled upon this wikipedia article of a Belgian who lived in the 19th and 20th centuries. The article had only 1400 visitors over the last 30 days. I thought it was a pity that I had never heard of that man. His name was Paul Otlet and he wanted access to knowledge for people. His friend and colleague Henry La Fontaine was the first president of the International Peace Bureau - the world’s oldest international peace organisation, with 10 of its members having receved a Nobel Peace prize - his article even less visited.
In short Otlet got fascinated by sharing information and setup a service where cards containing text were carefully curated and systematically queried. The amount of requests quickly grew in a matter of years. This is why I mentioned wikipedia earlier - it was the same idea - just in 1896, and much slower. It was meant to be easy to use for searching information and used a concept similar to hyperlinks.
The government of Belgium gave him buildings and budget to expand his efforts. But then the Great war started, where one of his sons lost his life. Otlet believed that forming an international organisation that promotes knowledge sharing can prevent future wars and he defined a charter of human rights.
I felt strange that the information about this man is not more well known. And it makes me think not about the man, but about the relationship we as a civilisation have with information. Its value over the centuries and our desire to ask for it and use it.
Another thing I found interesting is that he was not stuck in his ways and beliefs. When new tech like radio and microfilm came up he started incorporating it in his efforts. I can imagine how someone else would have seen these new changes as a threat.
He did also have dreams of building a utopian city that would be the center of the knowledge of the world, but despite never being even started he still worked on what he believed for the rest of his life.
References:
- Britannica. "Paul Otlet". Accessed 22 Feb, 2025. https://www.britannica.com/biography/Paul-Otlet
Keyboard - detecting keys
Because we don't have enough inputs on the arduio we can implement a key matrix to detect which keys are pressed. This way the needed input are the number of columns plus rows.
The way it works is that we send a signal on one row and check which columns receive it. Then check the next row the same way.
The basic idea of the matrix scanning:
There is the problem called ghosting - keys which are not really pressed register as being pressed. Using diodes we can prevent the effect.
Here is how it happens when diodes are not present:
As you can see the right column does receive a signal when it is the top row's turn to send it, because the signal goes through the top-left pressed key to the left column and then to the bottom column through the bottom-left key and then to the bottom row through the bottom-right key. That means that the top right switch is registered as pressed, but it's not.
And here is the same scenario, but with diodes:
It is a good idea to test by soldering using very little tin - just enough to hold on, instead of trying to fiddle with holding them in place.
I decided to switch the wiring from the blogs I read - my rows are connected directly and columns use diodes
You can use this jewelry making pliers to make little loops that can go onto the switch legs or the wires.
The next problem to solve is debouncing - detecting that a key is pressed and realeased many times in a short time, while in fact the key is only being pressed or released, but the contact between the metal plates in the switch is not really solid while in the process of moving. After doing the research I found there are hardware and software ways to solve this problem. The hardware one is with capacitors, but that makes it more of a hassle for me. There are multiple ways to solve it with software:
- waiting for a certain amount of time over which no change happens - this way we can be sure that the state is stable and we can read this as the current situation - this is slower, but might not be noticable
- detecting the first change and registering that one, then ignoring changes for some time and checking the state after this time - this is per key and is making the decision faster than the previous one
- only checking for changes every 50 milliseconds, for example - this is fast and easy to do, but is on all keys
- track the state over time and accumulate the values to calculate to which extreme it is more close - this is not fast and might not be very robust without tuning
My first approach is to do it the simple way and put more effort when those results become unsatisfactory for my needs. So I will only check the state of all keys every 25 milliseconds.
The next thing to do is make a layout for our keys. I decided to have preprocessor macros that map a readable name to a keycode of type byte. I can send these bytes to the receiving host without mapping them. The key names I can use to initialize a 2D array which will represent how I want my keys to be ordered. When I know the position of a pressed key in terms of row and column I can then access the resulting keycode through the array.

Later on this could become a 3D array when we need to have multiple layouts.
Keyboard - figuring it out

A colleague of mine has a fancy keyboard - the OKLB Planck. It is sold as a DIY kit that you assemble and program yourself. I was unsure how I feel about it when I first saw it. But his explanations quickly got me fascinated and I decided to make my own - even more DIY style. I would 3D print everything and make some improvements like bluetooth. Another problem he had with his was the lack of a way to indicate on which layout level you are. And maybe a gesture sensor to swipe with. And maybe a gyroscope to use it like a mouse or driving wheel.
First I decided to go simple and just make a keyboard without bluetooth or anything fancy. Then learn from the mistakes and improve to make a second one that is closer to the real goal.
I ordered cheap switches and a pcb to make the process easier. At this point I thought that a big step of this project will be coming up with a good layout for my needs. Also because I take key bindings too seriously.
I found 3D models from the original designer and decided to print them. I also found a model of a keycap and modified it to make it printable without much support and add a bump for the index fingers. The keycaps turned out not fitting the switches, so I had to rework them a few more times.
The 3D models can be found here on my github.
The pcb was not fitting the models I found and was not very happy with them anyway, and they were not designed to be 3D printed to start with. I started planning how could I make a case that does not use bolts and nuts.
I printed a few simple clips see if I could use those. Some of them I printed impoperly, some were very stiff and I struggled to design better ones. I realised something very important then: instead of going for a perfect solution I should have gone for a satisfactory one and improve when needed.
I immediately made a few simple holes in a new top half model and found some M3 and M2 12mm bolts. After that I started designing a new bottom case, but adjusting it's height would have to wait.
Another problem was that I damaged or bricked the PCB so I also had to start again with that. The big problem was that I had soldered the switches onto the PCB, so I had to unsolder them one by one. It cost me 4 hours and a few burns to do that. But I did learn to do less ambitious planning upfront. I will never forget this lesson because fixing broken switches is one of the most tedious and fiddly things I’ve ever done.
The next step was to figure out how to use an Arduino for the keyboard I could flash it with the QMK firmware. Or write some code myself.
When you want to detect button presses on 48 keys while having less than 48 pins is not straight forward. So I found a few excellent blogs on how matrix scanning works:
- http://pcbheaven.com/wikipages/How_Key_Matrices_Works/
- http://blog.komar.be/how-to-make-a-keyboard-the-matrix/
After understanding the hardware I did some searching. First I found the matrix scanning code in a file from the Keypad library for Arduino. Then I found a file from a stenography keyboard open source project, which also did a similar job. It was very helpful to learn from this available code.
Making a keyboard without a pcb is called handwiring and I found a nice post about it: https://geekhack.org/index.php?topic=87689.0.
With the plan changed, and my student budget limits, I actually had more fun finding my way through.
DnD murder mystery inspiration

I wanted to make a murder mystery for my DnD group. When the moment came and I started preparing for the session I found there is a lot of advice, but not a lot of easy inspiration. There are a few prepared adventures, and those are not very polished or detailed. I used some of them for inspiration and a base and stole some ideas from them. Maybe you can take mine and improve it to have fun with your players.
I had planned his for along time as I expected that one of my players would not attend at some point. Perfect opportunity to throw the characters into a pocket dimension for a one shot without advancing the main story. I gave one of them this book with empty pages and a whirlpool on the cover.
Make sure to have prepared statblocks for all the stuff they can encounter.
I was greatly inspired by the adventure called "How not to host a murder mystery"
I have not used names as I think it's easier to navigate the story with roles since we are looking at its structure and not for entertainment.
Everything starts in a whirlpool in the sea, hence the cover of the book. The characters are on a small ship with Victim heading to the city on the coast. During the whirlpool another group of adventurers (goblins?), who read another book of the same type, are summoned on the ship. Victim, Innocent and his colleagues are on the ship, and the characters are offered to to stay in the Victim's mansion. They were all summoned because he found this book and wanted to figure it out so cast some spells and activated all the books. Victim introduces Innocent as his brave but foolish, quick to act apprentice.
The mansion, Victim explains was built thanks to his father's successful business of trading rapiers, short swords and daggers. His father died 6 years ago.
Garden:
Morning glory - a flower that blooms in the morning because of sunlight and it's petals are closed when in shadow or darkness; a
letter is hidden under the open petals in the day, but when darkness makes them close it is easy to spot. The letter is written
with the same handwriting as the book of the Killer, it is to the girl of the apprentice saying that he will not give up on their
love. This can be used to have the Killer try to frame Innocent.
Time before dinner: the players are invited to do something before it is time for dinner:
- With Killer and Innocent to practice spells [openly said] (so all 3 including the PC seem suspicious); Killer doesn't want to waste mana; The Innocent calls Killer by his middle-name. The other players will not know whom this name belongs when they see it.
- With sister to show you around: offers a reward a knife from the father's collection of rare craftsman knives [openly said] (so the sister and the PC sound suspicious) In private they find that one specific knife is missing from its stand - Lord Kelnor's pearl dagger
- With cook to help prepare the big dinner. [openly said]; In private he tells the motive of both Innocent and Sister. He is grumpy and very skilled with cutting meat with his knife; he says the sister has a necklace with a gem that allows her to seduce and control weak men
The players are given the information separately through notes and they should get a bit confused and suspect each other.
Dining room:
During dinner the characters can talk to Innocent, Killer, Victim's sister who is a paid assassin, the cook, the old nanny
Killer says he will go and help the cook to set up the table. Innocent says that he will go and find the host Victim because he should have been here already. He leaves the room and in a bit everyone hears him scream.
Victim is stabbed in the heart in his study. There is no weapon there. Actually the knife Killer used was conjured by Spiritual weapon.
Killer points the finger at innocent and explains his motive. Innocent explains that he had the choice to give up on his study, but made up his mind and was no longer interested in marrying the girl. Innocent has a fresh letter to her stating that he will find a way to be with her and won't give up on their love.
The real killer has to be found before sunrise otherwise the framed Innocent will be hanged.
Innocent is a young novice, brave but foolish, quick to act. Victim doesn't let Innocent marry the daughter of a blacksmith because she will distract him from his studies.
Cook - not speaking a lot / low voice / bitter
The study:
Has a book with spells (belongs to:Killer's name on the inside cover), every page has notes in the margins. Some of the notes are
crossed out and with another color and handwriting more notes are taken. The second notes match the handwriting on the papers on Victim's
desk. On the spell Spiritual weapon there are a lot of scribbles and in all caps: HAVE TO ASK <Killer's middle name> HOW TO CAST THIS!
Trophies on a shelf saying: First place in spellcasting/conjuring/arcana competition - Victim
Building the structure of the mystery:
- Innocent has motive and opportunity(to go bring victim), but no means
- Killer has motive(fame,praise), means(stabbing), opportunity(the fact that the apprentice and the sister now have a motive)
- Cook has means(knife with blood), no motive and no opportunity
- Sister has motive(she is younger and can inherit the mansion that she likes very much if her brother dies), but no means and no opportunity, but definitely suspicious being an assassin
- Old nanny - no motive, no means, no opportunity
Switching to and customizing IntelliJ

Hello! Read on if you decided to switch to IntelliJ, set it up, make it comfortable and learn how to be more productive with it. This post covers some settings and custom shortcuts.
The first things that annoyed me were the popups in the top-right corner. To disable some of them: 'Event log' on the bottom-right (or Speech bubble icon) -> Settings. Disabling the balloons doesn't hide the notifications from the event log, so you can check that if you need to.
Next up is removing the default comment generated at the top of a new class file.
File -> Settings -> Editor -> File and Code Templates -> 'Includes' tab -> File header -> the comment is on the right
-> delete it.
If you would like to use this feature make sure to print the date in a less ambiguous format.
Another one is the vertical line in the editor that is set to 120 chars per line by default. To change the line
limit:
File -> Settings -> Editor -> Code Style -> Right margin columns.
The Google Java Style Guide recommends a column limit of 100 chars.
Ok hotkeys. Some are very hard to reach, hard to learn or conflict with something else. Here are some recommendations
how to make your life easier. You don't need to remove the old shortcut, so if someone else wants to use your Idea they
can still use the default keys they know. Go to
File -> Settings -> Keymap
and find the ones you need to change, then click on
them and select 'Add keyboard shortcut'. All of the recommended ones are unused and should be added as alternatives. For each
entry I have specified: the name of the command, the default shortcut, the suggested one and an explanation.
Rename: default - Shift F6, suggested - Alt R. This default key is famous for being so hard to reach. Usage of rename should be encouraged as naming things is one of the hardest tasks in programming. Remember it as R stands for rename.
Find usage: default - Alt F7, suggested - rightAlt U. Shows you where a method/variable is used. One hand - easy. Associate U with usage.
Surround with...: default - Ctrl Alt T, suggested - Alt T. Surrounds the selected code with if/elseif/while/for/try... etc. Some Linux distros have the terminal shortcut bound to Ctrl Alt T so that causes a conflict. Even if you don't care about that the alternative is still 1 less key to press.
Delete line at caret: default - Ctrl Y, suggested - Alt X. In Eclipse it was easy to Ctrl D with one hand, but Ctrl Y requires both hands. The Alt and X keys are next to each other and this one is very easy to press. The X can be associated with double-crossing something.
Run: default - Alt Shift F10, suggested - Alt P. Usefulness depends on how often you run the project. P as in play for the green arrow icon. An even easier variant is Alt E as in execute.
Next, I have compiled a list of the more notable shortcuts. Some are based on people's opinion from Stackoverflow. A more complete list can be found here and here and you can always check the key for actions by pressing Ctrl Alt A in Idea.
Go to declaration => Ctrl B
Open class => Ctrl N
Rename refactor => Alt R; Shift F6
Refactor variable from expression => Ctrl Alt V
Extract method => Ctrl Alt M
Smart completion => Ctrl Shift Space
Cyclic expand word => Alt forwardslash
Complete statement => Ctrl Shift Enter
Reformat, Checkstyle, fix imports => Ctrl Alt L
Find in whole project => Ctrl Shift F
Switch tabs => Ctrl Tab
Quick fix/intentions => Alt Enter
Smart template => iter/psf/thr Tab
Find Usage => rightAlt U; Alt F7
Brief info => Ctrl mouseover
Duplicate => Ctrl D
New line below => Shift Enter
New line above => Ctrl Alt Enter
Expanding select => Ctrl W
Delete to word start => Ctrl Backspace
Delete line => Alt X; Ctrl Y
Paste previous copies => Ctrl Shift V
JavaDoc => Ctrl Q
Run => Alt P; Alt Shift F10
Go to previous/next method => Alt Up/Down
Go back to code => Ctrl Alt Left
Go to next error => F2
Expand all folded => Ctrl Shift =
Parameter info in method call => Ctrl P
Show TODO list => Alt 6
Bring up the file/class/method quicksearch => Shift Shift
Open the switcher => Ctrl Tab
Hide active window => Shift Escape
If you think a shortcut or setting you often use is missing - tell me!
The mystery of termite communication

Until the 1960s scientists could not explain how termites manage to organise their activity to gather and pile up in one place all the wood pieces they find. Different theories tried to explain this like unaudible languages, but the truth was that they just followed simple rules, from which emerged a complex behaviour.
By taking a piece of wood when they encounter it and dropping it when they encounter another one the termites move the pieces to one or more piles, which in turn also get combined into bigger piles. The result is cleared areas and compact piles.
My implementation of the simulation: on github