В первая часть этой серии статей Я создал красивую функцию сохранения, а также еще одну функцию, которая позволяет создавать изменяемые таблицы, ориентированные на документы, на лету.
Они работают хорошо и делают именно то, что им нужно, но мы можем сделать гораздо больше.
Особенно: Мне нужен полнотекстовый поиск, индексация на лету и сохранение многих документов в транзакции.
Давайте сделаем это.
Полнотекстовый поиск
Наша документально-ориентированная таблица имеет поле поиска, например цвектор , который индексируется с использованием индекса GIN для скорости.Я хочу обновлять это поле каждый раз, когда сохраняю документ, и мне не нужен при этом большой шум от API. В этом отношении я прибегну к некоторой условности.
Обычно при создании полнотекстового индекса поля сохраняются с довольно конкретными именами.
Такой как:
- Имя или фамилия, возможно адрес электронной почты
- Название или описание чего-либо
- Информация об адресе
поиск .
Это можно сделать с помощью функции, которую я вызвал update_search :
Я снова использую для этой цели javascript (PLV8) и извлекаю документ на основе идентификатора.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;
Затем я просматриваю все ключи, чтобы увидеть, есть ли такие, которые мне хотелось бы сохранить, и если они есть, я помещаю их в массив.
Если в этом архиве есть попадания, я объединяю эти объекты и сохраняю их в поле поиск документ с помощью функции to_tsvector , встроенный в Postgres, который принимает простой текст и преобразует его в индексируемые значения.
Вот! Запустив этот скрипт, мы получим следующее:
Отлично! Теперь я могу просто вставить это в конец своего сохранить_документ функция, и она будет вызываться транзакционно каждый раз, когда я что-то сохраняю: 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 , возвращая всё, что накопилось за время выполнения.
Если это не массив, я просто делаю то же самое, возвращая весь документ. Результат:
Замечательный! Самое лучшее в этом то, что все это происходит внутри транзакции .
Мне это нравится!
Странности узла
Если бы это могло прекрасно работать с 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
-
Новая Коллекция Патчей Для Studio Artist 3.0
19 Oct, 24 -
Haipad M702T — Планшет/Медиацентр
19 Oct, 24 -
Форум Veeamon Москва
19 Oct, 24