Here's my code:
const events = await Event.find({'isFinished': false})
.where('attendants.employers').in(user._id);
Here's the model:
var eventSchema = new mongoose.Schema({
'attendants': {
'seekers': [mongoose.Schema.Types.ObjectId],
'employers': [],
},
'isFinished': {'type': Boolean, 'default': false},
});
I want to grab the events, which have the user's id in their attendants.employers array. I know I can filter them after downloading all events, but this is really inefficient.
Current code doesn't return any value. I tried flipping it around like so .where(user._id).in('attendants.employers');. But this causes node to say:
Error: in() must be used after where() when called with these arguments
Any idea how to achieve it, without downloading the data, and filtering it on the server?
Your find should look something like this:
Event.find({'isFinished': false, 'attendants.employers': user._id})
You do not need to do the where etc since in your find you list the rules and they will be AND-ed together.
You can use $in the other way around where you want to find out if a field in your document is IN a range of values.
Related
I have data that I would like to deploy to a Firestore collection from a cloud function that runs on Node10.
The collection is called "activities" and should hold united arrays with objects and additional fields.
First of all, this is the most important array with objects:
listOfNonEmptyObjects = [
{ pronounsDefinite: 'I' },
{ verbsInfinitiv: 'work' },
{ prepositions: 'in' },
{ articles: 'the' },
{ nouns: 'garden' }
];
This is how I upload it, while the sessionID_original is basically the document name and the intentDisplayName is just a string to create the object inside arrayUnion:
return writeToDatabase (
sessionID_original,
{"activities" : admin.firestore.FieldValue.arrayUnion(createObject(intentDisplayName, listOfNonEmptyObjects))}
);
These are the functions for this:
function createObject(key, list){
var test = {};
test[key] = list;
return test;
}
function writeToDatabase(sessionID_original, data) {
const docRef = db.collection('users').doc(sessionID_original);
return db.runTransaction(t => {
return t.get(docRef)
.then(doc => {
t.set(docRef, data, {merge: true});
});
}).catch(err => {
console.log(`Error writing to Firestore: ${err}`);
});
}
Now this is what it looks like when I look in Firestore:
Screenshot of collection with listOfNonEmptyObjects
But now I would like my functions to add a timestamp or additional information to the data:
Screenshot of collection with listOfNonEmptyObjects and timestamps and booleans and so on
When I try this it gives me an error that I can't write nested arrays.
But then how can I achieve this data structure. Or is my data structure a bad one?
Another attempt could be to just put a timestamp in the listOfNonEmptyObjects.
But I think this would make it more difficult to read the timestamps then.
So every time there is a new activity or something I would like the functions to add it to the activities collection with a timestamp and maybe other fields like booleans etc.
It is not clear to me that you are thinking about Firestore in the correct way.
Firestore is a Document Store Database. For the most part, you will read and write entire Documents (JSON objects or sets of key/value pairs) at at time. Though you can make nested and complicated data structures within a single document, there are limitations in terms of overall size and index capabilities that will ultimately restrict the usefulness of a single document being complex (see Firestore limits and quotas)
It sounds to me like the elements you are putting in your array might rather benefit from being their own documents?
There have been a few good threads on the subject of "Firestore arrays vs subcollections". This one seems to call out the pros/cons quite well.
What is the best practice for saving a document with a reference to another collection's document if the _id of that is not immediately available?
var ModelA = new Schema({
aUniqueIdentifer: String,
...
)};
ModelA's aUniqueIdentifier is provided from another datasource, and is used by other models to identify it.
var ModelB = new Schema({
aUniqueForeignKey: type String,
aRef : {
type: mongoose.Schema.Types.ObjectID,
ref: 'ModelA'
}
)};
So I might save a modelA: modelA = new ModelA({aUniqueIdentifer: '500'});
Then to save a mobdelB, I need to populate it's aRef with the ModelA object. What is the best practice to do so? Should I do a findOne(aUniqueForeignKey) to return the object before trying to save? This doesn't seem terribly efficient.
I looked into populate, but that seems to be for existing references.
You can use the .pre method to create a method that runs before saving and then put your logic inside that. It looks like this:
ModelB.pre('save', function(next) {
// Check if id is available
// if not run another method
// run next() to exit
next();
});
This will run before anytime you save ModelB.
Hope this helps, if you add some more information I might be able to provide a more specific solution.
You could try using populate.
Population is the process of automatically replacing the specified paths in the document with document(s) from other collection(s)
http://mongoosejs.com/docs/populate.html
Using Mongoose is it possible to have a field that references another object, when the model/type of that document is unknown?
For example, I have the models: Photos, Comments, Submissions, Posts, etc., and I would like to have a Like model that refers back to them:
var Like = new Mongoose.Schema({
// What would the value of `ref` be, should it just be left out?
target: { type: Schema.Types.ObjectId, ref: '*' }
});
From what I understand, ref needs to be a Model. I could leave it out all together, but would I still get the benefit of the Mongoose's populate method that way?
There are two approaches you can take.
1. Pass in the value of ref when you call populate
Based on the section Populating across Databases. When you call populate, you can specify the model you want to use.
Like.find().populate({
path: 'target',
model: 'Photo'
})
This requires that you know the model you want before you populate.
2. Store the value of ref together with the target
Based on the section Dynamic References.
You need to first adjust the target to something similar to the following:
var Like = new Mongoose.Schema({
target: {
kind: String,
item: {
type: Schema.Types.ObjectId,
refPath: 'target.kind'
}
}
});
target.kind is the value of "ref" that will be used for populate, and target.item is the ObjectId. We use refPath instead of ref for dynamic references.
Then, when you call populate, you will instead do something like:
Like.find().populate('target.item')
Note that we populate 'target.item' as opposed to just 'target'.
I am trying to search through several thousand records for a particular record. I have loaded a User model and it has several thousand Shipments that are async. In other words I have the serializer send down shipment_ids with the user model.
App.User = DS.Model.extend({
shipments: DS.hasMany('shipment', { async: true })
});
I want to search through all those ids to see if a particular record's id is among them. However I don't want to go to the api and load each and every single record. I just want to be able to search through the shipment_ids.
I have a jsbin that showcases it loading all the records. How do I change this so it doesn't make any api calls for a shipment and still finds the specific record?
Instead of doing
this.get('shipments').any(function(shipment){
return shipment.get('id') === "10124";
there has to be a way to go through only the ids, no?
Thanks!
The solution described in "Get belongsTo ID without fetching record"
seems to work as of Ember Data 1.0.0-beta.10. Briefly, you can do the following to access the underlying
modelInstance.get('data.belongsToRelation.id');
Presumably you can also do this (though I have not tested it):
modelInstance.get('data.hasManyRelation');
Original Answer (DO NOT USE):
I have accomplished this on a belongsTo by adding a second model field for the id. My example:
App.User = DS.Model.extend({
group: DS.belongsTo('group', { async: true }),
groupId: DS.attr('number')
});
So maybe you can do this?
App.User = DS.Model.extend({
shipments: DS.hasMany('shipment', { async: true }),
shipmentIds: DS.attr('array')
});
You will need to add an array transform in app/transforms/array.js (assuming you use ember-cli)
import DS from 'ember-data';
var ArrayTransform = DS.Transform.extend({
deserialize: function(serialized) {
return serialized;
},
serialize: function(deserialized) {
return deserialized;
}
});
export default ArrayTransform;
Let me know if it works as I probably will need to something similar in my app soon.
This hack is very smelly but it's the only way I have found. And it is to search with _data
this.get('content._data.shipments').any(function(shipment){
return shipment.get('id') === "10124";
});
It won't make any api calls. But there has to be a more acceptable method that won't be prone to breakage when updating Ember.
Here is an updated jsbin to show this. Does anyone have a better solution?
Thanks!
Since Ember Data 2.3 you can use this code:
// get all ids without triggering a request
var commentIds = post.hasMany('comments').ids();
See http://emberjs.com/blog/2016/01/12/ember-data-2-3-released.html for details.
I'm working on implementing a tree structure (similar to this one in the Mongo docs) using Mongoose 3.x, but I'm unsure of the best way to encapsulate all the logic for loading a specific node with its siblings and ancestors generally, and specifically how to best work with the population functionality where the ref is in the same collection as the ref-er.
For some context, the tree I'm working with is one in which nodes are not edited but new children might be added at any time to any node. So far I've got this working fine with a set of model methods that load objects after the initial find, but it seems there should be a better way to easily load a single branch with all the parent and sibling data I need with a single command in the controller, and encapsulate all the relevant population in some convenient find method on the model.
The basic Schema I'm trying to work with, then, might be something like this (also available here: https://gist.github.com/3889616):
// Sub-document to store parent ref along with it's value (a form of caching)
var Parent = new Schema({
id: ObjectId
, text: String
});
// Main tree-node element schema
var Branch = new Schema({
text: {
type: String
, required: true }
, date: {type: Date, default: Date.now }
, trail: [Parent]
, parentBranchId: ObjectId
, parentBranch: { type: Schema.Types.ObjectId, ref: 'Branch' }
, _children: [{type: Schema.Types.ObjectId, ref: 'Branch'}]
// These two have been commented out because I have no clue how to best implement
// , _priorSiblings: { type: Schema.Types.ObjectId, ref: 'Branch' }
// , _followingSiblings: { type: Schema.Types.ObjectId, ref: 'Branch' }
});
My hope would then be to be able to load a branch w/ the relevant related data via something like the following code, though at this point I'm pretty much lost and could be a good deal off base:
req.app.models.Branch
.findById(req.param("id"))
.populate("parentBranch")
.populate("parentBranch._children")
.exec(...)
Ultimately, I'd love to have something I could abstract into a "tree" plugin for Mongoose, but I think I've got to get this architected correctly first. Any ideas?
FWIW, at the end of the day, the data I really need for each branch is parent, next sibling, previous sibling (both in terms of creation time) and all children of parent.
Thanks in advance!
I know this question is old, but have you looked into the mongoose-tree module? https://github.com/franck34/mongoose-tree
It has a pretty good API for handling relationships between objects IMO.