GithubHelp home page GithubHelp logo

fremail / sequelize-nested-set Goto Github PK

View Code? Open in Web Editor NEW
39.0 4.0 10.0 111 KB

Library to store and manage nested set trees using Sequelize

License: MIT License

JavaScript 100.00%
sequelize trees multi-root-tree tree

sequelize-nested-set's People

Contributors

aslubsky avatar fremail avatar ilgiz-badamshin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

sequelize-nested-set's Issues

Add example with full working code that creates some Nested Set structure

Now we have a lof of examples for exact functions, but not the main "quick start" example that shows the code, that works out-of-the-box.

So I suggest to add a "Quick start" one-file example with full index.js file, that requires sequelize library, creates a connection, creates Tag nested set, adds some elements to it, then moves some to other branch, etc.

Cannot insert the node that has its place in the tree

Hey man! Can't get how to use addChild method? I can successfully add roots but when I do

.then(model=>{
model.addChild(new Model({...})
})

I always get this error Cannot insert the node that has its place in the tree

What I do wrong?

Error converting a sub tree of records into a separate tree, using the "makeRoot" command.

Hi good day. I try to convert a sub tree of records into a separate tree (as root) using the "makeRoot" command. As a result, the main registry becomes root, but its children do not.

Initially I have the following list of records:

img1

Next I want to convert the tree with record ID: 15 into an independent tree, executing the following code:

let category = await models.Category.findOne({where:{id:15}});
await category.makeRoot();

and as a result it only converts the record ID: 15 to root, but its children are not updated as children of the ID: 15.

img2

I hope you can help me.

parentId

Maybe worth adding parentId field (like pid or pnt) to interface for simplify usage tree nodes?

Write tests for single-root tree

Here is the full list of functions:

  • createRoot
  • fetchRoot
  • fetchTree
  • fetchRoots
  • hasPrevSibling
  • hasNextSibling
  • hasChildren
  • hasParent
  • getPrevSibling
  • getNextSibling
  • getSiblings
  • getFirstChild
  • getLastChild
  • getChildren
  • getDescendants
  • getParent
  • getAncestors
  • getNumberChildren
  • getNumberDescendants
  • insertAsParentOf
  • insertAsPrevSiblingOf
  • insertAsNextSiblingOf
  • insertAsFirstChildOf
  • insertAsLastChildOf
  • moveBetweenTrees
  • moveAsPrevSiblingOf
  • moveAsNextSiblingOf
  • moveAsFirstChildOf
  • moveAsLastChildOf
  • makeRoot
  • addChild
  • isLeaf
  • isRoot
  • isEqualTo
  • isDescendantOf
  • isDescendantOfOrEqualTo
  • isAncestorOf
  • isValidNode
  • detach
  • delete
  • insertNode
  • updateNode
  • shiftRlValues
  • shiftRlRange

Error while moving node between trees

From documentation:

  1. Moving a record
    Warning! You can move record only if it's a valid node == it has its own place in the tree == it has left and right values. If it doesn't, please see Creating a record page.
  2. Creating a record
    Warning! You can use these functions only with records w/o its own place in the tree (w/o left and right values in other words). If you need to move the existing records, see Moving a record page.

For example method: moveAsFirstChildOf.
If source node and dest node has different rootId, then called moveBetweenTrees, that call insertAsFirstChildOf, that throw exception: "Cannot insert the node that has its place in the tree", becouse node exists and we can't use methotds insertXXX.

So no one moveXXX from different trees is not working.
Maybe need to call detach(), before insertXXX methods?
Or i some where have mistake?

Make level field optional

Nested set requires only left and right values, others must be optional.

Here is a list of functions using level field:

  • fetchTree
  • getDescendants
  • getParent (really no profit of using level here)
  • getAncestors

Error creating the first tree record (level 0).

Hello,
During the testing process of this library, I encountered some problems when creating the first record of a tree. What you would expect when creating the first record of a tree, is that the library automatically manages the filling of the fields "lft", "rgt", "level", "root", and that only the other mandatory fields are completed. the entity. When trying to create the first record, request the fields "rootId" and "level".

I attach the code that replicates the error.

/models/category.js

'use strict';
const ns = require('sequelize-nested-set');
module.exports = (sequelize, DataTypes) => {
  const category = ns(sequelize, DataTypes, 'Category', {
    name: {
      type: DataTypes.STRING,
      allowNull: false,
      validate: {
        notEmpty: true,
      },
    },
  }, {
    underscored: true,
    freezeTableName: true,
    hasManyRoots: true,
    tableName: 'category',
    rootColumnName: 'root'
  });

  return category;

};

/server.js

var db = require('./models');
var Sequelize = require('sequelize');
const sequelize = new Sequelize({
    dialect: 'sqlite',
    storage: './finanzasdb.sqlite3'
});

db.sequelize.sync({force: true}).then(
    async function () {

        let category = await db.Category.create({
            type: 1,
            name: 'Root record',
            // rootId: 1,
            // level: 0
        });

    }
);

Then, when executing the file "server.js" the following error appears...

Unhandled rejection SequelizeValidationError: notNull Violation: Category.level cannot be null,
notNull Violation: Category.rootId cannot be null
    at Promise.all.then (/Users/jpsepulvedaf/Desktop/nestedset-test/node_modules/sequelize/lib/instance-validator.js:74:15)
    at tryCatcher (/Users/jpsepulvedaf/Desktop/nestedset-test/node_modules/bluebird/js/release/util.js:16:23)
    at Promise._settlePromiseFromHandler (/Users/jpsepulvedaf/Desktop/nestedset-test/node_modules/bluebird/js/release/promise.js:512:31)
    at Promise._settlePromise (/Users/jpsepulvedaf/Desktop/nestedset-test/node_modules/bluebird/js/release/promise.js:569:18)
    at Promise._settlePromise0 (/Users/jpsepulvedaf/Desktop/nestedset-test/node_modules/bluebird/js/release/promise.js:614:10)
    at Promise._settlePromises (/Users/jpsepulvedaf/Desktop/nestedset-test/node_modules/bluebird/js/release/promise.js:694:18)
    at Promise._fulfill (/Users/jpsepulvedaf/Desktop/nestedset-test/node_modules/bluebird/js/release/promise.js:638:18)
    at PromiseArray._resolve (/Users/jpsepulvedaf/Desktop/nestedset-test/node_modules/bluebird/js/release/promise_array.js:126:19)
    at PromiseArray._promiseFulfilled (/Users/jpsepulvedaf/Desktop/nestedset-test/node_modules/bluebird/js/release/promise_array.js:144:14)
    at Promise._settlePromise (/Users/jpsepulvedaf/Desktop/nestedset-test/node_modules/bluebird/js/release/promise.js:574:26)
    at Promise._settlePromise0 (/Users/jpsepulvedaf/Desktop/nestedset-test/node_modules/bluebird/js/release/promise.js:614:10)
    at Promise._settlePromises (/Users/jpsepulvedaf/Desktop/nestedset-test/node_modules/bluebird/js/release/promise.js:694:18)
    at _drainQueueStep (/Users/jpsepulvedaf/Desktop/nestedset-test/node_modules/bluebird/js/release/async.js:138:12)
    at _drainQueue (/Users/jpsepulvedaf/Desktop/nestedset-test/node_modules/bluebird/js/release/async.js:131:9)
    at Async._drainQueues (/Users/jpsepulvedaf/Desktop/nestedset-test/node_modules/bluebird/js/release/async.js:147:5)
    at Immediate.Async.drainQueues [as _onImmediate] (/Users/jpsepulvedaf/Desktop/nestedset-test/node_modules/bluebird/js/release/async.js:17:14)
    at processImmediate (timers.js:632:19)

fetchTree just return 1 level child

I use fake data to generate about 10 root menu and about 100 menu.

I have simple test unit with fake data:

          const tree = await Menu.fetchTree(0, rootId, {
            raw: true,
          });

Sample output:

[ { id: 10,
          lft: 1,
          rgt: 20,
          level: 0,
          rootId: 10,
          title: 'Root Menu 9',
          description:
           'Hic in molestiae sed vel sit quia. Totam nulla adipisci porro. Ipsam quo eligendi aut voluptatum nihil. Nesciunt voluptates doloribus pariatur dolor repudiandae.',
          url: '/page/9',
          enable: 1,
          createdAt: 2019-10-05T09:49:57.000Z,
          updatedAt: 2019-10-05T09:49:58.000Z },
        { id: 16,
          lft: 2,
          rgt: 19,
          level: 1,
          rootId: 10,
          title: 'Child menu 4 : parent 10',
          description: 'et voluptas distinctio',
          url: '/child/page/4',
          enable: 1,
          createdAt: 2019-10-05T09:49:57.000Z,
          updatedAt: 2019-10-05T09:49:58.000Z },
        { id: 85,
          lft: 3,
          rgt: 8,
          level: 2,
          rootId: 10,
          title: 'Child menu 73 : parent 16',
          description: 'Voluptatem veniam enim culpa maiores velit ut.',
          url: '/child/page/73',
          enable: 1,
          createdAt: 2019-10-05T09:49:58.000Z,
          updatedAt: 2019-10-05T09:49:58.000Z },
        { id: 104,
          lft: 4,
          rgt: 7,
          level: 3,
          rootId: 10,
          title: 'Child menu 92 : parent 21',
          description: null,
          url: 'https://shaniya.net',
          enable: 1,
          createdAt: 2019-10-05T09:49:58.000Z,
          updatedAt: 2019-10-05T09:49:58.000Z },
        { id: 110,
          lft: 5,
          rgt: 6,
          level: 4,
          rootId: 10,
          title: 'Child menu 98 : parent 104',
          description: 'Aut itaque unde commodi nostrum quos temporibus rerum.',
          url: '/child/page/98',
          enable: 1,
          createdAt: 2019-10-05T09:49:58.000Z,
          updatedAt: 2019-10-05T09:49:58.000Z },
        { id: 23,
          lft: 9,
          rgt: 16,
          level: 2,
          rootId: 10,
          title: 'Child menu 11 : parent 16',
          description: null,
          url: '/child/page/11',
          enable: 1,
          createdAt: 2019-10-05T09:49:57.000Z,
          updatedAt: 2019-10-05T09:49:58.000Z },
        { id: 34,
          lft: 10,
          rgt: 13,
          level: 3,
          rootId: 10,
          title: 'Child menu 22 : parent 21',
          description: null,
          url: 'http://britney.net',
          enable: 1,
          createdAt: 2019-10-05T09:49:57.000Z,
          updatedAt: 2019-10-05T09:49:58.000Z },
        { id: 70,
          lft: 11,
          rgt: 12,
          level: 4,
          rootId: 10,
          title: 'Child menu 58 : parent 27',
          description: null,
          url: '/child/page/58',
          enable: 0,
          createdAt: 2019-10-05T09:49:58.000Z,
          updatedAt: 2019-10-05T09:49:58.000Z },
        { id: 27,
          lft: 14,
          rgt: 15,
          level: 3,
          rootId: 10,
          title: 'Child menu 15 : parent 23',
          description: 'velit',
          url: '/child/page/15',
          enable: 1,
          createdAt: 2019-10-05T09:49:57.000Z,
          updatedAt: 2019-10-05T09:49:58.000Z },
        { id: 21,
          lft: 17,
          rgt: 18,
          level: 2,
          rootId: 10,
          title: 'Child menu 9 : parent 16',
          description: null,
          url: 'https://hester.name',
          enable: 0,
          createdAt: 2019-10-05T09:49:57.000Z,
          updatedAt: 2019-10-05T09:49:58.000Z } ]

Desire output:

[ { id: 10,
          lft: 1,
          rgt: 20,
          level: 0,
          rootId: 10,
          title: 'Root Menu 9',
          description:
           'Hic in molestiae sed vel sit quia. Totam nulla adipisci porro. Ipsam quo eligendi aut voluptatum nihil. Nesciunt voluptates doloribus pariatur dolor repudiandae.',
          url: '/page/9',
          enable: 1,
          createdAt: 2019-10-05T09:49:57.000Z,
          updatedAt: 2019-10-05T09:49:58.000Z
         // here
        children: [
           // same as above
        ],
 } ]

My full table json dump: https://paste.ubuntu.com/p/67sy2dhJkr/

Where is children of children of children.

i'v try depth 10 or any number but array return me one step of child.

Use case VueJS Menu: https://jsfiddle.net/chrisvfritz/pnqzspoe

Wrong parent of item when creating item in next branch

As example I'm trying to create Tag nested set with tree structure like this:

Electronics
- TVs
- - Apple
- - Samsung
- Phones
- - Xiaomi

Here is the code:

const { Sequelize, DataTypes, queryInterface } = require("sequelize");
const ns = require('sequelize-nested-set');

(async () => {

  const sequelize = new Sequelize({
    dialect: 'sqlite',
    storage: 'database.sqlite'
  });

  await sequelize.authenticate();

  await sequelize.getQueryInterface().dropAllTables();

  const Tag = ns(sequelize, DataTypes, 'Tag', {
    label: DataTypes.STRING,
  }, {
    tableName: 'tag',
    timestamps: false,
    hasManyRoots: true,
  });

  Tag.sync();

  const printTree = async function (ns) {
    const items = await ns.fetchTree(10);
    let strings = [];
    for (let item of items) {
      // console.log(item)
      let parent = await item.getParent();
      strings.push(("- ".repeat(item.level) + `${item.label} [id ${item.id}, parentId ` + (parent.id || '-') + `]`));
    }
    console.log(strings.join("\n"));
  }

  let tagElectronics = new Tag(); tagElectronics.label = 'Electronics';
  tagElectronics = await Tag.createRoot(tagElectronics);

  let tagPhones = new Tag(); tagPhones.label = 'Phones';
  await tagElectronics.addChild(tagPhones);

  let tagTVs = new Tag(); tagTVs.label = 'TVs';
  await tagElectronics.addChild(tagTVs);

  let tagSamsung = new Tag(); tagSamsung.label = 'Samsung';
  await tagTVs.addChild(tagSamsung);

  let tagApple = new Tag(); tagApple.label = 'Apple';
  await tagTVs.addChild(tagApple);

  let tagXiaomi = new Tag(); tagXiaomi.label = 'Xiaomi';
  await tagPhones.addChild(tagXiaomi);

  await printTree(Tag);
})();

But as result I see that Xiaomi item is placed in wrong parent branch! Here is output:

Electronics [id 1, parentId -]
- TVs [id 3, parentId 1]
- - Xiaomi [id 6, parentId 3]
- - Apple [id 5, parentId 3]
- - Samsung [id 4, parentId 3]
- Phones [id 2, parentId 1]

Can you please describe why this happens? Maybe some code parts are wrong?

Write docs for API

Here is the full list of functions:

  • createRoot
  • fetchRoot
  • fetchTree
  • fetchRoots
  • hasPrevSibling
  • hasNextSibling
  • hasChildren
  • hasParent
  • getPrevSibling
  • getNextSibling
  • getSiblings
  • getFirstChild
  • getLastChild
  • getChildren
  • getDescendants
  • getParent
  • getAncestors
  • getNumberChildren
  • getNumberDescendants
  • insertAsParentOf
  • insertAsPrevSiblingOf
  • insertAsNextSiblingOf
  • insertAsFirstChildOf
  • insertAsLastChildOf
  • moveAsPrevSiblingOf
  • moveAsNextSiblingOf
  • moveAsFirstChildOf
  • moveAsLastChildOf
  • makeRoot
  • addChild
  • isLeaf
  • isRoot
  • isEqualTo
  • isDescendantOf
  • isDescendantOfOrEqualTo
  • isAncestorOf
  • isValidNode
  • detach
  • delete

Write tests for multi-root trees

Here is the full list of functions:

  • createRoot
  • fetchRoot
  • fetchTree
  • fetchRoots
  • hasPrevSibling
  • hasNextSibling
  • hasChildren
  • hasParent
  • getPrevSibling
  • getNextSibling
  • getSiblings
  • getFirstChild
  • getLastChild
  • getChildren
  • getDescendants
  • getParent
  • getAncestors
  • getNumberChildren
  • getNumberDescendants
  • insertAsParentOf
  • insertAsPrevSiblingOf
  • insertAsNextSiblingOf
  • insertAsFirstChildOf
  • insertAsLastChildOf
  • moveBetweenTrees
  • moveAsPrevSiblingOf
  • moveAsNextSiblingOf
  • moveAsFirstChildOf
  • moveAsLastChildOf
  • makeRoot
  • addChild
  • isLeaf
  • isRoot
  • isEqualTo
  • isDescendantOf
  • isDescendantOfOrEqualTo
  • isAncestorOf
  • isValidNode
  • detach
  • delete
  • insertNode
  • updateNode
  • shiftRlValues
  • shiftRlRange

getChildren() doesn't return all children, always return 1 less child

For the code below, I expect getChildren() to return Phones and TVs but it only returns Phones.

const { Sequelize, DataTypes, BulkRecordError } = require('sequelize');
const ns = require('sequelize-nested-set');

// Connect to database.
const sequelize = new Sequelize({
    dialect: 'sqlite',
    storage: 'treeorg.sqlite'
});

// Define Tree model.
const Tree = ns(sequelize
                , DataTypes
                , 'Tree'
                , { label: DataTypes.STRING }
                , { //tableName: 'Tree'
                    timestamps: false
                    , hasManyRoots: true
                    , levelColumnName: 'level'
                }
);

sequelize.sync()
    .then(() => {

        (async () => {

            let electronics = new Tree(); electronics.label = 'Electronics';
            electronics = await Tree.createRoot(electronics);
          
            let phones = new Tree(); phones.label = 'Phones';
            await electronics.reload();
            await electronics.addChild(phones);

                let xiaomi = new Tree(); xiaomi.label = 'Xiaomi';
                await phones.reload();
                await phones.addChild(xiaomi);

            let TVs = new Tree(); TVs.label = 'TVs';
            await electronics.reload();
            await electronics.addChild(TVs);


            // Test getChildren()----------------------------------------
            let children = await electronics.getChildren();
            console.log(children);

        })();

    });

Output

[
  Tree {
    dataValues: { id: 2, lft: 2, rgt: 5, level: 1, rootId: 1, label: 'Phones' },
    _previousDataValues: { id: 2, lft: 2, rgt: 5, level: 1, rootId: 1, label: 'Phones' },
    uniqno: 1,
    _changed: Set(0) {},
    _options: {
      isNewRecord: false,
      _schema: null,
      _schemaDelimiter: '',
      raw: true,
      attributes: [Array]
    },
    isNewRecord: false
  }
]
id  lft  rgt  level  root_id  label      
--  ---  ---  -----  -------  -----------
1   1    8    0      1        Electronics
2   2    5    1      1        Phones
3   3    4    2      1        Xiaomi
4   6    7    1      1        TVs

How to add child node to root?

How to add child node to root node? I got error message "TypeError: root.addChild is not a function". Here is my code.

const { Sequelize, DataTypes, BulkRecordError } = require('sequelize');
const ns = require('sequelize-nested-set');

// Connect to database.
const sequelize = new Sequelize({
    dialect: 'sqlite',
    storage: 'treeorg.sqlite'
});

// Define Tree model.
const Tree = ns(sequelize
                , DataTypes
                , 'Tree'
                , { label: DataTypes.STRING }
                , { //tableName: 'Tree'
                    timestamps: false
                    , hasManyRoots: true
                    , levelColumnName: 'level'
                }
);

// Create root record.
sequelize.sync()
    .then(() => {

        let root = new Tree();
        root.label = 'Root record';
        root = Tree.createRoot(root);

        let childNode = new Tree();
        childNode.label = 'child node';
        root.addChild(childNode);
    });

Make rootId param optional

Actually we need rootId param only for multi-root trees.

Here is a list of API functions with the issue:

  • Model.fetchRoot = async function (rootId = 1, options = {})
  • Model.fetchTree = async function (depth = 0, rootId = 1, options = {})

Also several private functions:

  • Model.prototype.shiftRlValues = async function (first, delta, rootId = 1, options = {})
  • Model.prototype.shiftRlRange = async function (first, last, delta, rootId = 1, options = {})

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.