') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); GitHub - tinydesk/relmapper: Map a relational model to JSON · GitHub
Skip to content

Repository files navigation

relmapper

Map a relational model to JSON and back.

This library is a light-weight tool, that is only concerned with converting between a hierarchical representation and a flat column-like representation of the data. The idea behind this is, that a server is essentially an adapter transforming data from a database to a web based interface such as REST over HTTP and vice versa. relmapper facilitates this transformation while not interfering with your queries. This library is for all developers that have decided on a particular relational db technology and want to leverage its full potential without being limited by the abstraction of an ORM.

Support for transforming join results into arrays is planned but not yet implemented.

Getting started

npm install relmapper --save

Convert json to a db record:

varmapper=require('relmapper').defaultMapper;varjson={myJsonProperty: 5,myNested: {jsonProperty: 'with text'}};mapper.apply(json);/*returns { my_json_property: 5, my_nested__json_property: 'with_text'} */

Convert the result from a db query to nested json from a table looking like this:

CREATE TABLE(
my_json_property: INTEGER,
my_nested__json_property: VARCHAR(512)
);
varqueryResult=query('SELECT * FROM mytable');mapper.unapply(queryResult);/*returns [{ myJsonProperty: 5, myNested: { jsonProperty: 'with text' } },...] */

API

The basic concept of this library is that of a mapper. A mapper is a plain javascript object with two methods: apply and unapply. The transformation between the hierarchical and the relation representation is performed by applying a pipeline of mappers:

json -> mapper1.apply -> mapper2.apply -> mapper3.apply -> db object
db object -> mapper3.unapply -> mapper2.unapply -> mapper1.unapply -> json

A mapper can either transform a single object or an array of objects. The module exposes the following mappers:

flatten(delimiter)

Transforms a hierarchical structure to a flat property where the path is indicated by the given delimiter.

Examples:

varrelmapper=require('relmapper');varjson={a: {b: {c: 1}}};relmapper.flatten('__').apply(json);/*returns { a__b__c: 1 } */varresult={a__b__c: 1};relmapper.flatten('__').unapply(result);/*returns { a: { b: { c: 1 }}} */

camelCase

Transforms camel case to snake case and vice versa. This is needed since most relational database systems are case insensitive.

Examples:

varrelmapper=require('relmapper');varjson={aCamelCaseProperty: 1};relmapper.camelCase.apply(json);/*returns { a_camel_case_property: 1 } */varresult={a_camel_case_property: 1};relmapper.camelCase.unapply(result);/*returns { aCamelCaseProperty: 1 } */

Mappers can be combined to form more complex mappers:

sequence(...mappers)

Creates a pipeline of mappers that are applied in sequence. The order is reversed when unapplying.

The library also publishes a defaultMapper which is defined as a sequence of flatten and camelCase:

vardefaultMapper=relmapper.sequence(relmapper.flatten('__'),relmapper.camelCase);

See the getting started section for an example of this mapper.

fromObjectMapper(mapper)

Creates a mapper that can handle both arrays and objects from a simpler mapper that only processes objects.

Changelog

0.3.0

  • Reimplemented the existing functionality in typescript.
  • Changed the following names due to collisions with keywords:
    • default becomes defaultMapper
    • case becomes camelCase

0.2.0

  • Changed the mapper interface to also support arrays as arguments to apply and unapply.

About

Map a relational model to JSON

Resources

Stars

0 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages