es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

¿Cómo se crea este tsvector generado siempre como columna con Sequelize?

Veo que Sequelize tiene DataTypes.TSVECTOR para el dialecto de PostgreSQL.
Tengo una columna cuya definición en SQL en bruto es la siguiente

tsvector GENERATED ALWAYS AS (((
setweight(to_tsvector('english'::regconfig, (COALESCE(title, ''::character varying))::text), 'A'::"char") || 
setweight(to_tsvector('english'::regconfig, COALESCE(summary, ''::text)), 'B'::"char")) || 
setweight(to_tsvector('english'::regconfig, (COALESCE(content, ''::character varying))::text), 'C'::"char"))) 
STORED

¿Cómo puedo definir esto en mi modelo de Sequelize?

 const FeedItem = sequelize.define(
    'FeedItem', {
        feedItemId: {
            type: DataTypes.UUID,
            primaryKey: true,
            allowNull: false,
            defaultValue: DataTypes.UUIDV4,
        },
        pubdate: {
            type: DataTypes.DATE,
            allowNull: false,
            defaultValue: sequelize.literal('CURRENT_TIMESTAMP'),
            validate: {
                isDate: true,
            },
        },
        link: {
            type: DataTypes.STRING,
            allowNull: false,
            validate: {
                len: [0, 2047],
            },
        },
        guid: {
            type: DataTypes.STRING,
            validate: {
                len: [0, 2047],
            },
        },
        title: {
            type: DataTypes.TEXT,
            allowNull: false,
            validate: {
                len: [0, 65535],
            },
        },
        summary: {
            type: DataTypes.TEXT,
            validate: {
                len: [0, 65535],
            },
        },
        content: {
            type: DataTypes.TEXT,
            validate: {
                len: [0, 1048575],
            },
        },
        author: {
            type: DataTypes.STRING,
            validate: {
                len: [0, 63],
            },
        },
        tags: {
            type: DataTypes.ARRAY(DataTypes.STRING),
            defaultValue: [],
        },
        // ¿Cómo hago esta parte de "generated always" aquí?
        searchable: {
            type: DataTypes.TSVECTOR
        },
    }, {
        timestamps: false,
        underscored: true,
        indexes: [
            {
                name: 'idx_feed_items_searchable',
                fields: ['searchable'],
                using: 'gin',
            },
        ],
    }
  );
Tags:  , , ,

Answer

  1. Avatar for davy.ai

    Para definir la parte “generada siempre” en el modelo de Sequelize, se pueden utilizar los métodos “get” y “set” del tipo DataTypes.TSVECTOR.

    Primero, define el campo “searchable” como un campo virtual utilizando los métodos “get” y “set”:

    searchable: {
      type: DataTypes.VIRTUAL(DataTypes.TSVECTOR),
      get() {
        return this.getDataValue('searchable');
      },
      set(value) {
        this.setDataValue('searchable', value);
      }
    }
    

    Luego, crea un “hook” de “beforeSave” para el modelo que establezca el valor de “searchable” en función del contenido de los campos “title”, “summary” y “content”:

    hooks: {
      beforeSave: (feedItem) => {
        const searchableValue =
          sequelize.literal(`(
            setweight(to_tsvector('english', coalesce(${sequelize.escape(feedItem.title)}, '')), 'A') ||
            setweight(to_tsvector('english', coalesce(${sequelize.escape(feedItem.summary)}, '')), 'B') ||
            setweight(to_tsvector('english', coalesce(${sequelize.escape(feedItem.content)}, '')), 'C')
          )`);
    
        feedItem.searchable = searchableValue;
      },
    }
    

    Aquí, se utiliza el método “sequelize.escape” para escapar y entrecomillar de forma segura los valores de los campos.

    Por último, añade un índice en el campo “searchable” utilizando la clase de operador “gin” (Generalized Inverted Index):

    indexes: [
      {
        name: 'idx_feed_items_searchable',
        fields: [
          sequelize.literal('"searchable"'),
        ],
        using: 'gin',
      },
    ]
    

    El código completo para el modelo de Sequelize:

    const FeedItem = sequelize.define(
      'FeedItem',
      {
        feedItemId: {
          type: DataTypes.UUID,
          primaryKey: true,
          allowNull: false,
          defaultValue: DataTypes.UUIDV4,
        },
        pubdate: {
          type: DataTypes.DATE,
          allowNull: false,
          defaultValue: sequelize.literal('CURRENT_TIMESTAMP'),
          validate: {
            isDate: true,
          },
        },
        link: {
          type: DataTypes.STRING,
          allowNull: false,
          validate: {
            len: [0, 2047],
          },
        },
        guid: {
          type: DataTypes.STRING,
          validate: {
            len: [0, 2047],
          },
        },
        title: {
          type: DataTypes.TEXT,
          allowNull: false,
          validate: {
            len: [0, 65535],
          },
        },
        summary: {
          type: DataTypes.TEXT,
          validate: {
            len: [0, 65535],
          },
        },
        content: {
          type: DataTypes.TEXT,
          validate: {
            len: [0, 1048575],
          },
        },
        author: {
          type: DataTypes.STRING,
          validate: {
            len: [0, 63],
          },
        },
        tags: {
          type: DataTypes.ARRAY(DataTypes.STRING),
          defaultValue: [],
        },
        searchable: {
          type: DataTypes.VIRTUAL(DataTypes.TSVECTOR),
          get() {
            return this.getDataValue('searchable');
          },
          set(value) {
            this.setDataValue('searchable', value);
          },
        },
      },
      {
        timestamps: false,
        underscored: true,
        indexes: [
          {
            name: 'idx_feed_items_searchable',
            fields: [
              sequelize.literal('"searchable"'),
            ],
            using: 'gin',
          },
        ],
        hooks: {
          beforeSave: (feedItem) => {
            const searchableValue =
              sequelize.literal(`(
                setweight(to_tsvector('english', coalesce(${sequelize.escape(feedItem.title)}, '')), 'A') ||
                setweight(to_tsvector('english', coalesce(${sequelize.escape(feedItem.summary)}, '')), 'B') ||
                setweight(to_tsvector('english', coalesce(${sequelize.escape(feedItem.content)}, '')), 'C')
              )`);
    
            feedItem.searchable = searchableValue;
          },
        },
      }
    );
    

Comments are closed.