171

I'm currently having problems in creating a schema for the document below. The response from the server always returns the "trk" field values as [Object]. Somehow I have no idea how this should work, as I tried at least all approaches which made sense to me ;-)

If this helps, my Mongoose version is 3.6.20 and MongoDB 2.4.7 And before I forget, it would be nice to also set it as Index (2d)

Original data:

{
    "_id": ObjectId("51ec4ac3eb7f7c701b000000"),
    "gpx": {
        "metadata": {
            "desc": "Nürburgring VLN-Variante",
            "country": "de",
            "isActive": true
        },
    "trk": [
    {
        "lat": 50.3299594,
        "lng": 6.9393006
    },
    {
        "lat": 50.3295046,
        "lng": 6.9390688
    },
    {
        "lat": 50.3293714,
        "lng": 6.9389939
    },
    {
        "lat": 50.3293284,
        "lng": 6.9389634
    }]
    }
}

Mongoose Schema:

var TrackSchema = Schema({
            _id: Schema.ObjectId,
            gpx: {
                metadata: {
                    desc: String,
                    country: String,
                    isActive: Boolean
                },
                trk: [{lat:Number, lng:Number}]
            }
        }, { collection: "tracks" });

The response from the Network tab in Chrome always looks like this (that's only the trk-part which is wrong) :

{ trk: 
      [ [Object],
        [Object],
        [Object],
        [Object],
        [Object],
        [Object],

I already tried different Schema definitions for "trk":

  1. trk: Schema.Types.Mixed
  2. trk: [Schema.Types.Mixed]
  3. trk:[ { type:[Number], index: "2d" }]

Hope you can help me ;-)

niels_h
  • 1,869
  • 2
  • 13
  • 15

6 Answers6

317

You can declare trk by the following ways : - either

trk : [{
    lat : String,
    lng : String
     }]

or

trk : { type : Array , "default" : [] }

In the second case during insertion make the object and push it into the array like

db.update({'Searching criteria goes here'},
{
 $push : {
    trk :  {
             "lat": 50.3293714,
             "lng": 6.9389939
           } //inserted data is the object to be inserted 
  }
});

or you can set the Array of object by

db.update ({'seraching criteria goes here ' },
{
 $set : {
          trk : [ {
                     "lat": 50.3293714,
                     "lng": 6.9389939
                  },
                  {
                     "lat": 50.3293284,
                     "lng": 6.9389634
                  }
               ]//'inserted Array containing the list of object'
      }
});
Kundu
  • 3,944
  • 3
  • 16
  • 21
  • any idea how to name html fields in such case i.e in case we need to store an array of javascript objects in database? For e.g naming fields as `trk.lat` and `trk.lng` in html wont work. – Raeesaa Apr 10 '14 at 06:53
  • 5
    trk : { type : Array , "default" : [] } works best for me! It's simple and elegant! – spiralmoon Jun 02 '14 at 21:30
  • 1
    @DpGeek if you are declaring array in that format you can not update the array field directly. To update the array directly I used {lat : String,lng : String} subschema. If you do not want that facility then trk : { type : Array , "default" : [] } will be the best otherwise you have to declare the subschema. – Kundu Jun 04 '14 at 05:01
  • 1
    default without quotes worked for me `trk : { type : Array , default : ['item1', 'item2'] }` – Shardul Jul 19 '15 at 00:45
  • 1
    would it still work if the 'lat' and 'lng' fields where defined as Number instead of string? – jimijazz Mar 01 '16 at 15:10
  • @Kundu Hey how to search depending on search field – Apoorva sahay Jul 12 '16 at 16:54
  • @Apoorvasahay search criteria is an object. It is depend on you how you want to find an item. Once you provide search criteria mongo will find the items for you as per the criteria. – Kundu Jul 14 '16 at 05:46
67

I had a similar issue with mongoose :

fields: 
    [ '[object Object]',
     '[object Object]',
     '[object Object]',
     '[object Object]' ] }

In fact, I was using "type" as a property name in my schema :

fields: [
    {
      name: String,
      type: {
        type: String
      },
      registrationEnabled: Boolean,
      checkinEnabled: Boolean
    }
  ]

To avoid that behavior, you have to change the parameter to :

fields: [
    {
      name: String,
      type: {
        type: { type: String }
      },
      registrationEnabled: Boolean,
      checkinEnabled: Boolean
    }
  ]
Pierre Maoui
  • 5,976
  • 2
  • 27
  • 28
  • 4
    hell yeah, didn't even think about that. That solved my issue right before I was about to start slamming stuff on my desk haha thanks again. I'm going to just avoid 'type' in my mongoose schemas from now on. – blackops Jun 04 '15 at 21:30
  • Can you give an example of the json you were trying to insert? – owensmartin Sep 28 '15 at 01:27
  • 2
    or you could pass the typeKey option to your schema builder to override the type declaration – jimijazz Mar 01 '16 at 15:12
11

For making an array in schema we have to make one more schema as monetizationSchema which is used to store one data at a time and another as blogSchema we have monetization field containing monetizationSchema in square brackets as an array.

Schema for storing one data at a time.

const monetizationSchema = new Schema({
      amazonUrl: {
        type: String,
        required: true,
      } 
    });

Schema with monetization as an array.

const blogSchema = {
  monetization: [
   monetizationSchema
  ],
  image: {
   type: String,
   required: true
  },
  // ... etc
});
Harshit Gupta
  • 148
  • 3
  • 10
9

You can declare an array as below

trk : [{
    lat : String,
    lng : String
}]

but it will set [] (empty array) as the default value.

If you don't want this default value then to overwrite this default, you need to set the default value to undefined as below

trk: {
    type: [{
        lat : String,
        lng : String
    }],
    default: undefined
}
RobC
  • 22,977
  • 20
  • 73
  • 80
Manoj Rana
  • 3,068
  • 1
  • 24
  • 34
6

The problem I need to solve is to store contracts containing a few fields (address, book, num_of_days, borrower_addr, blk_data), blk_data is a transaction list (block number and transaction address). This question and answer helped me. I would like to share my code as below. Hope this helps.

  1. Schema definition. See blk_data.
var ContractSchema = new Schema(
    {
        address: {type: String, required: true, max: 100},  //contract address
        // book_id: {type: String, required: true, max: 100},  //book id in the book collection
        book: { type: Schema.ObjectId, ref: 'clc_books', required: true }, // Reference to the associated book.
        num_of_days: {type: Number, required: true, min: 1},
        borrower_addr: {type: String, required: true, max: 100},
        // status: {type: String, enum: ['available', 'Created', 'Locked', 'Inactive'], default:'Created'},

        blk_data: [{
            tx_addr: {type: String, max: 100}, // to do: change to a list
            block_number: {type: String, max: 100}, // to do: change to a list
        }]
    }
);
  1. Create a record for the collection in the MongoDB. See blk_data.
// Post submit a smart contract proposal to borrowing a specific book.
exports.ctr_contract_propose_post = [

    // Validate fields
    body('book_id', 'book_id must not be empty.').isLength({ min: 1 }).trim(),
    body('req_addr', 'req_addr must not be empty.').isLength({ min: 1 }).trim(),
    body('new_contract_addr', 'contract_addr must not be empty.').isLength({ min: 1 }).trim(),
    body('tx_addr', 'tx_addr must not be empty.').isLength({ min: 1 }).trim(),
    body('block_number', 'block_number must not be empty.').isLength({ min: 1 }).trim(),
    body('num_of_days', 'num_of_days must not be empty.').isLength({ min: 1 }).trim(),

    // Sanitize fields.
    sanitizeBody('*').escape(),
    // Process request after validation and sanitization.
    (req, res, next) => {

        // Extract the validation errors from a request.
        const errors = validationResult(req);
        if (!errors.isEmpty()) {
            // There are errors. Render form again with sanitized values/error messages.
            res.status(400).send({ errors: errors.array() });
            return;
        }

        // Create a Book object with escaped/trimmed data and old id.
        var book_fields =
            {
                _id: req.body.book_id, // This is required, or a new ID will be assigned!
                cur_contract: req.body.new_contract_addr,
                status: 'await_approval'
            };

        async.parallel({
            //call the function get book model
            books: function(callback) {
                Book.findByIdAndUpdate(req.body.book_id, book_fields, {}).exec(callback);
            },
        }, function(error, results) {
            if (error) {
                res.status(400).send({ errors: errors.array() });
                return;
            }

            if (results.books.isNew) {
                // res.render('pg_error', {
                //     title: 'Proposing a smart contract to borrow the book',
                //     c: errors.array()
                // });
                res.status(400).send({ errors: errors.array() });
                return;
            }

            var contract = new Contract(
                {
                    address: req.body.new_contract_addr,
                    book: req.body.book_id,
                    num_of_days: req.body.num_of_days,
                    borrower_addr: req.body.req_addr

                });

            var blk_data = {
                    tx_addr: req.body.tx_addr,
                    block_number: req.body.block_number
                };
            contract.blk_data.push(blk_data);

            // Data from form is valid. Save book.
            contract.save(function (err) {
                if (err) { return next(err); }
                // Successful - redirect to new book record.
                resObj = {
                    "res": contract.url
                };
                res.status(200).send(JSON.stringify(resObj));
                // res.redirect();
            });

        });

    },
];
  1. Update a record. See blk_data.
// Post lender accept borrow proposal.
exports.ctr_contract_propose_accept_post = [

    // Validate fields
    body('book_id', 'book_id must not be empty.').isLength({ min: 1 }).trim(),
    body('contract_id', 'book_id must not be empty.').isLength({ min: 1 }).trim(),
    body('tx_addr', 'tx_addr must not be empty.').isLength({ min: 1 }).trim(),
    body('block_number', 'block_number must not be empty.').isLength({ min: 1 }).trim(),

    // Sanitize fields.
    sanitizeBody('*').escape(),
    // Process request after validation and sanitization.
    (req, res, next) => {

        // Extract the validation errors from a request.
        const errors = validationResult(req);
        if (!errors.isEmpty()) {
            // There are errors. Render form again with sanitized values/error messages.
            res.status(400).send({ errors: errors.array() });
            return;
        }

        // Create a Book object with escaped/trimmed data
        var book_fields =
            {
                _id: req.body.book_id, // This is required, or a new ID will be assigned!
                status: 'on_loan'
            };

        // Create a contract object with escaped/trimmed data
        var contract_fields = {
            $push: {
                blk_data: {
                    tx_addr: req.body.tx_addr,
                    block_number: req.body.block_number
                }
            }
        };

        async.parallel({
            //call the function get book model
            book: function(callback) {
                Book.findByIdAndUpdate(req.body.book_id, book_fields, {}).exec(callback);
            },
            contract: function(callback) {
                Contract.findByIdAndUpdate(req.body.contract_id, contract_fields, {}).exec(callback);
            },
        }, function(error, results) {
            if (error) {
                res.status(400).send({ errors: errors.array() });
                return;
            }

            if ((results.book.isNew) || (results.contract.isNew)) {
                res.status(400).send({ errors: errors.array() });
                return;
            }

            var resObj = {
                "res": results.contract.url
            };
            res.status(200).send(JSON.stringify(resObj));
        });
    },
];
GoodApple
  • 171
  • 1
  • 3
2

Thanks for the replies.

I tried the first approach, but nothing changed. Then, I tried to log the results. I just drilled down level by level, until I finally got to where the data was being displayed.

After a while I found the problem: When I was sending the response, I was converting it to a string via .toString().

I fixed that and now it works brilliantly. Sorry for the false alarm.

royhowie
  • 11,075
  • 14
  • 50
  • 67
niels_h
  • 1,869
  • 2
  • 13
  • 15