I have a collection "prefs" with document structure as below
{
_id: {
userId: "abc"
},
val: {
status: 1,
prefs: [
{
value: "condition",
lastSent: ISODate("2017-07-17T23:46:53.717Z")
}
],
deal: 2,
prevDeal: 3
}
}
I am trying to update the date field lastSent with a condition on userId and status. Below are the queries that I derieved from my Java code.
Select Query:
{ "_id" : { "userId" : "abc"} , "val.status" : 1 , "val.prefs.value" : "condition"}
Update Query:
{ "$set" : { "val.prefs.$.lastSent" : { "$date" : "2017-07-17T23:50:07.009Z"}}}
The above query is giving error as follows:
The dotted field 'prefs.$.lastSent' in 'val.prefs.$.lastSent' is not valid for storage.
How do I achieve this?
Below is my Java code:
BasicDBObject _idObject = new BasicDBObject();
_idObject.put("userId", "abc");
BasicDBObject _selectQuery = new BasicDBObject();
_selectQuery.put("_id", _idObject);
_selectQuery.put("val.status", 1);
_selectQuery.put("val.prefs.value", "condition");
BasicDBObject _valueUpdateQuery = new BasicDBObject();
_valueUpdateQuery.put("prefs.$.lastSent", lastSent);
BasicDBObject _updateQuery = new BasicDBObject();
_updateQuery.put("$set", new BasicDBObject("val", _valueUpdateQuery));
prefs.update(_selectQuery, _updateQuery, true, true);
I just tested with your code in mongo shell this codes works fine you don't have to mention
$date
and i used this code for updating date
db.getCollection('tester').update({ "_id" : { "userId" : "abc"} , "val.status" : 1 , "val.prefs.value" : "condition"},{ "$set" : { "val.prefs.$.lastSent" : new Date()}})
Related
I have following document :
{
"_id" : ObjectId("5d42501efbf6f6108277ceb3"),
"customer" : {
"id" : "02c59458-8f0a-4a11-bff6-55b4710bc546",
"name" : "brookfield"
},
"userId" : "86c241de-16de-417d-8537-ed441fee9b0e",
"username" : "Fonville_f888f976_un_",
"userGroups" : [],
"lastSeen" : ISODate("2020-05-06T20:21:49.140Z"),
"devices" : [
{
"id" : "A920B6F24B63479B8796B709EA542951",
"displayId" : "A920B6F24B63479B8796B709EA542951",
"manufacturedId" : false,
"lastSeen" : ISODate("2020-03-20T14:36:24.961Z"),
"product" : {
"manufacturer" : "PSman",
"manufacturerCode" : 10,
"name" : "BlackHawk 880 series",
"modelId" : "C053"
}
},
{
"id" : "2476B0E045712cc-5904-11ea-af28-0612af3fc862",
"displayId" : "2476B0E16929512870992618563",
"lastSeen" : ISODate("2020-01-09T20:59:48.650Z"),
"product" : {
"manufacturer" : "GN",
"manufacturerCode" : 5,
"name" : "Telenor Speak 710",
"modelId" : "2476",
"vendorId" : "B0E"
}
}
],
"software" : {
"applicationId" : "f81d4fae-7dec-11d0-a765-00a0c91e6bf6",
"version" : "3.12.52334.30629",
"displayVersion" : "3.12.0"
},
"lastHost" : {
"name" : "hn_5934d3c5dbf",
"platform" : "WIN",
"osVersion" : "10.0.17763"
}
}
I have to update every product name inside devices array based on product modelId field.
So, user will pass two modelId values and if one of them match product's modelId than product name for that product should be updated.
I wrote following code, and I tested it with mongo db version 4.2, and it works. The problem is that when i tested it with older version of mongo db (3.4.23), it doesn't work,and I'm getting following exception :
com.mongodb.MongoCommandException: Command failed with error 9 (FailedToParse): 'Unrecognized field in update operation: arrayFilters' on server localhost:27017. The full response is { "ok" : 0.0, "errmsg" : "Unrecognized field in update operation: arrayFilters", "code" : 9, "codeName" : "FailedToParse" }
Here is how my method looks like :
private void updateCollection(String firstPid,String secondPid, String newProductName) {
MongoCollection<Document> collection = mongoTemplate.getCollection("user");
BasicDBObject searchQueryOne = new BasicDBObject();
BasicDBObject searchQueryTwo = new BasicDBObject();
BasicDBList or = new BasicDBList();
BasicDBObject query = new BasicDBObject("$or", or);
BasicDBObject updateQuery = new BasicDBObject();
searchQueryOne.append("devices.product.modelId", firstPid);
searchQueryTwo.append("devices.product.modelId", secondPid);
or.add(searchQueryOne);
or.add(searchQueryTwo);
updateQuery.append("$set",
new BasicDBObject().append("devices.$[elem].product.name", newProductName));
collection.updateMany(query, updateQuery, new UpdateOptions().arrayFilters(
Collections.singletonList( Filters.in("elem.product.modelId", Arrays.asList(firstPid,secondPid)))));
}
Please help
After some resarching, I managed to solve my problem. If someone have similar issue, here is the code that worked for me
public void updateUserCollection(String pidOne, String pidTwo) {
MongoCollection<Document> userCollection = mongoTemplate.getCollection("user");
BasicDBObject deviceCriteria = new BasicDBObject("devices.product.modelId", new BasicDBObject("$in", Arrays.asList(pidOne)));
FindIterable<Document> cursor = userCollection.find(deviceCriteria);
MongoCursor<Document> iterator = cursor.iterator();
while (iterator.hasNext()) {
Document user = iterator.next();
String userId = user.get("userId", String.class);
List<Document> devices = user.get("devices", List.class);
for (Document device : devices) {
Document product = device.get("product", Document.class);
String modelId = product.get("modelId", String.class);
if (modelId != null && (modelId.equals(pidOne) || modelId.equals(pidTwo))) {
product.put("name", newProductName);
}
}
saveUser(userId, user, userCollection);
}
iterator.close();
}
private void saveUser(String userId, Document user, MongoCollection<Document> userCollection) {
BasicDBObject updateQuery = new BasicDBObject("$set", user);
BasicDBObject searchQuery = new BasicDBObject("userId", userId);
userCollection.updateMany(searchQuery, updateQuery);
}
I have the following database:
{ stream :{ "name": "name1",
"error1": 1,
"error2": 1,
"error3": 1 }
}
,
{ stream : {"name": "name1",
"error1": 2,
"error2": 1,
"error3": 1 }
}
,
{ stream : {"name": "name2",
"error1": 1,
"error2": 1,
"error3": 1 }
}
I would like to group it by name and sum every time some different combination of errors.
this is what I did in mongo, I need to create the following query dynamically in java
db.collection.aggregate([{$group: {_id: "$stream.name",error1: {$sum:"$stream.error1" },error2: {$sum: "$stream.error2" }} ])
the thing is that every time I need different combinations of the errors:error1 with error2, only error 1 etc..
this is what I did: (the arguments in the "if" are some boolean values that I am getting)
List<String> totalError = new ArrayList<String>();
BasicDBObject group = new BasicDBObject( "$group", new BasicDBObject("_id","$stream.name" ));
if (error1)
{
group.append("error1",new BasicDBObject ("$sum", "$stream.error1" ));
}
if (error2)
{
group.append("error2",new BasicDBObject ("$sum", "$stream.error2" ));
}
if (error3)
{
group.append("error3",new BasicDBObject ("$sum", "$stream.error3" ));
}
the problem is that I am getting:
{ "$group" : { "_id" : "$stream.name"} , "error1" : { "$sum: "$stream.error1"} , "error2" : { "$sum" : "$stream.error2"}
},
instead of:
{ "$group" : { "_id" : "$stream.name", "error1" : { "$sum: "$stream.error1"} , "error2" : { "$sum" : "$stream.error2"}}
if I knew what error combination I need I could use append in the constructor of group dbobject.. but I don't know the combination and I need to use the "ifs"
Try
BasicDBObject fields = new BasicDBObject("_id","$stream.name" );
if (error1)
fields.append("error1",new BasicDBObject ("$sum","$stream.error1"));
if (error2)
fields.append("error2",new BasicDBObject ("$sum","$stream.error2"));
if (error3)
fields.append("error3",new BasicDBObject ("$sum","$stream.error3"));
BasicDBObject group = new BasicDBObject( "$group", fields);
You should use helper functions when possible.
List<BsonField> fieldAccumulators = new ArrayList<>();
if (error1)
fieldAccumulators.add(Accumulators.sum("error1","$stream.error1"));
if (error2)
fieldAccumulators.add(Accumulators.sum("error2","$stream.error2"));
if (error3)
fieldAccumulators.add(Accumulators.sum("error3","$stream.error3"));
collection.aggregate(Arrays.asList(Aggregates.group("$stream.name", fieldAccumulators)));
I want to do some kind of log tool.
my json structure will be like this.
{
_ObjectId:1
"sessionId":
"room":"oda1"
"roomType":
"attendees": {
{
"userObjectid":"1" ???
"userId":
"userDisplay":
"userAuthority":
"activities": {
{
"activity":"1" //1:joined
"timestamp":"1.1.1.1.1"
"result":"1"
},
{
"activity":"2" //2:disconnect
"timestamp":"1.1.1.1.1"
"result":"1"
},
}
},
{
"userObjectid":"1" ???
"userId":
"userDisplay":
"userAuthority":
"activities": {
{
"activity":"1" //1:joined
"timestamp":"1.1.1.1.1"
"result":"1"
},
{
"activity":"2" //2:disconnect
"timestamp":"1.1.1.1.1"
"result":"1"
},
}
}
}
}
My code works fine when create data for the first time.
Data should be updated while user do some activities(like join,quit (A tuple should have all session data.))
while update the data my code create new tag with named activities.
{ROOM=testo#conference.demo.com.tr, ROOM_TYPE=null, SESSION_ACTIVE=true, USERS={ "0" : { "ACTIVITIES" : [ { "RESULT" : "1" , "ACTIVITY" : "quit" , "TIMESTAMP" : { "$date" : "2016-07-26T12:12:09.518Z"}}]} , "ACTIVITIES" : { "RESULT" : "1" , "ACTIVITY" : "join" , "TIMESTAMP" : "Tue Jul 26 15:11:51 EEST 2016"} , "USERID" : "535e7387" , "USER_AUTHORITY_TYPE" : "Owner" , "USER_DISPLAY_NAME" : "focus"}, _id=57975388a297be1c19460557}
Here is my update code:
try {
DBCollection table = connectDB().getCollection("SESSIONS");
BasicDBObject sessionDetail = new BasicDBObject();
sessionDetail.put("ROOM", room);
sessionDetail.put("SESSION_ACTIVE", true);
sessionDetail.put("USERS.USERID", userName);
Map<String, Object> activityDetail = new HashMap<String, Object>();
//BasicDBObject activityDetail = new BasicDBObject();
activityDetail.put("ACTIVITY", "quit");
activityDetail.put("TIMESTAMP", new Date());
activityDetail.put("RESULT", "1");
BasicDBObject data = new BasicDBObject();
data.put("$push", new BasicDBObject("USERS.0.ACTIVITIES", activityDetail));
table.update(sessionDetail, data,true,true);
Also i get error change "USERS.0.ACTIVITIES" to "USERS.ACTIVITIES". Why 0 is necessary for this?
i do many research but couldn't find solution about it.
Could anyone help me please ?
I want to update a nested document filed if present increment an old value with new value or insert a new document.
Data
New Zealand,Waikato,Hamilton,1004
New Zealand,Waikato,Auckland,145
New Zealand,Otago,Dunedin,1068
Json
{ "_id" : ObjectId("55e7d2a72f68907c17cfcb2f"), "country" : "New Zealand",
"regions" : [ { "region" : "Waikato", "size" : 1004 },
{ "region" : "Waikato", "size" : 145 }, { "region" : "Otago", "size" : 1068 } ] }
In document regions array is dynamic in nature. In above document I need to update an increment field size value of ~Waikato`. Instead of putting an another record in array of regions.
My code
BasicDBObject query = new BasicDBObject();
query.put("country", "New Zealand");
query.put("regions.$.region", "Waikato");
BasicDBObject data = new BasicDBObject().append("$inc", new BasicDBObject().append("regions.$.size", 145));
BasicDBObject command = new BasicDBObject();
command.put("$set", data);
collection.update(query, command, true, false);
I need output like these:
{ "_id" : ObjectId("55e7d2a72f68907c17cfcb2f"), "country" : "New Zealand", "regions" : [ { "region" : "Waikato", "size" : 1149 }, { "region" : "Otago", "size" : 1068 } ] }
Please suggest me on these issue.
Your positional $ operator only belongs in the "update portion and not the query. Also you need to .append() in the query otherwise you overwrite:
BasicDBObject query = new BasicDBObject();
query.put("country", "New Zealand");
query.append("regions.region", "Waikato");
BasicDBObject update = new BasicDBObject()
.append("$inc", new BasicDBObject().append("regions.$.size", 145));
collection.update(query, update, true, false);
Basically looks like this ( shell wise ) :
collection.update(
{ "country": "New Zealand", "regions.region": " "Waikato" },
{ "$inc": regions.$.size": 145 },
true,
false
)
I've this document:
{
"_id" : ObjectId("54140782b6d2ca6018585093"),
"user_id" : ObjectId("53f4ae1ae750619418a20467"),
"date" : ISODate("2014-09-13T08:59:46.709Z"),
"type" : 0,
"tot" : 2,
"additional_info" : {
"item_id" : ObjectId("540986159ef9ebafd3dcb5d0"),
"shop_id" : ObjectId("53f4cc5a6e09f788a103d0a4"),
"ap_id" : ObjectId("53f4cc5a6e09f788a103d0a5")
},
"transactions" : [
{
"_id" : ObjectId("54140782b6d2ca6018585091"),
"date_creation" : ISODate("2014-09-13T08:59:46.711Z"),
"type" : -1
},
{
"_id" : ObjectId("54140782b6d2ca6018585092"),
"date_creation" : ISODate("2014-09-13T08:59:46.788Z"),
"type" : 1
}
]
}
and I need to add 2 more field to the first transaction opbject:
- date_execution: date
- result: this bson document
{ "server_used" : "xxx.xxx.xxx.xxx:27017" , "ok" : 1 , "n" : 1 , "updated_executed" : true} (m_OR.getDocument() in the following code example)
to obtaing that document
{
"_id" : ObjectId("54140811b6d25137753c1a1a"),
"user_id" : ObjectId("53f4ae1ae750619418a20467"),
"date" : ISODate("2014-09-13T09:02:09.098Z"),
"type" : 0,
"tot" : 2,
"additional_info" : {
"item_id" : ObjectId("540986159ef9ebafd3dcb5d0"),
"shop_id" : ObjectId("53f4cc5a6e09f788a103d0a4"),
"ap_id" : ObjectId("53f4cc5a6e09f788a103d0a5")
},
"transactions" : [
{
"_id" : ObjectId("54140811b6d25137753c1a18"),
"date_creation" : ISODate("2014-09-13T09:02:09.100Z"),
"type" : -1,
"result" : {
"server_used" : "xxx.xxx.xxx.xxx:27017",
"ok" : 1,
"n" : 1,
"updated_executed" : true
},
"date_execution" : ISODate("2014-09-13T09:02:15.370Z")
},
{
"_id" : ObjectId("54140811b6d25137753c1a19"),
"date_creation" : ISODate("2014-09-13T09:02:09.179Z"),
"type" : 1
}
]
}
The only way I was able to do that is the do 2 separates updates (update is a my wrapper funciont that execute the real updates in mongodb and it works fine):
// where
BasicDBObject query = new BasicDBObject();
query.append("transactions._id", m_Task.ID());
// new value for result - 1st upd
BasicDBObject value = new BasicDBObject();
value.put("$set",new BasicDBObject("transactions.$.date_execution",new Date()));
update(this._systemDB, "activities", query, value);
// new value for date_execution - 2nd upd
value = new BasicDBObject();
value.put("$set",new BasicDBObject("transactions.$.result",m_OR.getDocument()));
update(this._systemDB, "activities", query, value);
If I try to do this:
BasicDBObject value = new BasicDBObject();
value.put("$set",new BasicDBObject("transactions.$.date_execution",new Date()));
value.put("$set",new BasicDBObject("transactions.$.result",m_OR.getDocument()));
or = update(this._systemDB, "activities", query, value);
just the 2nd set will be applied.
Is there any way do avoid the double execution and apply the update with just one call?
Basic rule of "hash/map" objects is that you can only have one key. It's the "highlander" rule ( "There can be only one" ) applied in general reason. So just apply differently:
BasicDBObject value = new BasicDBObject();
value.put("$set",
new BasicDBObject("transactions.$.date_execution",new Date())
.add( new BasicDBObject("transactions.$.result",m_OR.getDocument() )
);
So basically "both" field arguments are part of the "$set" statement as in the serialized form:
{
"$set": {
"transactions.$.date_execution": new Date(),
"transactions.$.result": m_Or.getDocument()
}
}
Which is basically what you want in the end.
Your suggestion was right, just had to fix a little the syntax this way:
BasicDBObject value = new BasicDBObject();
value.put("$set",
new BasicDBObject("transactions.$.date_execution",new Date())
.append("transactions.$.result",m_OR.getDocument())
);
This worked perfectly ;)
Thanks!
Samuel