Skip to content

Instantly share code, notes, and snippets.

@o0101
Last active October 26, 2017 10:38
Show Gist options
  • Save o0101/d17eca7e68506590fda8c2e26a6dd887 to your computer and use it in GitHub Desktop.
Save o0101/d17eca7e68506590fda8c2e26a6dd887 to your computer and use it in GitHub Desktop.
key-value-datastore
/** see a live demo here: https://runkit.io/dosyago-coder-0/kvdb/branches/master **/
const express = require("express");
const bodyParser = require("body-parser");
const db = new Map();
const page = () => `
<!DOCTYPE html>
<meta charset=utf-8>
<title>kvdb</title>
<style>
:root {
background: #af1;
}
body {
text-align: center;
}
article {
display: inline-flex;
margin: 0 auto;
}
iframe {
border-width: 0px;
}
footer ul li {
display: inline-block;
text-align: center;
margin: 0 0.5rem;
}
</style>
<main>
<header>
<h1>key-value-database console</h1>
</header>
<article>
<form action="set" method=GET target="setresult">
<fieldset><legend>Set a key</legend>
<p>
<input required type=text
placeholder=key name=key value="" maxlength=63>
<p>
<textarea name=value placeholder=value
rows=6 cols=23
maxlength=2046 value=""></textarea>
<p>
<button>Set</button>
</fieldset>
<fieldset><legend>Set result</legend>
<iframe name=setresult src=about:blank
width=100% height=64px></iframe>
</fieldset>
</form>
<form action="get" method=GET target="getresult">
<fieldset><legend>Get a key</legend>
<p>
<input required type=text
placeholder=key name=key value="" maxlength=63>
<p>
<button>Get</button>
</fieldset>
<fieldset><legend>Get result</legend>
<iframe name=getresult src=about:blank
width=100% height=128px></iframe>
</fieldset>
</form>
</article>
<footer>
<ul>
<li> <a rel=alternate type=text/javascript
target=_blank
href=https://gist.github.com/dosyago-coder-0/d17eca7e68506590fda8c2e26a6dd887>the Gist</a>
<li> <a rel=alternate type=application/zip
download
href=https://gist.github.com/dosyago-coder-0/d17eca7e68506590fda8c2e26a6dd887/archive/b2bc1c4415dc98ec4229c27a96f80c3fda7abf83.zip>download the Gist (zip)</a>
<li> <a rel=alternate type=text/javascript
target=_blank
href=https://runkit.com/dosyago-coder-0/kvdb>the RunKit</a>
<li> <a rel=alternate type=application/javascript
target=_blank
href=https://runkit.io/dosyago-coder-0/kvdb/branches/master>the RunKit endpoint (live demo)</a>
</ul>
</footer>
</main>
`;
const setresult = ({status,key,date}={}) => `
<!DOCTYPE html>
<meta charset=utf-8>
<title>kvdb set result</title>
<style>
:root {
background: #c0ffee;
}
.keyname {
font-weight: bold;
}
.statustext {
font-variant: italic;
}
.timestamp {
font-size: 0.75rem;
font-family: monospace;
color: grey;
}
</style>
<p>
Key <span class=keyname>${key}</span> is <span class=statustext>${
status ? 'set' : 'not set'
}</span> @ <time value="${date}" class=timestamp>${date.toLocaleTimeString()}</time>.
</p>
`;
const getresult = ({status,key,value,date}={}) => `
<!DOCTYPE html>
<meta charset=utf-8>
<title>kvdb get result</title>
<style>
:root {
background: #c0ffee;
}
.keyname {
font-weight: bold;
}
.statustext {
font-variant: italic;
}
.valuetext {
white-space: pre-wrap;
font-family: monospace;
}
.timestamp {
font-size: 0.75rem;
font-family: monospace;
color: grey;
}
</style>
<p>
Key <span class=keyname>${key}</span> is <span class=statustext>${
status ? 'retrieved' : 'not found'
}</span> @ <time value="${date}" class=timestamp>${date.toLocaleTimeString()}</time>.
</p>
<dl>
<dt><span class=keyname>${key}</span>
<dd>
<p class=valuetext>${value}</p>
</dd>
</dl>
`;
const app = express();
/**
app.use(bodyParser.json() ); // to support JSON-encoded bodies
app.use(bodyParser.urlencoded({ // to support URL-encoded bodies
extended: true
}));
**/
app.get("/", async (req,res,next) => {
res.type('html');
res.end(page());
});
app.get("/set", async (req,res,next) => {
res.type('html');
const {key,value} = req.query;
const date = new Date;
let status = false;
if ( !! key ) {
db.set(key,value);
status = true;
}
res.end(setresult({key,status,date}));
});
app.get("/get", async (req,res,next) => {
res.type('html');
const {key} = req.query;
const date = new Date;
const status = db.has( key );
let value;
if ( status ) {
value = db.get(key);
}
res.end(getresult({key,status,value,date}));
});
app.get("/:anything", async (req,res,next) => {
res.type('html');
res.status(404);
res.end('404 Not found');
});
app.listen(4000, () => console.log(`Server up @ ${new Date}`));
{
"name": "kvdb",
"version": "1.0.0",
"description": "key-value-database",
"main": "kvdb.js",
"dependencies": {
"express": "^4.16.2"
},
"devDependencies": {},
"scripts": {
"test": "node kvdb.js"
},
"repository": {
"type": "git",
"url": "https://gist.github.com/dosyago-coder-0/d17eca7e68506590fda8c2e26a6dd887/edit"
},
"keywords": [
"key-value",
"simple",
"database",
"store"
],
"author": "Cris <writetocris@outlook.com>",
"license": "MIT"
}
@o0101
Copy link
Author

o0101 commented Oct 21, 2017

There's another part to this which is persisting a database to a file.
I think I'm okay to save a JSON object, or even a list of lines to a file ( encoding and so on is tricky )
I really have no idea at first impression how to go about making that "more efficient"
I'll think about this but won't work out a full solution
So I can stay within the requirements

@o0101
Copy link
Author

o0101 commented Oct 25, 2017

I'm thinking about this.

I like simple, append only, flat file.

That means we have DB history, and everything else.

The issue is startup where we read in and process the events ( one event per line ). But that's fine.

We shall not be touching a memory limit in this implementation anyway.

And there's nothing wrong with putting your table into memory. Advice for SQL is to put the entirety of your biggest table in memory. So that's fine.

I like the idea of "event" database. Since insertion is easy, even if we are not querying.

Also I want to make each line base 64 encoded.

The file itself needs to query information.

Index is built in memory.

What sort of index ?

Index on keys. Obviously.
Index on values. Reverse index. Okay.
Index on functions of keys or values. Such as key prefix or value prefix, or key chunks or value chunks. Things like that.
We can make a general "index builder function" that takes two parameters, slot, which can be "key" or "value", and transform function,
which transforms a single k,v into an index. In otherwords, transform takes an index, and a k,v and adds that k,v to that index.

Simple.

Indexes need a simple format, such as to be a class that accepts a query function call.

@o0101
Copy link
Author

o0101 commented Oct 26, 2017

Just trying to quickly study up on file system API. I am not familiar with it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment