tsvector_update_trigger无法找到tsvector列

时间:2014-01-15 20:49:14

标签: sql ruby-on-rails postgresql plpgsql pg-search

我正在Ruby on Rails上创建一个非常简单的博客,目前正在使用pg_search实现PostgreSQL全文搜索的搜索功能。不幸的是,我遇到了一个问题(我相信)我在post表中更新tsvector列的触发器。经过几个小时的搜索,我自己无法解决这个问题 - 即使我怀疑如果你以前做过类似的事情也很容易。

无论如何,错误内容如下:

irb(main):001:0> post = FactoryGirl.build :post
=> #<Post id: nil, title: "Optimized uniform infrastructure", body: "<p>Inventore consectetur culpa nulla eius voluptati...", published: true, created_at: "2012-09-09  04:08:51", updated_at: nil, tsv: nil>
irb(main):002:0> post.save
ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR:  column "tsv" does not exist
    LINE 1: SELECT tsvector_update_trigger(tsv, 'pg_catalog.english', ti...
    QUERY:  SELECT tsvector_update_trigger(tsv, 'pg_catalog.english', title, body)
    CONTEXT:  PL/pgSQL function "posts_before_insert_update_row_tr" line 3 at assignment
    : INSERT INTO "posts" ("body", "created_at", "published", "title", "updated_at") VALUES
    ($1, $2, $3, $4, $5) RETURNING "id"

tsv列是在迁移中完成的:

class AddTsvToPosts < ActiveRecord::Migration
    def change
        add_column :posts, :tsv, :tsvector
        add_index(:posts, :tsv, using: 'gin')
    end
end

使用hair_trigger以这种方式定义触发器:

class Post < ActiveRecord::Base
  include PgSearch

  pg_search_scope :search, against: [:title, :body],
    using: {
      tsearch: {
        dictionary: 'english',
        prefix: true,
        tsvector_column: 'tsv'
      }
    }

  trigger.before(:insert, :update) do
    "new.tsv := tsvector_update_trigger(tsv, 'pg_catalog.english', title, body);"
  end
end

整个事情产生以下sql架构(config.active_record.schema_format = :sql) -      - PostgreSQL数据库转储      -

SET statement_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SET check_function_bodies = false;
SET client_min_messages = warning;

--
-- Name: plpgsql; Type: EXTENSION; Schema: -; Owner: -
--

CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;


--
-- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner: -
--

COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language';


SET search_path = public, pg_catalog;

--
-- Name: posts_before_insert_update_row_tr(); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION posts_before_insert_update_row_tr() RETURNS trigger
    LANGUAGE plpgsql
    AS $$
BEGIN
    new.tsv := tsvector_update_trigger(tsv, 'pg_catalog.english', title, body);
    RETURN NEW;
END;
$$;


SET default_tablespace = '';

SET default_with_oids = false;

--
-- Name: posts; Type: TABLE; Schema: public; Owner: -; Tablespace: 
--

CREATE TABLE posts (
    id integer NOT NULL,
    title character varying(255),
    body text,
    published boolean,
    created_at timestamp without time zone,
    updated_at timestamp without time zone,
    tsv tsvector
);


--
-- Name: posts_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE posts_id_seq
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: posts_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE posts_id_seq OWNED BY posts.id;


--
-- Name: schema_migrations; Type: TABLE; Schema: public; Owner: -; Tablespace: 
--

CREATE TABLE schema_migrations (
    version character varying(255) NOT NULL
);


--
-- Name: id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY posts ALTER COLUMN id SET DEFAULT nextval('posts_id_seq'::regclass);


--
-- Name: posts_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: 
--

ALTER TABLE ONLY posts
    ADD CONSTRAINT posts_pkey PRIMARY KEY (id);


--
-- Name: index_posts_on_tsv; Type: INDEX; Schema: public; Owner: -; Tablespace: 
--

CREATE INDEX index_posts_on_tsv ON posts USING gin (tsv);


--
-- Name: unique_schema_migrations; Type: INDEX; Schema: public; Owner: -; Tablespace: 
--

CREATE UNIQUE INDEX unique_schema_migrations ON schema_migrations USING btree (version);


--
-- Name: posts_before_insert_update_row_tr; Type: TRIGGER; Schema: public; Owner: -
--

CREATE TRIGGER posts_before_insert_update_row_tr BEFORE INSERT OR UPDATE ON posts FOR EACH ROW EXECUTE PROCEDURE posts_before_insert_update_row_tr();


--
-- PostgreSQL database dump complete
--

SET search_path TO "$user",public;

INSERT INTO schema_migrations (version) VALUES ('20131230213035');

INSERT INTO schema_migrations (version) VALUES ('20140115101632');

INSERT INTO schema_migrations (version) VALUES ('20140115183846');

如果有人能帮我弄清楚为什么rails不承认tsv栏的存在,我会 soooooo 开心! :)

2 个答案:

答案 0 :(得分:1)

  

如果有人可以帮我弄清楚为什么没有导轨   承认tsv栏的存在

这不是铁轨,但这段代码:

   new.tsv := tsvector_update_trigger(tsv, 'pg_catalog.english', title, body);

除了在这种情况下tsv无法解释的句法问题之外,真正关心的是tsvector_update_trigger返回trigger,而不是tsvector,所以它应该是由插入或更新后的SQL引擎调用,而不是由用户代码显式调用。

事实上,对于预先存在的tsvector_update_trigger而言,正是程序员首先避免编写触发器的原因。您应该使用CREATE TRIGGER语句直接引用它,如下所示:

CREATE TRIGGER trigger_name 
BEFORE INSERT OR UPDATE ON posts FOR EACH ROW EXECUTE PROCEDURE 
tsvector_update_trigger(tsv, 'pg_catalog.english', title, body);

然后不需要你的函数posts_before_insert_update_row_tr()

请参阅文档中的Triggers for Automatic Updates

答案 1 :(得分:1)

根据DanielVérité的回答,我编写了一个自定义触发器(将其放在单独的迁移中,而不是在模型中)。

class CreateUpdateTsvPostsTrigger < ActiveRecord::Migration
  def up
    create_trigger(compatibility: 1).name('update_tsv_posts').on(:posts).before(:insert, :update) do
      "new.tsv :=
          setweight(to_tsvector('pg_catalog.english', coalesce(new.title,'')), 'A') ||
          setweight(to_tsvector('pg_catalog.english', coalesce(new.body,'')), 'D');"
    end
  end

  def down
    drop_trigger("update_tsv_posts", "posts")
  end
end

整个事情就像一个魅力。 :)