I'm making a MongoDB statistic system using the Java driver, and I am wondering if it is possible (and how) to change the value of a key nested inside many objects. Here is how my database is formatted:
{
location : “chicago”,
stats : [
{
"employee" : "rob",
"stat1" : 1,
"stat2" : 3,
"stat3" : 2
},
{
"employee" : "krista",
"stat1" : 1,
"stat2" : 3,
"stat3" : 2
}
]
}
So, for example, how could I change Rob's "stat2" to another value? I am new to JSON and the MongoDB Java driver. Any help is appreciated!
You need to use the positional $ operator and $set in order to update what you want.
db.collection.update(
{ _id: <docId>, "stats.employee": "rob" },
{ "$set": { "stats.$.stat2": <value> } }
)
So you match your document and the required element of the array. The update side uses that array index to know in which element to update. The $set operator only updates the specified field.
In Java, Build with BasicDBObject.
BasicDBObject query = new BasicDBObject("_id", id);
query.append( new BasicDBObject("stats.employee", "rob") );
BasicDBObject update = new BasicDBObject("$set",
new BasicDBObject("stats.$.stat2", value));
collection.update(query,update);
Related
If you can help me, I have an update in mongo with $cond , this update is only done if the field is empty, otherwise it updates the field with another value. Example in mongo db
I want to update the field camp1
if camp1 no exits = values
if camp1 exits = value2
db.getCollection('prueba').update(
{"cdAccount": "ES3100810348150001326934"},
[{$set:{camp1 :{"$cond": [{"$not": ["$camp1"]}, "values", "value2"]}}}]);
Result:
{
"_id" : ObjectId("62dd08c3f9869303b79b323b"),
"cdAccount" : "ES3100810348150001326934",
"camp1" : "value2"
}
Now I do the same in scala with this code
def appendIfNotNull(key: String,value :Object) = {
var eq2Array = new util.ArrayList[Object]()
eq2Array.add("$"+key)
val eq2Op = new Document("$not", eq2Array)
var condList = new util.ArrayList[Object]()
condList.add(eq2Op)
condList.add(value.asInstanceOf[AnyRef])
//condList.add("$"+key)
condList.add("value2")
val availDoc =
new Document("$cond",
new Document("$cond", condList)).toBsonDocument(classOf[BsonDocument],getCodecRegistry).get("$cond")
println("availDoc : " + availDoc)
documentGrab.append(key,availDoc)
}
val finalVar = appendIfNotNull("camp1","values")
println("finalVar : " + finalVar)
availDoc : {"$cond": [{"$not": ["$camp1"]}, "values", "value2"]}
finalVar : Document{{camp1={"$cond": [{"$not": ["$camp1"]}, "values", "value2"]}}}
val updateDocument = new Document("$set" , finalVar )
println("updateDocument : " + updateDocument)
collectionA.updateMany(Filters.eq("cdAccount", "ES3100810348150001326934"),updateDocument)
The only difference I see is that in mongodb the "[" is added at the beginning of the $set and it does it well
MongoDB
[ {$set:{camp1 :{"$cond": [{"$not": ["$camp1"]}, "values", "value2"]}}} ] --> Ok Update
Scale
{$set:{camp1 :{"$cond": [{"$not": ["$camp1"]}, "values", "value2"]}}} --> Ok in scala , but I get the result II
I am using mongodb 5.0.9
Now in mongodb I execute the statement made in scala
db.getCollection('prueba').update(
{"cdAccount": "ES3100810348150001326934"},
{$set :{camp1 :{"$cond": [{"$not": ["$camp1"]}, "values", "value2"]}}});
When I run it in scala the same thing happens
Result II :
{
"cdAccount" : "ES3100810348150001326934",
"camp1" : {
"$cond" : [
{
"$not" : [
"$camp1"
]
},
"values",
"value2"
]
}
}
Can someone tell me how to fix it?
Thank you so much
You see the very important difference when priting the queries.
$cond is an aggregation pipeline operator. It is processed only when aggregation pipeline is used to update the data. When a simple (non-pipelined) update is used, the operator has no special meaning and this is exactly what you see in the output.
You indicate "pipeline update" by passing an array instead of simple object as update description in javascript API (and mongo console). In Scala/Java you have to use one of the updateMany overloads that takes update description as List, not Bson. I.e. you need something like
collectionA.updateMany(
Filters.eq("cdAccount", "ES3100810348150001326934"),
Collections.singletonList(updateDocument)
)
How can I sort a MongoDB collection by a given field, case-insensitively? By default, I get A-Z before a-z.
Update:
As of now mongodb have case insensitive indexes:
Users.find({})
.collation({locale: "en" })
.sort({name: 1})
.exec()
.then(...)
shell:
db.getCollection('users')
.find({})
.collation({'locale':'en'})
.sort({'firstName':1})
Update: This answer is out of date, 3.4 will have case insensitive indexes. Look to the JIRA for more information https://jira.mongodb.org/browse/SERVER-90
Unfortunately MongoDB does not yet have case insensitive indexes: https://jira.mongodb.org/browse/SERVER-90 and the task has been pushed back.
This means the only way to sort case insensitive currently is to actually create a specific "lower cased" field, copying the value (lower cased of course) of the sort field in question and sorting on that instead.
Sorting does work like that in MongoDB but you can do this on the fly with aggregate:
Take the following data:
{ "field" : "BBB" }
{ "field" : "aaa" }
{ "field" : "AAA" }
So with the following statement:
db.collection.aggregate([
{ "$project": {
"field": 1,
"insensitive": { "$toLower": "$field" }
}},
{ "$sort": { "insensitive": 1 } }
])
Would produce results like:
{
"field" : "aaa",
"insensitive" : "aaa"
},
{
"field" : "AAA",
"insensitive" : "aaa"
},
{
"field" : "BBB",
"insensitive" : "bbb"
}
The actual order of insertion would be maintained for any values resulting in the same key when converted.
This has been an issue for quite a long time on MongoDB JIRA, but it is solved now. Take a look at this release notes for detailed documentation. You should use collation.
User.find()
.collation({locale: "en" }) //or whatever collation you want
.sort({name:1})
.exec(function(err, users) {
// use your case insensitive sorted results
});
Adding the code .collation({'locale':'en'}) helped to solve my issue.
As of now (mongodb 4), you can do the following:
mongo shell:
db.getCollection('users')
.find({})
.collation({'locale':'en'})
.sort({'firstName':1});
mongoose:
Users.find({})
.collation({locale: "en" })
.sort({name: 1})
.exec()
.then(...)
Here are supported languages and locales by mongodb.
In Mongoose:-
Customer.find()
.collation({locale: "en" })
.sort({comapany: 1})
Here it is in Java. I mixed no-args and first key-val variants of BasicDBObject just for variety
DBCollection coll = db.getCollection("foo");
List<DBObject> pipe = new ArrayList<DBObject>();
DBObject prjflds = new BasicDBObject();
prjflds.put("field", 1);
prjflds.put("insensitive", new BasicDBObject("$toLower", "$field"));
DBObject project = new BasicDBObject();
project.put("$project", prjflds);
pipe.add(project);
DBObject sort = new BasicDBObject();
sort.put("$sort", new BasicDBObject("insensitive", 1));
pipe.add(sort);
AggregationOutput agg = coll.aggregate(pipe);
for (DBObject result : agg.results()) {
System.out.println(result);
}
If you want to sort and return all data in a document, you can add document: "$$ROOT"
db.collection.aggregate([
{
$project: {
field: 1,
insensitive: { $toLower: "$field" },
document: "$$ROOT"
}
},
{ $sort: { insensitive: 1 } }
]).toArray()
Tried all the above and answers
Consolidating the result
Answer-1:
db.collection.aggregate([
{ "$project": {
"field": 1,
"insensitive": { "$toLower": "$field" }
}},
{ "$sort": { "insensitive": 1 } } ])
Aggregate query converts the field into lower, So performance is low for large data.
Answer-2:
db.collection.find({}).collation({locale: "en"}).sort({"name":1})
By default mongo follows uft-8 encoding(Z has high piriority then a) rules ,So overriding with language-specific rules.
Its fast compare to above query
Look into an official document to customize rules
https://docs.mongodb.com/manual/reference/collation/
We solve this problem with the help of .sort function in JavaScript array
Here is the code
function foo() {
let results = collections.find({
_id: _id
}, {
fields: {
'username': 1,
}
}).fetch();
results.sort((a, b)=>{
var nameA = a.username.toUpperCase();
var nameB = b.username.toUpperCase();
if (nameA nameB) {
return 1;
}
return 0;
});
return results;
}
my mongo collections contains following documents
{
"_id" : ObjectId("52d43cd29b85346a4aa6fe17"),
"windowsServer" : [
{
"topProcess" : [ ]
}]
},
{
"_id" : ObjectId("52d43cd29b85346a4aa6fe18"),
"windowsServer" : [
{
"topProcess" : [ {pid:1,name:"wininit"}]
}]
}
Now in my java code I want to used only topProcess in above case I want only second document which topProcess having some data. For this I write my java code as below
BasicDBObject criteria = new BasicDBObject();
BasicDBObject projections = new BasicDBObject();
criteria.put("windowsServer.topProcess", new BasicDBObject("$ne", "[]"));
projections.put("windowsServer.topProcess",1);
DBCursor cur = coll.find(criteria,projections);
while(cur.hasNext() && !isStopped()) {
String json = cur.next().toString();
}
when I execute above code and print json string it also contains the both topProcess. Can any one knows how should I get only second documents topProcess?
Try this one (and translate it to your java driver):
"windowsServer.topProcess": {$not: {$size: 0} }
In your code, you only have mistake in the following line.
criteria.put("windowsServer.topProcess", new BasicDBObject("$ne", "[]"));
You try to compare if an array is empty by using brackets as a String. You can use BasicDBList() for an empty array. Update above line with the following and it should work.
criteria.put("windowsServer.topProcess", new BasicDBObject("$ne", new BasicDBList()));
I am new to mongodb. Plz help me with this.
I need a document like this
employee{
_id:111,
name:xxx,
dependents : [ {
name:a,
age:51,
dep_children:[{name:aa}{name:bb}{name:c}]
}
{
name:b,
age:52,
dep_children:[{name:aa}{name:bb}{name:c}]
}
{
name:c,
age:51,
dep_children:[{name:aa}{name:bb}{name:cc}]
}
]
}
I am using the below script to migrate data for SQL and update it into mongoDB
My code looks something like this:
while(personcount>=0)
{
BasicDBObject doc = new BasicDBObject("_id",ind1.get(count)).
append("name",ind2.get(count));
coll.insert(doc);
while(dependentcount>0)
{
BasicDBObject querymongo = new BasicDBObject();
querymongo.put( "name",ind.get(count));
BasicDBObject tenant = new BasicDBObject();
tenant.put("name",indsa.get(innercount) );
tenant.put("age", indsa2.get(innercount));
BasicDBObject update = new BasicDBObject();
update.put("$push", new BasicDBObject("dependents",tenant));
coll.update(querymongo, update,true,true);
while(kidcount>0)
{
BasicDBObject querymongofact = new BasicDBObject();
querymongokid.put( "dependent.name",indsa.get(innercount));
BasicDBObject tenantkid = new BasicDBObject();
tenantkid .put("name",indfact.get(innercountfact) );
BasicDBObject updatekid = new BasicDBObject();
updatekid .put("$push", new BasicDBObject("dependent.dep_children",tenantkid));
coll.update(querymongokid, updatekid ,true,true);
}
}
}
when we print querymongokid and updatekid, the data inside it are expected values itself. This code is not throwing any error. But in data base the only dep_children data is not getting updated . I am not getting wt went wrong. please help me
Thanks in advance
Your last query fails in the mongo driver in a sense that the update has no effect - but it's not an actual error. Just let me reproduce what you are doing in the mongo shell:
> db.coll.insert({_id:1,name:"name1"})
> db.coll.update({name:"name1"}, {"$push": {dependents: {name:"a", age:50}}})
> db.coll.update({name:"name1"}, {"$push": {dependents: {name:"b", age:55}}})
> db.coll.findOne()
{
"_id" : 1,
"dependents" : [
{
"name" : "a",
"age" : 50
},
{
"name" : "b",
"age" : 55
}
],
"name" : "name1"
}
> db.coll.update({"dependents.name": "a"}, {"$push": {"dependents.dep_children": {name:"aa"}}})
can't append to array using string field name: dep_children
> db.coll.update({"dependents.name": "a"}, {"$push": {"dependents.$.dep_children": {name:"aa"}}})
> db.coll.findOne()
{
"_id" : 1,
"dependents" : [
{
"age" : 50,
"dep_children" : [
{
"name" : "aa"
}
],
"name" : "a"
},
{
"name" : "b",
"age" : 55
}
],
"name" : "name1"
}
Unfortunately, I have very little experience with the native mongo java driver (I'm usually on Spring data), but changing your line
updatekid.put("$push", new BasicDBObject("dependent.dep_children",tenantkid));
to
updatekid.put("$push", new BasicDBObject("dependent.$.dep_children",tenantkid));
should do the trick as well.
The reason for that behavior is that "dependent.dep_children" is not a valid selector as it corresponds to "go to field dep_children within the subdocument dependent". However, "dependent" happens to be an array without any fields. The $ replaces an explicit index and will make sure the correct subdocument from your query is selected.
Also see here for a less error-prone formulation of your query - without using $elemMatch it will only work if your query uniquely identifies a certain array element. With $elemMatch it will always work, but only update the first match.
I have a collection with embedded documents.
System
{
System_Info: "automated",
system_type:
{
system_id:1,
Tenant: [
{
Tenant_Id: 1,
Tenant_Info: "check",
Prop_Info: ...
},
{
Tenant_Id: 2,
Tenant_Info: "sucess",
Prop_Info: ...
} ]
}}
I need to update and set the field Tenant_Info to "failed" in Tenant_Id: 2
I need to do it using mongodb java. I know to insert another tenant information in the tenant array. But here I need to set the field using java code.
Could anyone help me to do this?
How about something like this (untested):
db.coll.update(
{
"System.system_type.Tenant.Tenant_Id" : 2
},
{
$set : {
"System.system_type.Tenant.$.Tenant_Info" : "failed"
}
},
false,
true
);
It should update the first nested document in the collection with a Tenant_id of 2 for all top level documents. If you need to target a specific top level document, you need to add it to the as field on the first object argument in the update call.
And the equivalent in Java:
BasicDBObject find = new BasicDBObject(
"System.system_type.Tenant.Tenant_Id", 2
);
BasicDBObject set = new BasicDBObject(
"$set",
new BasicDBObject("System.system_type.Tenant.$.Tenant_Info", "failed")
);
coll.update(find, set);