Comment définir correctement un objet dans un tableau dans un schéma Mongoose avec un index géographique 2D

113

Je rencontre actuellement des problèmes pour créer un schéma pour le document ci-dessous. La réponse du serveur renvoie toujours les valeurs du champ "trk" sous la forme [Object]. D'une manière ou d'une autre, je n'ai aucune idée de comment cela devrait fonctionner, car j'ai essayé au moins toutes les approches qui avaient du sens pour moi ;-)

Si cela aide, ma version Mongoose est 3.6.20 et MongoDB 2.4.7 Et avant que j'oublie, ce serait bien de le définir également comme Index (2d)

Données d'origine:

{
    "_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
    }]
    }
}

Schéma de la mangouste:

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

La réponse de l'onglet Réseau dans Chrome ressemble toujours à ceci (ce n'est que la partie trk qui est fausse):

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

J'ai déjà essayé différentes définitions de schéma pour "trk":

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

J'espère que vous pourrez m'aider ;-)

niels_h
la source

Réponses:

219

Vous pouvez déclarer trk des manières suivantes: - soit

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

ou

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

Dans le second cas, lors de l'insertion, créez l'objet et poussez-le dans le tableau comme

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

ou vous pouvez définir le tableau d'objets par

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
la source
une idée comment nommer les champs html dans ce cas, c'est-à-dire au cas où nous aurions besoin de stocker un tableau d'objets javascript dans la base de données? Par exemple, nommer les champs en html trk.latet trk.lngen html ne fonctionnera pas.
Raeesaa
3
trk: {type: Array, "default": []} fonctionne le mieux pour moi! C'est simple et élégant!
spiralmoon
1
@DpGeek si vous déclarez un tableau dans ce format, vous ne pouvez pas mettre à jour le champ du tableau directement. Pour mettre à jour le tableau directement, j'ai utilisé le sous-schéma {lat: String, lng: String}. Si vous ne voulez pas de cette fonctionnalité, trk: {type: Array, "default": []} sera le meilleur sinon vous devrez déclarer le sous-schéma.
Kundu le
par défaut sans guillemets a fonctionné pour moitrk : { type : Array , default : ['item1', 'item2'] }
Shardul
1
cela fonctionnerait-il encore si les champs «lat» et «lng» étaient définis comme Number au lieu de string?
jimijazz
63

J'ai eu un problème similaire avec la mangouste:

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

En fait, j'utilisais "type" comme nom de propriété dans mon schéma:

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

Pour éviter ce comportement, vous devez changer le paramètre en:

fields: [
    {
      name: String,
      type: {
        type: { type: String }
      },
      registrationEnabled: Boolean,
      checkinEnabled: Boolean
    }
  ]
Pierre Maoui
la source
4
bon sang ouais, je n'y ai même pas pensé. Cela a résolu mon problème juste avant de commencer à claquer des trucs sur mon bureau, merci encore. Je vais simplement éviter de «taper» dans mes schémas de mangouste à partir de maintenant.
blackops
Pouvez-vous donner un exemple du json que vous essayiez d'insérer?
owensmartin le
1
ou vous pouvez passer l'option typeKey à votre constructeur de schéma pour remplacer la déclaration de type
jimijazz
2

Merci pour les réponses.

J'ai essayé la première approche, mais rien n'a changé. Ensuite, j'ai essayé d'enregistrer les résultats. J'ai juste exploré niveau par niveau, jusqu'à ce que j'arrive enfin à l'endroit où les données étaient affichées.

Au bout d'un moment, j'ai trouvé le problème: lorsque j'envoyais la réponse, je la convertissais en une chaîne via .toString().

J'ai corrigé cela et maintenant cela fonctionne à merveille. Désolé pour la fausse alarme.

niels_h
la source
1

Le problème que je dois résoudre est de stocker des contrats contenant quelques champs (adresse, carnet, num_of_days, borrower_addr, blk_data), blk_data est une liste de transactions (numéro de bloc et adresse de transaction). Cette question et réponse m'a aidé. Je souhaite partager mon code comme ci-dessous. J'espère que cela t'aides.

  1. Définition du schéma. Voir 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. Créez un enregistrement pour la collection dans MongoDB. Voir 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. Mettez à jour un enregistrement. Voir 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
la source