Проектирование Документо-Ориентированного Api В Postgresql: Полнотекстовый Поиск И Сохранение Большого Количества Документов (Часть 2)

В первая часть этой серии статей Я создал красивую функцию сохранения, а также еще одну функцию, которая позволяет создавать изменяемые таблицы, ориентированные на документы, на лету.

Они работают хорошо и делают именно то, что им нужно, но мы можем сделать гораздо больше.

Особенно: Мне нужен полнотекстовый поиск, индексация на лету и сохранение многих документов в транзакции.

Давайте сделаем это.



Полнотекстовый поиск

Наша документально-ориентированная таблица имеет поле поиска, например цвектор , который индексируется с использованием индекса GIN для скорости.

Я хочу обновлять это поле каждый раз, когда сохраняю документ, и мне не нужен при этом большой шум от API. В этом отношении я прибегну к некоторой условности.

Обычно при создании полнотекстового индекса поля сохраняются с довольно конкретными именами.

Такой как:

  • Имя или фамилия, возможно адрес электронной почты
  • Название или описание чего-либо
  • Информация об адресе
Я хотел бы проверить свой документ во время сохранения на наличие ключей, которые я хотел бы проиндексировать, а затем сохранить их в поле.

поиск .

Это можно сделать с помощью функции, которую я вызвал update_search :

  
  
  
  
  
  
   

create function update_search(tbl varchar, id int) returns boolean as $$ //get the record var found = plv8.execute("select body from " + tbl + " where id=$1",id)[0]; if(found){ var doc = JSON.parse(found.body); var searchFields = ["name","email","first","first_name", "last","last_name","description","title", "street", "city", "state", "zip", ]; var searchVals = []; for(var key in doc){ if(searchFields.indexOf(key.toLowerCase()) > -1){ searchVals.push(doc[key]); } }; if(searchVals.length > 0){ var updateSql = "update " + tbl + " set search = to_tsvector($1) where id =$2"; plv8.execute(updateSql, searchVals.join(" "), id); } return true; }else{ return false; } $$ language plv8;

Я снова использую для этой цели javascript (PLV8) и извлекаю документ на основе идентификатора.

Затем я просматриваю все ключи, чтобы увидеть, есть ли такие, которые мне хотелось бы сохранить, и если они есть, я помещаю их в массив.

Если в этом архиве есть попадания, я объединяю эти объекты и сохраняю их в поле поиск документ с помощью функции to_tsvector , встроенный в Postgres, который принимает простой текст и преобразует его в индексируемые значения.

Вот! Запустив этот скрипт, мы получим следующее:

Проектирование документо-ориентированного API в PostgreSQL: полнотекстовый поиск и сохранение большого количества документов (часть 2)

Отлично! Теперь я могу просто вставить это в конец своего сохранить_документ функция, и она будет вызываться транзакционно каждый раз, когда я что-то сохраняю:

create function save_document(tbl varchar, doc_string jsonb) returns jsonb as $$ var doc = JSON.parse(doc_string); var result = null; var id = doc.id; var exists = plv8.execute("select table_name from information_schema.tables where table_name = $1", tbl)[0]; if(!exists){ plv8.execute("select create_document_table('" + tbl + "');"); } if(id){ result = plv8.execute("update " + tbl + " set body=$1, updated_at = now() where id=$2 returning *;",doc_string,id); }else{ result = plv8.execute("insert into " + tbl + "(body) values($1) returning *;", doc_string); id = result[0].

id; doc.id = id; result = plv8.execute("update " + tbl + " set body=$1 where id=$2 returning *",JSON.stringify(doc),id); } //run the search indexer plv8.execute("perform update_search($1, $2)", tbl,id); return result[0] ? result[0].

body : null; $$ language plv8;



Сохранение большого количества документов

На этом этапе я могу передать в функцию один документ сохранить_документ , но я бы хотел иметь возможность передать ему массив.

Я могу сделать это, проверив тип аргумента и затем запустив цикл:

create function save_document(tbl varchar, doc_string jsonb) returns jsonb as $$ var doc = JSON.parse(doc_string); var exists = plv8.execute("select table_name from information_schema.tables where table_name = $1", tbl)[0]; if(!exists){ plv8.execute("select create_document_table('" + tbl + "');"); } //function that executes our SQL statement var executeSql = function(theDoc){ var result = null; var id = theDoc.id; var toSave = JSON.stringify(theDoc); if(id){ result=plv8.execute("update " + tbl + " set body=$1, updated_at = now() where id=$2 returning *;",toSave, id); }else{ result=plv8.execute("insert into " + tbl + "(body) values($1) returning *;", toSave); id = result[0].

id; //put the id back on the document theDoc.id = id; //resave it result = plv8.execute("update " + tbl + " set body=$1 where id=$2 returning *;",JSON.stringify(theDoc),id); } plv8.execute("select update_search($1,$2)", tbl, id); return result ? result[0].

body : null; } var out = null; //was an array passed in? if(doc instanceof Array){ for(var i = 0; i < doc.length;i++){ executeSql(doc[i]); } //just report back how many documents were saved out = JSON.stringify({count : i, success : true}); }else{ out = executeSql(doc); } return out; $$ language plv8;

Преимущество работы с javascript заключается в том, что логика, необходимая для такой процедуры, довольно проста (в отличие от PLPGSQL).

Я выделил весь процесс сохранения в отдельную функцию (в конце концов, это javascript), чтобы избежать дублирования.

Затем я хочу проверить, что входной аргумент является массивом.

Если да, то я хожу по его членам и звоню выполнитьSql , возвращая всё, что накопилось за время выполнения.

Если это не массив, я просто делаю то же самое, возвращая весь документ. Результат:

Проектирование документо-ориентированного API в PostgreSQL: полнотекстовый поиск и сохранение большого количества документов (часть 2)

Замечательный! Самое лучшее в этом то, что все это происходит внутри транзакции .

Мне это нравится!

Странности узла

Если бы это могло прекрасно работать с Node! Я пробовал и в .

NET, и в Node, с .

NET все работает (как ни странно) с использованием библиотеки Npgsql. Из Node, не совсем.

Вкратце: драйвер node_pg выполняет очень странное преобразование, когда видит объект массива в качестве входного параметра.

Обратим внимание на следующее:

var pg = require("pg"); var run = function (sql, params, next) { pg.connect(args.connectionString, function (err, db, done) { //throw if there's a connection error assert.ok(err === null, err); db.query(sql, params, function (err, result) { //we have the results, release the connection done(); pg.end(); if(err){ next(err,null); }else{ next(null, result.rows); } }); }); }; run("select * from save_document($1, $2)", ['customer_docs', {name : "Larry"}], function(err,res){ //works just fine }

Это обычный код узла/PG. В самом конце функция запуска настроена на вызов моей функции save_document и передачу некоторых данных.

Когда PG видит входной объект, он преобразует его в строку, и сохранение будет работать нормально.

Если вы отправите массив.



run("select * from save_document($1, $2)", ['customer_docs', [{name : "Larry"}, {name : "Susie"}], function(err,res){ //crashes hard }

Я получаю сообщение об ошибке, сообщающее, что это неверный JSON. В сообщении об ошибке (от Postgres) будет сказано, что это связано с разреженным форматированием JSON:

{"{name : "Larry"}, .

}

Что.

да, это ужасно.

Я пытался сформулировать, что происходит, но кажется, что драйвер node_pg анализирует внешний массив - возможно, вызывая метод сгладить Подчеркивает библиотеки.

Я не знаю.

Чтобы обойти это, вам нужно изменить свой вызов на следующее:

run("select * from save_document($1, $2)", ['customer_docs', JSON.stringify([{name : "Larry"}, {name : "Susie"}]), function(err,res){ //Works fine }



Вперед!

Процедура сохранения довольно плавная, и это меня радует. В следующей статье я настрою поисковые системы, а также создам функцию полнотекстового поиска.

Теги: #postgresql #JavaScript #plv8 #JavaScript #postgresql #sql

Вместе с данным постом часто просматривают: