抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

上一次说到,使用三元组可以使数据自身描述模式关系,下面就继续挖掘一下它的潜能。

上篇文章:初探语义Web - 孙泽辉 (foggy.shop)

构建

下面我将介绍如何构建一个简单的三元组存储。

参考:Programming-the-Semantic-Web/chapter2/simplegraph.py · master · examples / Programming the Semantic Web · GitLab (oreilly.com)

索引

首先创建一个类,这个类代表整个数据结构

class simpleGraph {
  private _spo: spoGraph;
  private _pos: posGraph;
  private _osp: ospGraph;

  constructor() {
    this._spo = {};
    this._pos = {};
    this._osp = {};
  }
}

类中每个索引存放了三元组的不同排列

type sub = string | Object;
type pred = string | Object;
type obj = string | Object;

// 主谓宾 { 我: { 吃: Set<饭> } }
interface spoGraph {
  sub?: {
    prod?: Set<obj>;
  };
}
// 谓宾主 { 吃: { 饭: Set<我> } }
interface posGraph {
  pred?: {
    obj?: Set<sub>;
  };
}
// 宾主谓 { 饭: { 我: Set<吃> } }
interface ospGraph {
  obj?: {
    sub?: Set<pred>;
  };
}

通过不同排列,每个三元组都出现在对应的索引中

向索引中添加元组

class simpleGraph{
  add([sub, pred, obj]) {
    this._addToIndex(this._spo, sub, pred, obj);
    this._addToIndex(this._pos, pred, obj, sub);
    this._addToIndex(this._osp, obj, sub, pred);
  }

  private _addToIndex(index, a, b, c): void {
    if (!(a in index)) {
      index[a] = { [b]: new Set([c]) };
    } else {
      if (!(b in index[a])) {
        index[a][b] = new Set([c]);
      } else {
        index[a][b].add(c);
      }
    }
  }
}

对着三种不同的排列插入元组,见缝插针

实现查询

基本的查询方法需要提供某个(主语,谓语,宾语)的模式,并返回与该模式匹配的所有三元组,三元组中设置为 null 的项被视为通配符。

class simpleGraph {
  *triples([sub = null, pred = null, obj = null]) {
    try {
      if (sub != null) {
        if (pred != null) {
          if (obj != null) {
            // sub pred obj
            if (this._spo[sub][pred].has(obj)) yield [sub, pred, obj];
          } else {
            //   sub pred null
            for (const retObj of this._spo[sub][pred]) {
              yield [sub, pred, retObj];
            }
          }
        } else {
          if (obj != null) {
            // sub null obj
            for (const retPred of this._osp[obj][sub])
              yield [sub, retPred, obj];
          } else {
            //   sub null null
            for (const [key, value] of Object.entries(this._spo[sub]) as [
              string,
              any
            ]) {
              const [retPred, objSet] = [key, value];
              for (const retObj of objSet) yield [sub, retPred, retObj];
            }
          }
        }
      } else {
        if (pred !== null) {
          if (obj !== null) {
            // null pred obj
            for (const retSub of this._pos[pred][obj]) {
              yield [retSub, pred, obj];
            }
          } else {
            // null pred null
            for (const [key, value] of Object.entries(this._pos[pred]) as [
              string,
              any
            ]) {
              const [retObj, subSet] = [key, value];
              for (const retSub of subSet) yield [retSub, pred, retObj];
            }
          }
        } else {
          if (obj !== null) {
            // null null obj
            for (const [key, value] of Object.entries(this._osp[obj]) as [
              string,
              any
            ]) {
              const [retSub, predSet] = [key, value];
              for (const retPred of predSet) yield [retSub, retPred, obj];
            }
          } else {
            // null null null
            for (const [key, value] of Object.entries(this._osp) as [
              string,
              any
            ]) {
              const [retSub, predSet] = [key, value];
              for (const [key, value] of Object.entries(predSet) as [
                string,
                any
              ]) {
                const [retPred, objSet] = [key, value];
                for (const retObj of objSet) yield [retSub, retPred, retObj];
              }
            }
          }
        }
      }
    } catch (e) {
      // maybe no-exist
    }
  }
}

为了更方便的拿到单个结果,实现 value 方法

class simpleGraph{
  value([sub = null, pred = null, obj = null]): string {
    const result = [...this.triples([sub, pred, obj])].flat();
    if (sub === null) {
      return result[0];
    } else if (pred === null) {
      return result[1];
    } else {
      return result[2];
    }
  }
}

最佳实践

// 导入写好的 simpleGrapth 类
import simpleGraph from "./tools/simplegraph";
let graph: simpleGraph = new simpleGraph();
// 添加知识
graph.add(["sunzehui", "like", "coding"]);
graph.add(["wangdefa", "like", "eat"]);

// 查询 sunzehui like 什么?
const result = graph.value(["sunzehui", "like", null]);
console.log(result);	// coding

// 查询 sunzehui 和 coding 的关系?
const result = graph.value(["sunzehui", null, "coding"]);
console.log(result);	// coding

// 查询 谁 like coding?
const result = graph.value([null, "like", "coding"]);
console.log(result);	// sunzehui


// 查询 所有知识
const result = [...graph.triples([null, null, null])];
console.log(result);	
/* 
[ 
	[ 'sunzehui', 'like', 'coding' ],
	[ 'wangdefa', 'like', 'eat' ] 
]

*/

现在知识库都是在内存里的,不方便持久化存储,下篇文章继续完善一下。

评论