Editing/Updating nested objects in documents CouchDB (node.js) - node.js

I'm trying to add (aka. push to existing array) in couchDB document.
Any feedback is greatly appreciated.
I have a document called "survey" inside my database called "database1".
I have "surveys" as a set of arrays which consists of objects that has information on each survey.
My goal is to update my "survey" document. Not replacing my array, but adding a new object to the existing array. I've used "nano-couchdb" and "node-couchdb", but could not find a way around it. I was able to update my "surveys", but it would replace the whole thing, not keeping the existing objects in array.
1) Using Nano-couchdb:
db.insert({ _id, name }, "survey", function (error, resp) {
if(!error) { console.log("it worked")
} else {
console.log("sad panda")}
})
2) Using couchdb-node:
couch.update("database1", {
_id: "survey",
_rev:"2-29b3a6b2c3a032ed7d02261d9913737f",
surveys: { _id: name name: name }
)
These work well with adding new documents to a database, but doesn't work with adding stuff to existing documents.
{
"_id": "survey",
"_rev": "2-29b3a6b2c3a032ed7d02261d9913737f",
"surveys": [
{
"_id": "1",
"name": "Chris"
},
{
"_id": "2",
"name": "Bob"
},
{
"_id": "1",
"name": "Nick"
}
]
}
I want my request to work as it would for
"surveys.push({_id:"4",name:"harris"})
whenever new data comes in to this document.

Your data model should be improved. In CouchDB it doesn't make much sense to create a huge "surveys" document, but instead store each survey as a separate document. If you need all surveys, just create a view for this. If you use CouchDB 2.0, you can also query for survey documents via Mango.
Your documents could look like this:
{
"_id": "survey.1",
"type": "survey",
"name": "Chris"
}
And your map function would look like that:
function (doc) {
if (doc.type === 'survey') emit(doc._id);
}
Assuming you saved this view as 'surveys' in the design doc '_design/documentLists', you can query it via http://localhost:5984/database1/_design/documentLists/_view/surveys.

Related

Does MongoDB have a way to update a document without dropping existing elements not contained in the update data

I have been using MongoDB with both .NET core and Node.js. This question is specific to a node project I am working on, but I think the client language is irrelevant to my questions.
I have a document that has elements like name and address current ("add_current") below:
{
"_id" : ObjectId("5d858718c1d9f856881dc9ce"),
...
"name": "John Doe",
"add_current" :
{
"add1" : "456 Old Ave.",
"city" : "Stafford",
"state" : "VA",
"zip" : "23234"
},
...
}
Sometimes I am just updating some parts of the child object like this:
const updateDocument2 = function (db, callback) {
const collection = db.collection('documents');
collection.update(
{
'_id': ObjectID("5d858718c1d9f856881dc9ce")
},
{
$set: {
name: "John Doe Jr.",
add_current: {
add1: "123 New Street",
}
}
}, function (err, result) {
console.log("Updated the document");
callback(result);
});
}
When I execute the $set above, I delete the fields city, state and zip. I don't want to do that.
I am seeking the most efficient way to update name and add_current.add1 without deleting other fields (like add_current.state). I realize that there are ways to do this with multiple touches to the data record (.findOne(...) then .update(...)). Is there a way to do it with a single .update(...) touch?
you are setting add_current's value to {add1: "123 New Street"}
try {$set:{"add_current.add1": "123 New Street"}}

what is the return value of ArangoJS collection.save()?

The documentation is located here:
Document Manipulation · ArangoDB v3.4.1 Drivers Documentation
I see the documentation for collection.replace() and collection.update(), but nothing for collection.save(). I know the save function exits because I'm using it. But it doesn't return the expected value and I'd like to reference the documentation.
My specific problem is that I want to save a document to the ArangoDB database and get back the saved document in full. Here's what I have so far:
async createDocument(collectionName, data) {
try {
const collection = this.db.collection(collectionName);
return collection.save(data); //I want to return the saved document
} catch(err) {
console.log(err.message, "saving failed")
}
}
The documentation of the save method is found under DocumentCollection:
https://docs.arangodb.com/3.4/Drivers/JS/Reference/Collection/DocumentCollection.html#documentcollectionsave
The answer you look for:
returns an object containing the document's metadata
I admit this isn't very detailed. What it returns are the system attributes _id, _key and _rev. This also applies if you save an edge with a _from and a _to attribute, they are not returned as meta data, nor any user attributes even if their names start with an underscore.
If you want it to return the full document, then set the option returnNew:
collection.save(data, { returnNew: true} );
If set to true, return additionally the complete new documents under the attribute new in the result.
The result looks like this:
{
"_id": "coll/123",
"_key": "123",
"_rev": "_YDWaEaa--B",
"new": {
"_id": "coll/123",
"_key": "123",
"_rev": "_YDWaEaa--B",
"foo": "bar"
}
}

How to get result to array in mongoose?

I’m new to angular and I just started dealing with mongodb using mongoose.
In my application I need to get name list of drivers as string array, but, I’m getting array of objects with object id.
{
"_id": "5aa90ab23c49a72488afab7a",
"name": "mr. Rusiru ekanayaka"
},
{
"_id": "5aa90d4ba8c6b35438a8b132",
"name": "mr. Gihan ekanayaka"
},
{
"_id": "5aa90d56a8c6b35438a8b133",
"name": "mr. Gihan ekanayaka"
}
}
But I need something like this,
[
' mr. Gihan ekanayaka,
' mr. rusiru ekanayaka ',
' mr. Gihan ekanayaka '
]
I think I can re-format this in my backend by looping through the object. By is there anyway to get only one field of document in collection as string array without object id?
In my Driver model I do like this.
module.exports.getDrivers = function(callback){
Driver.find({},'name',callback);
}
Try this
module.exports.getDrivers = function(callback){
Driver.find({},{'_id' : 0,'name' : 1},callback);
}
Try this:
module.exports.getDrivers = function(callback){
Driver.distinct('name', callback);
}
One way to do it is to use a map, for example like this:
Driver.find({}).map(driver => driver.name);
That (with adjustments to the code to fit your coding environment) will return an array of driver.name items, like this:
[
"mr. Rusiru ekanayaka",
"mr. Gihan ekanayaka",
"mr. Gihan ekanayaka"
]

Finding Overlap date ranges between startdate and enddate in couch Db or cloudant

Hello I am building a reservation app using database as couchDb. I have several reservation documents and each of them has roomId, start date and end date.
Now when user creates a meeting request with roomId, start date and end date, I need to search for overlaps time ranges between the start time and endtime in the existing reservations and create a reservations only when there is no conflict. Along with this I also need to check for roomid.
The requirement is similar to Determine Whether Two Date Ranges Overlap.
I had created a view on my couch db emitting three keys:
function (doc) {
if (doc.type == "reservation") {
emit([doc.roomid, doc.startTime, doc.endTime], doc);
}
}
I did try creating something like
?startkey=["1970-01-01T00:00:00Z", ""]&endkey=["\ufff0", "1971-01-01T00:00:00Z"]
However I am not really getting how to compound query the view to find range of date along with the roomid.
Any help would be appreciated.
You could use Cloudant Query and specify the (StartA <= EndB) and (EndA >= StartB) search condition that's outlined in the referenced answer.
Create an index
Send a POST request to the _index endpoint, passing the following JSON data structure as payload.
POST https://$USERNAME:$PASSWORD#$HOST/$DATABASE/_index HTTP/1.1
{
"index": {
"fields": [
{ "name":"startTime",
"type":"string"
},
{
"name":"endTime",
"type":"string"
},
{
"name":"roomid",
"type":"string"
}
]
},
"type": "text"
}
Query the index
Send a POST request to the _find endpoint, passing the following JSON data structure as payload.
POST https://$USERNAME:$PASSWORD#$HOST/$DATABASE/_find HTTP/1.1
{
"selector": {
"startTime": {
"$lte": "2017-03-06T15:00:00Z"
},
"endTime": {
"$gte": "2017-03-06T14:00:00Z"
},
"roomid": {
"$eq": "room 123"
}
}
}
Replace the timestamp and room identifier values as needed. If the query returns at least one document you've encountered a booking conflict.

Combine Mongo Output with Node for API

I''m really new to Node but I currently have a NodeJS / Express open source CMS and would like to output some API data for an app that I am working. Forgive me if I'm not using the correct terminology or whatnot, this is new to me.
What I currently have are two collections, locations and tours. The CMS allows me to create a relationship between the two. This simply stores an array of ObjectID's in the locations record for each associated tour record.
What I want to do is take my API output code (below) and have it output the entire tours array, complete with all the fields (title, description, etc), in with each location record. Currently it only outputs an array of the ID's.
Here is my current code:
var async = require('async'),
landmark = require('keystone');
var Location = keystone.list('Location'),
Tour = keystone.list('Tour');
/**
* List Locations
*/
exports.list = function(req, res) {
Location.model.find(function(err, items) {
if (err) return res.apiError('database error', err);
res.apiResponse({
locations: items
});
});
}
/**
* Get Location by ID
*/
exports.get = function(req, res) {
Location.model.findById(req.params.id).exec(function(err, item) {
if (err) return res.apiError('database error', err);
if (!item) return res.apiError('not found');
res.apiResponse({
location: item
});
});
}
Current API output (truncated):
{
"locations": [
{
"_id": "53a47997ebe91d8a4a26d251",
"slug": "test-location",
"lastModified": "2014-06-20T20:19:14.484Z",
"commonName": "test location",
"__v": 3,
"url": "",
"tours": [
"53a47963ebe91d8a4a26d250"
],
"images": []
}
]
}
What I'm looking for:
{
"locations": [
{
"_id": "53a47997ebe91d8a4a26d251",
"slug": "test-location",
"lastModified": "2014-06-20T20:19:14.484Z",
"commonName": "test location",
"__v": 3,
"url": "",
"tours": [
{
"_id": "53a47963ebe91d8a4a26d250",
"title": "my test tour title",
"url": "url_to_audio_file"
}
],
"images": []
}
]
}
Anyone know if this is possible? Any help would be appreciated! Thanks!
It looks like you have setup your Location model to have a reference to the Tours, defined as an array of Tours. This means that when you store the Tour within your Location, you're not storing the data that represents that Tour, but instead an ID that references the Tour. When you perform the find operation, you're seeing that in the response that you send back to the client.
If this is the case, then you might want to take a look at Mongoose's populate function. This will take those references and populate them fully with the data that they contain.
So for instance, you can change your query to the following:
Location.model.find().populate('tours').exec(function(err, items) {
// items should now contain fully populated tours
}
Let me know if this isn't what you mean and I can try to help further.
The solution provided by #dylants is absolutely correct. However, for it to work you need to have tours declared as a Types.Relationship field in your Location list with the ref option set to Tour.
Check out the Keystone docs on Relationship Fields.
I included the many: true option in my example below, because I assumed this is a one-to-many relationship. If it isn't, you can discard it.
var keystone = require('keystone'),
Location = keystone.list('Location');
Location.add({
...
tours: { type: Types.Relationship, ref: 'Tour', many: true },
...
});
The List.relationship() method you mentioned is meant to be used only if you want a list of related documents to automatically appear in the Keystone Admin UI, and not to establish the actual relationship.
Hope this helps.

Resources