Nicksxs's Blog

What hurts more, the pain of hard work or the pain of regret?

博客之前使用的是 local search,最开始感觉使用体验还不错,速度也不慢,最近自己搜了下觉得效果差了很多,不知道是啥原因,所以接入有 next 主题支持的 Algolia 搜索,next 主题的文档已经介绍的很清楚了,这边就记录下,
首先要去 Algolia 开通下账户,创建一个索引

创建好后要去找一下 api key 的配置,这个跟 next 主题的说明已经有些不一样了
在设置里可以找到

这里默认会有两个 key

一个是 search only,一个是 admin key,需要再创建一个自定义 key
这个 key 需要有这些权限,称为 High-privilege API key, 后面有用

然后就是到博客目录下安装

1
2
cd hexo-site
npm install hexo-algolia

然后在 hexo 站点配置中添加

1
2
3
4
algolia:
applicationID: "Application ID"
apiKey: "Search-only API key"
indexName: "indexName"

包括应用 Id,只搜索的 api key(默认给创建好的那个),indexName 就是最开始创建的 index 名,

1
2
3
4
export HEXO_ALGOLIA_INDEXING_KEY=High-privilege API key # Use Git Bash
# set HEXO_ALGOLIA_INDEXING_KEY=High-privilege API key # Use Windows command line
hexo clean
hexo algolia

然后再到 next 配置中开启 algolia_search

1
2
3
4
5
# Algolia Search
algolia_search:
enable: true
hits:
per_page: 10

搜索的界面其实跟 local 的差不多,就是搜索效果会好一些

也推荐可以搜搜过往的内容,已经左边有个热度的,做了个按阅读量排序的榜单。

最近一次推送博客,发现报了个错推不上去,

1
2
3
4
5
WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!

IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.

错误信息是这样,有点奇怪也没干啥,网上一搜发现是We updated our RSA SSH host key
简单翻一下就是

在3月24日协调世界时大约05:00时,出于谨慎,我们更换了用于保护 GitHub.com 的 Git 操作的 RSA SSH 主机密钥。我们这样做是为了保护我们的用户免受任何对手模仿 GitHub 或通过 SSH 窃听他们的 Git 操作的机会。此密钥不授予对 GitHub 基础设施或客户数据的访问权限。此更改仅影响通过使用 RSA 的 SSH 进行的 Git 操作。GitHub.com 和 HTTPS Git 操作的网络流量不受影响。

要解决也比较简单就是重置下 host key,

Host Key是服务器用来证明自己身份的一个永久性的非对称密钥

使用

1
ssh-keygen -R github.com

然后在首次建立连接的时候同意下就可以了

我们在使用 ssh 连接的使用有一个很好用功能,就是端口转发,而且使用的方式也很多样,比如我们经常用 vscode 来做远程开发的话,一般远程连接就可以基于 ssh,前面也介绍过 vscode 的端口转发,并且可以配置到 .ssh/config 配置文件里,只不过最近在一次使用的过程中发现了一个问题,就是在一台 Ubuntu 的某云服务器上想 ssh 到另一台服务器上,并且做下端口映射,但是发现报了个错,

1
bind: Cannot assign requested address

查了下这个问题,猜测是不是端口已经被占用了,查了下并不是,然后想到是不是端口是系统保留的,

1
sysctl -a |grep port_range

结果中

1
net.ipv4.ip_local_port_range = 50000    65000      -----意味着50000~65000端口可用

发现也不是,没有限制,最后才查到这个原因是默认如果有 ipv6 的话会使用 ipv6 的地址做映射
所以如果是命令连接做端口转发的话,

1
ssh -4 -L 11234:localhost:1234 x.x.x.x

使用-4来制定通过 ipv4 地址来做映射
如果是在 .ssh/config 中配置的话可以直接指定所有的连接都走 ipv4

1
2
Host *
AddressFamily inet

inet代表 ipv4,inet6代表 ipv6
AddressFamily 的所有取值范围是:”any”(默认)、”inet”(仅IPv4)、”inet6”(仅IPv6)。
另外此类问题还可以通过 ssh -v 来打印更具体的信息

上次就比较简单的讲了使用,这块也比较简单,因为封装得不是很复杂,首先我们从 select 作为入口来看看,这个具体的实现,

1
2
3
4
5
String selectSql = new SQL() {{
SELECT("id", "name");
FROM("student");
WHERE("id = #{id}");
}}.toString();

SELECT 方法的实现,

1
2
3
4
5
public T SELECT(String... columns) {
sql().statementType = SQLStatement.StatementType.SELECT;
sql().select.addAll(Arrays.asList(columns));
return getSelf();
}

statementType是个枚举

1
2
3
public enum StatementType {
DELETE, INSERT, SELECT, UPDATE
}

那这个就是个 select 语句,然后会把参数转成 list 添加到 select 变量里,
然后是 from 语句,这个大概也能猜到就是设置下表名,

1
2
3
4
public T FROM(String table) {
sql().tables.add(table);
return getSelf();
}

往 tables 里添加了 table,这个 tables 是什么呢
这里也可以看下所有的变量,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
StatementType statementType;
List<String> sets = new ArrayList<>();
List<String> select = new ArrayList<>();
List<String> tables = new ArrayList<>();
List<String> join = new ArrayList<>();
List<String> innerJoin = new ArrayList<>();
List<String> outerJoin = new ArrayList<>();
List<String> leftOuterJoin = new ArrayList<>();
List<String> rightOuterJoin = new ArrayList<>();
List<String> where = new ArrayList<>();
List<String> having = new ArrayList<>();
List<String> groupBy = new ArrayList<>();
List<String> orderBy = new ArrayList<>();
List<String> lastList = new ArrayList<>();
List<String> columns = new ArrayList<>();
List<List<String>> valuesList = new ArrayList<>();

可以看到是一堆 List 先暂存这些sql 片段,然后再拼装成 sql 语句,
因为它重写了 toString 方法

1
2
3
4
5
6
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sql().sql(sb);
return sb.toString();
}

调用的 sql 方法是

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
29
30
31
public String sql(Appendable a) {
SafeAppendable builder = new SafeAppendable(a);
if (statementType == null) {
return null;
}

String answer;

switch (statementType) {
case DELETE:
answer = deleteSQL(builder);
break;

case INSERT:
answer = insertSQL(builder);
break;

case SELECT:
answer = selectSQL(builder);
break;

case UPDATE:
answer = updateSQL(builder);
break;

default:
answer = null;
}

return answer;
}

根据上面的 statementType判断是个什么 sql,我们这个是 selectSQL 就走的 SELECT 这个分支

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private String selectSQL(SafeAppendable builder) {
if (distinct) {
sqlClause(builder, "SELECT DISTINCT", select, "", "", ", ");
} else {
sqlClause(builder, "SELECT", select, "", "", ", ");
}

sqlClause(builder, "FROM", tables, "", "", ", ");
joins(builder);
sqlClause(builder, "WHERE", where, "(", ")", " AND ");
sqlClause(builder, "GROUP BY", groupBy, "", "", ", ");
sqlClause(builder, "HAVING", having, "(", ")", " AND ");
sqlClause(builder, "ORDER BY", orderBy, "", "", ", ");
limitingRowsStrategy.appendClause(builder, offset, limit);
return builder.toString();
}

上面的可以看出来就是按我们常规的 sql 理解顺序来处理
就是select ... from ... where ...这样子
再看下 sqlClause 的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void sqlClause(SafeAppendable builder, String keyword, List<String> parts, String open, String close,
String conjunction) {
if (!parts.isEmpty()) {
if (!builder.isEmpty()) {
builder.append("\n");
}
builder.append(keyword);
builder.append(" ");
builder.append(open);
String last = "________";
for (int i = 0, n = parts.size(); i < n; i++) {
String part = parts.get(i);
if (i > 0 && !part.equals(AND) && !part.equals(OR) && !last.equals(AND) && !last.equals(OR)) {
builder.append(conjunction);
}
builder.append(part);
last = part;
}
builder.append(close);
}
}

这里的拼接方式还需要判断 AND 和 OR 的判断逻辑,其他就没什么特别的了,只是where 语句中的 lastList 不知道是干嘛的,好像只有添加跟赋值的操作,有知道的大神也可以评论指导下

mybatis 还有个比较有趣的功能,就是使用 SQL 类生成 sql,有点类似于 hibernate 或者像 php 的 laravel 框架等的,就是把sql 这种放在 xml 里或者代码里直接写 sql 用对象的形式

select语句

比如这样

1
2
3
4
5
6
7
8
public static void main(String[] args) {
String selectSql = new SQL() {{
SELECT("id", "name");
FROM("student");
WHERE("id = #{id}");
}}.toString();
System.out.println(selectSql);
}

打印出来就是

1
2
3
SELECT id, name
FROM student
WHERE (id = #{id})

应付简单的 sql 查询基本都可以这么解决,如果习惯这种模式,还是不错的,
其实以面向对象的编程模式来说,这样是比较符合面向对象的,先不深入的解析这块的源码,先从使用角度讲一下

比如 update 语句

1
2
3
4
5
String updateSql = new SQL() {{
UPDATE("student");
SET("name = #{name}");
WHERE("id = #{id}");
}}.toString();

打印输出就是

1
2
3
UPDATE student
SET name = #{name}
WHERE (id = #{id})

insert 语句

1
2
3
4
5
6
String insertSql = new SQL() {{
INSERT_INTO("student");
VALUES("name", "#{name}");
VALUES("age", "#{age}");
}}.toString();
System.out.println(insertSql);

打印输出

1
2
3
INSERT INTO student
(name, age)
VALUES (#{name}, #{age})

delete语句

1
2
3
4
5
String deleteSql = new SQL() {{
DELETE_FROM("student");
WHERE("id = #{id}");
}}.toString();
System.out.println(deleteSql);

打印输出

1
2
DELETE FROM student
WHERE (id = #{id})
0%