This question already has answers here:
How to Update Multiple Array Elements in mongodb
(16 answers)
Closed 5 years ago.
I recently started using MongoDB and I have a question regarding updating arrays in a document.
I got structure like this:
{
"_id" : ObjectId(),
"post" : "",
"comments" : [
{
"user" : "test",
"avatar" : "/static/avatars/asd.jpg",
"text" : "....."
}
{
"user" : "test",
"avatar" : "/static/avatars/asd.jpg",
"text" : "....."
}
{
"user" : "test",
"avatar" : "/static/avatars/asd.jpg",
"text" : "....."
}
...
]
}
I'm trying to execute the following query:
update({"comments.user":"test"},{$set:{"comments.$.avatar": "new_avatar.jpg"}},false,true)
The problem is that it update all documents, but it update only the first array element in every document. Is there any way to update all array elements or I should try to do it manually?
Thanks.
You cannot modify multiple array elements in a single update operation. Thus, you'll have to repeat the update in order to migrate documents which need multiple array elements to be modified. You can do this by iterating through each document in the collection, repeatedly applying an update with $elemMatch until the document has all of its relevant comments replaced, e.g.:
db.collection.find().forEach( function(doc) {
do {
db.collection.update({_id: doc._id,
comments:{$elemMatch:{user:"test",
avatar:{$ne:"new_avatar.jpg"}}}},
{$set:{"comments.$.avatar":"new_avatar.jpg"}});
} while (db.getPrevError().n != 0);
})
Note that if efficiency of this operation is a requirement for your application, you should normalize your schema such that the location of the user's avatar is stored in a single document, rather than in every comment.
One solution could be creating a function to be used with a forEach and evaling it (so it runs quickly). Assuming your collection is "article", you could run the following:
var runUpdate = function(){
db.article.find({"comments.user":"test").forEach( function(article) {
for(var i in article.comments){
article.comments[i].avatar = 'new_avatar.jpg';
}
db.article.save(article);
});
};
db.eval(runUpdate);
If you know the indexes you want to update you can do this with no problems like this:
var update = { $set: {} };
for (var i = 0; i < indexesToUpdate.length; ++i) {
update.$set[`comments.${indexesToUpdate[i]}. avatar`] = "new_avatar.jpg";
}
Comments.update({ "comments.user":"test" }, update, function(error) {
// ...
});
be aware that must of the IDE's will not accept the syntax but you can ignore it.
It seems like you can do this:
db.yourCollection.update({"comments.user":"test"},{$set:{"comments.0.avatar": "new_avatar.jpg", "comments.1.avatar": "new_avatar.jpg", etc...})
So if you have a small known number of array elements, this might be a little easier to do. If you want something like "comments.*.avatar" - not sure how to do that. It is probably not that good that you have so much data duplication tho..
Related
I am trying to make a query to a Firebase collection using swift.
My Collection is like this :
{
"networks" : {
"-KF9zQYGA1r_JiFam8gd" : {
"category" : "friends",
"members" : {
"facebook:10154023600052295" : true
},
"name" : "My friends",
"owner" : "facebook:10154023600052295",
"picture" : "https://my.img/img.png"
},
"-KF9zQYZX6p_ra34DTGh" : {
"category" : "friends",
"members" : {
"tototata" : true
},
"name" : "My friends2",
"owner" : "facebook:10154023600052295",
"picture" : "https://my.img/img.png"
}
}
and my query is like that :
let networksRef = ref.childByAppendingPath("networks")
.queryOrderedByChild("members")
.queryEqualToValue(true, childKey: uid)
Then I am populating a TableView using FirebaseUI.
the thing is that query doesn't return anything, I also tried using this kind of query :
let networksRef = ref.childByAppendingPath("networks")
.queryOrderedByChild("members")
.queryStartingAtValue(true, childKey: uid)
.queryEndingAtValue(true, childKey: uid)
And to be clear "uid" is the node name nested in the "members" node with true as value. (As on the picture)
I can't get all the nodes without filtering because of the amount of data to be downloaded.
I'm stuck on that for a day, and I'm going crazy.. :-)
If someone can help ?
The correct syntax would be:
let networksRef = ref.childByAppendingPath("networks")
.queryOrderedByChild("members/\(uid)")
.queryEqualToValue(true)
But note that you'll need an index for each uid for this to work efficiently:
{
"rules": {
"networks": {
".indexOn": ["facebook:10154023600052295", "tototata"]
}
}
}
Without these indexes, the Firebase client will download all data and do the filtering locally. The result will be the same, but it'll consume a lot of unneeded bandwidth.
A proper solution for this requires that you change your data structure around. In Firebase (and most other NoSQL databases) you often end up modeling the data in the way that your application wants to access it. Since you are looking for the networks that a user is part of, you should store the networks per user:
"networks_per_uid": {
"facebook:10154023600052295": {
"-KF9zQYGA1r_JiFam8gd": true
},
"tototata": {
"-KF9zQYZX6p_ra34DTGh": true
},
}
Adding this so-called index to your data structure is called denormalizing and is introduces in this blog post, the documentation on data structuring and this great article about NoSQL data modeling.
I tried many things but of no use.
I have already raised a question on stackoverflow earlier but I am still facing the same issue.
Here is the link to old stackoverflow question
creating multiple nodes with properties in json in neo4j
Let me try out explaining with a small example
This is the query I want to execute
{
"params" : {
"props" : [
{
"LocalAsNumber" : 0,
"NodeDescription" : "10TiMOS-B-4.0.R2 ",
"NodeId" : "10.227.28.95",
"NodeName" : "BLR_WAO_SARF7"
}
]
},
"query" : "MATCH (n:Router) where n.NodeId = {props}.NodeId RETURN n"}
For simplicity I have added only 1 props array otherwise there are around 5000 props. Now I want to execute the query above but it fails.
I tried using (props.NodeId}, {props[NodeID]} but everything fails.
Is it possbile to access a individual property in neo4j?
My prog is in c++ and I am using jsoncpp and curl to fire my queries.
If you do {props}.nodeId in the query then the props parameter must be a map, but you pass in an array. Do
"props" : {
"LocalAsNumber" : 0,
"NodeDescription" : "10TiMOS-B-4.0.R2 ",
"NodeId" : "10.227.28.95",
"NodeName" : "BLR_WAO_SARF7"
}
You can use an array of maps for parameter either with a simple CREATE statement.
CREATE ({props})
or if you loop through the array to access the individual maps
FOREACH (prop IN {props} |
MERGE (i:Interface {nodeId:prop.nodeId})
ON CREATE SET i = prop
)
Does this query string work for you?
"MATCH (n:Router) RETURN [p IN {props} WHERE n.NodeId = p.NodeId | n];"
I have this json model:
model/data.json
{
"orders" : [
{
"header" : { "id" : "00001", "description" : "This is the first order" },
"items" : [
{ "name" : "Red Book","id" : "XXYYZZ" },
{ "name" : "Yellow Book", "id" : "AACCXX" },
{ "name" : "Black Book", "id" : "UUEEAA" },
]
},
{
// another order with header + items
},
.....
]
}
and I'm assigning it onInit to the view, like this:
var model = new sap.ui.model.json.JSONModel("model/data.json");
sap.ui.getCore().setModel(reqModel);
I'm trying to display a list of orders in the first view (showing the id), like this:
var list = new sap.m.List({
id: "mainList",
items: []
});
var items = new sap.m.ActionListItem({
text : "{id}",
press : [ //click handler, onclick load the order details page ]
});
list.bindItems("/orders", items);
.... // add list to the page etc etc
What I cannot do, is connect each order to its header->id.. I tried
text: "/header/{id}"
text: "{/header/id}"
in the items declaration, and
list.bindItems("/orders/header", items)
in the list binding, but none of them works.. The id value is not displayed, even though a "blank" list item is shown..
Any idea? What am I doing wrong?
Thank you
The solution was one of those I tried (but I don't know why it didn't work at that time)
text: "{/header/id}"
The ListItem acts as a Template for a list/array of objects. That's why you bind it against an array structure in your data:
list.bindItems("/orders", itemTemplate)
That makes bindings of the ListItem relative to /orders and therefore your item should look like this without leading '/' (absolute paths would look like this /orders/0/header/id asf.):
var itemTemplate = new sap.m.ActionListItem({
text : "{header/id}",
press : [ //click handler, onclick load the order details page ]
});
Not quite sure how you made it work the way you have shown... May be it's not as picky as I thought.
Btw: For whatever reason the ResourceModel builds an exception of that syntax. You can always omit the leading '/' when dealing with ResourceModels (probably because they do not allow nested structures).
BR
Chris
Cannot add comments yet, therefore an answer to you solved Problem, that could answer the initial problem. (And inform People using that example in any way)
In the current code listing you use the variable "reqModel" to set the model, but the variable with the model in it is named "model" in the line before. Maybe that was the first reason why both of your examles would not work?
Perhaps this error was cleared on rewriting some passages while testing.
greetings! -nx
I am still relatively new to Backbone. I'm just beginning to get a sense of how it works. I 've been using Rails for a while and it's what is giving me some hint at times of using Backbone. so here goes:
Simple, I have a Company model over in Rails say I do in javascript console
companies = new Backbone.Collection();
companies.url = '/companies';
companies.url; // '/companies'
companies.fetch();
company = companies.at(0);
company.url
The last line, "company.url" doens't return what I expect, what I expect is something like '/companies/12345' so that when I update company and decide to save it, it will know where to "put" to.
So does that mean that everytime I want something saved, I have to save on the whole collection?(!)
I would take a look at what company.url() is returning. Saving the whole collection should not be necessary.
I was trying your problem, and found that the models are not getting an id to it. So the url method on the models is not working. So i think you need to put your collections like below (what i tried)
cltn = Backbone.Collection.extend({
model:modelName,
parse:function(res){
var i = 0;
var itms = _.map(res.items, function(o){
o.id = ++i;
return o
})
return itms;
}
});
cltnInst = new cltn();
cltnInst.url="/combodata.json?";
cltnInst.fetch();
Then in your firebug type the below codes.
cltnInst.url; // this is a string props. output will be "/combodata.json?"
cltnInst.at(0).url() // this is a method props output will be "/combodata.json?/1"
combodata.json will be of this format
{
"identifier": "title",
"items": [
{
"title": "A",
"tag": "htmlcss",
"date": "today"
}, ...
]}
Please correct me if my answer is wrong.
I have actually made a mistake in the step where I make an attempt to create a new companies collection.
So instead of
var Companies = new Backbone.Collection()
I should really do something like this:
var Companies = Backbone.Collection.extend({
model: Company,
url : '/companies'
});
var Company = Backbone.Model.extend();
var companies_collection = new Companies()
companies_collection.fetch()
companies_collection.models[0].url() // '/projects/123'
I have an MVC-3 (RC1) application using Entity Framework 4.
I wish to return a JSON object from a controller action. This object is referenced by other objects, which obviously return the reference.
I thus receive the following circular reference error:
Server Error in '/' Application.
A circular reference was detected
while serializing an object of type
'Application.Models.ReferenceObject'.
Description: An unhandled exception
occurred during the execution of the
current web request. Please review the
stack trace for more information about
the error and where it originated in
the code.
Exception Details:
System.InvalidOperationException: A
circular reference was detected while
serializing an object of type
'Application.Models.ReferenceObject'.
NB: Application & ReferenceObject are obviously replacements for the actual namespace / object.
According to Stack Overflow: Circular reference exception when serializing LINQ to SQL classes, this can be overcome using JSON.Net; however I would like to avoid this and instead try to exclude the offending reference properties from the object being serialized.
What do I mean?
I want to do something like this:
IList<ReferenceObject> list = Repository.GetReferenceObjects();
return Json(list.**<method>**("ObjectsReferencingThis"));
where **<method>** is some method that does the opposite to the ObjectQuery(Of T).Include method and ObjectsReferencingThis is the property that is causing the circular reference.
NB: I do not wish to remove these properties or create POCOs as this only affects the Json serialization.
Anyone able to help please?
:)
I had a similar problem when worked on one of my previous project.
Here is what I ended up doing:
IList<Product> list = Repository.GetProducts();
var collection = products.Select(product => new
{
id = product.Id,
name = product.Name,
detailUrl = product.DetailUrl,
imageLargeUrl = product.ThumbNailUrl,
tagtitle = product.Name.ToUpper(),
tagheader = "Words our cherished patrons use to describe this product",
tagwords = from tag in product.Tags group tag by tag.Name into words select new { name = words.Key, weight = words.Count() }
});
var result = new {id = inquiry.Id, products = collection, };
return this.Jsonp(result);
Here is how the result Json would look like:
{
"id" : 2,
"products" : [{
"id" : "3605970008857",
"name" : "TITLE1",
"detailUrl" : "http://www.urlhere.com",
"tagwords" : [{
"name" : "roses",
"weight" : 1
},
{
"name" : "cotton",
"weight" : 1
},
{
"name" : "happy",
"weight" : 1
}]
},
{
"id" : "3605970019891",
"name" : "TITLE2",
"detailUrl" : "http://www.urlhere.com",
"tagwords" : []
}],
}
You can also add any other properties from you referenced objects to the result as you wish,to be shown in your Json object :)
I made a very trivial solution which is not recommended if you have very big list
letters=UserOperations.GetDepartmentLettersForSecretary(pageNumber, pageSize,(Session["User"] as User).DepartmentID.Value, (Session["User"] as User).ID);
foreach (Letter letter in letters)
{
letter.LetterStatus.Letters = null;
}
the problem of circular reference in my case is in LetterStatus.Letters
so I Iterated through the list and assigned it to null
as I told you its not recommended if you have very big list