Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cant push array item that has .path() getter? #6637

Closed
Xpucmoc-Bockpec opened this issue Jun 29, 2018 · 6 comments
Closed

Cant push array item that has .path() getter? #6637

Xpucmoc-Bockpec opened this issue Jun 29, 2018 · 6 comments
Labels
docs This issue is due to a mistake or omission in the mongoosejs.com documentation
Milestone

Comments

@Xpucmoc-Bockpec
Copy link

Xpucmoc-Bockpec commented Jun 29, 2018

I have model

ExcursionModel.js

const mongoose = require("mongoose");


const schema = new mongoose.Schema({
  images: {
    preview: String,
    background: String,
    gallery: [{
        preview: String,
        original: String
    }]
  }
});

const UPLOADS_BASE_URL = "http://example.com/";

schema.path("images.preview").get(preview => UPLOADS_BASE_URL + preview);
schema.path("images.background").get(background => UPLOADS_BASE_URL + background);
schema.path("images.gallery").get(gallery => gallery.map(photo => {
  return {
    id: photo._id,
    preview: UPLOADS_BASE_URL + photo.preview,
    original: UPLOADS_BASE_URL + photo.original
  }
}));


mongoose.model("Excursion", schema, "gh6637");

When i trying to add new images.gallery items by .push() method there is nothing changes in array
But when i modify images.preview or images.background everything works correctly
Once i remove path getters, things getting normal

index.js

const mongoose = require("mongoose");
const ExcursionModel = require("./ExcursionModel");
const Excursion = mongoose.model("Excursion");


const settings = {
  ip: "127.0.0.1",
  port: 27017,
  database: "database",
  username: "username",
  password: "password"
};

mongoose.connect(`mongodb://${settings.username}:${settings.password}@${settings.ip}:${settings.port}/${settings.database}`).then(() => {

  return Excursion.findOne()
    .then(excursion => {
      // just key, it works properly in any cases
      e.images.background = "new value";
			
      e.images.gallery.push({
        preview: "some string",
        original: "some another string"
      });

      // there is no new array item, but if i remove path getters this will work properly
      console.log(e.images); 
  });
})
.catch(console.log);

thanks in advance

Im using mongoose 5.1.7 & node 10.5.0

@Xpucmoc-Bockpec Xpucmoc-Bockpec changed the title Cant push array item that has .path() getter Cant push array item that has .path() getter? Jun 29, 2018
@lineus
Copy link
Collaborator

lineus commented Jun 30, 2018

@zhepa I'll take a look at this ASAP. In the meantime, you can add if (!gallery) { return undefined; } to your images.gallery getter. Here is an example:

6637.js

#!/usr/bin/env node
'use strict';

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/gh-6637');
const conn = mongoose.connection;
const Schema = mongoose.Schema;

const schema = new Schema({
  images: {
    preview: String,
    background: String,
    gallery: {
      type: [{
        preview: String,
        original: String
      }]
    }
  }
});

const UPLOADS_BASE_URL = 'http://example.com/';

schema.path('images.preview').get(preview => UPLOADS_BASE_URL + preview);
schema.path('images.background').get(background => UPLOADS_BASE_URL + background);
schema.path('images.gallery').get(gallery => {
  if (!gallery) { return undefined; }
  return gallery.map(photo => {
    return {
      id: photo._id,
      preview: UPLOADS_BASE_URL + photo.preview,
      original: UPLOADS_BASE_URL + photo.original
    };
  });
});


const Test = mongoose.model('Excursion', schema, 'gh6637');

const test = new Test({
  images: {
    preview: 'preview.jpg',
    background: 'background.png',
    gallery: [
      { preview: 'preview1', original: 'original1' },
      { preview: 'preview2', original: 'original2' },
      { preview: 'preview3', original: 'original3' }
    ]
  }
});

async function run() {
  await conn.dropDatabase();
  await test.save();
  let doc = await Test.findOne();
  console.log(doc.images.preview);
  console.log(test.images.gallery);
  return conn.close();
}

run();

Output:

issues: ./6637.js
http://example.com/preview.jpg
CoreMongooseArray [
  { id: 5b376106b0173915eda5387d,
    preview: 'http://example.com/preview1',
    original: 'http://example.com/original1' },
  { id: 5b376106b0173915eda5387c,
    preview: 'http://example.com/preview2',
    original: 'http://example.com/original2' },
  { id: 5b376106b0173915eda5387b,
    preview: 'http://example.com/preview3',
    original: 'http://example.com/original3' } ]
issues:

@Xpucmoc-Bockpec
Copy link
Author

Xpucmoc-Bockpec commented Jun 30, 2018

@lineus try to

test.images.gallery.push({ preview: 'preview4', original: 'original4' });
console.log(test.images.gallery);

there is no problem with empty array i guess

@lineus
Copy link
Collaborator

lineus commented Jun 30, 2018

@zhepa in the process of adapting your example for this, when I would create a model instance the code would fail even before the save. Once I got that to work, I neglected to test .push(). I'll look into this further. Thanks!

@vkarpov15 vkarpov15 added the needs repro script Maybe a bug, but no repro script. The issue reporter should create a script that demos the issue label Jul 3, 2018
@vkarpov15 vkarpov15 added this to the 5.2.1 milestone Jul 3, 2018
@lineus
Copy link
Collaborator

lineus commented Jul 4, 2018

I've been playing around with this some. Here's a minimal repro script:

6637.js

#!/usr/bin/env node
'use strict';

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/gh6637', { useNewUrlParser: true });
const conn = mongoose.connection;
const Schema = mongoose.Schema;

const schema = new Schema({
  nested: {
    arr: [{ key: String }]
  }
});

schema.path('nested.arr').get(function(v) {
  return v.map(el => {
    return {
      key: 'foobar' + el.key,
    };
  });
});

const Test = mongoose.model('test', schema);

const test = new Test;

test.nested.arr.push({ key: 'value' });
test.nested.arr.push({ key: 'value2' });

async function run() {
  await conn.dropDatabase();
  await test.save();
  let doc = await Test.findOne();
  console.log(doc.nested.arr);
  return conn.close();
}

run();

Output:

issues: ./6637.js
CoreMongooseArray []
issues:

@lineus
Copy link
Collaborator

lineus commented Jul 4, 2018

@zhepa while this is getting sorted out, here are a couple of possible ways around this based on my code above:

One is a bit of a hack.

if you change the getter to:

schema.path('nested.arr').get(function(v) {
  if (v.length > 1) {
    return v.map(el => {
      return {
        key: 'foobar' + el.key,
      };
    });
  } else {
    return v;
  }
});

the output becomes:

issues: ./6637.js
CoreMongooseArray [ { key: 'foobarvalue' }, { key: 'foobarvalue2' } ]
issues:

note if the array only contains one value, getting the array will not apply the prefix.

The second is more sane

apply the getter to the subdoc schema explicitly:

#!/usr/bin/env node
'use strict';

const assert = require('assert');
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/gh6637', { useNewUrlParser: true });
const conn = mongoose.connection;
const Schema = mongoose.Schema;

const subSchema = new Schema({
  key: String
});

subSchema.path('key').get(function(v) {
  return 'foobar' + v;
});

const schema = new Schema({
  nested: {
    arr: [subSchema]
  }
});

const Test = mongoose.model('test', schema);

const test = new Test;

test.nested.arr.push({ key: 'value' });
test.nested.arr.push({ key: 'value2' });

async function run() {
  await conn.dropDatabase();
  await test.save();
  let doc = await Test.findOne();
  console.log(doc.nested.arr[0].key);
  console.log(doc.nested.arr[1].key);
  return conn.close();
}

run();

Output:

issues: ./6637.js
foobarvalue
foobarvalue2
issues:

@vkarpov15 vkarpov15 modified the milestones: 5.2.2, 5.2.4 Jul 8, 2018
@vkarpov15 vkarpov15 modified the milestones: 5.2.4, 5.2.6 Jul 16, 2018
@vkarpov15
Copy link
Collaborator

Upon more careful inspection, this is expected behavior. The below is how you do what you're trying to do:

const subSchema = new Schema({
  key: String
});

subSchema.path('key').get(function(v) {
  return 'foobar' + v;
});

const schema = new Schema({
  nested: {
    arr: [subSchema]
  }
});

Or:

const schema = new Schema({
  nested: {
    arr: [{ key: String }]
  }
});

schema.path('nested.arr.0.key').get(v => 'foobar' + v);

When a getter returns an object, changes to that object do not get tracked because getters are designed to not modify the underlying document, just to transform the output.

@vkarpov15 vkarpov15 added docs This issue is due to a mistake or omission in the mongoosejs.com documentation and removed needs repro script Maybe a bug, but no repro script. The issue reporter should create a script that demos the issue labels Jul 28, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs This issue is due to a mistake or omission in the mongoosejs.com documentation
Projects
None yet
Development

No branches or pull requests

3 participants