I'm using mongo-java-driver:3.3.0 and trying to update one value of my sub-document using $inc operator and findOneAndUpdate, but only under certain conditions (id comparison and greaterThan filter).
Following is a snippet to reproduce the problem:
MongoCollection<Document> coll = db.getCollection("update_increase");
Document docBefore = new Document()
.append("subdocs", Arrays.asList(
new Document("id", "AAA").append("count", 10),
new Document("id", "BBB").append("count", 20)
));
coll.insertOne(docBefore);
Document filter = new Document()
.append("subdocs.id", "BBB")
.append("subdocs.count", new Document("$gt", 7));
Document update = new Document()
.append("$inc", new Document("subdocs.$.count", -7));
Document docAfter = coll.findOneAndUpdate(
filter,
update,
new FindOneAndUpdateOptions().returnDocument(ReturnDocument.AFTER));
docBefore:
{ "_id" : { "$oid" : "5819c85977a8cb12f8d706c9" },
"subdocs" : [
{ "id" : "AAA", "count" : 10 },
{ "id" : "BBB", "count" : 20 }
]
}
docAfter:
{ "_id" : { "$oid" : "5819c85977a8cb12f8d706c9" },
"subdocs" : [
{ "id" : "AAA", "count" : 3 },
{ "id" : "BBB", "count" : 20 }
]
}
What I expected is count:13 on the second subdoc (id:"BBB"), but I got an update on the first one (count:3).
This works fine if I remove greaterThan condition line (.. new Document("$gt", 5) ..):
{ "_id" : { "$oid" : "5819c92577a8cb13404cfc91" },
"subdocs" : [
{ "id" : "AAA", "count" : 10 },
{ "id" : "BBB", "count" : 13 }
]
}
What I'm doing wrong?
Thanks!
Here is the java equlivant for $elemMatch.
Document filter = new Document("subdocs", new Document().append("$elemMatch", new Document().append("id", "BBB").append("count", new Document("$gt", 7))));
Related
I have a question about how to retrieve data from a mongodb and how to store it in my Java code. I select all the documents (there are only 5) from my mongodb and then store its content in my Java application.
So my code is as follows:
public class MongoReader {
private Invoice invoice;
public void mongoReader () {
MongoClientURI uri = new MongoClientURI("my-url");
try (MongoClient mongoClient = new MongoClient(uri)) {
MongoDatabase database = mongoClient.getDatabase("BiFiBEP02");
MongoCollection<Document> mongoCollection = database.getCollection("bifi");
FindIterable<Document> documents = mongoCollection.find();
for (Document document : documents){
invoice.setCustomerId(document.getInteger("customerId"));
invoice.setDate(document.getDate("date"));
invoice.setInvoiceId(document.getInteger("invoiceId"));
invoice.setInvoiceLines(document.getList("invoiceLines", ArrayList<InvoiceLine>));
invoice.setNote(document.getString("note"));
invoice.setPersonId(document.getInteger("personId"));
}
}
catch (MongoException mongoException) {
mongoException.printStackTrace();
}
}
}
The document.getList() doesn't seem to be satisfied with my input.
An example of the the mongo document:
"customerId" : 2,
"date" : ISODate("2018-05-16T10:23:40.049Z"),
"invoiceId" : 1,
"invoiceLines" : [
{
"btwCode" : "hoog",
"productId" : 1,
"productName" : "BiFi worstjes voordeelstrip",
"quantity" : 20,
"totalPrice" : 30,
"unit" : "kg"
},
{
"btwCode" : "hoog",
"productId" : 2,
"productName" : "BiFi worstjes kip",
"quantity" : 20,
"totalPrice" : 30,
"unit" : "kg"
},
{
"btwCode" : "laag",
"productId" : 3,
"productName" : "BiFi worstjes extra scherp",
"quantity" : 30,
"totalPrice" : 100.22,
"unit" : "kg"
},
{
"btwCode" : "geen",
"productId" : 1,
"productName" : "BiFi worstjes promotiestand",
"quantity" : -1,
"totalPrice" : 30.32,
"unit" : "kg"
}
],
"note" : "This invoice is very important!",
"personId" : 2
So my question here is: I have to put the invoiceLines in arraylist in my Java object, but I cannot get the array items out of the mongoDb. How do I do this?
I'm new to working with MongoDb and do not know a lot of things.
I need to write an aggregation request.
Here is the JSON document structure.
{
"_id" : ObjectId("5a72f7a75ef7d430e8c462d2"),
"crawler_id" : ObjectId("5a71cbb746e0fb0007adc6c2"),
"skill" : "stack",
"created_date" : ISODate("2018-02-01T13:19:03.522+0000"),
"modified_date" : ISODate("2018-02-01T13:22:23.078+0000"),
"connects" : [
{
"subskill" : "we’re",
"weight" : NumberInt(1),
"parser_id" : [
ObjectId("5a71d88d5ef7d41964fbec11")
]
},
{
"subskill" : "b1",
"weight" : NumberInt(2),
"parser_id" : [
ObjectId("5a71d88d5ef7d41964fbec11"),
ObjectId("5a71d88d5ef7d41964fbec1b")
]
},
{
"subskill" : "making",
"weight" : NumberInt(2),
"parser_id" : [
ObjectId("5a71d88d5ef7d41964fbec1b"),
ObjectId("5a71d88d5ef7d41964fbec1c")
]
},
{
"subskill" : "delivery",
"weight" : NumberInt(2),
"parser_id" : [
ObjectId("5a71d88d5ef7d41964fbec1c"),
ObjectId("5a71d88d5ef7d41964fbec1e")
]
}
]
}
I need the result return the name of skill and the number of unique parser_id.
In this case, the result should be:
[
{
"skill": "stack",
"quantity": 4
}
]
where "stack" - skill name,
and "quantity" - count of unique parser_id.
ObjectId("5a71d88d5ef7d41964fbec11")
ObjectId("5a71d88d5ef7d41964fbec1b")
ObjectId("5a71d88d5ef7d41964fbec1c")
ObjectId("5a71d88d5ef7d41964fbec1e")
Can some one help me with this request ???
Given the document supplied in your question, this command ...
db.collection.aggregate([
{ $unwind: "$connects" },
// count all occurrences
{ "$group": { "_id": {skill: "$skill", parser_id: "$connects.parser_id"}, "count": { "$sum": 1 } }},
// sum all occurrences and count distinct
{ "$group": { "_id": "$_id.skill", "quantity": { "$sum": 1 } }},
// (optional) rename the '_id' attribute to 'skill'
{ $project: { 'skill': '$_id', 'quantity': 1, _id: 0 } }
])
... will return:
{
"quantity" : 4,
"skill" : "stack"
}
The above command groups by skill and connects.parser_id and then gets a distinct count of those groups.
Your command includes the java tag so I suspect you are looking to execute the same command using the MongoDB Java driver. The code below (using MongoDB Java driver v3.x) will return the same result:
MongoClient mongoClient = ...;
MongoCollection<Document> collection = mongoClient.getDatabase("...").getCollection("...");
List<Document> documents = collection.aggregate(Arrays.asList(
Aggregates.unwind("$connects"),
new Document("$group", new Document("_id", new Document("skill", "$skill").append("parser_id", "$connects.parser_id"))
.append("count", new Document("$sum", 1))),
new Document("$group", new Document("_id", "$_id.skill").append("quantity", new Document("$sum", 1))),
new Document("$project", new Document("skill", "$_id").append("quantity", 1).append("_id", 0))
)).into(new ArrayList<>());
for (Document document : documents) {
logger.info("{}", document.toJson());
}
Note: this code deliberately uses the form new Document(<pipeline aggregator>, ...) instead of the Aggregators utilities to make it easier to see the translation between the shell command and its Java equivalent.
try $project with $reduce
$setUnion is used to keep only the distinct ids and finally $size used to get the distinct array count
db.col.aggregate(
[
{$project : {
_id : 0,
skill : 1,
quantity : {$size :{$reduce : {input : "$connects.parser_id", initialValue : [] , in : {$setUnion : ["$$value", "$$this"]}}}}
}
}
]
).pretty()
result
{ "skill" : "stack", "quantity" : 4 }
I am getting the following exception:
com.mongodb.CommandFailureException: { "serverUsed" : "192.168.3.25:27017" ,
"errmsg" : "exception: The argument to $size must be an Array,
but was of type: EOO" , "code" : 17124 , "ok" : 0.0}
Whenever I run this pipeline using the MongoClient driver for Java:
[{ "$match" : { "_id" : "1"}}, { "$project" : { "count" : { "$size" : "$tags"}}}]
What is causing this? The pipeline works fine in the Mongo shell?
Here is my Java code:
private void countArrayLength(String id, String arrayName) {
AggregationOptions options = AggregationOptions.builder().build();
DBObject matchFields = new BasicDBObject("_id", id);
DBObject match = new BasicDBObject("$match", matchFields);
DBObject projectCount = new BasicDBObject("$size", "$" + arrayName);
DBObject projectFields = new BasicDBObject("count", projectCount);
DBObject project = new BasicDBObject("$project", projectFields);
Cursor cursor = db.getCollection(collectionName)
.aggregate(asList(match, project), options);
}
DB Structure:
{
"_id" : "1",
"_class" : "com.mongodb.BasicDBObject",
"tags" : [
{
"tag" : "tagName"
},
{
"tag" : "tagName"
},
{
"tag" : "tagName"
},
{
"tag" : "tagName"
},
{
"tag" : "tagName"
}
]
}
In mongo console I have:
> db.test.find({})
{ "_id" : ObjectId("515afcfedba6a529520becfa"), "array" : [ { "key" : "one", "value" : 1 }, { "key" : "two", "value" : 2 } ] }
{ "_id" : ObjectId("515b0e48dba6a529520becfd"), "array" : [ { "key" : "one", "value" : 1 }, { "key" : "two", "value" : 2 }, {"key" :"three", "value" : 3 } ] }
> db.test.find({array: {$all:[{key:'one', value:1}, {key:'two',value:2}]}});
{ "_id" : ObjectId("515afcfedba6a529520becfa"), "array" : [ { "key" : "one", "value" : 1 }, { "key" : "two", "value" : 2 } ] }
{ "_id" : ObjectId("515b0e48dba6a529520becfd"), "array" : [ { "key" : "one", "value" : 1 }, { "key" : "two", "value" : 2 }, {"key" :"three", "value" : 3 } ] }
> db.test.find({_id:ObjectId("515afcfedba6a529520becfa"), array: {$all:[{key:'one', value:1}, {key:'two',value:2}]}});
{ "_id" : ObjectId("515afcfedba6a529520becfa"), "array" : [ { "key" : "one", "value" : 1 }, { "key" : "two", "value" : 2 } ] }
How do I write the second and third query using:
org.springframework.data.mongodb.core.query.Criteria ?
This is the best solution I found so far:
...
DBObject obj = new BasicDBObject();
obj.put( "key", 1 );
obj.put( "value", "one" );
DBObject obje1 = new BasicDBObject();
obje1.put( "$elemMatch", obj );
obj = new BasicDBObject();
obj.put( "key", 2 );
obj.put( "value", "two" );
DBObject obje2 = new BasicDBObject();
obje2.put( "$elemMatch", obj );
Query qry = new Query( where("array").all(obje1, obje2) );
...
Here's an option that I think will work:
Criteria criteria = new Criteria("array");
criteria.all(Criteria.where("key").is(1).and("value").is("one"),Criteria.where("key").is(2).and("value").is("two"));
Query query = new Query(criteria);
Hello all i am trying to match a document using mongodb java driver for eg :
{
"fName" : "abc",
"lName" : "456",
"dob" : "00",
"address" : "xyz"
}
with
"nameIdentity" : [
{
"fName" : "abc",
"lName" : "def",
"dob" : "00",
"address" : "xyz"
},
{
"fName" : "123",
"lName" : "456",
"dob" : "00",
"address" : "789"
}
If i found the document then i don't do anything else add the document. My problem here is if my source document contains fname : abc and lname: 456 this is matching fname in the first set of nameIdentity and lname in the second set of identity. I want this to be a one complete match. I have tried something like this
List<Document> nameIdentities = (List<Document>) matchedDocument.get("nameIdentity");
for (int i=0;i<nameIdentities.size();i++)
{
temp.add(nameIdentities.get(0));
quBasicDBObject=new BasicDBObject("$and",temp);
}
iterable = mongoDatabase.getCollection("entity").find(updatedDocumentTypeOne);
if (iterable.first() == null)
{
updateResult = mongoDatabase.getCollection("entity")
.updateOne(
new Document("_id", new ObjectId(objectId)),
new Document("$push", new Document("nameIdentity", nameList.get(0))));
}
any suggestions where am i going wrong?
UPDATE
You may have to use the aggregation framework.
Maybe something like:
List<Bson> filterList = new ArrayList<>();
filterList.add(new BsonDocument().append("nameIdentity.fName", new BsonString("abc") ));
filterList.add(new BsonDocument().append("nameIdentity.lName", new BsonString("456") ));
FindIterable<org.bson.Document> it = collection.find(Filters.and(filterList));