34
votes

In my Mongoose schema, I have a field which is a String, and I want to be able to store an JSON object in it. Is it possible? In Postgres, it's possible to store a dictionary in a string column.

I want to do that because the dictionary (actually a JSON object in JS) is just a simple read and write value and doesn't need queries, but also, because it is just one value and not an array of values.

5

5 Answers

111
votes

Yes, you can just store {myJsonProperty: JSON.stringify(myObject)}. Hopefully you know that you can also just set {myJsonProperty: Object} in your mongoose schema and store the whole object without converting it to a string for no reason. It doesn't have to be a nested document with a schema, it can be just a plain javascript object.

18
votes

if you can change the type of your field form "String" to "Object" you can save the json as it is.

var schema_obj = new Schema({
field1: Object,
..
});
13
votes

The accepted answer is good for the majority of situations.

However, if you have an object which you want to store, and you have no control over the object keys (e.g. they might be user-submitted), you might want to consider storing these as stringifed JSON. This allows you to overcome the limitation imposed by MongoDB that keys must not contain the reserved characters $ or ..

You can achieve this using Mongoose getters and setters, for example:

data: {
  type: String,
  get: function(data) {
    try { 
      return JSON.parse(data);
    } catch(error) { 
      return data;
    }
  },
  set: function(data) {
    return JSON.stringify(data);
  }
}
6
votes

Couldn't alter the original due to the 6 change limit on stack-overflow. re-posted, awesome work Tom, was just missing catch(err) in the catch block

data: {
  type: String,
  get: function(data) {
    try { 
      return JSON.parse(data);
    } catch(err) { 
      return data;
    }
  },
  set: function(data) {
    return JSON.stringify(data);
  }
}
1
votes

We needed to retain structure and not do a string so I came up with the following methods to handle this:

const setMongoMixedWithBadKeys = data =>
  Array.isArray(data)
  ? data.map(setMongoMixedWithBadKeys)
  : typeof data === 'object' 
  ? Object.entries(data).reduce((a, [key,value])=>({...a, [key.replace('.','__').replace('$','___')]:setMongoMixedWithBadKeys(value)}), {})
  : data

const getMongoMixedWithBadKeys = data =>
  Array.isArray(data) 
  ? data.map(getMongoMixedWithBadKeys) 
  : typeof data === 'object'
  ? Object.entries(data).reduce((a, [key, value])=> ({...a, [key.replace('__','.').replace('___','$')]:getMongoMixedWithBadKeys(value)}), {})
  : data


data: {
  type: Mixed,
  get:  getMongoMixedWithBadKeys,
  set:  setMongoMixedWithBadKeys
}