Mongo and Java read nested documents values - java

My mongo collections contains following documents
{
"syslog": [
{
"alertId": "N/A",
"time": "06:46:57",
"device": "10.10.20.1"
},
{
"alertId": "N/A",
"time": "06:47:30",
"device": "10.10.20.2"
}
]
}
now I want to read only values of given device.
Suppose if I set device="10.10.20.2" it will shows only "alertId": "N/A",
"time": "06:47:30",
"device": "10.10.20.2" for this I write java code as below
BasicDBObject criteria = new BasicDBObject();
criteria.put("syslog.device","10.10.20.2");
DBCursor cur = coll.find(criteria);
while(cur.hasNext() && !isStopped()) {
String json = cur.next().toString();
}//end of while
When I print json it shows me whole json values. How I should find only selected values of given device?

You should use $elemMatch operator to project array fields. You should update your query as follows :
db.collection.find({"syslog.device" : "10.10.20.2"},{syslog : {$elemMatch : {device : "10.10.20.2"}}})
In Java :
BasicDBObject criteria = new BasicDBObject();
criteria.put("syslog.device","10.10.20.2");
BasicDBObject elemMatchObj = new BasicDBObject();
elemMatchObj.put("$elemMatch", new BasicDBObject("device", "10.10.20.2"));
DBCursor cur = coll.find(criteria, new BasicDBObject("syslog", elemMatchObj));
while(cur.hasNext() && !isStopped()) {
String json = cur.next().toString();
}//end of while

Related

MongoDb - not adding array object at proper place instead its adding it to root

I have only one collection in my Mongodb and that is as follows
[
{
"_id": {
"timestamp": 1626753855,
"date": "2021-07-20T04:04:15.000+00:00"
},
"metadata": [
{
"country": "Canada",
"authors": [
{
"author": "Test Author",
"Books": [
{
"name": "Sample Book",
"code": "JCAR"
}
]
}
]
}
]
}
]
The method I am using is as follows
private void updateAuthorsMetadata(AuthorMetadata authorMetadata) {
MongoCollection<Document> collection = mDatabase.getCollection(authorMetadataCollection);
Bson eqFilter = eq("metadata.country", "Canada");
FindIterable<Document> itrDoc = collection.find(eqFilter);
if (itrDoc.first() == null) {
Document document = new Document();
document.append("metadata",
Arrays.asList(new Document("country", authorMetadata.getCountry()).append("authors",
Arrays.asList(new Document("author", authorMetadata.getAuthorName()).append("Books",
Arrays.asList(new Document("name", authorMetadata.getBookName()).append("code",
authorMetadata.getBookCode())))))));
collection.insertOne(document);
} else {
Bson andFilter = and(eq("metadata.country", "Canada"),
eq("metadata.authors.author", "John Doe"));
FindIterable<Document> itrAndDoc = collection.find(docFilter);
if (itrAndDoc.first() == null) {
Document doc = new Document("author", "John Doe).append("Books",
Arrays.asList(new Document("name", "Some Book").append("code",
"SBD4")));
Bson update = push("metadata[].authors", doc);
UpdateResult result = collection.updateOne(eqFilter, update);
}
}
}
The issue I am facing is that when I update collection at
else block
then it is adding the document to the root instead of adding it to the authors array, as shown in the image below.
How I can update it at the proper location i.e. adding to "authors" array? I am using Java driver v-3.12.7.
Finally, I figured it out. Anyone out there struggling with same issue can refer to this method. The issue was positional operators, some forums says that multiple positional operators can't be used below v 3.x.x but I am using 3.12.x and still had this issue. I think the way I am doing is not best/optimal but it works and if anyone direct me to the right direction then it would be great.
private void updateAuthorMetadata(AuthorMetadata authorMetadata) {
MongoCollection<Document> collection = mDatabase.getCollection(authorMetadataCollection);
Bson bsonEqFilter = eq("metadata.country", authorMetadata.getCountry());
FindIterable<Document> itrDoc = collection.find(bsonEqFilter);
Document found = itrDoc.first();
if (found == null) {
Document document = new Document();
document.append("metadata",
Arrays.asList(new Document("country", authorMetadata.getCountry()).append("authors",
Arrays.asList(new Document("author", authorMetadata.getAuthor()).append("books",
Arrays.asList(new Document("name", authorMetadata.getBookName())
.append("code", authorMetadata.getBookCode())))))));
collection.insertOne(document);
} else {
Bson bsonAndFilter = and(eq("metadata.country", authorMetadata.getCountry()),
eq("metadata.authors.author", authorMetadata.getAuthor()));
FindIterable<Document> itrAndDoc = collection.find(bsonAndFilter);
if (itrAndDoc.first() == null) {
Document document = new Document("author", authorMetadata.getAuthor()).append("Books",
Arrays.asList(new Document("name", authorMetadata.getBookName()).append("code",
authorMetadata.getBookCode())));
Bson update = push("metadata.$.authors", document);
UpdateResult result = collection.updateOne(bsonEqFilter, update);
} else {
Bson bsonMoreAndFilter = and(eq("metadata.country", authorMetadata.getCountry()),
eq("metadata.authors.author", authorMetadata.getAuthor()),
eq("metadata.authors.books.code", authorMetadata.getBookCode()));
FindIterable<Document> itrMoreAndDoc = collection.find(bsonMoreAndFilter);
if (itrMoreAndDoc.first() == null) {
Document docBooks = new Document("name", authorMetadata.getBookName()).append("code",
authorMetadata.getBookCode());
Bson update = push("metadata.0.authors.$.books", docBooks);
UpdateResult result = collection.updateOne(bsonAndFilter, update);
}
}
}
}

Mongo Template's findAndModify is returning null values for adding or updating nested elements

My data is formatted as:
{
"_id" : "149",
"books" : {
"32" : "0.12",
"33" : "0.21"
}
}
I would like to update/insert values inside the nested books document. If I insert a new row say "39" : "0.19", then the updated document should look as below:
{
"_id" : "149",
"books" : {
"32" : "0.12",
"33" : "0.21",
"39" : "0.19"
}
}
And updates should work, the way they are meant to work. By updating the values.
I tried several ways, but couldn't update the way I wanted.
Method1: Working but wrong result
MongoCollection<Document> document = mongoTemplate.getCollection("booksCollection");
BasicDBObject query = new BasicDBObject();
query.put("_id", storeId);
BasicDBObject bookDiscount = new BasicDBObject();
bookDiscount.put(bookId, discount);
BasicDBObject update = new BasicDBObject();
update.put('$push', new BasicDBObject("books", bookDiscount));
document.findOneAndUpdate(query, update);
Method1 Output: Each value is added in a new row
{
"_id" : "1664",
"books" : [
{
"28" : NumberDecimal("0.75")
},
{
"29" : NumberDecimal("0.18")
},
{
"30" : NumberDecimal("0.23")
},
{
"245" : NumberDecimal("0.26")
},
{
"277" : NumberDecimal("0.13")
},
{
"270" : NumberDecimal("0.19")
}
]
}
Method2: Working but wrong result
MongoCollection<Document> document = mongoTemplate.getCollection("booksCollection");
BasicDBObject query = new BasicDBObject();
query.put("_id", storeId);
BasicDBObject bookDiscount = new BasicDBObject();
bookDiscount.put(bookId, discount);
BasicDBObject update = new BasicDBObject();
update.put('$set', new BasicDBObject("books", bookDiscount));
document.findOneAndUpdate(query, update);
Method2 Output: Value always gets replaced
{
"_id" : "16644158",
"locationInfRate" : {
"2857" : NumberDecimal("0.68")
},
"_class" : "com.test.books"
}
What I learned:
$push method pushes the data as a member of a list.
$set method updates the data, but for maps, it replaces data if the map is not correctly formatted in the query.
I edited my $set query to below format and it worked:
MongoCollection<Document> document = mongoTemplate.getCollection("booksCollection");
BasicDBObject query = new BasicDBObject();
query.put("_id", storeId);
BasicDBObject bookDiscount = new BasicDBObject();
bookDiscount.put("books." + bookId, discount);
update.put('$set', bookDiscount);
document.findOneAndUpdate(query, update);

creating dynamically group query in java

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)));

Mongodb - update specific array element

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()}})

Find MongoDB records where status is a, b or c and it has some array which is greater than 0

I am struggling to build up a query for one of the criteria, I have a documents like this:
[
{
"status": "a"
},
{
"status": "b"
},
{
"status": "foo"
},
{
"status": "a",
"flightDetails": [
{
"test": "xyz"
}
]
}
]
My criteria to select like this:
Get all data where status is either a, b or c, or
Get all data where status is either a, b or c and flightDetails, which size is > 0
Where not all documents contains flightDetails key.
I tried my java code like this:
BasicDBObject inQuery = new BasicDBObject();
inQuery.append("status", new BasicDBObject("$nin", Arrays.asList("a", "b", "c")));
BasicDBObject inQuery2 = new BasicDBObject();
inQuery2.append("$where", "return Object.keys( this.flightDetails").length > 0");
BasicDBList criteria = new BasicDBList();
criteria.add(inQuery);
criteria.add(inQuery2);
BasicDBObject finalQuery = new BasicDBObject("$or",criteria);
But it returns all the data, seems my query is not correct. I searched lot and based on some SO answers, I tried to build query. Can someone kindly help me in this? Since I am beginner in NoSQL and MongoDB and started on this few days back.
Using the dot notation for the "0" element of the array along with $exists means that it has some content, and of course exists:
BasicDBObject query = BasicDBObject("status",
new BasicDBObject("$in", Arrays.asList("a", "b", "c"))
)
.append("flightDetails.0",
new BasicDBObject("$exists",true)
);
Which is:
{
"status": { "$in": [ "a","b","c" ] },
"flightDetails.0": { "$exists": true }
}
For those used to the JSON notation.
To incorporate "no array at all" just contruct the $or condition:
{
"status": { "$in": [ "a","b","c" ] },
"$or": [
{ "flightDetails.0": { "$exists": true } },
{ "flightDetails": { "$exists": false } }
]
}
Or for Java:
BasicDBObject query = BasicDBObject("status",
new BasicDBObject("$in", Arrays.asList("a", "b", "c"))
)
.append("$or",Arrays.<Object>asList(
new BasicDBObject("flightDetails.0",
new BasicDBObject("$exists",true)
),
new BasicDBObject("flightDetails",
new BasicDBObject("$exists",false)
)
));

Categories

Resources