On Node.js/Mongoose/Mongo is
SomeModel.findOne({_id: id}, callback).populate('ref')
equivalent to
SomeModel.findOne({_id: id}).populate('ref').exec(callback)
"ref" is single doc (not an array).
The problem is that using the first syntax the "child" document is randomly not populated when callback is called.
I don't know the internals, but I'd say they are not the same.
The first probably does this:
find the document
call the callback with the document
populate the ref (this is done through a separate query)
The second probably does this:
find the document
populate the ref
call the callback when the ref has been resolved
The randomness that you're witnessing is because the "populate the ref" call, when fast enough, may populate the document before you use it in the callback. In other words: a race condition.
Related
Is it possible to unify in a single instruction these two Firestore document set/update?
await batchArray[batchIndex].set(finref, doc2.data());
await batchArray[batchIndex].update(finref, {"esito" : 1, "timestamp": currentTime});
Where "finref" is a document reference and doc2 is a DocumentSnapshot
You can merge those two objects using spread syntax and pass it to single command.
await batchArray[batchIndex].set(finref, {
...doc2.data(),
...{
"esito": 1,
"timestamp": currentTime
}
});
If you want to perform both operations at once, then you can execute multiple write operations as a single batch that contains any combination of set(), update(), or even delete() operations. A batch of writes completes atomically, meaning that all operations will succeed or all will fail.
As you can see in the docs, the first argument is always a document reference. If you already have a document snapshot, then you need to get the document reference out of that object in order to commit the batch.
Edit:
If you try to update a document, that doesn't exist, indeed you'll get an error that says "No document to update". In that case, you should consider using set() with merge: true. This means that, if the document does not exist, then it will be created, and if the document does exist, the data will be merged into the existing document.
Saving an aggregation query using "mongodb": "^3.0.6" as result with the $out operator is only working when calling .toArray().
The aggregation step(s):
let aggregationSteps = [{
$group: {
_id: '$created_at',
}
}, {'$out': 'ProjectsByCreated'}];
Executing the aggregation:
await collection.aggregate(aggregationSteps, {'allowDiskUse': true})
Expected result: New collection called ProjectsByCreated.
Result: No collection, query does not throw an exception but is not being executed? (takes only 1ms)
Appending toArray() results in the expected behaviour:
await collection.aggregate(aggregationSteps, {'allowDiskUse': true}).toArray();
Why does mongodb only create the result collection when calling .toArray() and where does the documentation tell so? How can I fix this?
The documentation doesn't seem to provide any information about this:
https://mongodb.github.io/node-mongodb-native/3.0/api/Collection.html#aggregate
https://docs.mongodb.com/manual/reference/operator/aggregation/out/index.html
MongoDB acknowledge this behaviour, but they also say this is working as designed.
It has been logged as a bug in the MongoDB JIRA, $out aggregation stage doesn't take effect, and the responses say it is not a fault:
This behavior is intentional and has not changed in some time with the node driver. When you "run" an aggregation by calling Collection.prototype.aggregate, we create an intermediary Cursor which is not executed until some sort of I/O is requested. This allows us to provide the chainable cursor API (e.g. cursor.limit(..).sort(..).project(..)), building up the find or aggregate options in a builder before executing the initial query.
... Chaining toArray in order to execute the out stage doesn't feel quite right. Is there something more natural that I haven't noticed?
Unfortunately not, the chained out method there simply continues to build your aggregation. Any of the following methods will cause the initial aggregation to be run: toArray, each, forEach, hasNext, next. We had considered adding something like exec/run for something like change streams, however it's still in our backlog. For now you could theoretically just call hasNext which should run the first aggregation and retrieve the first batch (this is likely what exec/run would do internally anyway).
So, it looks like you do have to call one of the methods to start iterating the cursor before $out will do anything. Adding .toArray(), as you're already doing, is probably safest. Note that to.Array() does not load the entire result into RAM as normal; because it includes a $out, the aggregation returns an empty cursor.
Because the Aggregation operation returns a cursor, not the results.
In order to return all the documents from the cursor, we need to use toArray method.
I'm trying to fetch more than one document that satisfies a particular criteria from a collection in my MongoDB database. When I use findOne(), it works perfectly fine and returns the first document that follows the criteria, but find() isn't returning all of the documents. I've checked a lot of websites and the documentation but still haven't found good examples of how it is done. This is the current syntax that I'm trying :
db.collection('mycollection').find({someproperty :
somevalue}).then((docs)=>{
// trying to do something with the returned documents 'docs'
}
Also, I'd really prefer a non-mongoose solution, unless this is absolutely impossible using plain MongoDB. I know it's probably easier with mongoose but I want to know the MongoDB implementation.
In the Mongo Docs the find function returns a cursor.
A cursor to the documents that match the query criteria. When the find() method “returns documents,” the method is actually returning a cursor to the documents.
I'm guessing you expect an array? You need to use the toArray function, the docs for this are https://mongodb.github.io/node-mongodb-native/api-generated/cursor.html#toarray
Unfortunately it's a callback, no promise implementation so you will need to put the promise in there yourself.
return new Promise((resolve, reject) => db.collection('mycollection')
.find({someproperty : somevalue})
.toArray((err, documents) => resolve(documents)));
I have a question regarding compound index creation in mongodb:
Say I want to create this compound index:
cSchema.index({account:1, authorization: 1, c_type: 1});
Problem is, javascript doesn't guarantee dictionary order, so I won't be sure the compound index is in the order I want.
How can I make sure it's indeed {account:1, authorization:1, c_type:1} in that order ?
Thanks !
The simple answer is that most abuse the behaviour that simple String properties that don't parse to an integer on an Object will enumerate in creation order. Although not guaranteed in ES2015 for some enumeration methods, it does work in a confined environment like Node/V8. This has worked in most JS engines for a while now but had never been part of the ES spec before ES2015.
MongoDB
The underlying MongoDB driver createIndex function supports String, Array and Object index definitions. The parsing code is in a util function called parseIndexOptions.
If you specify an array of Strings, Array pairs, or Objects then the order will be fixed:
['location','type']
[['location', '2d'],['type', 1]]
[{location: '2d'},{type: 1}]
Also note that the createIndex code does use Object.keys to enumerate when it gets an Object of index properties.
Mongoose
The Schema#index function is documented as requiring an Object to pass to "MongoDB driver's createIndex() function" so looks to support passing through whatever options you provide.
There are a couple of places where indexes are further processed though. For example when you create a sub document with an index, the index needs to have the parent schema prefixed onto field names. On a quick glance I think this code still works with an Array but I can't see any tests for that in the Mongoose code so you might want to confirm it yourself.
Mongoose does have a compound index test that relies on Object property order.
There are two different methods to obtain a reference to a MongoDB collection - both of them are used throughout the official documentation.
There is
var mycollection = db.collection('mycollection)'
and there is
db.collection('mycollection', function(err, collection){
//use collection
}
I tend to use the second one because it is consistent with "db.createCollecion(collection, callback)"
What is the difference between these methods?
Is there any database interaction when using these methods?
If you look at the code for Database, currently around line 456, you'll see that the only difference between the two in the way you've used them is how the collection object is returned. If you specify a callback, then it's returned that way, otherwise, it's returned as the value to the function. If you set the options however and specifically the option strict to true, you need to use the callback. When strict is set to true, the collection is verified before continuing (asynchronously).
Given that collections can be created dynamically (and usually are upon first use), there often isn't need to use strict mode.
So, it's really matter of personal coding preference otherwise. There is normally no activity to the database when creating a Collection object via: db.collection('collectionname') with the exception I mentioned above.