简体   繁体   English

如何设计一个链接的Model.find()函数?

[英]How to design a chained Model.find() function?

I'm trying to write a ORM in Node.js. 我正在尝试在Node.js中编写ORM。 I want to declare a class named Model which will be used to declare a data object, like: 我想声明一个名为Model的类,该类将用于声明数据对象,例如:

Users = new Model(someModelRules);
newUser = new Users(userInfomation);

the data model User have a function named find() . 数据模型User具有一个名为find()的函数。 Now, I want to make find() chained, like: 现在,我想将find()链接起来,例如:

Users.find(" name = 'John' ")
.orderedBy("age").desc()
.limit(0,10)

or maybe just a simply find : 或只是一个简单的find

Users.find(" name = 'John' ")

to code this find function, I believe I must build the SQL first,and do the SQL query at the end of this find chain. 要编写此find功能的代码,我相信我必须先构建SQL,然后在此find链的末尾进行SQL查询。

I don't know how to do this, all I can think of is to add a function like: doQuery() , so I will know it's time to do the SQL query when the doQuery() function was called, like: 我不知道该怎么做,我所能想到的就是添加一个函数: doQuery() ,所以我知道该在doQuery()函数时执行SQL查询了,例如:

Users.find(" name = 'John' ")
.orderedBy("age").desc()
.limit(0,10)
.doQuery();

I know this is a simply solution, but I don't want the extra doQuery() function. 我知道这是一个简单的解决方案,但是我不需要额外的doQuery()函数。 :( :(

So, how should I design this? 那么,我应该如何设计呢? It would so nice of you if you can show me some example code with comments. 如果您可以向我展示一些带有注释的示例代码,那对您真是太好了。

Thx! 谢谢! (sorry for my poor English) (对不起,我英语不好)

ps. PS。 I know the ORM2 has a find function I just want, but I wanna know how to code it and I can barely understand the code in ORM2 as there are no comments. 我知道ORM2有一个我只想要的查找功能,但是我想知道如何编写它,由于没有注释,所以我几乎无法理解ORM2中的代码。 (I'm not gonna use orm2.) (我不会使用orm2。)

================================= SOLUTION ============================== ===============================解决方案================ ==============

Inspired by @bfavaretto : 受到@bfavaretto的启发:

function User() {
    this.find = function(id, condition) {
        return new findChain(id, condition);
    }
}


function findChain(id, condition) {
    this._id = id
    this._condition = condition
    this.queryTimerSet = false;

    this.scheduleQuery = function () {
        var self = this;
        if(!self.queryTimerSet) {
            console.log('[TEST CASE: ' + self._id + '] Insert query into eventLoop');
            setTimeout(function(){
                console.log('[TEST CASE: ' + self._id + '] Start query: '+self._condition);
            }, 0);
            self.queryTimerSet = true;
        } else {
            console.log('[TEST CASE: ' + self._id + '] No need to insert another query');
        }
    }

    this.orderedBy = function(column) {
        console.log('[TEST CASE: ' + this._id + '] orderedBy was called');
        this._condition = this._condition + ' ORDER BY ' + column
        this.scheduleQuery();
        return this;
    }

    this.desc = function() {
        // simply add DESC to the end of sql
        this._condition = this._condition + ' DESC'
    }

    this.scheduleQuery();

}


var user = new User();
user.find(1,'SELECT * FROM test').orderedBy('NAME1').desc();
user.find(2,'SELECT * FROM test').orderedBy('NAME2');
user.find(3,'SELECT * FROM test');

runnning this code, you will get the result: 运行此代码,您将获得结果:

[TEST CASE: 1] Insert query into eventLoop
[TEST CASE: 1] orderedBy was called
[TEST CASE: 1] No need to insert another query
[TEST CASE: 2] Insert query into eventLoop
[TEST CASE: 2] orderedBy was called
[TEST CASE: 2] No need to insert another query
[TEST CASE: 3] Insert query into eventLoop
[TEST CASE: 1] Start query: SELECT * FROM test ORDER BY NAME1 DESC
[TEST CASE: 2] Start query: SELECT * FROM test ORDER BY NAME2
[TEST CASE: 3] Start query: SELECT * FROM test

I believe there must be a better way to achieve this, but this is the best I can get for now. 我相信必须有一种更好的方法来实现这一目标,但这是我目前可以获得的最好方法。 Any comments? 任何意见?

It is possible to achieve that if you schedule the doQuery logic to run asynchronously (but as soon as possible). 如果将doQuery逻辑安排为异步运行(但尽快),则有可能实现这一目标。 I am thinking on something like this: 我在想这样的事情:

function User() {

    // Flag to control whether a timer was already setup
    var queryTimerSet = false;

    // This will schedule the query execution to the next tick of the
    // event loop, if it wasn't already scheduled.
    // This function is available to your model methods via closure.
    function scheduleQuery() {
        if(!queryTimerSet) {
            setTimeout(function(){
                // execute sql
                // from the query callback, set queryTimerSet back to false
            }, 0);
            queryTimerSet = true;
        }
    }

    this.find = function() {
        // ... logic that builds the sql

        scheduleQuery();
        return this;
    }

    this.orderedBy = function() {
        // ... logic that appends to the sql

        scheduleQuery();
        return this;
    }

    // etc.
}

One totally different approach is to have a single method for building the SQL, and passing the ORDER BY and LIMIT parameters in an options object. 一种完全不同的方法是使用一种方法来构建SQL,并在options对象中传递ORDER BY和LIMIT参数。 Then your call would look like this: 然后您的呼叫将如下所示:

user.find({
    what : " name = 'John' ",
    orderedBy : "age DESC",
    limit : "0,10"
});

This is more suited for SQL queries than what you're trying to do. 比您要执行的操作更适合SQL查询。 What you have looks like noSQL stuff like MongoDB, where fetching the records and sorting are separate operations (I think). 您所拥有的看起来像noSQL之类的东西,例如MongoDB,其中获取记录和排序是单独的操作(我认为)。

You will always have to have a execute/doQuery function at the end of the chain. 您将始终必须在链的末尾具有execute / doQuery函数。 This is because all the other functions before the doQuery help build the query that needs to be executed at the end. 这是因为doQuery之前的所有其他功能都有助于构建需要在最后执行的查询。

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

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