I'm trying to create a schema in Mongoose that have a map (object, associative array, key-value pairs), but no success so far.
The documents of my schema must be something like this:
{
"_id": ObjectId("..."),
"groups"
{
"groupA":
{
"value1": "...",
"value2": "..."
},
"groupB":
{
"value3": "...",
},
"groupC":
{
"value4": "...",
"value5": "...",
},
...
}
}
groups is as object with a variable number of keys. I don't know those keys ahead of time as it'll be create by the user.
Every entry in groups is another object. As before, I don't know the key identifiers, but I know that the values are String (or Boolean, or Number, doesn't matter).
Of course, those keys are Strings.
Is it even possible (is there anyway to build such schema/model) in Mongoose?
Best practice is to keep any dynamic data out of your field names. The typical approach for a use case like this is to make groups an array and move the name of the group into a name field of the contained objects. Then you can use the Mixed type to contain the user-defined set of values:
{
"_id": ObjectId("..."),
"groups":
[
{
name: "groupA",
values: {
"value1": "...",
"value2": "..."
}
},
{
name: "groupB",
values: {
"value3": "..."
}
},
{
name: "groupC",
values: {
"value4": "...",
"value5": "..."
}
},
...
]
}
You'd define the schema as:
var schema = new Schema({
groups: [{
name: String,
values: Schema.Types.Mixed
}]
});
Map is supported from version 5.1 -
http://mongoosejs.com/docs/schematypes.html#maps
Looks like somebody made a plugin, Hooray!
https://www.npmjs.com/package/mongoose-map
Once you install and configure according to the readme you can just do {groups:[MongooseMap]}...
Have you tried something like this:
var schema = new mongoose.Schema({
groups: [mongoose.Schema.Types.Mixed]
});
Reference: Mongoose Schema Types
Edit, given my comments below, and riffing on the answer before mine, let's assume that all values are of type string (I had understood that they might be anything):
var schema = new mongoose.Schema({
groups:[{
name: String,
values:[String]
}]
});
Edit #2: Assuming that "value1" is a placeholder for some more meaningful name and assuming that the number of member names is finite, as long as they are not required, then you can put them all in your schema, so instead of the array version, you should be able to do this:
var schema = new mongoose.Schema({
groups:[{
name: String,
value1: String,
value2: String,
value3: String,
value4: String,
value5: String
}]
});
Related
My data model:
{
_id: ObjectId,
persons:[{
_id: ObjectId,
name: String,
...
}],
relations: [{
type: String,
personId: ObjectId,
...
}],
...
}
Here's my issue:
I am trying to find documents where person's name is x and it's _id is inside the relations array (personId) with a given type.
Example:
My data:
[{
_id:"1",
persons:[{
_id:"1",
name: "Homer"
},
{
_id:"2",
name: "Bart"
}],
relations: [{
type:"House_Owner",
personId: 1,
}],
}]
Request_1:
Find all documents where "Homer" is the house owner
Result:
[{
_id:"1",
...
}]
Request_2:
Find all documents where "Bart" is the house owner
Result:
[]
Any help would be appreciated.
The only solution I see here is to do the find operation with the given name value and after that filter the mongodb result.
PS: I cannot change the existing data model
EDIT:
I found a solution to do this by using $where operator with a javascript function but I am not sure that's the most efficient way.
db.myCollection("x").find({
$where: function() {
for (const relation of this.relations) {
if(relation.type === "House_Owner") {
for (const person of this.persons) {
if(person.name === "Homer" && person._id.equals(relation.personId)) {
return true;
}
}
}
}
}
})
You can do something like this:
const requiredName="x"
const requiredId = "id"
await yourModel.find({$and:[{"relations.personId":requiredId },{"persons.name":requiredName}]})
For example, I have a schema as below.
const packSchema = new mongoose.Schema({
company: [
name: {type: String},
realName: {type: String, default: ''}
]
})
So I can save a JSON data as below.
[{
"name": "abc",
"realName": "efg"
}]
But when the data doesn't have realName, I want that realName gets data from his own name.
For example, I request this array from ajax call,
[{
"name": "KA"
},
{
"name": "MC"
}]
when I save this object, the result in mongoDB is as below.
[{
"name": "KA",
"realName": "KA"
},
{
"name": "MC",
"realName": "MC"
}]
I think the solution using 'set' option in schema.
function duplicate(){
}
const packSchema = new mongoose.Schema({
company: [
name: {type: String},
realName: {type: String, set: duplicate}
]
})
But I don't know how it can get 'name' data from his own element of array.
Should I use 'for' loop this? or is there other solution?
Thank you for reading it.
The this inside of your function will get bound to the object which you're creating. As a result, you'll be able to use:
function duplicate(v){
return v === undefined ? this.name : v;
}
Above, if the value for realName (ie: v) is undefined, then you'll return this.name, which is the name of the object you're inserting/creating. Otherwise, if realName is already set, it will use that value.
Convert company into sub schema and use a pre save hook to update realname
const companySchema = mongoose.Schema({
name: String,
realName: String
})
companySchema.pre('save',function(next) {
if(!this.realName)
this.realName = this.name
next();
})
const packSchema = mongoose.Schema({
company: [companySchema]
})
const Pack = mongoose.model('Pack', packSchema);
I am just practicing Mongoose with JavaScript.
I have this mongoose schema below:
const item = new mongoose.Schema({
name: {
type: String,
required: true,
unique: true
},
qty: {
type: Number,
required: true
},
costPrice: Number
});
const supplySchema = new mongoose.Schema({
items: [item]
});
const Supply = mongoose.model("Supply", supplySchema);
Right now I just want to retrieve an array of item using mongoose.
I wish my data to look like this:
[
{
"name" : "Lemon",
"qty" : 5
},
{
"name": "Sugar",
"qty": 5
}
]
But right now the data I get back looks something like this:
[
{
"_id": "5e4df1c1d48926133879f650",
"costPrice": 0.4,
"name": "Sugar",
"qty": 5
}
]
I tried writing such a code below but it does not work:
const supplyDoc = await Supply.findOne();
const supplies = await supplyDoc.items.select("-_id");
return supplies;
I thought I could somehow use projection on the items and specify that I don't want the _id property for each object in the array. But my code does not work. Is there a mongoose or MongoDB method I can utilize? Or do I have to use javascript to manipulate the array?
Hope to hear some advice. thank you!
Try to do in this way
let supplies= await models.Supplier.find( { }, { name:1,qty:1,_id: 0 } )
First of all you should use Model.find() instead of Model.findOne() if you want an array of items.
And yes you can use the projection parameter to select the fields you want.
So
const supplies = await Supply.find({}, "name qty").exec()
Edit: So apparently you need to specifically exclude the _id field, you can do this using - as prefix.
const supplies = await Supply.find({}, "-_id name qty").exec()
I am trying to create a ngx datatable that creates columns dynamically from nested arrays, which with some research is not possible - so to achieve my desired result, I must flatten my nested arrays with the key / values that i need from each nested object into my parent object.
I need to manipulate my data so that my end result is a flat array and contains a line item for each object in the nested array earnings with 'abbreviation' being the key and 'amount' being the value..
I.e
[
{
employee_uuid: 978f37df-7e07-4118-be93-d82507ce5c46,
employee_code: JB00024,
full_name: Thulisile Sandra,
last_name: Bhekiswayo
earnings:[{
abbreviation: "NT HRS"
amount: "45.00"
money: false
name: "Normal Time HRS"
time: true
unique: "7d783469-717e-408a-bc3c-93115cb632dd_true"
uuid: "7d783469-717e-408a-bc3c-93115cb632dd"
value: "45.00"
},
{
abbreviation: "OT HRS"
amount: "25.00"
money: false
name: "Normal Time HRS"
time: true
unique: "7d783469-717e-408a-bc3c-93115cb632dd_true"
uuid: "7d783469-717e-408a-bc3c-93115cb632dd"
value: "45.00"
}],
terminated false
}
...
]
I'd like to look like this:
[
{
employee_uuid: 978f37df-7e07-4118-be93-d82507ce5c46,
employee_code: JB00024,
full_name: Thulisile Sandra,
last_name: Bhekiswayo,
NT HRS: '45.00',
OT HRS, '25.00',
terminated:false
}
...
]
I am not sure how to go about this, I've tried reducing and map functions but no success.. I can add the nested arrays to the parent object with Object.assign but that takes the whole object, I need to create a new parameter from that object..
Any help would be hugely appreciated..
You can use es6 destructuring for this, simply expose whatever properties you need and then "recompose" then into the object shape you want.
For example:
return myEmployeeArray.map(employee => {
const {earn } = employee
earn.map(field => field.abbreviation)
myDesiredObject = { fieldA: employee.abbreviation....fieldE:field.abbreviation}
}
This would get you one of your nested fields
With a compound index like the following
db.data.ensureIndex({ userId: 1, myObject: 1 })
Will the following find use this index?
db.data.find({
userId: 1,
myObject: {
a:'test',
b:'test2'
}
})
I know in Javascript that objects don't preserve their order so is this the same in Mongo?
So what will happen if I have documents with objects in different orders like this.
{
_id: ObjectId,
userId:1,
myObject: {
b: 'test',
a: 'test2'
}
},
{
_id: ObjectId,
userId:1,
myObject: {
b: 'test',
a: 'test2'
}
},
{
_id: ObjectId,
userId:2,
myObject: {
b: 'test',
a: 'test2'
}
}
Does the order of the properties matter when indexing an object like above?
EDIT:
In the documentation, http://docs.mongodb.org/manual/core/index-single/ it says "When performing equality matches on subdocuments, field order matters and the subdocuments must match exactly."
And in their example, the following would work
db.factories.find( { metro: { city: "New York", state: "NY" } } )
but if the metro fields were the other way around it wont work
db.factories.find( { metro: { state: "NY", city: "New York" } } )
So how do you preserve the property order in Javascript/Node when the language itself doesn't support it?
Actually, JavasScript or JSON notation objects do preserve their order. While some top level properties might shuffle around if MongoDB has to move them as a document grows, generally speaking this "sub-level" document should not change from the order in which it was originally inserted.
That said some languages try to order their "Hash", "Map", "Dict" keys by default, and therefore it becomes important to find and "ordered" or "indexed" form when dealing with these, but that is another issue.
To your question of an "index" for MongoDB affecting this order, no it does not. In fact the index you have added, though it does not error as such, does not really have any value as an Object or {} essentially does not have a value for an index to act on.
What you can do is this:
db.data.ensureIndex({ "userId": 1, "myObject.a": 1 })
Which keeps an ascending index on the "a" property of your object. That is useful if this is regularly viewed in queries.
You can also have problems with positions if you attempt to query like this:
db.data.find({ "Object": { a: "test2", b: "test" } })
Where the actual stored keys are not in order. But this is typically fixed by the "dot notation" form:
db.data.find({ "Object.a": "test2", "Object.b": "test" })
Which will not care about the actual order of keys.
But the index you defined does not keep the "keys" in insertion order, that is entirely language specific
The order matters for indexes on sub-documents. To quote the documentation:
This query returns the above document. When performing equality
matches on subdocuments, field order matters and the subdocuments must
match exactly. For example, the following query does not match the
above document
Consider this document:
{
_id: ObjectId(...),
metro: {
city: "New York",
state: "NY"
},
name: "Giant Factory"
}
and this index:
db.factories.ensureIndex( { metro: 1 } )
this query matches:
db.factories.find( { metro: { city: "New York", state: "NY" } } )
and this one does not:
db.factories.find( { metro: { state: "NY", city: "New York" } } )
See http://docs.mongodb.org/manual/core/index-single/#indexes-on-subdocuments
and http://docs.mongodb.org/manual/reference/method/db.collection.find/#query-subdocuments