Update records within arrays - mongodb

I've just started to look at meteor and I'm stuck trying to update a record within an array within a record. Say i have a document that looks something like this:
Users:
{
_id: "somerandom",
name: "name1",
items, [
{
name: "item1",
data: "somedata",
...
},
{
name: "item2",
data: "somedata",
...
}
],
...
},
...
And I want to update the data of items 'item1'. I can't find anything on how to do this in meteor, but from what I have read about MongoDB I think that the correct way would be something like
Users.update({_id: userId, "items.name": 'item1}, {$set: {"items.$.data": newData}});
but since the MongoDB used in Meteor doesn't support $ it won't work, on the other hand I might be way of how this should be done.
Does anyone have a solution for this?

Use Meteor.methods() and implement your database operation on the server side and call it using Meteor.call() from the client. This feature is not yet implemented in Meteor and it is documented here.
You can read more in the discussion on this issue raised on Github, here.

I found a workaround at meteor's github https://github.com/meteor/meteor/blob/master/examples/parties/model.js
The idea is to first get the index of the array-item you want to change and then use that index instead of $. So in this case it would be something like
user = Users.findOne(userId);
index = _.indexOf(_.pluck(user.items, 'name'), 'item1');
modifier = {$set: {}};
modifier.$set["items." + index + ".data"] = newData;
Users.update(userId, modifier);

Related

mongodb update an existing field without overwritting

{
questions: {
q1: "",
}
}
result after updating:
{
questions: {
q1: "",
q2: ""
}
}
I want to add q2 inside questions, without overwritting what's already inside it (q1).
One solution I found is to get the whole document, and modify it on my backend, and send the whole document to replace the current one. But it seems really in-effiecient as I have other fields in the document as well.
Is there a query that does it more efficiently? I looked at the mongodb docs, didn't seem to find a query that does it.
As turivishal said, you can use $set like this
But you also can use $addFields in this way:
db.collection.aggregate([
{
"$match": {
"questions.q1": "q1"
}
},
{
"$addFields": {
"questions.q2": "q2"
}
}
])
Example here
Also, reading your comment where you say Cannot create field 'q2' in element {questions: "q1"}. It seems your original schema is not the same you have into your DB.
Your schema says that questions is an object with field q1. But your error says that questions is the field and q1 the value.

Using a $match and/or reference fields in DocumentDB causes Aggregation not supported error

I have a what I consider to be a pretty straight forward search. I am developing it on MOngoDB locally but deploying it for DocumentDB on AWS. The function that I've written looks something like this:
if (searchCategory) {
return [
{
$match: {
$or: [
{ title: params.searchString },
{ description: params.searchString },
{ workingNotes: params.searchString },
],
active: true,
},
},
];
}
This query used to not have the $match statement around it and it worked fine, but something that we wanted to do was to use reference fields in our model for searching linked items as well. So we have something like this in our model:
appendix: [{type: Schema.Types.ObjectId, ref: "document"}],
Now when I fire off the search query, I get an error back saying "Aggregation stage not supported: '$lookup on multiple join conditions and uncorrelated subquery.'"
Adding the reference field and the $match were the only changes made to the functionality, which worked before we added those.
Based off the documentation I've read about DocumentDB, the $match operator is supported, so it's possible I am missing something else about how to structure the query or handle the reference fields but I've been unable to determine what that might be.
Any help would be appreciated. Thanks!

Meteor/Mongo, push into an array inside an object inside an array

I am trying to teach myself Meteor and Mongo. I have a particular insertion inside my Meteor method that is driving me nuts.
My document object looks like this:
{
_id
name: "name",
stuff: {},
array: [
{
id: 0,
target:[
{
id: 0,
name: "1"
},{
id: 1,
name: "2"
}
]
},{
id: 1,
target:[
{
id: 0,
name: "A"
},{
id: 1,
name: "B"
}
]
}
],
}
I am trying to add objects into the target array, which is inside an object inside the array-array.
I have tried a number of different approaches over a few days based on some things I have seen here on stack overflow. The most recent attempt is:
Documents.update({_id: id, 'array.id': arrayId}, {$addToSet:{'array.$.target': objectToInsert}},{upsert: false, multi: false})
If anyone could point me in the right direction I would appreciate it.
I donot know the mongo query to do something like this but it can be done as below
let theArray=Document.findOne({_id:id}).array,
arrayOfIds=_.pluck(theArray,"id"),
index=_.indexOf(arrayOfIds,arrayId),
theArray[index].target.push(objectTobeInserted)
Now update the document with the modified array
Document.update({_id:id},{$set:{array:theArray})
If you donot understand _.pluck and _.indexOf, you can refer to underscorejs
Seems this could be an issue with where you are doing the update. If this is on the client, then see here:
Update an subdocument contained in an array contained in a MongoDB document
Solution is to move this to the server.
Also, you may want to look at the difference between $addToSet and $push here MongoDb: Difference between $push/$addtoset
If this is not on the client side and changing to push does not fix your issue, can you add to your question what the error is.

Removing Non-Collection Embedded Document via Mongo Shell

So I've got an application that uses a lot of embedded documents, which is fine. However I've noticed that some embedded documents aren't displayed when you show collections in Mongo shell.
Normally this isn't an issue, but whilst dicking with setting embedded documents, I accidentally added an empty entry to one of the entries. I'd normally do something like this to remove the entry db.collection.remove({_id: ObjectId('<OBJECT_ID>')}), but since some of these aren't actual collections I'm unable to do it like this.
I'm also not sure how I'd splice out this successfully while actually removing the document. I could splice it out of the entry, but I have a feeling it would leave that embedded document floating around somewhere in the DB.
Any ideas how to do this?
To give you an idea what I'm talking about, an example of the entry:
entry = {
_id: ObjectId('blah blah blah'),
name: {
first: 'example',
last: 'city'
},
log : [
{
_id: ObjectId('some id'),
action: 'whatever',
someField: 'etc.'
},
{
_id: ObjectId('another id')
},
{
_id: ObjectId('yet another id'),
action: 'who cares',
someField: 'data'
}
]
}
If you want to remove one of the log entries, then you want the $pull operator.
The format would be something like:
db.collection.update({_id:<id-of-document-to-update>},
{$pull:{"log._id":<id-of-log-entry-to-remove>"}}
)
This says, find document with certain _id and remove from log array an entry with certain sub_id.

Ways to implement data versioning in MongoDB

Can you share your thoughts how would you implement data versioning in MongoDB. (I've asked similar question regarding Cassandra. If you have any thoughts which db is better for that please share)
Suppose that I need to version records in an simple address book. (Address book records are stored as flat json objects). I expect that the history:
will be used infrequently
will be used all at once to present it in a "time machine" fashion
there won't be more versions than few hundred to a single record.
history won't expire.
I'm considering the following approaches:
Create a new object collection to store history of records or changes to the records. It would store one object per version with a reference to the address book entry. Such records would looks as follows:
{
'_id': 'new id',
'user': user_id,
'timestamp': timestamp,
'address_book_id': 'id of the address book record'
'old_record': {'first_name': 'Jon', 'last_name':'Doe' ...}
}
This approach can be modified to store an array of versions per document. But this seems to be slower approach without any advantages.
Store versions as serialized (JSON) object attached to address book entries. I'm not sure how to attach such objects to MongoDB documents. Perhaps as an array of strings.
(Modelled after Simple Document Versioning with CouchDB)
The first big question when diving in to this is "how do you want to store changesets"?
Diffs?
Whole record copies?
My personal approach would be to store diffs. Because the display of these diffs is really a special action, I would put the diffs in a different "history" collection.
I would use the different collection to save memory space. You generally don't want a full history for a simple query. So by keeping the history out of the object you can also keep it out of the commonly accessed memory when that data is queried.
To make my life easy, I would make a history document contain a dictionary of time-stamped diffs. Something like this:
{
_id : "id of address book record",
changes : {
1234567 : { "city" : "Omaha", "state" : "Nebraska" },
1234568 : { "city" : "Kansas City", "state" : "Missouri" }
}
}
To make my life really easy, I would make this part of my DataObjects (EntityWrapper, whatever) that I use to access my data. Generally these objects have some form of history, so that you can easily override the save() method to make this change at the same time.
UPDATE: 2015-10
It looks like there is now a spec for handling JSON diffs. This seems like a more robust way to store the diffs / changes.
There is a versioning scheme called "Vermongo" which addresses some aspects which haven't been dealt with in the other replies.
One of these issues is concurrent updates, another one is deleting documents.
Vermongo stores complete document copies in a shadow collection. For some use cases this might cause too much overhead, but I think it also simplifies many things.
https://github.com/thiloplanz/v7files/wiki/Vermongo
Here's another solution using a single document for the current version and all old versions:
{
_id: ObjectId("..."),
data: [
{ vid: 1, content: "foo" },
{ vid: 2, content: "bar" }
]
}
data contains all versions. The data array is ordered, new versions will only get $pushed to the end of the array. data.vid is the version id, which is an incrementing number.
Get the most recent version:
find(
{ "_id":ObjectId("...") },
{ "data":{ $slice:-1 } }
)
Get a specific version by vid:
find(
{ "_id":ObjectId("...") },
{ "data":{ $elemMatch:{ "vid":1 } } }
)
Return only specified fields:
find(
{ "_id":ObjectId("...") },
{ "data":{ $elemMatch:{ "vid":1 } }, "data.content":1 }
)
Insert new version: (and prevent concurrent insert/update)
update(
{
"_id":ObjectId("..."),
$and:[
{ "data.vid":{ $not:{ $gt:2 } } },
{ "data.vid":2 }
]
},
{ $push:{ "data":{ "vid":3, "content":"baz" } } }
)
2 is the vid of the current most recent version and 3 is the new version getting inserted. Because you need the most recent version's vid, it's easy to do get the next version's vid: nextVID = oldVID + 1.
The $and condition will ensure, that 2 is the latest vid.
This way there's no need for a unique index, but the application logic has to take care of incrementing the vid on insert.
Remove a specific version:
update(
{ "_id":ObjectId("...") },
{ $pull:{ "data":{ "vid":2 } } }
)
That's it!
(remember the 16MB per document limit)
If you're looking for a ready-to-roll solution -
Mongoid has built in simple versioning
http://mongoid.org/en/mongoid/docs/extras.html#versioning
mongoid-history is a Ruby plugin that provides a significantly more complicated solution with auditing, undo and redo
https://github.com/aq1018/mongoid-history
I worked through this solution that accommodates a published, draft and historical versions of the data:
{
published: {},
draft: {},
history: {
"1" : {
metadata: <value>,
document: {}
},
...
}
}
I explain the model further here: http://software.danielwatrous.com/representing-revision-data-in-mongodb/
For those that may implement something like this in Java, here's an example:
http://software.danielwatrous.com/using-java-to-work-with-versioned-data/
Including all the code that you can fork, if you like
https://github.com/dwatrous/mongodb-revision-objects
If you are using mongoose, I have found the following plugin to be a useful implementation of the JSON Patch format
mongoose-patch-history
Another option is to use mongoose-history plugin.
let mongoose = require('mongoose');
let mongooseHistory = require('mongoose-history');
let Schema = mongoose.Schema;
let MySchema = Post = new Schema({
title: String,
status: Boolean
});
MySchema.plugin(mongooseHistory);
// The plugin will automatically create a new collection with the schema name + "_history".
// In this case, collection with name "my_schema_history" will be created.
I have used the below package for a meteor/MongoDB project, and it works well, the main advantage is that it stores history/revisions within an array in the same document, hence no need for an additional publications or middleware to access change-history. It can support a limited number of previous versions (ex. last ten versions), it also supports change-concatenation (so all changes happened within a specific period will be covered by one revision).
nicklozon/meteor-collection-revisions
Another sound option is to use Meteor Vermongo (here)