lykmapipo / mongoose-gridfs

mongoose gridfs on top of new gridfs api
MIT License
91 stars 39 forks source link

Using generic mongoose, not the real one #33

Closed chumager closed 6 years ago

chumager commented 6 years ago

Hi, AFAIK some statics and methods refers to mongoose and not the real db, in my case i've several connections with mongoose.Mongoose, and when tried to unlink or use another function referred to mongoose it throws an error.

You just have to change all mongoose for this inside all methods and statics.

regards.

lykmapipo commented 6 years ago

@chumager I will appreciate the spec to reproduce the problem you are facing.

chumager commented 6 years ago

sorry for the delay, my github mails go to a filtered place.

Let's begin... first of all you use mongoose, you should let the developer give you the connection, and use it, for everything, so when you declare a Schema or Model, they will get in the correct object.

second you use getters in the storage, so, in the meanwhile you don't ask for them, they ain't exist...

third in the schema declaration, inside methods and statics you use mongoose object, this is the standard for connections, but not the only way to connect.

I use let db = new mongoose.Mongoose(); because I connect to severan DBs... I can develop a PR if you like, either way I had to change your code to make my programs work.

chumager commented 6 years ago

After looking all your code, IMHO, will be better to return the model, and apply all the statics and methods directly to the schema. so you should pass all the storage logic into the schema, use promises instead of callbacks...

What do you think?

Regards.

chumager commented 6 years ago

Hi this is my test, to provide stream, promises and CB, I just develop the methods a needed, take a look and tell what do you think.

BTW, if you ain't like Q promises you can change them with new Promise

read-all-stream is not the most popular module, but it allows to use promises and cb and define the encode option so you can get a buffer of a string,

"use strict";
const mongoose = require("mongoose");
const fs = require("fs");
const Q = require("q");
Q.longStackSupport = true;
let db = new mongoose.Mongoose();
const Grid = require("gridfs-stream");
const read = require("read-all-stream");
db.connect("mongodb://localhost/test").then(()=>{
    console.log("connected");

    const bucket =  "fs";
    const gfs = new Grid(db.connection.db, db.mongo);
    let File = new db.Schema({//Esquema base de los usuarios.
        length: {
            type: Number
        },
        chunkSize: {
            type: Number
        },
        uploadDate: {
            type: Date
        },
        md5: {
            type: String
        },
        filename: {
            type: String
        },
        contentType: {
            type: String
        },
        metadata: {
            type: Object
        },
        path:{
            type:String,
            readonly:true
        }
    },{collection:`${bucket}.files`});
    File.path("_id").options.hidden=true;
    const fixQuery = q=>{
        if(q.contentType){
            q.content_type = q.contetType;
            delete q.contentType;
        }
        q.root = bucket;
        return q;
    };
    File.method({
        read(){
            let query = this.toJSON({setters:false,getters:false,versionKey:false});
            let res = gfs.createReadStream(fixQuery(query));
            const handler = {
                get(target,prop){
                    switch(prop){
                        case "then":
                        case "catch":
                            //pseudo promise;
                            let r = read(res,null);
                            return r[prop].bind(r);
                        case "raw":
                            //in case to redefine options or wanting CB
                            return (...args)=>read(res,...args);
                        default:
                            return Reflect.get(target,prop);
                    }
                }
            };
            return new Proxy(res,handler);
        },
    });
    File.method("remove",function remove(){
        let q = {_id:this._id};
        return Q.Promise((res,rej)=>{
            gfs.remove(fixQuery(q),err=>{
                if(err) rej(err);
                res();
            });
        });
    },{ 
        suppressWarning: true 
    });
    File.static({
        write(file){
            let self = this;
            let query = fixQuery(file);
            let res = gfs.createWriteStream(query);
            let resolve, reject;
            let p = new Promise((res,rej)=>{
                resolve = res;
                reject = rej;
            });
            res.on("error",err=>{
                reject(err);
            });
            res.on("close",file=>{
                resolve(self.findById(file._id));
            });
            const handler = {
                get(target,prop){
                    switch(prop){
                        case "then":
                        case "catch":
                            return p[prop].bind(p);
                        default:
                            return Reflect.get(target,prop);
                    }
                }
            };
            return new Proxy(res,handler);

        },
        remove(q){
            return Q.Promise((res,rej)=>{
                gfs.remove(fixQuery(q),err=>{
                    if(err) rej(err);
                    res();
                });
            });
        },
        exist(q){
            return Q.Promise((res,rej)=>{
                gfs.exist(fixQuery(q),(err,found)=>{
                    if(err) rej(err);
                    res(found);
                });
            });
        },
    });
    //TEST!!!
    db.model("File",File);
    console.log("File model", db.models.File);
    let file = fs.createReadStream("test.txt");
    let writer = db.model("File").write({
        filename:"test",
        contentType:"application/octet-stream"
    });
    writer.then(res=>{
        console.log("result",res);
        db.models.File.exist(res).then(console.log,console.error);
        return Q.all([
            res,//to keep the data
            res.read(),
            //with a grapper to conver it into a promise but it's a CB anyway
            new Promise(r=>{
                res.read().raw(null,(err,data)=>{
                    r(data);
                });
            }),
            res.read().raw(null),
        ]);
    }).then(res=>{
        console.log(res);
        console.log("writing into stream");
        let arch = fs.createWriteStream("test2.txt");
        let reader = res[0].read();
        return new Promise(resolve=>{
            arch.on("close",()=>{
                console.log("arch writed");
                resolve(res[0]);
            });
            reader.pipe(arch);
        });

    }).then(res=>{
        console.log("removing file");
        return res.remove();
    }).then(()=>{
        console.log("file removed");
    }).catch(err=>{
        console.log("ERROR",err);
    });
    file.pipe(writer);

}).catch(console.error);
lkho commented 6 years ago

Hi everyone, I am facing the same problem when I use a separate mongoose connection. I came across this bug while I tried to write into gridfs, the callback never fires.

      let conn = await mongoose.createConnection(config, { keepAlive: true });
      let gridfs = require('mongoose-gridfs')({
        collection: 'fs',
        model: 'gridfile',
        mongooseConnection: conn,
      });
      // .... apply plugins to gridfs.schema
      let GridFile = conn.model('gridfile', gridfs.schema); // actually this line is also wrong but I can't think of how to register the model in my own `conn`, and gridfs.model is readonly
      (new GridFile({
        filename: 'a'
      })).write(stream, (err, file) => { console.log(file); }); // cb won't fire

In which I was unaware that the underlying Grid was using my conn to write the file while the GridFSSchema.methods.write is using the default connection and for some reason the done is never called at https://github.com/lykmapipo/mongoose-gridfs/blob/40d05e975c32a0bdc9626eb316504ca8db6b967f/lib/schema.js#L83

lykmapipo commented 6 years ago

@lkho Appreciated for the trace.