在PostgreSQL的基础上创建一个MongoDB的副本的教程
我有一个偷懒的想法。这个好点子该如何开始呢?好吧,这是一个恰如其分的小疯狂:为什么不直接在Postgres的基础上建立我们自己的MongoDB版本呢?这听起来有点牵强附会,但却简单而实在。 当NoSQL运动风生水起的时候,Postgres社区没有干坐着摆弄他们的大拇指。他们持续开发,贯穿整个Postgres的生态系统,几个突出的功能吸引了我的眼球:整合JSON支持和PLV8。PLV8把V8 Javascript引擎引入到Postgres,他让Javascript成为一个第一类别的语言(first-class language)。拥有JSON类型让它能更容易地处理JSON(这很有效)。 开始前需要做的准备:
MongoDB的最低级别是集合. 集合可以用表来表示: CREATE TABLE some_collection ( some_collection_id SERIAL NOT NULL PRIMARY KEY,data JSON ); 字符型的JSON 被保存在 Postgres 表里,简单易行 (现在看是这样). 下面实现自动创建集合. 保存在集合表里: CREATE TABLE collection ( collection_id SERIAL NOT NULL PRIMARY KEY,name VARCHAR ); -- make sure the name is unique CREATE UNIQUE INDEX idx_collection_constraint ON collection (name); 一旦表建好了,就可以通过存储过程自动创建集合. 方法就是先建表,然后插入建表序列. CREATE OR REPLACE FUNCTION create_collection(collection varchar) RETURNS boolean AS $$ var plan1 = plv8.prepare('INSERT INTO collection (name) VALUES ($1)',[ 'varchar' ]); var plan2 = plv8.prepare('CREATE TABLE col_' + collection + ' (col_' + collection + '_id INT NOT NULL PRIMARY KEY,data JSON)'); var plan3 = plv8.prepare('CREATE SEQUENCE seq_col_' + collection); var ret; try { plv8.subtransaction(function () { plan1.execute([ collection ]); plan2.execute([ ]); plan3.execute([ ]); ret = true; }); } catch (err) { ret = false; } plan1.free(); plan2.free(); plan3.free(); return ret; $$ LANGUAGE plv8 IMMUTABLE STRICT; 有了存储过程,就方便多了: SELECT create_collection('my_collection');
CREATE OR REPLACE FUNCTION find_in_obj(data json,key varchar) RETURNS VARCHAR AS $$ var obj = JSON.parse(data); var parts = key.split('.'); var part = parts.shift(); while (part && (obj = obj[part]) !== undefined) { part = parts.shift(); } // this will either be the value,or undefined return obj; $$ LANGUAGE plv8 STRICT; 上述功能返回VARCHAR,并不适用所有情形,但对于字符串的比较很有用: SELECT data FROM col_my_collection WHERE find_in_obj(data,'some.element') = 'something cool' 除了字符串的比较,MongoDB还提供了数字类型的比较并提供关键字exists . 下面是find_in_obj() 方法的不同实现: CREATE OR REPLACE FUNCTION find_in_obj_int(data json,key varchar) RETURNS INT AS $$ var obj = JSON.parse(data); var parts = key.split('.'); var part = parts.shift(); while (part && (obj = obj[part]) !== undefined) { part = parts.shift(); } return Number(obj); $$ LANGUAGE plv8 STRICT; CREATE OR REPLACE FUNCTION find_in_obj_exists(data json,key varchar) RETURNS BOOLEAN AS $$ var obj = JSON.parse(data); var parts = key.split('.'); var part = parts.shift(); while (part && (obj = obj[part]) !== undefined) { part = parts.shift(); } return (obj === undefined ? 'f' : 't'); $$ LANGUAGE plv8 STRICT; 接下来是数据查询. 通过现有的材料来实现 find() 方法. CREATE OR REPLACE FUNCTION save(collection varchar,data json) RETURNS BOOLEAN AS $$ var obj = JSON.parse(data); var id = obj._id; // if there is no id,naively assume an insert if (id === undefined) { // get the next value from the sequence for the ID var seq = plv8.prepare("SELECT nextval('seq_col_" + collection + "') AS id"); var rows = seq.execute([ ]); id = rows[0].id; obj._id = id; seq.free(); var insert = plv8.prepare("INSERT INTO col_" + collection + " (col_" + collection + "_id,data) VALUES ($1,$2)",[ 'int','json']); insert.execute([ id,JSON.stringify(obj) ]); insert.free(); } else { var update = plv8.prepare("UPDATE col_" + collection + " SET data = $1 WHERE col_" + collection + "_id = $2",[ 'json','int' ]); update.execute([ data,id ]); } return true; $$ LANGUAGE plv8 IMMUTABLE STRICT; 基于这个观点,我们可以构建一些插入的简单文档: { "name": "Jane Doe","address": { "street": "123 Fake Street","city": "Portland","state": "OR" },"age": 33 } { "name": "Sarah Smith","address": { "street": "456 Real Ave","city": "Seattle","state": "WA" } } { "name": "James Jones","address": { "street": "789 Infinity Way","city": "Oakland","state": "CA" },"age": 23 } 让我们创建一个集合并插入一些数据: work=# SELECT create_collection('data'); create_collection ------------------- t (1 row) work=# SELECT save('data','{ our object }'); save ------ t (1 row) 你可以通过检查“col_data”表的内容来查看对象。 其它翻译版本(1) 现在我们已经有了一些数据,让我们再查询一下。假设我们想查找住在俄勒冈或华盛顿州年龄大于30的所有人,使用一个MongoDB风格的find(): { "$or": [ { "address.state": "OR" },{ "address.state": "WA" } ],"age": { "$gt": 30 } } 因为上次我们已经创建了一些深度的包检测,现在就很容易创建查询并返回Jane Doe: SELECT data FROM col_data WHERE find_in_obj_int(data,'age') > 30 AND ( find_in_obj(data,'address.state') = 'OR' OR find_in_obj(data,'address.state') = 'WA' ) 我采用了写一个递归调用函数来建立WHERE子句的方法。它有点长,所以我没有把它贴在这里而是放在GitHub上。一旦find()存储过程被创建,我们就可以在查询中使用它。我们应该能够看到Jane Doe被返回: work=# SELECT find('data','{ "$or": [ { "address.state": "OR" },{ "address.state": "WA" } ],"age": { "$gt": 30 } }'); 这样奏效:它不优雅,但它奏效。这是一个概念的证明,而且几乎没有像它一样好的可能。我之前曾被问过为什么不使用HSTORE。虽然你可以存储嵌套的HSTORE和数组值,但它仍不是JSON,并且不容易通过PLV8操作。这将需要一个从HSTORE到JSON的序列器,这个序列器在任何时间将请求的返回序列化成MongoDB接受的数据形式,但依旧太容易在JavaScript中处理。这是次优选择,毕竟我们是要在Postgres的基础上创建一个MongoDB的副本。 源码可以在GitHub上找到:fork并尝试一下吧,记得回馈哦。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- 分发服务器 系统抛出18483错误,未能连接服务器,因为'
- SqlServer和Oracle中一些常用的sql语句9 SQL优化
- sql – ‘in’子句如何在oracle中工作
- MSSQLSERVER CAN NOT START AFTER ENABLE & DIABLE VIA
- SQLServer的ISNULL函数和Mysql的IFNULL函数
- SQL Server 数据库分离与附加(图文教程)
- Transact-SQL简写连接语法?
- 分组后分组合计以及总计SQL语句(稍微整理了一下)
- DB2死锁超时Sqlstate:40001,原因代码68是由于使用SQL从ser
- MySQL 5.7双主同步部分表的实现过程详解