Finding an item in an array with partial match in ArangoDB

Query

1
2
3
4
LET needle = "eedl"
LET haystack = ["needle", "in", "a", "haystack"]
RETURN haystack[** FILTER CONTAINS(CURRENT, needle)]
// Output: ["needle"]

Variant: Case-insensitive

1
2
3
4
LET needle = "EEDL"
LET haystack = ["needle", "in", "a", "haystack"]
RETURN haystack[** FILTER CONTAINS(LOWER(CURRENT), LOWER(needle))]
// Output: ["needle"]

References

  1. Array operators - Inline filter
  2. String functions - CONTAINS()
  3. String functions - LOWER()

UML and OpenAPI for full stack development

There are many good articles introducing how to become a full stack developer. Lots of technologies — HTML, CSS, JavaScript, Express, React are mentioned. They are really good resources. However, I think they miss some importatnt things and here are two importatnt things.

UML: Data Model of Service

Before implementing a service, it is important to construct concrete a data model, definitions and relationships between entities. Without a concrete data model, design and implementation of service are highly likely to be changed during development, resulting in an unstable product and risk of missing feature.

To design a class diagram, you can use one of software (or others):

OpenAPI: Protocol between Backend and Frontend

After designing data model, a developer needs to implement backend service provider and frontend service request. OpenAPI enables a developer to define a protocol: which paths, parameters and body contents to use, how responses will be.

To design OpenAPI, you can use one of software (or others):

Embedding worker in Electron app using Webpack and electron-builder

Disclaimer: as per electron-boilerplate

Make webpack bundle your worker code. I’ll skip the entire code here because it’s too long.

Remeber the output path

1
2
3
4
5
6
{
output: {
path: path.join(__dirname, '..'),
filename: './app/dist/worker.js',
}
}

Make electron-builder copy your worker bundle file .

1
2
3
4
5
6
7
{
"build": {
"files": [
"dist/"
]
}
}

This will place your worker bunlde file inside resources/app.asar.

Adjust worker path and working directory for fork in main.dev.ts.

1
2
3
4
5
6
7
8
9
const workerPath =
process.env.NODE_ENV === 'development'
? 'app/worker.js'
: 'app.asar/dist/worker.js';
const workerCwd =
process.env.NODE_ENV === 'development'
? undefined
: join(__dirname, '..');
const worker = fork(workerPath, { cwd: workerCwd });

References

Installing Electron manually for electron-builder

This article tries to resolve an uncommon issue when you encounter the following error and it cannot be resolved automatically, e.g. behind proxy, etc.:

1
Error: Electron failed to install correctly, please delete node_modules/electron and try installing again

Download the right version (with consideration of package.json) of Electron from https://github.com/electron/electron/releases

Unpack the downloaded Electron and put all files under node_modules/electron/dist.

Create path.txt file under node_modules/electron with the following content:

  • Windows: electron.exe
  • Linux: electron
  • macOS: Electron.app/Contents/MacOS/Electron

Note: Make sure there is no whitespace characters (including a newline character) after the filename. This is a problem that I struggled for minutes and hours.

References

Quick start on ANTLR4 in Rust - part3

This is my note in adopting and learning ANTLR4Rust

Series


In the previous article, we implemented a parser with internal state variables. However, if a grammar is huge, it is practically impossible to manage a huge number of state variables. Alternatively, a visitor-like approach can be used.

In an exit method of the root rule in the Listener, we can grab the current context and its children and call custom parser methods:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
impl Listener {
fn hdr(&self, ctx: &HdrContextAll) -> Row {
let row_ctx = ctx.row().unwrap();
self.row(&row_ctx)
} fn row(&self, ctx: &RowContextAll) -> Row {
let mut row = Row::new();
let field_ctx_list = ctx.field_all();
for (_i, field_ctx) in field_ctx_list.iter().enumerate() {
let field = self.field(&field_ctx);
row.push(field);
}
row
} fn field(&self, ctx: &FieldContextAll) -> String {
ctx.get_text()
}
}
impl CSVListener for Listener {
fn exit_csvFile(&mut self, ctx: &CsvFileContext) {
let hdr_ctx = ctx.hdr().unwrap();
let header = self.hdr(&hdr_ctx);
self.csv.header = header;
let row_ctx_list = ctx.row_all();
for (_i, row_ctx) in row_ctx_list.iter().enumerate() {
let row = self.row(&row_ctx);
self.csv.rows.push(row);
}
}
}

The minimal working example can be found here.

Quick start on ANTLR4 in Rust - part2

This is my note in adopting and learning ANTLR4Rust

Series


In the previous article, we built the minimal working example walking a parse tree using a listener. In this article, we are going to store a parsed CSV structure into a variable to use in the future.

Define a CSV structure:

1
2
3
4
5
type Row = Vec<String>;#[derive(Debug)]
struct CSV {
header: Row,
rows: Vec<Row>,
}

Add fields to a listener:

1
2
3
4
5
struct Listener {
csv: Box<CSV>,
add_to_header: bool,
row_to_add: Vec<String>,
}

csv will be a resultant CSV structure. add_to_header and row_to_add are internal state variables to generate a CSV structure.

Implement a listener:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
impl CSVListener for Listener {
fn enter_csvFile(&mut self, _ctx: &CsvFileContext) {}
fn exit_csvFile(&mut self, _ctx: &CsvFileContext) {}
fn enter_hdr(&mut self, _ctx: &HdrContext) {
self.add_to_header = true;
}
fn exit_hdr(&mut self, _ctx: &HdrContext) {
self.csv.header = self.row_to_add.to_vec();
self.row_to_add.clear();
self.add_to_header = false;
}
fn enter_row(&mut self, _ctx: &RowContext) {}
fn exit_row(&mut self, _ctx: &RowContext) {
if self.add_to_header {
return;
}
self.csv.rows.push(self.row_to_add.to_vec());
self.row_to_add.clear();
}
fn enter_field(&mut self, _ctx: &FieldContext) {}
fn exit_field(&mut self, _ctx: &FieldContext) {
self.row_to_add.push(_ctx.get_text());
}
}

Feed an input to a parse and extract the result:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
fn main() {
let input = String::from(
"This, is, a, header
This, is, a, row
",
);
let lexer = CSVLexer::new(Box::new(InputStream::new(input)));
let token_source = CommonTokenStream::new(lexer);
let mut parser = CSVParser::new(Box::new(token_source));
let listener_id = parser.add_parse_listener(Box::new(Listener {
csv: Box::new(CSV {
header: Row::new(),
rows: Vec::new(),
}),
add_to_header: false,
row_to_add: Row::new(),
}));
let result = parser.csvFile();
assert!(result.is_ok());
let listener = parser.remove_parse_listener(listener_id);
let csv = listener.csv;
println!("{:#?}", csv);
}

A tricky part is that we have to extract csv from listener. And to this end, we have to extract listener with listener_id.

The minimal working example can be found here.

Quick start on ANTLR4 in Rust - part1

This is my note in adopting and learning ANTLR4Rust

Series


Install nightly version of Rust (and make it default if you want for convenience).

1
2
$ rustup toolchain install nightly
$ rustup default nightly

Get ANTLR4 runtime for Rust from here:

Prepare a grammar. I will use an example grammar: https://raw.githubusercontent.com/antlr/grammars-v4/master/csv/CSV.g4

Generate a parser:

1
$ java -jar <ANTLR4 runtime path> -Dlanguage=Rust CSV.g4

You will get csvlexer.rs, csvlistener.rs , csvparser.rs . Place them into your project src directory.

Add dependencies in Cargo.toml:

1
2
3
[dependencies]
lazy_static = "1.4"
antlr-rust = "0.1"

Add a feature and import lazy_static macros to the root module:

1
2
3
#![feature(try_blocks)]
#[macro_use]
extern crates lazy_static;

Import common and essential things:

1
2
3
4
use antlr_rust::common_token_stream::CommonTokenStream;
use antlr_rust::input_stream::InputStream;
use antlr_rust::parser_rule_context::ParserRuleContext;
use antlr_rust::tree::{ErrorNode, ParseTreeListener, TerminalNode};

Import grammar-specific things:

1
2
3
4
5
6
mod csvlexer;
mod csvlistener;
mod csvparser;
use csvlexer::CSVLexer;
use csvlistener::CSVListener;
use csvparser::*;

Implement ParseTreeListener, a supertraint of CSVListener:

1
2
3
4
5
6
struct Listener;impl ParseTreeListener for Listener {
fn visit_terminal(&mut self, node: &TerminalNode) {}
fn visit_error_node(&mut self, node: &ErrorNode) {}
fn enter_every_rule(&mut self, ctx: &dyn ParserRuleContext) {}
fn exit_every_rule(&mut self, ctx: &dyn ParserRuleContext) {}
}

Implement CSVListener:

1
2
3
4
5
6
7
8
9
10
impl CSVListener for Listener {
fn enter_csvFile(&mut self, _ctx: &CsvFileContext) {}
fn exit_csvFile(&mut self, _ctx: &CsvFileContext) {}
fn enter_hdr(&mut self, _ctx: &HdrContext) {}
fn exit_hdr(&mut self, _ctx: &HdrContext) {}
fn enter_row(&mut self, _ctx: &RowContext) {}
fn exit_row(&mut self, _ctx: &RowContext) {}
fn enter_field(&mut self, _ctx: &FieldContext) {}
fn exit_field(&mut self, _ctx: &FieldContext) {}
}

Read and parse an input. Note that csvFile in the last line is a rule name in CSV.g4:

1
2
3
4
5
6
7
8
9
10
11
12
fn main() {
let input = String::from(
"This, is, a, header
This, is, a, row
");
let lexer = CSVLexer::new(Box::new(InputStream::new(input)));
let token_source = CommonTokenStream::new(lexer);
let mut parser = CSVParser::new(Box::new(token_source));
parser.add_parse_listener(Box::new(Listener {}));
let result = parser.csvFile();
assert!(result.is_ok());
}

The minimal working example can be found here.

Trilateration using the Levenberg-Marquardt method

True range multilateration is a method to determine the location of a movable vehicle or stationary point in space using multiple ranges (distances) between the vehicle/point and multiple spatially-separated known locations (often termed ‘stations’). — True range multilateration, Wikipedia

We can define a trilateration as an optimization problem to minimize a cost function \(S\) for a given estimate \(\beta\), a tuple of \((x, y, z)\)

$$\begin{aligned}
S(\beta)&=\sum_{i=1}^{N}f_i(\beta)\\
&=\sum_{i=1}^{N}\left[r_i-\sqrt{(x-X_i)^2+(y-Y_i)^2+(z-Z_i)^2}\right]^2
\end{aligned}$$

where \(r_i\) is a measured distnace between an Anchor (i), whose coordinates are \((X_i, Y_i, Z_i)\).

Levenberg-Marquardt method is defined by:

$$\beta_{k+1}=\beta_k-\left(J^TJ+\mu_kdiag\left(J^TJ\right)\right)^{-1}J^Tf(\beta_k)$$

where \(J\) is the Jacobian matrix and \(f\) is a column vector composed of \(f_i\):

$$\begin{aligned}
J&=\begin{bmatrix}
\frac{\partial f_1}{\partial x} & \frac{\partial f_1}{\partial y} & \frac{\partial f_1}{\partial z} \\
\frac{\partial f_2}{\partial x} & \frac{\partial f_2}{\partial y} & \frac{\partial f_2}{\partial z} \\
\vdots & \vdots & \vdots \\
\frac{\partial f_N}{\partial x} & \frac{\partial f_N}{\partial y} & \frac{\partial f_N}{\partial z}
\end{bmatrix}\\
&=\begin{bmatrix}
F_1(x-X_1) & F_1(y-Y_1) & F_1(z-Z_1) \\
F_2(x-X_2) & F_2(y-Y_2) & F_2(z-Z_2) \\
\vdots & \vdots & \vdots \\
F_N(x-X_N) & F_N(y-Y_N) & F_N(z-Z_N)
\end{bmatrix}
\end{aligned}$$

where \(F_i\) is a shorthand of each derivative:

$$F_i=\frac{r_i}{\sqrt{(x-X_i)^2+(y-Y_i)^2+(z-Z_i)^2}}$$

Now, we can estimate \(\beta\) by iterating the equation until it converges.

Git Hooks with stderr

For example, it’s tricky to validate a commit with pre-commit hook if an app does not return a proper exit code. Here’s a way to validate with the stderr.

  1. Make a temporary file
  2. Redirect stderr to the temp file
  3. Count the length of its content

You can understand it with code snippets below:

PowerShell

1
2
3
4
5
6
7
$errFile = New-TemporaryFile
command > /dev/null 2>$errFile
$err = Get-Content $errFile
If ($err.Length -ne 0) {
Exit 1
}
Exit 0

Unix/Linux shell

1
2
3
4
5
6
7
errFile = $(mktemp)
command > /dev/null 2>$errFile
$l = wc -l $errFile
if [$l -ne 0]; then
exit 1
fi
exit 0

Preventing privacy leak with TuringFonts (substitution cipher)

Download one of web fonts (.woff2) from TuringFonts. Assumed Zebra is used here

Put the font to the web directory and define the below in a stylesheet:

1
2
3
4
5
6
7
@font-face {
font-family: ArialZebra;
src: url("arial_zebra.woff2")
}
.zebra-substitution {
font-family: ArialZebra
}

Assign zebra-substitution class to where it needs

1
<span class="zebra-substitution">[email protected]</span>

See how it looks.

TuringFonts also provides Encoder to get a ciphered text.