how to query from update handler javascript code - javascript

What I'm trying to do:
I want to add data (id, data) to db. While doing so, I want to check if the id already exists and if so, append to existing data. else add a new (id, data) entry.
Ex: This could be a db of student id and grades in multiple tests. If an id exists in the db already, I want to just append to the existing test scores already in db, else create a new entry: (id, grades)
I have setup an update handler function to do this. I understand, couchdb insert by default does not do the above. Now - how do I check the db for that id and if so, decide to whether to add a new entry or append. I know there is db.get(). However, I presume since the update handler function is already part of the db itelf, there may be a more efficient way of doing it.
I see this sample code in the couchdb wiki:
function(doc, req){
if (!doc){
if ('id' in req && req['id']){
// create new document
return [req, 'New Document'];
}
// change nothing in database
return [null, 'Incorrect data format'];
}
doc[id] = req;
return [doc, 'Edited World!'];
}
a few clarifications in this example that's not clear: where do we get the id from? Often the id is not explicitly passed in while adding to db.
Does that mean, we need to explicitly pass a field called "_id"?

how do I check the db for that id and if so, decide to whether to add a new entry or append.
CouchDB does this for you, assuming HTTP client triggers your update function using the ID. As the documentation describes:
When the request to an update handler includes a document ID in the URL, the server will provide the function with the most recent version of that document
In the sample code you found, the update function looks like function(doc, req) and so in the code inside of that function the variable doc will have the existing document already "inside" your CouchDB database. All the incoming data from the client/user will be found somewhere within req.
So for your case it might look something like:
function(doc, req){
if (doc) {
doc.grades.push(req.form.the_grade);
return [doc, "Added the grade to existing document"];
} else {
var newDoc = {_id:req.uuid, grades:[req.form.the_grade]};
return [newDoc, "Created new document ("+newDoc._id+") for the grade"];
}
}
If the client does a POST to /your_db/_design/your_ddoc/_update/your_update_function/existing_doc_id then the first part of the if (doc) will be triggered, since you will have gotten the existing document (pre-fetched for you) in the doc variable. If the client does a POST to just /your_db/_design/your_ddoc/_update/your_update_function, then there is no doc provided and you must create a new one (else clause).
Note that the client will need to know the ID to update an existing document. Either track it, look it up, or — if you must and understand the drawbacks — make them determinate based on something that is known.
Aside: the db.get() you mentioned is probably from a CouchDB HTTP client library and is not availble (and would not work) in the context of any update/show/list/etc. functions that are running sandboxed in the database "itself".

Cited from the documentation:
If you are updating an existing document, it should already have an _id set, and if you are creating a new document, make sure to set its _id to something, either generated based on the input or the req.uuid provided. The second element is the response that will be sent back to the caller.
So tl;dr, if the _id is not specified, use req.uuid.
Documentation link: http://docs.couchdb.org/en/stable/ddocs/ddocs.html#update-functions

Related

Firestore - Skip document update if it doesn't exists, without need of failures

I have a collection
/userFeed
Where I create/delete docs (representing users) when the current user starts following/unfollowing them.
...
/userFeed (C)
/some-followed-user (D)
-date <timestamp>
-interactions <number>
When the user likes a post, the interactions field will be updated. But... what if the user doesn't follow the post owner? Then, I will just need to skip the document update, without necessity of producing failures/errors.
const currentUserFeedRef = firestore
.collection("feeds")
.doc(currentUserId)
.collection("userFeed")
.doc(otherUserId);
const data = {
totalInteractions: admin.firestore.FieldValue.increment(value),
};
const precondition = {
exists: false, // I am trying weird things
};
if (batchOrTransaction) {
return batchOrTransaction.update(
currentUserFeedRef,
data,
precondition
);
}
Is it possible to just "skip the update if the doc doesn't exist"?
Is it possible to just "skip the update if the doc doesn't exist"?
No, not in the way that you're explaining it. Firestore updates don't silently fail.
If you need to know if a document exists before updating it, you should simply read it first and check that it exists. You can do this very easily in a transaction, and you can be sure that the update won't fail due to the document being missing if you check it this way first using the transaction object.
In fact, what you are trying to do is illustrated as the very first example in the documentation.

What's remove() and save() mean in mongodb node.js when initializing one database

I am newly using node.js. I am reading the code of one app. The code below is to initialize the db, to load some question into the survey system I can't understand what's remove() and save() means here. Because I can't find any explanation about these two method. It seems mongoose isn't used after being connected. Could any one explain the usage of these methods?
Well, this is my understanding of this code, not sure to be correct. My TA tell me it should be run before server.js.
/**
* This is a utility script for dropping the questions table, and then
* re-populating it with new questions.
*/
// connect to the database
var mongoose = require('mongoose');
var configDB = require('./config/database.js');
mongoose.connect(configDB.url);
// load the schema for entries in the 'questions' table
var Question = require('./app/models/questions');
// here are the questions we'll load into the database. Field names don't
// quite match with the schema, but we'll be OK.
var questionlist = [
/*some question*/
];
// drop all data, and if the drop succeeds, run the insertion callback
Question.remove({}, function(err) {
// count successful inserts, and exit the process once the last insertion
// finishes (or any insertion fails)
var received = 0;
for (var i = 0; i < questionlist.length; ++i) {
var q = new Question();
/*some detail about defining q neglected*/
q.save(function(err, q) {
if (err) {
console.error(err);
process.exit();
}
received++;
if (received == questionlist.length)
process.exit();
});
}
});
To add some additional detail, mongoose is all based on using schemas and working with those to manipulate your data. In a mongodb database, you have collections, and each collection holds different kinds of data. When you're using mongoose, what's happening behind the scenes is every different Schema you work with maps to a mongodb collection. So when you're working with Question Schema in mongoose land, there's really some Question collection behind the scenes in the actual db that your working with. You might also have a Users Schema, which would act as an abstraction for some Users collection in the db, or maybe you could have a Products Schema, which again would map to some collection of products behind the scenes in the actual db.
As mentioned previously, when calling remove({}, callback) on the Questions Schema, you're telling mongoose to go find the Questions collection in the db and remove all entries, or documents as they're called in mongodb, that match a certain criteria. You specify that criteria in the object literal that is passed in as the first argument. So if the Questions Schema has some boolean field called correct and you wanted to delete all of the incorrect questions, you could say Question.remove({ correct: false }, callback). Also as mentioned previously, when passing an empty object to remove, your telling mongoose to remove ALL documents in the Schema, or collection rather. If you're not familiar with callback functions, pretty much the callback function says, "hey after you finish this async operation, go ahead and do this."
The save() function that is used here is a little different than how save() is used in the official mongodb driver, which is one reason why I don't prefer mongoose. But to explain, pretty much all save is doing here is you're creating this new question, referred to by the q variable, and when you call save() on that question object, you're telling mongoose to take that object and insert it as a new document into your Questions collection behind the scenes. So save here just means insert into the db. If you were using the official mongo driver, it would be db.getCollection('collectionName').insert({/* Object representing new document to insert */}).
And yes your TA is correct. This code will need to run before your server.js file. Whatever your server code does, I assume it's going to connect to your database.
I would encourage you to look at the mongoose API documentation. Long term though, the official mongodb driver might be your best bet.
Mongoose basically maps your MongoDB queries to JavaScript objects using schema.
remove() receives a selector, and callback function. Empty selector means, that all Questions will be affected.
After that a new Question object is created. I guess that you omitted some data being set on it. After that it's being saved back into MongoDB.
You can read more about that in the official documentation:
http://mongoosejs.com/docs/api.html#types-subdocument-js
remove query is use for removing all documents from collection and save is use for creating new document.
As per your code it seems like every time the script run it removes all the record from Question collection and then save new records for question from question list.

How to only get new data without existing data from a Firebase?

I have a node in Firebase getting continually updated with information from a logfile. The node is lines/ and each child of lines/ is from a post() so it has a unique ID.
When a client first loads, I want to be able to grab the last X number of entries. I expect I'll do this with once(). From then on, however, I want to use an on() with child_added so that I get all new data. However, child_added gets all data stored in the Firebase and, after the initial setup, only want the new stuff.
I see that I can add a limitToLast() on the on(), but, if I say limitToLast(1) and a flood of entries come in, will my app still get all the new entries? Is there some other way to do this?
You need to include a timestamp property and run a query.
// Get the current timestamp
var now = new Date().getTime();
// Create a query that orders by the timestamp
var query = ref.orderByChild('timestamp').startAt(now);
// Listen for the new children added from that point in time
query.on('child_added', function (snap) {
console.log(snap.val()
});
// When you add this new item it will fire off the query above
ref.push({
title: "hello",
timestamp: Firebase.ServerValue.TIMESTAMP
});
The Firebase SDK has methods for ordering, orderByChild() and methods for creating a range startAt(). When you combine the two you can limit what comes back from Firebase.
I think there is a problem in #David East's solution. He is using the local timestamp which may cause problem if the time is not accurate in client device. Here is my suggested solution (iOS Swift):
Using observeSingleEvent to get the complete data set
Then returned it in reversed order by reversed()
Get the last timestamp by for example data[0].timestamp
Using queryStarting for timestamp
self._dbref.queryOrdered(byChild: "timestamp").queryStarting(atValue: timestamp+1)
.observe(.childAdded, with: {
snapshot in
print(snapshot.value)
})
You have the right idea. child_added should be called only for the new nodes. Without source code it's hard to tell why you get all the data in your child_added event.
You can check the chat demo app to see how they load new chat messages. The use case sounds similar.
https://github.com/firebase/firechat/blob/master/src/js/firechat.js#L347
Here's temporary but quick solution:
// define a boolean
var bool = false;
// fetch the last child nodes from firebase database
ref.limitToLast(1).on("child_added", function(snap) {
if (bool) {
// all the existing child nodes are restricted to enter this area
doSomething(snap.val())
} else {
// set the boolean true to doSomething with newly added child nodes
bool = true;
}
});
Disadvantage: It will load all the child nodes.
Advantage: It will not process existing child nodes but just the newly added child nodes.
limitToLast(1) will do the work.

DocumentDB: Access document by database name, collection ID and document ID in Node.js

I'm making my first application using DocumentDB. I'm developing an API for it in Node.js. As others have noted, the DocumentDB APIs are very confusing and appear to require convoluted code to achieve simple things.
My API will allow me to access data in the database with a URL of the form http://<host>/data/<databaseName>/<collectionID>/<documentId>/<pathToData>. If <pathToData> is empty, then I will get the whole document as a JSON object.
I want a function with the signature GetDocument(databaseName,collectionID,documentId,callback), where callback is a function that takes the particular document as a JavaScript object. What implementation of GetFunction achieves my goal?
The DoQmentDB library makes for a trivial solution.
// dbClient: require('documentdb').DocumentClient; new DocumentClient(host,options);
// callback: function(document)
function getDocument(dbClient,databaseId,collectionId,documentId,callback) {
var DoQmentDB = require('doqmentdb');
var db = new DoQmentDB(dbClient,databaseId);
var collection = db.use(collectionId);
collection.findById(documentId).then(callback);
}
You first need your method to initialize a documentclient object with the database and collection parameters, which you do with the readorcreatedatabase and readorcreatecollection methods, as showcased in the documentation. Once you have that object initialized, you can query specific objects by document id or by a custom query string.
Ideally, you should cache those database and collection objects upon first request, so that you don't hit the db asking for the same information upon every single request you issue

Meteor - Allow multiple users to edit a post

I'm not able to use the node server debugger so I'm posting here to see if I can get a nudge in the right direction.
I am trying to allow multiple users to edit documents created by any of the users within their specific company. My code is below. Any help would be appreciated.
(Server)
ComponentsCollection.allow({
// Passing in the user object (has profile object {company: "1234"}
// Passing in document (has companyId field that is equal to "1234"
update: function(userObject, components) {
return ownsDocument(userObject, components);
}
});
(Server)
// check to ensure user editing document created/owned by the company
ownsDocument = function(userObject, doc) {
return userObject.profile.company === doc.companyId;
}
The error I'm getting is: Exception while invoking method '/components/update' TypeError: Cannot read property 'company' of undefined
I'm trying to be as secure as possible, though am doing some checks before presenting any data to the user, so I'm not sure if this additional check is necessary. Any advice on security for allowing multiple users to edit documents created by the company would be awesome. Thanks in advance. -Chris
Update (solution):
// check that the userId specified owns the documents
ownsDocument = function(userId, doc) {
// Gets the user form the userId being passed in
var userObject = Meteor.users.findOne(userId);
// Checking if the user is associated with the company that created the document being modified
// Returns true/false respectively
return doc.companyId === userObject.profile.companyId;
}
Looking at the docs, it looks like the first argument to the allow/deny functions is a user ID, not a user document. So you'll have to do Meteor.users.findOne(userId) to get to the document first.
Do keep in mind that users can write to their own profile subdocument, so if you don't disable that, users will be able to change their own company, allowing them to edit any post. You should move company outside of profile.
(If you can't use a proper debugger, old-fashioned console.log still works. Adding console.log(userObject) to ownsDocument probably would have revealed the solution.)

Categories

Resources