I need to check if some table already exists into IndexedDB just after it was openned. But I don't know how to get DexieDB object inside 'then' statement.
this.db = new Dexie("DBNAME");
if (!this.db.isOpen()) {
this.db.open().then(function () {
//how to get this.db.table(storeName) here?
}).catch(function (error) {
console.log(error)
});
}
So this.db doesn't exist inside 'then' statement. How to get it?
In Dexie
In Dexie in particular you don't have to call isOpen and open() like that, you can just .open and things will work like this:
// Declare db instance
var db = new Dexie("MyDatabase");
// Define Database Schema
//...
// Open Database
db.open();
db.trasnaction(...
In General
This is a classic JS context value. The way this works in JavaScript is different - here is the canonical reference about it which you should read.
In addition - about passing parameters in then chains you should refer to this excellent Q&A which covers the more general approach
The workarounds described there (with context) generally apply and contain more library specific code which could help you here.
Related
New to MongoDB, very new to Atlas. I'm trying to set up a trigger such that it reads all the data from a collection named Config. This is my attempt:
exports = function(changeEvent) {
const mongodb = context.services.get("Cluster0");
const db = mongodb.db("TestDB");
var collection = db.collection("Config");
config_docs = collection.find().toArray();
console.log(JSON.stringify(config_docs));
}
the function is part of an automatically created realm application called Triggers_RealmApp, which has Cluster0 as a named linked data source. When I go into Collections in Cluster0, TestDB.Config is one of the collections.
Some notes:
it's not throwing an error, but simply returning {}.
When I change context.services.get("Cluster0"); to something else, it throws an error
When I change "TestDB" to a db that doesnt exist, or "Config" to a collection which doesn't exist, I get the same output; {}
I've tried creating new Realm apps, manually creating services, creating new databases and new collections, etc. I keep bumping into the same issue.
The mongo docs reference promises and awaits, which I haven't seen in any examples (link). I tried experimenting with that a bit and got nowhere. From what I can tell, what I've already done is the typical way of doing it.
Images:
Collection:
Linked Data Source:
I ended up taking it up with MongoDB directly, .find() is asynchronous and I was handling it incorrectly. Here is the reply straight from the horses mouth:
As I understand it, you are not getting your expected results from the query you posted above. I know it can be confusing when you are just starting out with a new technology and can't get something to work!
The issue is that the collection.find() function is an asynchronous function. That means it sends out the request but does not wait for the reply before continuing. Instead, it returns a Promise, which is an object that describes the current status of the operation. Since a Promise really isn't an array, your statment collection.find().toArray() is returning an empty object. You write this empty object to the console.log and end your function, probably before the asynchronous call even returns with your data.
There are a couple of ways to deal with this. The first is to make your function an async function and use the await operator to tell your function to wait for the collection.find() function to return before continuing.
exports = async function(changeEvent) {
const mongodb = context.services.get("Cluster0");
const db = mongodb.db("TestDB");
var collection = db.collection("Config");
config_docs = await collection.find().toArray();
console.log(JSON.stringify(config_docs));
};
Notice the async keyword on the first line, and the await keyword on the second to last line.
The second method is to use the .then function to process the results when they return:
exports = function(changeEvent) {
const mongodb = context.services.get("Cluster0");
const db = mongodb.db("TestDB");
var collection = db.collection("Config");
collection.find().toArray().then(config_docs => {
console.log(JSON.stringify(config_docs));
});
};
The connection has to be a connection to the primary replica set and the user log in credentials are of a admin level user (needs to have a permission of cluster admin)
I am working on versioning changes for an application. I am making use of the mongoose pre-hook to alter the queries before processing according to the versioning requirements, I came across a situation where I need to do a separate query to check whether the other document exists and if it is I don't have to execute the current query as shown below,
schema.pre('find', { document: false, query: true }, async function (next) {
const query = this.getQuery();
const doc = await model.find(query).exec();
if (!doc) {
const liveVersion = { ...query, version: "default" };
this.setQuery(liveVersion);
} else {
return doc;
}
});
In the above find pre-hook, I am trying to
check the required doc exists in the DB using the find query and return if does exist and
if the document does not exist, I am executing the query by setting the default version based query.
The problem here is mongoose will execute the set query no matter what and the result its returning is also the one which I got for the this.setQuery, not the other DB query result(doc).
Is there a way to stop the default query execution in mongoose pre-hook?
Any help will be appreciated.
The only way to stop the execution of the subsequent action would be to throw an error, so you can throw a specific error in else, with your data in the property of the error object, something like:
else {
let err = new Error();
err.message = "not_an_error";
err.data = doc;
}
but that would mean wrapping all your find calls with a try/catch, and in the catch deal with this specific error in the way of extracting your data, or throw for the main error checking if it's an actual error. In the end you'll be having a very ugly code and logic.
This is specifically for the way you ask it, but normally you can just define another method, like findWithCheck(), and do your checks of the pre hook above in this custom method.
Of course you could try also overriding the actual find(), but that would be overkill, and in this case it means pretty much breaking the whole thing more for test purposes rather than development.
I have a strange issue with Dust.js contexts. Previously, I would pass the view model along as a plain old JavaScript object, e.g. res.render('page', { something: [1,2,3] } etc. When doing that, I could access something on the local context with {#something}...{/something} etc.
However, I changed the way I manage the context to use dust.makeBase, so that I can have globals and some sort of stack, rather than just an object. I'm using consolidate with express FWIW.
Now, I create the baseViewModel.
//
// at application init
//
app.baseViewModel = dust.makeBase({
someGlobal: 'example'
})
Later on, when rendering, I may extend it like so:
//
// in route handler
//
const viewModel = app.baseViewModel.push({
collection: someCollection
})
res.render('index', viewModel)
But then, the context stack looks like this:
{
"settings": {
// snip
},
"stack": {
"isObject": true,
"head": {
"collection": [
// snip
]
}
},
"global": {
"someGlobal": "example"
}
}
The problem is, now, to access collection, I must prefix the variables with stack.head.:
{#stack.head.collection}
<!-- etc -->
{/stack.head.collection}
Does anyone know why this is, and how I can get back to the simple way of just referring to {#collection} etc?
Thank you.
I managed to get this working, but it's a hack and should have to be done this way. There is a GitHub ticket now for this issue:
https://github.com/linkedin/dustjs/issues/743
Anyway, this is what I did:
dust.helpers.collection = (chunk, context, bodies, params) => {
return context.current().get('collection')
}
And then modified my markup to:
{#collection}
{.Name} etc
{/collection}
Strange, but I guess that's the way it works!
As per our comment chain, here's what I suspect to be the issue.
When you create a new Context object in Dust, it checks to see if you're passing in an existing Context to hydrate from. This is done using an instanceof check.
If the object does not seem to be an instance of a Context, it gets wrapped in a new Context, which would account for the behavior you're seeing here. This isn't great, so I'll go work on a PR to use a flag instead of an instanceof check.
You're using consolidate, which includes a dependency on dust, and I suspect that the version it includes is different than whatever you depend on in your package.json. I would check to see how many copies of dustjs-linkedin are in your tree (one in your root and one in consolidate's node_modules, perhaps). If you require dustjs-helpers it simply requires dustjs-linkedin itself, so the latter is what you really care about.
I am iterating through items in an object using the forEach method.
function scrapeRssFeedData(rssFeeds, cb){
rssFeeds.forEach(function(rssFeed){
rssFeed.property = 'value';
console.log(rssFeed.property);
console.log(rssFeed);
});
}
The value that is logged for rssFeed.property is, correctly 'value'.
However, when the rssFeed object itself is logged, the property is not visible.
The original rssFeeds object is built using a Mongoose model.
Why can I not see the property when logging the object? What can I do to fix it?
Update: Even this code is not working:
function scrapeRssFeedData(rssFeeds, cb){
rssFeeds[1]['property'] = 'value';
console.log(rssFeeds[1]);
}
The fact that this example doesn't work makes me believe almost certainly it's something to do with the object having been built by Mongoose. I want to be done with Mongoose though at this point of the program and want to start adding values to this object.
As I grew to suspect, it was due to it being an instance of a Mongoose document, as opposed to just a normal Javascript object.
I was able to find the solution here:
How do you turn a Mongoose document into a plain object?
And adding .lean() into the query chain, ie:
function getRssFeeds(status, cb){
if(status == 'on'){
RSSFeed
.find({})
.populate('endpoints')
.lean() // <-- Needed to be added
.exec(function (err, feeds) {
cb(null, feeds);
});
}
else if(status == 'off'){
cb({'status' : 'functionality', 'message': 'app is turned off'})
}
}
I am creating my own error library to have a custom catalog of specific and well documented errors to return on my API. I am doing something like this:
module.exports = CError;
function CError () {
}
// CUSTOM ERROR TYPES
CError.EmptyParamError = createErrorType(...);
CError.InvalidFormatError = createErrorType(...);
A sample of how I use my custom error types right now:
CError = require('cerror');
if(!passwd)
callback(new CError.EmptyParamError(passwd, ...));
I will use this errors through my entire project and I wish to have a cleaner code like this: (without the CError reference)
if(!passwd)
callback(new EmptyParamError(passwd, ...);
Is there a way to export the module or to require it that allows me to do this?
I googled without finding any answer, I also checked all this interface design patterns for Node.js modules but no one applies.
You can set it as a global, though as always when using globals, beware of the side-effects.
EmptyParamError = createErrorType(...);
That's it. Just leave off the var keyword, and don't set it as a property.
If it's only one or two types, you can skip the CError variable like this:
var EmptyParamError = require('cerror').EmptyParamError;
if(!passwd)
callback(new EmptyParamError(passwd, ...));
If you have multiple types in a single file, there will be multiple require('cerror') statements, but I believe there's no significant performance hit there because (if I understand correctly) Node will cache it the first time.