Anonymous avatar Anonymous committed 271174f

add paper of China PostgreSQL Users Meeting 2011
add postgresql contrib
remove id column from arch
mark subject id and predicate id as primary key

Comments (0)

Files changed (11)

Binary file added.

+-- 试验 PostgreSQL 的特化实现
+
+-- 原语定义
+-- SUBJECT = 1
+-- PREDICATE = 2
+-- IS = 3
+-- OBJTYPE = 4
+-- TYPE = 5
+-- STRING = 6
+-- NAME = 7
+-- URI = 8
+-- STORAGE = 9
+
+-- write segemnt 的原子实现
+create or replace function write_segment(sbj_id integer, pred_id integer, object anyelement) returns integer as $$
+declare
+  sbj integer; 
+  stg text;
+  has_it integer;
+  objType integer;
+begin
+  if sbj_id is null then
+     sbj := nextval('subject_seq_id_seq');
+  else
+     sbj := sbj_id;
+  end if;
+
+  select obj into objType from segment_subject where predicate_id=4 and subject_id = pred_id;
+  select obj into stg from segment_string where predicate_id=9 and subject_id = objType;
+  execute 'select count(*) from '|| stg || ' where predicate_id= '|| quote_nullable(pred_id) ||' and subject_id='|| quote_nullable(sbj) into has_it ;
+  if has_it > 0 then
+     execute 'delete from '|| stg ||' where predicate_id = ' || quote_nullable(pred_id) || 'and subject_id=' || quote_nullable(sbj);
+  end if;
+  execute 'insert into ' || stg || '(subject_id, predicate_id, obj) values('|| quote_nullable(sbj)||', '|| quote_nullable(pred_id)||', '|| quote_nullable(object)||');';
+  return sbj;
+end;
+$$ language plpgsql;
+
+-- write segment 的易读版本
+create or replace function write_segment(sbj_id integer, predicate text, object anyelement) returns integer as $$
+declare
+  pred_id integer;
+begin
+  select subject_id into pred_id from segment_string where obj = predicate and predicate = 7;
+  -- TODO: 如果得到 NULL,应该 raise 错误。
+  return write_segment(sbj_id, pred_id, object);
+end;
+$$ language plpgsql;
+
+-- get storage name by predicate id
+create or replace function get_storage_by_predicate_id(pred_id integer) returns TEXT as $$
+       select obj from segment_string where subject_id = (select obj from segment_subject where subject_id = $1 and predicate_id = 4) and predicate_id = 9;
+$$ language sql;
+
+-- get storage name by predicate name
+create or replace function get_storage_by_predicate(pred text) returns TEXT as $$
+      select get_storage_by_predicate_id((select subject_id from segment_string where obj=$1 and predicate_id=7));
+$$ language sql;
+
+-- get predicate's ids
+create or replace function get_subject(sbj_id integer) returns setof integer as $$
+declare
+  typeid integer;
+  stgName TEXT;
+begin
+  -- 取得所有注册的 type
+  for typeid in select subject_id from segment_subject where predicate_id = 3 and obj = 5 loop
+      -- 取得该 type 的 storage
+      select obj into stgName from segment_string where subject_id = typeid and predicate_id = 9;
+      return query execute 'select predicate_id from '|| stgName || ' where subject_id=' || quote_nullable(sbj_id);
+  end loop;
+end;
+$$ language plpgsql;
+
+-- get object by subject id and predicate id
+create or replace function get_object(sbj_id integer, pred_id integer) returns RECORD as $$
+declare
+  stg TEXT;
+  re RECORD;
+begin
+  stg := get_storage_by_predicate_id(pred_id);
+  execute 'select obj from '|| stg || ' where subject_id=' || sbj_id || ' and predicate_id = ' || pred_id into re;
+  return re;
+end;
+$$ language plpgsql;
+
+-- get object by subject id and predicate name
+create or replace function get_object(sbj_id integer, predicate text) returns RECORD as $$
+declare
+  stg TEXT;
+  pred_id integer;
+  re RECORD;
+begin
+  stg := get_storage_by_predicate(predicate);
+  select subject_id into pred_id from segment_string as str where predicate_id = 7 and obj = predicate and exists(select * from segment_subject where subject_id=str.subject_id and predicate_id=3 and obj=2);
+  execute 'select obj from '|| stg || ' where subject_id=' || sbj_id || 'and predicate_id = ' || pred_id into re;
+  return re;
+end;
+$$ language plpgsql;

Binary file added.

Add a comment to this file

docs/CPGM2011/SocratesOnPG.pdf

Binary file added.

docs/CPGM2011/SocratesOnPG.tex

+\documentclass[utf8,b6paper]{article}
+\usepackage{hyperref}
+\usepackage{makeidx}
+\usepackage[boldfont,slantfont]{xeCJK}
+\usepackage{fontspec}
+\setmainfont{Times New Roman}
+\setsansfont{Helvetica}
+\setmonofont{Monaco}
+\setCJKmainfont{文泉驿正黑}
+\setCJKsansfont{文泉驿微米黑}
+\setCJKmonofont{WenQuanYi Micro Hei Mono}
+\setCJKfamilyfont{tt}{Monaco}
+
+%中文断行
+\XeTeXlinebreaklocale "zh"
+\XeTeXlinebreakskip = 0pt plus 1pt
+\usepackage{color}
+\definecolor{listinggray}{gray}{0.9} 
+\usepackage{xcolor}
+\usepackage{graphicx}
+
+\usepackage{listings}
+\lstset{language=sql,
+%numbers=left,
+backgroundcolor=\color{listinggray},
+showspaces=false,
+showstringspaces=false,
+basicstyle=\ttfamily, 
+frame=single,
+%framexleftmargin=7mm,
+frameshape={RYN}{y}{y}{RYN}}
+\usepackage{hyperref}
+\usepackage[top=.25in, bottom=0.25in, left=.3in, right=.2in]{geometry}
+
+% 行间距
+%\usepackage{setspace}
+\renewcommand{\baselinestretch}{2}
+
+\hypersetup{pdfauthor={刘鑫<march.liu@gmail.com>}, pdftitle={Socrates 动态数据建模技术}}
+\begin{document}
+\title{Scrates 动态数据建模技术}
+\author{刘鑫(March Liu) <march.liu@gmail.com>}
+\maketitle
+
+\tableofcontents
+
+%\begin{spacing}{2.0}
+
+\section{什么是Socrates}
+
+\subsection{基于关系型数据库的动态建模技术}
+
+2010 年初,我在金山开始从事 PostgreSQL DBA 的工作,当时我们的一些内部
+项目使用 PostgreSQL 作为数据库。在当时,我开始尝试一些我长期以来一直思
+考的数据库设计问题,主要包括:
+
+\begin{itemize}
+\item 稀疏结构的存储,如果统一格式,会存在大量的NULL值。
+\item 业务来讲同类的数据,但是条目的依赖信息不同。
+\item 分布式的数据关联。
+\item 静态的数据结构,调整表字段时,可能时间成本难以接受。
+\item 数据库,特别是已经在线的数据库,不容易调整结构,跟不上应用层代码
+  的重构节奏。
+\end{itemize}
+
+对于动态结构的数据管理,我们已经有了很多方案,典型的如XML或文档式数据
+库。但是通常来讲,这些数据库需要对条目进行整体访问。另一方面,现代的项
+目型开发,关系型数据库在架构中普遍存在,如果可以直接在关系型数据库上实
+现动态的数据组织,架构成本可以更低。
+
+在当时,我首先考虑的是利用KV结构建立一个URL/Value映射机制,这样的架构
+好处很多。它容易映射为RESTful架构,对于层次型的结构很容易表达。但是这意味
+着会有大量使用局部匹配的字符串操作。而大多数的KV系统,只对hash式的匹配
+支持良好。另外,这种表示方式只适用于针对Key的查询,如果针对value建立索
+引,就不够理想。如果可以接受这种结构,推荐使用 PostgreSQL 的函数索引和
+TEXT 类型。
+
+对于KV结构,我当时还遇到两个设计上的困难,一个是,KV过于简单,无法在结
+构上表现数据的组织形式,例如有很多条日志,每一条都包含id、日期、ip、正文;
+如果以id为key,则从结构上只能知道,该条目有id和日志消息两部分。另一方
+面,如果我们以id为key,将来需要调整为以日期为key来组织的话,就非常困难。
+
+层次型数据库,如LDAP,已经是很成熟的技术,但是这类数据库面临的问题,类
+似于 URL/Value 型架构的问题。
+
+\subsection{Socrates 的实现目标}
+
+我希望形成一个数据库建模方法,达到以下目标:
+
+\begin{itemize}
+\item 在存储层面,面向关系型数据库,优先保证PostgreSQL可用,为了测试方
+  便,后来我基本上兼容sqlite。
+\item 在应用层,表达为动态结构,可以任意互相嵌套、引用的字典集合。
+\item 可以透明的读写,隐藏关系型数据库存储,以便在必要的时候使用非关系
+  型存储。
+\item 可以适应未知的结构和数据类型,有足够的通用性。
+\item 可以非常方便的调整结构。
+\item 以每一个条目为信息节点,关注每一个条目间的关系,不考虑共性。
+\item 信息的表达与其所在的层面和平台环境无关,可以依赖一组很小的规则集
+  中立的解读。
+\end{itemize}
+
+经过一段时间的尝试,我做出了一个基于 Python 语言的实现,并且将其用在了
+几个企业内部项目。其代码在 http://bitbucket.org/March/socrates 发布。
+
+在2010年,我深入了 Python 的数据库访问工具 SQLAlchemy 和 PostgreSQL,
+可以说这两项技术是我实现这个想法的技术基础。没有这样强大的技术,我不太
+可能在有限的时间内做出做出成果。
+
+\section{Socrates 的数据库结构和实现方法}
+
+\subsection{三值语义结构}
+
+我将信息的表达划分为三个部分:
+
+\begin{description}
+\item[subject] 主语,用于表达信息的分组,每个subject表示一个条目。
+\item[predicate] 谓语,用于表达原子信息的定位,通过 subject 和
+  predicate ,可以定位一个信息内容。
+\item[object] 宾语,宾语是一个最终的值,实际的信息存储在这里。宾语可能
+  是一个数值,一段文本,也可能是指向另一个subject的链接。
+\end{description}
+
+每一个三段表达式,表达一个信息内容在某个条目(即subject)中的属性(即谓词
+predicate)关系。一个subject,可以方便的表达为一个字典。
+
+\subsection{元语表达式}
+
+Socrates 的实现基础,是一组与平台和编程语言无关的元语义,通过该语义,
+可以描述任意的信息。由于每个原子信息都可以使用\{主语,谓语,宾语\}的三
+段结构描述,特别是每一个subject和一个predicate可以唯一确定一个object,
+这样的结构使我联想到著名的柏拉图三段论,所以使用三段论著名的例子"苏格
+拉底三段论'',将这个项目命名为 Socrates(即苏格拉底)。
+
+Socrates 架构初始化的时候,需要建立一组元语表达式,通过这组表达式,应
+该可以使任何信息都可以以表达式的形式描述。
+
+\subsubsection{基础元语}
+
+目前版本设计的基础元语有以下几个:
+
+\begin{itemize}
+\item subject is type
+\item type is subject
+\item predicate is subject
+\item is is predicate
+\item is objType subject
+\item objType is predicate
+\item storage is predicate
+\item storage objType string
+\item name is predicate
+\item name objType string
+\item string is type
+\end{itemize}
+
+\subsubsection{元语释义}
+
+这里解释一下元语表达式的含义:
+
+\begin{itemize}
+\item subject 是(is)一种类型(type)。
+\item 每种 type 标注自己的storage,即对应的存储类型。
+\item 每种 predicate 是一个 subject。
+\item 每种 predicate 有 objType。
+\item is 是 谓词。
+\item is 的 objType 是 subject(即它将指向一个subject id 或 subject link)。
+\item name 是谓词,objType 为 string。
+\item string 是一个 type,是基础数据类型。对于 PostgreSQL ,对应的是 TEXT。
+\end{itemize}
+
+\subsubsection{退化的隐含元语}
+
+最初的 Socrates 还包括若干这里没有列出的元语,后来它们退化为模型实现代
+码的一部分,没有在这里列出,其中包括:
+
+[has] ,表示一个 subject 必须要包含的 predicate 数组。实践中这个属性并非
+必须,特别是对于元语表达式,虽然多数元语都隐含了若干必要的谓词,但是都
+是通过编程行为体现的。has 谓词的文档意义高于runtime期表现。
+
+[null], null is type,它表示空值。在存储时,object 为 null 的predicate
+其实不需要保存。所以这个 subject 也没有出现在初始元语列表中。
+
+[id],id其实是每一个subject一定会有的属性。但是它应该对调用者透明。事实上
+在当前的原型中,我确实发现,可以不在调用代码中显式的使用这个属性。所以
+最终它没有出现在元语列表中。
+
+不显式注明 id,还有一个原因是id可能会出现多种数据类型,它首先是一个为
+存储和引用机制,而非为信息表达设计的谓语。因此,我暂时考虑使 id 成为一
+个透明的机制。在后面的章节,我们会讨论为 id 谓词不同的实现方式。
+
+\subsection{架构}
+
+Socrates 使用关系型数据库作为存储,在应用层组件中组织逻辑和数据。调用
+方通过应用层组件的API接口使用其中的功能。
+
+\subsection{操作}
+
+Socrates 的最基本操作有以下几种:
+
+\begin{description}
+\item[取值] (read subject predicate) 给定 subject 和 predicate,求
+  object 值,这个操作的返回值类型不确定,由于 Socrates 允许 object 引
+  用 subject,这里应该是惰性求值的。
+\item[写入表达式] (write subject predicate object) 给定 subject、
+  predicate、object,将这个表达式写入存储。如果 subject 为空,创建一个
+  新的 subject,如 object 为空,则尝试在存储中寻找对应用的表达式,将其
+  删除。如果表达式存在,刚将其宾语修改为新的 object。
+\item[批量写入] (write '((subject predicate object)...)) 当给定一个 表
+  达式列表, write 操作批量写入所有的表达式,如果有若干表达式属于同一
+  个 subject,它们自动分组,如果存在若干 subject 为 null 的。
+\item[查询 subject] (query expr) 给定一个条件表达式 ,从存储中取得一个
+  subject 或一个 subjects 集合。这部分仍没有非常完善。但是基于
+  sqlalchemy 的 Python 版可以尽可能使用 sqlalchemy 的功能。
+\end{description}
+
+\subsubsection{数据库初始化}
+
+数据库初始化时,在指定的 URI (这里遵循 SQLAlchemy 的 url 表达法) 位置
+建立数据库结构,并写入基本元语。在目前的实现中,将在 PostgreSQL 数据库
+中建立:
+
+\begin{itemize}
+\item 建立一系列的 segment 表,每个 segment 表保存一种 object 类型的语
+  句(segment)。
+\item 写入最基本的元语,创建 socrates 语义结构。
+\end{itemize}
+
+初始化时,首先建立 segment\_subject 和 segment\_string 表,
+segment\_subject 表的结构是 
+
+    \verb|(subject_id integer, predicate_id integer, object integer)|
+
+其中 Object 表示某个 subject 的 id 。
+
+\subsubsection{写入操作}
+
+Socrates 数据分为二级结构,每一个完整的条目称为一个 subject,subject
+中每一条细节描述称为一个 segment 。一个 segment 即为一个(subject,
+predicate, object) 三元组。写操作记为 segment 的写入。下面分两种情况讨
+论写入操作:
+
+单行写入时,如果 segment 的 subject 为空,则建立一个新的 subject,否则
+写入已有的 subject 。
+
+多行写入时,每一条 segment 按其 subject 字段写入对应的条目。但是如果同
+一批中有若干 segment 没有 subject 信息(即  subject 为 NULL),则将这些
+segment 写入到同一个新增的 subject。
+
+如果写入一条 (subject, predicate, NULL),则视作删除此 segment。
+
+\subsubsection{谓词约束}
+
+每一个谓词(predicate)是一个 subject ,它至少要包含 name 和 objType 两
+个谓词,前一个是 string 类型,表示该谓词的名字,后一个是 Type 类型,表
+示该谓词对应的 object (宾语)的类型。
+
+Type 也是一个 subject,它至少有两个谓词,一个是 name ,表示类型名。另
+一个是 storage,表示存储位置,通常来讲,它描述了这个数据存储到哪个数据
+表(当前是 "segment\_"+类型组成表名)。这个属性由应用层逻辑读取,通常
+不需要用户直接访问。
+
+\subsubsection{读取与查询}
+
+理论上讲,SQL 的查询结构仍可以适用于 Socrates 结构:给出指定 predicate
+匹配的 object 比较表达式,过滤出匹配的subject。
+
+在实践中,因为业余时间有限,我还没有来得及实现其DSL,仅仅是做了初步的
+构思。实践应用大多使用这样的逻辑:
+
+\begin{itemize}
+\item 取得 predicate 的 id(谓词直接缓存到engine中)和storage name
+\item 将 predicate 与 object 的关系式转为 predicate id 与 object 的关
+  系式,在storage中查询
+\item 根据取得到的 subject id 的集合,从engine中查询所有的storage,得到匹配的
+  完整的subject集合
+\end{itemize}
+
+当然,使用DSL的话,只是将这个过程封装在解释器背后。目前则是仰赖 Python
+的 SQLAlchemy 工具来简化这个查询过程。
+
+\section{Python 实现}
+
+我在 2010 年的时候,用 Python 语言,基于 SQLAlchemy 工具,实现了一个
+Python 版的 Socrates 工具。这个工具小规模的用于金山软件的过程改进中心
+部分企业内部项目中。
+
+在这些应用中,主要使用 Socrates 实现用户行为日志,以及与传统的 RDB 设
+计方法混用,实现一些结构不定的业务模型。
+
+如我当时搭建了一个基于 PostgreSQL+TSearch2+nlpbamboo的搜索引擎。在其中,
+我先将爬虫抓到的数据写入到一个socrates数据集中,再将其组织为全文索引和
+XML文档。
+
+如我所预料, Socrates 系统在插入时性能比较满意,但是读取时速度太慢。另
+外由于没有DSL,直接使用Socrates数据集构造报表比较烦琐。
+
+除了一些小规模的实验项目,我还将这个技术用于一些一次性的工具脚本,临时
+需要将一些数据写入数据库,又不想仔细设计其数据结构时,这个工具非常的方
+便省事。
+
+\section{Objective C 实现}
+
+在实现了一个初步可用的 Python 版本手,我开始着手设计了一些其它语言的实
+现,主要是 Objective C,这是因为 C 系底层语言中,它可以与 C 二进制兼容,
+有足够高的效率,又有非常好的语言抽象能力。
+
+在 Socrates 的项目站中,我上传了一个未完成的 Eucild 子项目。这原本是想
+要开发一个纯内存的 Socrates 数据结构。它最初是为了金山毒霸的云查杀项目
+设计。但是这个子项目只开了一个头,没有完成。它不使用关系数据库,而是用
+一个线性容器和若干字典组合成一个三值语义容器。
+
+目前,我在 PCOnline 的工作中,正在尝试在 iOS 的受限环境下,实现一个特
+化(主要是为了优化查询性能)的,基于 SQLite 的 Socrates。SQLite 虽然是一
+个非常小的嵌入式数据库引擎,但是它拥有一些出色的特性,甚至有些特性在主
+流的数据库中,只有它和 PostgreSQL 可以做到,如 TEXT 类型。
+
+\section{缺陷,待实现的内容与一些构想}
+
+\subsection{缺陷}
+
+如前所述,Socrates 最大的缺点在于查询速度,这主要是因为需要应用层组件
+与数据库服务器进行多次查询造成的,先查得所有匹配的segment,再根据其中
+的 subject id 查询完整的subject。而且,subject中包含的segment保存于哪
+些 storage,是无法预知的,只能将所有的 storage 表都查询一遍。另外,
+object 字段需要混合保存各条目的值,不容易建立索引。
+
+有几种方式可以在一定程度上缓解性能问题:
+
+\begin{itemize}
+\item 利用 PostgreSQL 的函数索引能力,根据业务建立object字段的索引。
+\item 对于固定的查询,特别是报表类查询,可以尽量用直接的SQL去简化。
+\item 根据业务,寻找合理的缓存策略
+\item 适当的做退化,例如数据模型末端的 subject,可以退化为JSON文本或者
+  PostgreSQL 的 hstore类型。
+\item 如果可以减少object使用到的类型,就可以尽量减少查询次数。参考
+  SQLite 规范,本质上 SQLite 只提供了四种基本数据类型。而我们在特定场
+  合,可以构造只包含一到两种数据类型的 socrates 系统。如果只包含
+  string 类型的 object,那么理论上讲大部分查询可以简化为一次 select。
+  这对于性能提升有时会有帮助。
+\item 可以将socrates与普通的关系型数据库设计混合使用,扬长避短。
+\item 有些数据内容可以写为 XML 文本,作为一个 object 存储和访问。
+  PostgreSQL 可以安装 libxml2支持,能够很好的进行基于 xpath 的操作。
+\end{itemize}
+
+每个 segment 都要有一个 subject id和一个predicate id,所以 Socrates 用
+来保存小数据类型并不经济。它更适合大多数subject都包含一定量的大数据(如
+TEXT 类型的 object)的应用。
+
+\subsection{待实现的功能}
+
+DSL 大概不会是最先填补的空缺,但是它绝对应该是最值得重视的一个。有一个
+通用的 DSL,不但有利于实用化,也可以更容易的发现问题和需求。
+
+另一个需要解决的是性能问题,不解决性能问题,这个设计永远是一个理论模型,
+无法实用化。
+
+最初的设计中,我考虑兼容四种数据库,但是接下来,我希望基于两种数据库:
+容易构造测试的 SQLite 和功能完备的 PostgreSQL ,构造功能更强大,也更为
+特化的实现。特别是值得尝试做一个极端特化的版本,充分利用 PostgreSQL 的
+服务端编程能力,尽可能挖掘 Socrates 模型的潜力。
+
+Socrates 是面向字典型数据条目设计的,它不便于表达线性序列。如何构造一
+个高效的线性序列表达,还在思考中。目前对于规模不大的线性序列,可以使用
+JSON 文本或 PostgreSQL 的数组类型。
+
+\subsection{一些构想}
+
+如果限定一个有限的元类型集,我们可以通过 mod 运算,将映射为由多个表组
+成的 KV 系统,每个表的主键都是大整数,由一个单一的sequence生成,值是不
+同类型的数据。通过predicate对应的object id,可以知道这个object保存在哪
+个表里。那么,我们就可以把 Socrates的object变成动态类型。
+
+这样的结构可以很好的表达类似 Python 或 Ruby 这类动态语言的数据结构,更
+进一步,它可以用来完整的表达 LISP 语义(LISP的七个元语都可以拆解为
+socrates 的 subject)。那么,我可以实现一个将 LISP image 直接保存在
+socrates 数据库中的机制。这样,就将传统上保存于文件中的 LISP 虚拟机映
+像,变成了一个可以直接在做任意层面解读的数据库信息。这个模型的运用,可
+以带来开发模式的根本改变。
+
+如果可以将 PostgreSQL 的服务器端编程语法做一定的扩展,使其有一定的动态
+化,就可以解决不同数据类型的storage需要单独查询的问题。可以大大提高
+Socrates 的可用性。
+
+%\end{spacing}
+\end{document}

Binary file added.

+#coding:utf8
+/Users/march/Documents/myself/socrates/src

Binary file added.

src/socrates/core.py

                 raise 'must give the object storage type if you want to create storage'
 
             table = Table(storname, self.metadata, 
-                          Column('id', Integer, index=True, primary_key=True),
-                          Column('subject_id', Integer, index=True, default=self.sequence),
-                          Column('predicate_id', Integer, index=True, ),
+                          Column('subject_id', Integer, index=True, default=self.sequence, primary_key=True),
+                          Column('predicate_id', Integer, index=True, primary_key=True),
                           Column('obj', colType),
                           schema=schname)
             table.create()
 >>> retype = storage.registeType("datetime", colType=DateTime, create=True)
 >>> pid = storage.newPredicate('ssoid', 'string')
 >>> segment = storage.writeSegment(500, 'ssoid', 'liuxin2')
->>> print segment.id
-15
+>>> print segment.subject_id, segment.predicate_id
+500 11
 >>> print segment.subject_id
 500
 >>> segment = storage.writeSegment(None, 'ssoid', 'nobody')
 
     def initStorage(self, schema=None):
         schname = schema if schema else self.schema
-        self.segment_id = Column('id', Integer, primary_key=True)
-        self.subject_id = Column('subject_id', Integer, default=self.sequence,)
-        self.predicate_id = Column('predicate_id', Integer, )
+        self.subject_id = Column('subject_id', Integer, default=self.sequence, primary_key=True)
+        self.predicate_id = Column('predicate_id', Integer, primary_key=True)
         self.obj = Column('obj', Integer, )
         table = Table('segment_subject', self.metadata, 
-                      self.segment_id, self.subject_id, 
-                      self.predicate_id, self.obj,
+                      self.subject_id, self.predicate_id, self.obj,
                       schema=schname)
         table.create()
         self.registed_storage["subject"]=table
         self.registed_type["subject"] = self.__make_type_helper("subject", table)
-        if self.engine.url.drivername in ['postgres']:
-            from sqlalchemy.databases.postgres import PGText
-            string = Column('obj', PGText,)
+        if self.engine.url.drivername in ['postgres', 'postgresql']:
+            from sqlalchemy.dialects.postgresql import TEXT
+            string = Column('obj', TEXT,)
         elif self.engine.url.drivername in ['sqlite']:
-            from sqlalchemy.databases.sqlite import SLText
-            string = Column('obj', SLText,)
+            from sqlalchemy.dialects.sqlite import TEXT
+            string = Column('obj', TEXT,)
         else:
             string = Column('obj', String(8000))
 
         table = Table('segment_string', self.metadata,
-                      Column('id', Integer, primary_key=True),
-                      Column('subject_id', Integer, default=self.sequence),
-                      Column('predicate_id', Integer, ),
+                      Column('subject_id', Integer, default=self.sequence, primary_key=True),
+                      Column('predicate_id', Integer, primary_key=True),
                       string,
                       schema=schname)
+        
         table.create()
         self.registed_type["string"] = self.__make_type_helper("string", table)
         self.registed_storage["string"]=table
 class Serial(object):
     def __init__(self, engine, metadata, create=False, name='subject_seq', schema=None):
         self.engine = engine
-        if self.engine.url.drivername in ['postgres', 'oracle', 'firebird']:
+        if self.engine.url.drivername in ['postgres', 'postgresql', 'oracle', 'firebird']:
             self.sequence = Sequence(name, schema=schema)
             if create:
                 self.sequence.create(bind=self.engine)
         self.last_in_current_scope=None
 
     def __call__(self):
-        if self.engine.url.drivername in ['postgres', 'oracle', 'firebird']:
+        if self.engine.url.drivername in ['postgres', 'oracle', 'firebird', 'postgresql']:
             self.last_in_current_scope = self.sequence.execute(bind=self.engine)
         else:
             res = self.engine.execute(self.seq_table.insert())

src/socrates/meta.py

  - is 定义主语,此子句的 id 即 subject id
  - 容器可以为元素指定统一的 type 约束,也可以是无类型的
  - 容器指定元素类型的时候,元素可以只有值,没有类型
- - elementType 
+ - objType 
  - type 定义类型 subject
  - objType 定义谓词的客体类型
  - objType 是谓词的隐式定义内容
  - name objType string
  - storage is predicate
  - storage objType string
-
+ - uri is predicate
+ - uri objType string
 ** 常量定义
 
 """
 TYPE = 5
 STRING = 6
 NAME = 7
-SET = 8
+URI = 8
 STORAGE = 9
 
 def init_meta(engine, subjects, strings, sequence, clearfirst=False):
             subject_id=subject, predicate_id=predicate, obj=obj))
 
     update = lambda _id, subject, predicate, obj : session.execute(subjects.update().values(
-            subject_id=subject, predicate_id=predicate, obj=obj).where(subjects.c.id==_id))
+            subject_id=subject, predicate_id=predicate, obj=obj).where(subjects.c.subject_id==_id))
 
     name = lambda subject, name: session.execute(strings.insert().values( 
             subject_id=subject, predicate_id=NAME, obj=name))
         session.execute(subjects.delete())
 
     for i in range(9):
-        session.execute(subjects.insert().values(subject_id=sequence()))
+        session.execute(subjects.insert().values(subject_id=sequence(), predicate_id=1))
     session.flush()
 
     update(SUBJECT, SUBJECT, IS, TYPE)
     name(NAME, 'name')
     insert(NAME, OBJTYPE, STRING)
 
-    update(SET, SET, IS, PREDICATE)
-    name(SET, 'set')
-    insert(SET, OBJTYPE, SUBJECT)
+    update(URI, URI, IS, PREDICATE)
+    name(URI, 'uri')
+    insert(URI, OBJTYPE, STRING)
 
     update(STORAGE, STORAGE, IS, PREDICATE)
     name(STORAGE, 'storage')
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.