Создайте древовидную карту с помощью Sequelize - PullRequest
0 голосов
/ 04 августа 2020

Я использую Node.JS, Express, Sequelize и MySQL. У меня есть две таблицы, которые представляют узлы и ребра в древовидной структуре:

Таблица узлов

Таблица параметров

Узлы может иметь несколько вариантов. Параметры могут иметь 1 дочерний узел.

Я создал программу, которая рекурсивно генерирует объект JS, содержащий указанный узел и все дочерние узлы вплоть до листьев. Используя этот код:

/**
 * Function to return a representation of the tree
 * @param {number} aNode the id of the node to begin mapping from
 * @returns {Object} object representing the database tree structure
 */
const dbMap = async (aNode) => {

    const visited = [];
    const queue = [];
    const map = {};
    try {

        const node = await getNode(aNode);

        const options = await getOptions(aNode);
        if (node.article_id !== null) {

            const articleTitle = await getNodeArticleTitle(node.article_id);
            map.articleTitle = articleTitle.internal_name;
        }

        if (node){

            map.node = node;

            if (options){
                map.options = options;
                for (const option of options){
                    if (option.child_node_id){
                        queue.push(option.child_node_id);
                    }
                }
            }
        }

        map.children = [];

        while (queue.length){
            const node_to_find = queue.shift();
            if (!visited.includes(node_to_find)){
                map.children.push(await dbMap(node_to_find));
                visited.push(node_to_find);
            }
        }

        return map;
    } catch (err) {
        console.log(err);
        return {error: err};
    }
};

/**
 * Get a node from its ID
 * @param {number} node node's id to get
 * @returns {Object} object representing a node
 */
const getNode = node => db.query('SELECT * FROM `nodes` WHERE `nodes`.`id` = ?',{
    replacements: [node],
    type: QueryTypes.SELECT,
    plain: true,
    raw:true
});

const getOptions = node => db.query('SELECT * FROM `options` WHERE `options`.`parent_node_id` = ?',{
    replacements:[node],
    type: QueryTypes.SELECT,
    raw: true
});

Теперь я пытаюсь сделать то же самое, используя модели и ассоциации Sequelize, но могу вернуть только один уровень объекта дерева, используя этот код:

const node = db.define('node', {
    id: {
        type: DataTypes.INTEGER,
        allowNull: false,
        unique: true,
        primaryKey: true
    },
    title: {
        type: DataTypes.STRING(120),
        defaultValue: null
    },
    subtitle: {
        type: DataTypes.STRING(500),
        defaultValue: null
    },
    article_id: {
        type: DataTypes.INTEGER,
        defaultValue: null,
        references: {
            model: article,
            key: 'id'
        }
    },
    options_title: {
        type: DataTypes.STRING(120),
        defaultValue: null
    },
    options_type: {
        type: DataTypes.ENUM,
        values: ['grid', 'yes_no'],
        defaultValue: null
    }
});

node.hasMany(option, {
    as: 'options',
    foreignKey: 'parent_node_id'
});

node.hasOne(article, {
    as: 'article',
    foreignKey: 'id'
});


const option = db.define('option', {
    id: {
        type: DataTypes.INTEGER,
        allowNull: false,
        unique: true,
        primaryKey: true
    },
    title: {
        type: DataTypes.STRING(120),
        allowNull: false,
        unique: true
    },
    sub_title: {
        type: DataTypes.STRING(500),
        defaultValue: null
    },
    image: {
        type: DataTypes.STRING(120),
        defaultValue: null
    },
    position: {
        type: DataTypes.INTEGER,
        allowNull: false
    },
    parent_node_id: {
        type: DataTypes.INTEGER,
        allowNull: false,
        references: {
            model: node,
            key: 'id'
        }
    },
    child_node_id: {
        type: DataTypes.INTEGER,
        references: {
            model: node,
            key: 'id'
        }
    }
});

option.associate = (models) => {
    option.belongsTo(models.node, {
        foreignKey: 'child_node_id',
        as: 'childNode'
    });
};

node.findOne({
    where: {
        id: 1
    },
    include: [{all: true, nested: true}]
}).then((response) => response.toJSON()).then((result) => {
        console.log(result);
});

который возвращает:

{
  id: 1,
  title: 'How can we help you today?',
  subtitle: '',
  article_id: null,
  options_title: null,
  options_type: 'grid',
  options: [
    {
      id: 1,
      title: '',
      sub_title: null,
      image: null,
      position: 1,
      parent_node_id: 1,
      child_node_id: 2
    },
    {
      id: 2,
      title: '+',
      sub_title: null,
      image: null,
      position: 2,
      parent_node_id: 1,
      child_node_id: 3
    },
    {
      id: 3,
      title: 'Block',
      sub_title: null,
      image: null,
      position: 3,
      parent_node_id: 1,
      child_node_id: 4
    },
    {
      id: 4,
      title: 'Orders / Returns',
      sub_title: null,
      image: null,
      position: 4,
      parent_node_id: 1,
      child_node_id: 5
    },
    {
      id: 5,
      title: 'App',
      sub_title: null,
      image: null,
      position: 5,
      parent_node_id: 1,
      child_node_id: 6
    },
    {
      id: 6,
      title: 'Suggestions',
      sub_title: null,
      image: null,
      position: 6,
      parent_node_id: 1,
      child_node_id: 7
    }
  ],
  article: null
}

Как мне определить модели и ассоциации, а затем запросить их, чтобы вернуть объект на листья?

...