首页 > 数据库 > PostgreSQL > 正文

在PostgreSQL的基础上创建一个MongoDB的副本的教程

2020-03-12 23:53:47
字体:
来源:转载
供稿:网友

这篇文章主要介绍了在PostgreSQL的基础上创建一个MongoDB的副本的教程,使在使用NoSQL的同时又能用到PostgreSQL中的东西,需要的朋友可以参考下

我有一个偷懒的想法。这个好点子该如何开始呢?好吧,这是一个恰如其分的小疯狂:为什么不直接在Postgres的基础上建立我们自己的MongoDB版本呢?这听起来有点牵强附会,但却简单而实在。

当NoSQL运动风生水起的时候,Postgres社区没有干坐着摆弄他们的大拇指。他们持续开发,贯穿整个Postgres的生态系统,几个突出的功能吸引了我的眼球:整合JSON支持和PLV8。PLV8把V8 Javascript引擎引入到Postgres,他让Javascript成为一个第一类别的语言(first-class language)。拥有JSON类型让它能更容易地处理JSON(这很有效)。

开始前需要做的准备:

Postgres 9.2+ (as of this blog entry, 9.2 is in beta) - http://www.postgresql.org/ftp/source/

V8 - https://github.com/v8/v8

PLV8 - http://code.google.com/p/plv8js/wiki/PLV8

MongoDB的最低级别是集合. 集合可以用表来表示:

 

 
  1. CREATE TABLE some_collection ( 
  2. some_collection_id SERIAL NOT NULL PRIMARY KEY
  3. data JSON 
  4. ); 

字符型的JSON 被保存在 Postgres 表里,简单易行 (现在看是这样).

下面实现自动创建集合. 保存在集合表里:

 

 
  1. CREATE TABLE collection ( 
  2. collection_id SERIAL NOT NULL PRIMARY KEY
  3. name VARCHAR 
  4. ); 
  5.  
  6. -- make sure the name is unique 
  7. CREATE UNIQUE INDEX idx_collection_constraint ON collection (name); 

一旦表建好了,就可以通过存储过程自动创建集合. 方法就是先建表,然后插入建表序列.

 

 
  1. CREATE OR REPLACE FUNCTION create_collection(collection varcharRETURNS 
  2. boolean AS $ 
  3. var plan1 = plv8.prepare('INSERT INTO collection (name) VALUES ($1)', [ 'varchar' ]); 
  4. var plan2 = plv8.prepare('CREATE TABLE col_' + collection + 
  5. ' (col_' + collection + '_id INT NOT NULL PRIMARY KEY, data JSON)'); 
  6. var plan3 = plv8.prepare('CREATE SEQUENCE seq_col_' + collection); 
  7.  
  8. var ret; 
  9.  
  10. try { 
  11. plv8.subtransaction(function () { 
  12. plan1.execute([ collection ]); 
  13. plan2.execute([ ]); 
  14. plan3.execute([ ]); 
  15.  
  16. ret = true
  17. }); 
  18. } catch (err) { 
  19. ret = false
  20.  
  21. plan1.free(); 
  22. plan2.free(); 
  23. plan3.free(); 
  24.  
  25. return ret; 
  26. $ LANGUAGE plv8 IMMUTABLE STRICT; 

有了存储过程,就方便多了:

 

 
  1. SELECT create_collection('my_collection'); 

解决了集合存储的问题,下面看看MongoDB数据解析. MongoDB 通过点式注解方法操作完成这一动作:

 

 
  1. CREATE OR REPLACE FUNCTION find_in_obj(data json, key varcharRETURNS 
  2. VARCHAR AS $ 
  3. var obj = JSON.parse(data); 
  4. var parts = key.split('.'); 
  5.  
  6. var part = parts.shift(); 
  7. while (part && (obj = obj[part]) !== undefined) { 
  8. part = parts.shift(); 
  9.  
  10. // this will either be the value, or undefined 
  11. return obj; 
  12. $ LANGUAGE plv8 STRICT; 

上述功能返回VARCHAR,并不适用所有情形,但对于字符串的比较很有用:

 

 
  1. SELECT data 
  2. FROM col_my_collection 
  3. WHERE find_in_obj(data, 'some.element') = 'something cool' 

除了字符串的比较, MongoDB还提供了数字类型的比较并提供关键字exists . 下面是find_in_obj() 方法的不同实现:

 

 
  1. CREATE OR REPLACE FUNCTION find_in_obj_int(data json, key varcharRETURNS 
  2. INT AS $ 
  3. var obj = JSON.parse(data); 
  4. var parts = key.split('.'); 
  5.  
  6. var part = parts.shift(); 
  7. while (part && (obj = obj[part]) !== undefined) { 
  8. part = parts.shift(); 
  9.  
  10. return Number(obj); 
  11. $ LANGUAGE plv8 STRICT; 
  12.  
  13. CREATE OR REPLACE FUNCTION find_in_obj_exists(data json, key varcharRETURNS 
  14. BOOLEAN AS $ 
  15. var obj = JSON.parse(data); 
  16. var parts = key.split('.'); 
  17.  
  18. var part = parts.shift(); 
  19. while (part && (obj = obj[part]) !== undefined) { 
  20. part = parts.shift(); 
  21.  
  22. return (obj === undefined ? 'f' : 't'); 
  23. $ LANGUAGE plv8 STRICT; 

接下来是数据查询. 通过现有的材料来实现 find() 方法.

保存数据到集合中很简单。首先,我们需要检查JSON对象并寻找一个_id值。这部分代码是原生的假设,如果_id已存在这意味着一个更新,否则就意味着一个插入。请注意,我们目前还没有创建objectID,只使用了一个序列待其发生:

 

 
  1. CREATE OR REPLACE FUNCTION save(collection varchar, data json) RETURNS 
  2. BOOLEAN AS $ 
  3. var obj = JSON.parse(data); 
  4.  
  5. var id = obj._id; 
  6.  
  7. // if there is no id, naively assume an insert 
  8. if (id === undefined) { 
  9. // get the next value from the sequence for the ID 
  10. var seq = plv8.prepare("SELECT nextval('seq_col_" + 
  11. collection + "') AS id"); 
  12. var rows = seq.execute([ ]); 
  13.  
  14. id = rows[0].id; 
  15. obj._id = id; 
  16.  
  17. seq.free(); 
  18.  
  19. var insert = plv8.prepare("INSERT INTO col_" + collection + 
  20. " (col_" + collection + "_id, data) VALUES ($1, $2)"
  21. 'int''json']); 
  22.  
  23. insert.execute([ id, JSON.stringify(obj) ]); 
  24. insert.free(); 
  25. else { 
  26. var update = plv8.prepare("UPDATE col_" + collection + 
  27. " SET data = $1 WHERE col_" + collection + "_id = $2"
  28. 'json''int' ]); 
  29.  
  30. update.execute([ data, id ]); 
  31.  
  32. return true
  33. $ LANGUAGE plv8 IMMUTABLE STRICT; 

基于这个观点,我们可以构建一些插入的简单文档:

 

 
  1. "name""Jane Doe"
  2. "address": { 
  3. "street""123 Fake Street"
  4. "city""Portland"
  5. "state""OR" 
  6. }, 
  7. "age": 33 
  8.  
  9. "name""Sarah Smith"
  10. "address": { 
  11. "street""456 Real Ave"
  12. "city""Seattle"
  13. "state""WA" 
  14.  
  15. "name""James Jones"
  16. "address": { 
  17. "street""789 Infinity Way"
  18. "city""Oakland"
  19. "state""CA" 
  20. }, 
  21. "age": 23 

让我们创建一个集合并插入一些数据:

 

 
  1. work=# SELECT create_collection('data'); 
  2. create_collection 
  3. ------------------- 
  4. (1 row) 
  5.  
  6. work=# SELECT save('data''{ our object }'); 
  7. save 
  8. ------ 
  9. (1 row) 

你可以通过检查“col_data”表的内容来查看对象。

其它翻译版本(1)

现在我们已经有了一些数据,让我们再查询一下。假设我们想查找住在俄勒冈或华盛顿州年龄大于30的所有人,使用一个MongoDB风格的find():

 

 
  1. "$or": [ 
  2. "address.state""OR" 
  3. }, 
  4. "address.state""WA" 
  5. ], 
  6. "age": { 
  7. "$gt": 30 

因为上次我们已经创建了一些深度的包检测,现在就很容易创建查询并返回Jane Doe:

 

 
  1. SELECT data 
  2. FROM col_data 
  3. WHERE find_in_obj_int(data, 'age') > 30 
  4. AND ( 
  5. find_in_obj(data, 'address.state') = 'OR' 
  6. OR 
  7. find_in_obj(data, 'address.state') = 'WA' 

我采用了写一个递归调用函数来建立WHERE子句的方法。它有点长,所以我没有把它贴在这里而是放在GitHub上。一旦find()存储过程被创建,我们就可以在查询中使用它。我们应该能够看到Jane Doe被返回:

 

 
  1. 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并尝试一下吧,记得回馈哦。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表