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, 后面有用

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

cd hexo-site
npm install hexo-algolia

然后在 hexo 站点配置中添加

algolia:
  applicationID: "Application ID"
  apiKey: "Search-only API key"
  indexName: "indexName"

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

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

# Algolia Search
algolia_search:
  enable: true
  hits:
    per_page: 10

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

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

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

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是服务器用来证明自己身份的一个永久性的非对称密钥

使用

ssh-keygen -R github.com

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

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

bind: Cannot assign requested address

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

sysctl -a |grep port_range

结果中

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

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

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

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

Host *
    AddressFamily inet

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

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

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

SELECT 方法的实现,

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

statementType是个枚举

public enum StatementType {
  DELETE, INSERT, SELECT, UPDATE
}

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

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

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

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 方法

@Override
public String toString() {
  StringBuilder sb = new StringBuilder();
  sql().sql(sb);
  return sb.toString();
}

调用的 sql 方法是

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 这个分支

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 的代码

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语句

比如这样

public static void main(String[] args) {
    String selectSql = new SQL() {{
        SELECT("id", "name");
        FROM("student");
        WHERE("id = #{id}");
    }}.toString();
    System.out.println(selectSql);
}

打印出来就是

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

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

比如 update 语句

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

打印输出就是

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

insert 语句

String insertSql = new SQL() {{
            INSERT_INTO("student");
            VALUES("name", "#{name}");
            VALUES("age", "#{age}");
        }}.toString();
        System.out.println(insertSql);

打印输出

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

delete语句

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

打印输出

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