简体   繁体   English

如何在几乎整个postgresql数据库中查询每个项目的属性? (使用sequelize和Node.js)

[英]How to query almost the entire postgresql database for a property on every item? (Using sequelize and Node.js)

I'm trying to scan through an entire tree in my database, looking for two properties ('title' and 'id') for every item, and then I need to check if there's a linked database table below the current table, and if so, I need to perform the same actions on that table. 我正在尝试浏览数据库中的整个树,为每个项目查找两个属性(“ title”和“ id”),然后需要检查当前表下方是否有链接数据库表,以及是否因此,我需要在该表上执行相同的操作。

Once I get the whole tree, I need to insert those into a master variable that I can then import elsewhere throughout my web app. 一旦获得完整的树,我需要将它们插入到主变量中,然后可以将其导入整个Web应用程序中的其他位置。

(I'm at the point where I'm pretty sure I'm reinventing the wheel. I've searched the Sequelize documentation, but if there's a simple solution, I didn't see it.) (我现在很确定自己正在重新发明轮子。我已经搜索了Sequelize文档,但是如果有一个简单的解决方案,我没有看到。)

Here's the relevant portions of my current code (it's not fully working yet, but should give the idea). 这是我当前代码的相关部分(它尚不能完全正常工作,但应该给出想法)。

( You can find the full project repository by clicking this link here. ) 您可以通过单击此处的链接找到完整的项目资源库。

``` ```

const buildTopicTreeFromCurrentDatabase = (callback) => {
  let topicTree = [];
  let isFinished = false;
  const isSearchFinished = new Emitter();
  console.log(`emitter created`);
  isSearchFinished.on('finished', () => {
    console.log(`the search is done`);
    if (isFinished = true) {
      callback(null, this.primaryTopicsShort)
    };
  });
  /* need to go back and refactor -- violates DRY */
  this.primaryTopicsShort = [];
  PrimaryTopic.all()
  .then((primaryTopics) => {
    if (primaryTopics.length === 0) {
      return callback('no Primary Topics defined');
    }
    for (let i = 0; i < primaryTopics.length; i++) {
      this.primaryTopicsShort[i] = {
        title: primaryTopics[i].title,
        id: primaryTopics[i].id,
        secondaryTopics: []
      };
      PrimaryTopic.findById(this.primaryTopicsShort[i].id, {
        include: [{
          model: SecondaryTopic,
          as: 'secondaryTopics'
        }]
      })
      .then((currentPrimaryTopic) => {
        if (currentPrimaryTopic.secondaryTopics.length !== 0) {
          for (let j = 0; j < currentPrimaryTopic.secondaryTopics.length; j++) {
            this.primaryTopicsShort[i].secondaryTopics[j] = {
              title: currentPrimaryTopic.secondaryTopics[j].title,
              id: currentPrimaryTopic.secondaryTopics[j].id,
              thirdTopics: []
            };
            SecondaryTopic.findById(this.primaryTopicsShort[i].secondaryTopics[j].id, {
              include: [{
                model: ThirdTopic,
                as: 'thirdTopics'
              }]
            })
            .then((currentSecondaryTopic) => {
              if (currentPrimaryTopic.secondaryTopics.length - 1 === j) {
                isSearchFinished.emit('finished');
              }
              if (currentSecondaryTopic.thirdTopics.length !== 0) {
                for (let k = 0; k < currentSecondaryTopic.thirdTopics.length; k++) {
                  this.primaryTopicsShort[i].secondaryTopics[j].thirdTopics[k] = {
                    title: currentSecondaryTopic.thirdTopics[k].title,
                    id: currentSecondaryTopic.thirdTopics[k].id,
                    fourthTopics: []
                  };
                  ThirdTopic.findById(this.primaryTopicsShort[i].secondaryTopics[j].thirdTopics[k].id, {
                    include: [{
                      model: FourthTopic,
                      as: 'fourthTopics'
                    }]
                  })
                  .then((currentThirdTopics) => {
                    if (currentThirdTopics.fourthTopics.length !== 0) {
                      for (let l = 0; l < currentThirdTopics.fourthTopics.length; l++) {
                        this.primaryTopicsShort[i].secondaryTopics[j].thirdTopics[k].fourthTopics[k] = {
                          title: currentThirdTopics.fourthTopics[l].title,
                          id: currentThirdTopics.fourthTopics[l].id,
                          fifthTopics: []
                        }
                        FourthTopic.findById(this.primaryTopicsShort[i].secondaryTopics[j].thirdTopics[k].fourthTopics[l].id, {
                          include: [{
                            model: FifthTopic,
                            as: 'fifthTopics'
                          }]
                        })
                        .then((currentFourthTopics) => {
                          if (currentFourthTopics.fifthTopics.length !== 0) {
                            for (let m = 0; m < currentFourthTopics.fifthTopics.length; m++) {
                              this.primaryTopicsShort[i].secondaryTopics[j].thirdTopics[k].fourthTopics[k].fifthTopics[m] = {
                                title: currentFourthTopics.fifthTopics[m].title,
                                id: currentFourthTopics.fifthTopics[m].id
                              }
                            }
                          }
                        })
                        .catch((err) => {
                          callback(err);
                        });
                      }
                    }
                  })
                  .catch((err) => {
                    callback(err)
                  });
                }
              }
            })
            .catch((err) => {
              callback(err);
            })
          }
        }
      })
      .catch((err) => {
        callback(err);
      })
    }
  })
  .catch((err) => {
    callback(err);
  });
};

There are a few problems with this code. 此代码存在一些问题。

First, I need to be using a DRY and recursive solution, since the database structure may change in the future. 首先,由于数据库结构将来可能会更改,因此我需要使用DRY和递归解决方案。

Second, I've played around a lot with the 'finished' emitter, but I haven't figured out yet how to place it so that the event is emitted at the end of searching the database, and also so that I don't cycle back through the database multiple times. 其次,我与“完成的”发射器玩了很多次,但是我还没有弄清楚如何放置它,以便在搜索数据库结束时发出该事件,并且也不要遍历数据库多次。

I've been working on the following recursive solution, but the hours keep stretching by and I don't feel like I'm getting anywhere. 我一直在研究以下递归解决方案,但是工作时间一直在延长,我觉得自己无处可去。

const buildDatabaseNames = (DatabaseNameStr, callback) => {
  let camelSingular = DatabaseNameStr.slice(0,1);
  camelSingular = camelSingular.toLowerCase();
  camelSingular = camelSingular + DatabaseNameStr.slice(1, DatabaseNameStr.length);
  let camelPlural = DatabaseNameStr.slice(0,1);
  camelPlural = camelPlural.toLowerCase();
  camelPlural = camelPlural + DatabaseNameStr.slice(1, DatabaseNameStr.length) + 's';
  let databaseNameStr = `{ "capsSingular": ${"\"" + DatabaseNameStr + "\""}, "capsPlural": ${"\"" + DatabaseNameStr + 's' + "\""}, "camelSingular": ${"\"" + camelSingular + "\""}, "camelPlural": ${"\"" + camelPlural + "\""} }`;
  let names = JSON.parse(databaseNameStr);
  return callback(names);
};

const isAnotherDatabase = (DatabaseName, id, NextDatabaseName) => {
  DatabaseName.findById({
    where: {
      id: id
    }
  })
  .then((res) => {
    if (typeof res.NextDatabaseName === undefined) {
      return false;
    } else if (res.NextDatabaseName.length === 0) {
      return false;
    } else {
      return true;
    }
  })
  .catch((err) => {
    console.error(err);
    process.exit();
  });
};

const searchDatabase = (first, i, current, next, list, callback) => {
  if (typeof first === 'string') {
    first = buildDatabaseNames(first);
    current = buildDatabaseNames(current);
    next = buildDatabaseNames(next);
  }
  if (first === current) {
    this.first = current;
    let topicTree = [];
    const isSearchFinished = new Emitter();
    console.log(`emitter created`);
    isSearchFinished.on('finished', () => {
      console.log(`the search is done`);
      callback(null, this.primaryTopicsShort);
    });
  }
  current.CapsSingular.all()
  .then((res) => {
    current.camelPlural = res;
    if (if current.camelPlural.length !== 0) {
      for (let j = 0; j < currentParsed.camelPlural.length; j++) {
        if (first === current) {
          this.first[i].current[j].title = current.camelPlural[j].title,
          this.first[i].current[j].id = current.camelPlural[j].id
          next.camelSingular = []
        } else {
          this.first[i]..current[j].title = current.camelPlural[j].title,
          this.first[i].id = current.camelPlural[j].id,
          next.camelSingular = []
        }
        let isNext = isAnotherDatabase(current.)
        searchDatabase(null, j, next, list[i + 1], list, callback).bind(this);
      }
    } else {
      callback(null, this.first);
    }
  })
  .catch((err) => {
    callback(err);
  });
};

I decided to stop and ask for help, as I just realized that in order to make the properties ( this.first[i].current[j].title = current.camelPlural[j].title ) on each recursive iteration accurate, I'll have to do a JSON.stringify , alter the string for the next iteration, place all the required iterations of itinto a variable, pass it into the next recursion, and then do JSON.parse again afterwards. 我决定停下来寻求帮助,因为我刚刚意识到,为了使每个递归迭代的属性( this.first[i].current[j].title = current.camelPlural[j].title )准确,我将必须执行JSON.stringify ,为下一次迭代更改字符串,将其所有必需的迭代放入变量中,将其传递到下一个递归中,然后再进行JSON.parse It seems like I'm making this ridiculously complicated? 似乎我在使这个荒谬的复杂?

Any help is appreciated, thank you. 任何帮助表示赞赏,谢谢。

While I wasn't able to make a recursive, generic solution, I did at least find some result. 虽然我无法做出一个递归的通用解决方案,但我至少找到了一些结果。

const buildTopicTreeFromCurrentDatabase = (callback) => {
  let topicTree = [];
  const isSearchFinished = new Emitter();
  isSearchFinished.on('finished', () => {
    if (isFinished === 0) {
      callback(null, this.primaryTopicsShort);
    };
  });
  /* need to go back and refactor -- violates DRY */
  this.primaryTopicsShort = [];
  let isFinished = 0;
  isFinished = isFinished++;
  PrimaryTopic.all()
  .then((primaryTopics) => {
    isFinished = isFinished + primaryTopics.length;
    if (primaryTopics.length === 0) {
      return callback('no Primary Topics defined');
    }
    for (let i = 0; i < primaryTopics.length; i++) {
      if (i === 0) {
        var isLastPrimary = false;
      };
      this.primaryTopicsShort[i] = {
        title: primaryTopics[i].title,
        id: primaryTopics[i].id,
        secondaryTopics: []
      };
      PrimaryTopic.findById(this.primaryTopicsShort[i].id, {
        include: [{
          model: SecondaryTopic,
          as: 'secondaryTopics'
        }]
      })
      .then((currentPrimaryTopic) => {
        isFinished = isFinished - 1 + currentPrimaryTopic.secondaryTopics.length;
        if (i === primaryTopics.length - 1) {
          isLastPrimary = true;
        };
        if (currentPrimaryTopic.secondaryTopics.length === 0 && isLastPrimary) {
          isSearchFinished.emit('finished');
        } else if (currentPrimaryTopic.secondaryTopics.length !== 0) {
          for (let j = 0; j < currentPrimaryTopic.secondaryTopics.length; j++) {
            if (j === 0) {
              var isLastSecondary = false;
            }
            this.primaryTopicsShort[i].secondaryTopics[j] = {
              title: currentPrimaryTopic.secondaryTopics[j].title,
              id: currentPrimaryTopic.secondaryTopics[j].id,
              thirdTopics: []
            };
            SecondaryTopic.findById(this.primaryTopicsShort[i].secondaryTopics[j].id, {
              include: [{
                model: ThirdTopic,
                as: 'thirdTopics'
              }]
            })
            .then((currentSecondaryTopic) => {
              isFinished = isFinished - 1 + currentSecondaryTopic.thirdTopics.length;
              if (j === currentPrimaryTopic.secondaryTopics.length - 1) {
                isLastSecondary = true;
              }
              if (currentSecondaryTopic.thirdTopics.length === 0 && isLastPrimary && isLastSecondary) {
                isSearchFinished.emit('finished');
              } else if (currentSecondaryTopic.thirdTopics.length !== 0) {
                for (let k = 0; k < currentSecondaryTopic.thirdTopics.length; k++) {
                  if (k === 0) {
                    var isLastThird = false;
                  };
                  this.primaryTopicsShort[i].secondaryTopics[j].thirdTopics[k] = {
                    title: currentSecondaryTopic.thirdTopics[k].title,
                    id: currentSecondaryTopic.thirdTopics[k].id,
                    fourthTopics: []
                  };
                  ThirdTopic.findById(this.primaryTopicsShort[i].secondaryTopics[j].thirdTopics[k].id, {
                    include: [{
                      model: FourthTopic,
                      as: 'fourthTopics'
                    }]
                  })
                  .then((currentThirdTopic) => {
                    isFinished = isFinished - 1 + currentThirdTopic.fourthTopics.length;
                    if (k === currentSecondaryTopic.thirdTopics.length - 1) {
                      isLastThird = true;
                    };
                    if (currentThirdTopic.fourthTopics.length === 0 && isLastPrimary && isLastSecondary && isLastThird) {
                      isSearchFinished.emit('finished');
                    } else if (currentThirdTopic.fourthTopics.length !== 0) {
                      for (let l = 0; l < currentThirdTopic.fourthTopics.length; l++) {
                        if (l = 0) {
                          var isLastFourth = false;
                        }
                        this.primaryTopicsShort[i].secondaryTopics[j].thirdTopics[k].fourthTopics[k] = {
                          title: currentThirdTopic.fourthTopics[l].title,
                          id: currentThirdTopic.fourthTopics[l].id,
                          fifthTopics: []
                        }
                        FourthTopic.findById(this.primaryTopicsShort[i].secondaryTopics[j].thirdTopics[k].fourthTopics[l].id, {
                          include: [{
                            model: FifthTopic,
                            as: 'fifthTopics'
                          }]
                        })
                        .then((currentFourthTopics) => {
                          isFinished = isFinished - 1 + currentFourthTopics.fifthTopics.length;
                          if (l = currentThirdTopic.fourthTopics.length - 1) {
                            isLastFourth = true;
                          }
                          if (currentFourthTopic.fifthTopics.length === 0 && isLastPrimary && isLastSecondary && isLastThird && isLastFourth) {
                            isSearchFinished.emit('finished');
                          } else if (currentFourthTopic.fifthTopics.length !== 0) {
                            for (let m = 0; m < currentFourthTopic.fifthTopics.length; m++) {
                              if (m = 0) {
                                var isLastFifth = false;
                              }
                              if (m === currentFourthTopic.fifthTopics.length - 1) {
                                isLastFifth = true;
                              }
                              this.primaryTopicsShort[i].secondaryTopics[j].thirdTopics[k].fourthTopics[k].fifthTopics[m] = {
                                title: currentFourthTopic.fifthTopics[m].title,
                                id: currentFourthTopic.fifthTopics[m].id
                              };
                              if (isLastPrimary === true && isLastSecondary === true && isLastThird === true && isLastFourth === true && isLastFifth === true) {
                                isFinished = isFinished - 1;
                                isSearchFinished.emit('finished');
                              };
                            }
                          }
                        })
                        .catch((err) => {
                          callback(err);
                        });
                      }
                    }
                  })
                  .catch((err) => {
                    callback(err)
                  });
                }
              }
            })
            .catch((err) => {
              callback(err);
            })
          }
        }
      })
      .catch((err) => {
        callback(err);
      });
    }
  })
  .catch((err) => {
    callback(err);
  });
};

I eventually learned that the root of the problem I face was in the asynchronous nature of the database calls. 最终,我了解到我所面对的问题的根源在于数据库调用的异步特性。

To prevent the final callback from getting fired before all the calls were complete, I used something called a semaphore. 为了防止在所有调用完成之前触发最终的callback ,我使用了一种称为信号量的东西。 It's a thing used often in other languages, but not often used in JavaScript (so I hear). 这是其他语言中经常使用的东西,但是在JavaScript中却不经常使用(所以我知道)。

You can see how it works by looking at the variable isFinished . 您可以通过查看isFinished变量来了解其工作原理。 The value starts at zero and goes up for every entry it retrieves from the database. 该值从零开始,并且从数据库检索到的每个条目都增加。 When it finishes processing the data, the code subtracts 1 from the total value. 完成数据处理后,代码将从总值中减去1 The final callback event (in the emitter), can only go off once the isFinished value returns to zero. 最后的callback事件(在发射器中)只能在isFinished值返回零时关闭。

This isn't the most elegant solution. 这不是最优雅的解决方案。 As @Timshel said, it probably would have been wiser not to design this database portion not have so many different tables. 正如@Timshel所说,不设计没有太多不同表的数据库部分可能会更明智。

But this solution will do for now. 但是此解决方案现在可以使用。

Thanks. 谢谢。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM