I am trying to populate references nested within other references. I have it working but it seems kinda hacky and was wondering if there is any other way to accomplish this:
return Q.ninvoke(BoardSchema, 'find', {'_id': id}).then(function(board) {
return Q.ninvoke(BoardSchema, 'populate', board, {path: 'lanes'}).then(function(board){
return Q.ninvoke(LaneSchema, 'populate', board[0].lanes, {path: 'cards'}).then(function(lanes){
board.lanes = lanes;
return board;
});
});
});
Is there some method to populate all references, or return the second populate as part of the board call without manually setting it like I am now?
You should be able to populate multiple to populate nested documents like so:
Item.find({}).populate('foo foo.child').exec(function(err, items) {
// Do something here
});
This requires that refs are setup in the Schema definitions.
If this doesn't work, which to be honest is most of the times for some reason, you can chain your finds. But this doesn't differ much from your code.
Item.find({}).populate('foo').exec(function(err, items) {
Item.find(items).populate('bar').exec(function(err, items) {
// Even more nests if you like
});
});
Based on the response of Gideon
Item.find({ _id: id})
.populate({
path: 'foo',
model: 'FooModel',
populate: {
path: 'child',
model: 'ChildModel'
}
})
.exec(function(err, items) {
// ...
});
Related
I have a Documents in a Collection that have a field that is an Array (foo). This is an Array of other subdocuments. I want to set the same field (bar) for each subdocument in each document to the same value. This value comes from a checkbox.
So..my client-side code is something like
'click #checkAll'(e, template) {
const target = e.target;
const checked = $(target).prop('checked');
//Call Server Method to update list of Docs
const docIds = getIds();
Meteor.call('updateAllSubDocs', docIds, checked);
}
I tried using https://docs.mongodb.com/manual/reference/operator/update/positional-all/#positional-update-all
And came up with the following for my Server helper method.
'updateAllSubDocs'(ids, checked) {
Items.update({ _id: { $in: ids } }, { $set: { "foo.$[].bar": bar } },
{ multi: true }, function (err, result) {
if (err) {
throw new Meteor.Error('error updating');
}
});
}
But that throws an error 'foo.$[].bar is not allowed by the Schema'. Any ideas?
I'm using SimpleSchema for both the parent and subdocument
Thanks!
Try passing an option to bypass Simple Schema. It might be lacking support for this (somewhat) newer Mongo feature.
bypassCollection2
Example:
Items.update({ _id: { $in: ids } }, { $set: { "foo.$[].bar": bar } },
{ multi: true, bypassCollection2: true }, function (err, result) {
if (err) {
throw new Meteor.Error('error updating');
}
});
Old answer:
Since you say you need to make a unique update for each document it sounds like bulk updating is the way to go in this case. Here's an example of how to do this in Meteor.
if (docsToUpdate.length < 1) return
const bulk = MyCollection.rawCollection().initializeUnorderedBulkOp()
for (const myDoc of docsToUpdate) {
bulk.find({ _id: myDoc._id }).updateOne({ $set: update })
}
Promise.await(bulk.execute()) // or use regular await if you want...
Note we exit the function early if there's no docs because bulk.execute() throws an exception if there's no operations to process.
If your data have different data in the $set for each entry on array, I think you need a loop in server side.
Mongo has Bulk operations, but I don't know if you can call them using Collection.rawCollection().XXXXX
I've used rawCollection() to access aggregate and it works fine to me. Maybe work with bulk operations.
I want to let the user create the structure of my website. For example, I have buildings and rooms. The user must be able to create a building and subsequently insert rooms into it. However, what I tried to do seems not to achieve it:
JSFiddle of what I have done so far.
js
new Vue({
el: '#vue-app',
data: {
buildings: []
},
computed: {
buildingCount() {
return this.buildings.length
},
getBuildingRoomsLength(section) {
return this.buildings.rooms.length
}
},
methods: {
addNewRoomToBuilding(buildingId, newRoom) {
if(newRoom !== undefined) { this.buildings[parseInt(buildingId)-1].rooms.push(newRoom.title)
console.log(this.buildings[parseInt(buildingId)-1])
}
},
addNewBuilding() {
this.buildings.push({
id: this.buildings.length+1,
rooms: []
})
},
deleteTodo(todo) {
this.todos.$remove(todo)
}
}
});
I am not sure how to make it work. A couple of the things I have noticed is that the room model now is same for all buildings and I have to change it according to the buildingId, however, I can't figure it out yet. Could you please assist me how to do this.
Make your model unique for each item in the buildings array by appending the building id to the end of the name.
So the model name becomes v-model="newRoom[building.id]"
And pass the same into your method addNewRoomToBuilding(building.id, newRoom[building.id])
I got the following "problem". I am used to having an API like that.
/users
/users/{id}
The first one returns a list of users. The second just a single object. I would like the same with GraphQL but seem to fail. I got the following Schema
var schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
users: {
type: new GraphQLList(userType),
args: {
id: {type: GraphQLString}
},
resolve: function (_, args) {
if (args.id) {
return UserService.findOne(args.id).then(user => [user]);
} else {
return UserService.find()
}
}
}
}
})
});
How can I modify the type of users to either return a List OR a single object?
You shouldn't use one field for different purposes. Instead of that, make two fields. One for single object and another for list of objects. It's better practice and better for testing
fields: {
user: {
type: userType,
description: 'Returns a single user',
args: {
id: {type: GraphQLString}
},
resolve: function (_, args) {
return UserService.findOne(args.id);
}
},
users: {
type: new GraphQLList(userType),
description: 'Returns a list of users',
resolve: function () {
return UserService.find()
}
}
}
The above answer is correct, the usual approach is to add singular and plural form of queries. However, in large schema, this can duplicate a lot of logic and can be abstracted a little bit for example with Node interface and node, nodes queries. But the nodes query is usually applied with ids as argument (in Relay viz node Fields), but you can build your own abstracted way for fetching so that you have just nodes with some argument for type and based on that you can say what type of list to fetch. However, the simpler approach is to just duplicate the logic for every type and use singular and plural form of query and do the same type of queries as above or in this code snippet for every type. For more detail explanation on implementing GraphQL list modifiers in queries or even as an input for mutations. I just published the article on that.
I am using firebase, and angularfire.
there are so many ways to do CRUD with the Firebase Api
actually, I still don't get what is specific difference for using
$add with $firebaseArray
.push() method
.set() method
I think they are technically same, I prefer to use .set method() without knowing the exact reason, why I'd using that. is there any specific reason to not use it? what is exactly $firebaseArray did? if we could just declare basic reference variable.
in this case:
var usersRef = Ref.child('users');
$scope.createUser = function() {
$scope.userRef.child($id).set({
name: name
});
};
or
$scope.data = $firebaseArray(Ref.child('users'));
$scope.createUser = function() {
$scope.data.child($id).$add({
name: name
});
};
thank you.
If I have the following data tree in Firebase:
{
users:
{
key: { name:"bob" }
}
}
When I do an $add, I will create a new item in the tree
$scope.data.child('users').$add({
name: name
});
Since $add uses the Push method in Firebase, new random Key will be used when pushing data to the child.
{
users:
{[
key: { name:"bob" },
key2: { name:"name" }
]}
}
If I do a set on the same Users object, I will overwrite the data that is already there. So, in your example, without specifying a key, you will overwrite the entire user object.
$scope.userRef.child('users').set({
name: name
});
};
This will result with this data
{
users:
{
name: "name"
}
}
This happens because any null values you pass to the Set method will delete any data that was originally there.
Passing null to set() will remove the data at the specified location.
https://www.firebase.com/docs/web/api/firebase/set.html
I have two arrays (models and modelsErrors), I want to map the reuslt and merge them in a way that if the inputs are the following :
models=['user', 'employee', 'customers'];
modelsErrors=['userError', 'employeeError', 'customersError'];
Desired output should be:
Results=[{model: user, err: userError}, {model: employee, err: employeeError}, {model: customers, err: customersError}]
I guess it is possible to use .map of javascript; if not I'm looking for a clean way like .map() function: My attempt:
models=['user', 'employee', 'customers'];
modelsErrors=['userError', 'employeeError', 'customersError'];
var results = models.map(function(model){
return {model: model, err: modelsErrors[model]}
})
console.log(results);
I'm looking for a clean way if map is not possible...
Please let me know if you need more clarification
Thanks
You were almost there; just use the index argument of the callback to find the corresponding value of the other array:
models.map(function(value, index) {
return {
model: value,
err: modelsErrors[index]
};
});