modify array element's value with Java MongoDB driver - mongodb

I want to modify array element using Java MongoDB driver. I am able to insert new pair to the array, but can't modify value corresponding to particular key.
How can I increase a by 2 in dummy array for document
{ "_id" : ObjectId("57a87614d03a435e4be44bb9"), "dummy" : [ { "a" : 1 }, { "b" : 5 } ] }
using Java MongoDB driver?
Here is what I've tried
BasicDBObject query = new BasicDBObject();
query.put("_id",doc_id_here);
BasicDBObject incValue = new BasicDBObject("dummy.$.a", 1);
BasicDBObject intModifier = new BasicDBObject("$inc", incValue);
coll.update(query, intModifier, false, false, WriteConcern.SAFE);

Your query in mongo shell
db.collection.update(
{ "_id": ObjectId("57a87614d03a435e4be44bb9") },
{ $inc: { "dummy.$.a": 1 } }
);
will result in error
The positional operator did not find the match needed from the query.
Unexpanded update: dummy.$.a
because in order to use positional $ operator for dummy array you need to set condition on this array in your query
db.collection.update(
{ "_id": ObjectId("57a87614d03a435e4be44bb9"), "dummy.a": { $exists: true } },
{ $inc: { "dummy.$.a": 1 } }
);
and then it will increment a as you expect.
With Java MongoDB driver it will be
BasicDBObject query = new BasicDBObject();
query.put("_id", new ObjectId("57a87614d03a435e4be44bb9"));
query.put("dummy.a", new BasicDBObject("$exists", true));
BasicDBObject incValue = new BasicDBObject("dummy.$.a", 1);
BasicDBObject intModifier = new BasicDBObject("$inc", incValue);
coll.update(query, intModifier, false, false, WriteConcern.SAFE);
Advice: before trying to construct your query with Java MongoDB Driver first try if it works in mongo shell.

Related

How can I complete a string concatenation inside a project stage using C# and the Mongo aggregation pipeline?

I'm trying to perform a string concatenation within a project stage of a MongoDb aggregation pipeline operation.
I need to add a field called "coid" to a document, which is to be the result of a string concation between 2 strings:
the string literal: "prefix-"
the string coming from the "SecId" field in the first document of the "values" array field.
My attempt is below, but it keeps generating a compiler error. Does anyone know how I can accomplish this string concatenation, within the aggregation pipeline project stage?
new BsonDocument("$project",
new BsonDocument
{
{ "_id", 1 },
{ "coid",
new BsonDocument("$concat",new BsonDocument[
new BsonDocument("prefix-"),
new BsonDocument("$first", "$values.SecId")])
}
})
Edit:
Here is an example of one string concatenation: If the value of $values.Secid is "12345", then the concatenation should be "prefix-12345".
Update here is an enlarged view of my pipeline
new BsonDocument("$lookup",
new BsonDocument
{
{"from","accounts"},
{ "localField", "ig" },
{ "foreignField", "ipi" },
{ "as", "accounts" },
}),
new BsonDocument("$project",
new BsonDocument
{
{ "_id", 1 },
{
"coid",
new BsonDocument("$first", "$accounts._id")
},
{ "newField",
new BsonDocument("$concat","prefix-" + [from first element of $accounts array, take the _id value]
},
}),
new BsonDocument("$out", LocalOutputCollection)
There are a couple problems with that code:
The literal passed in the $concat array should be string, not BsonDocument
The $first operator is only available in a group stage, you probably need to use $arrayElemAt
new BsonDocument("$concat",
new BsonDocument[
"prefix-",
new BsonDocument(
"$arrayElemAt",
new BsonDocument[ "$values.SecId", 0]
)
]
)
you can easily do $concat with the AsQueryable interface like so:
var result = collection
.AsQueryable()
.Select(x => new
{
x.Id,
coid = "prefix-" + x.Values.First().SecId
})
.ToList();
it generates the following projection:
{
"$project": {
"Id": "$_id",
"coid": { "$concat": ["prefix-", { "$arrayElemAt": ["$Values.SecId", 0] } ]
}
}
}

MongoDb isDeleted attribute - not always exist

When fetching records in mongoDb using QueryBuilder:
DBObject query = QueryBuilder.start("field1).is("foo").and("field2").is("bar").get();
Sometimes isDeleted attribute will be on the record,
But sometimes it will not be there,
How do I use QueryBuilder to tell MongoDb to fetch records where isDeleted attribute is not true, or not there..
Using expressions I can do:
myOr.add(new BasicDBObject("isDeleted", bool));
myOr.add(new BasicDBObject("isDeleted", new BasicDBObject("$exists", bool)));
DBObject query = baseFilter.append("$or", myOr);
You essentially want the following mongodb query
var query = {
"$or": [
{ "isDeleted": false },
{ "isDeleted": { "$exists": false } }
]
}
db.collection.find(query)
Using the QueryBuilder, this will translate to
QueryBuilder query = new QueryBuilder();
query.or(
QueryBuilder.start("isDeleted").is(false).get(),
QueryBuilder.start("isDeleted").exists(false).get()
);
DBCursor cursor = collection.find(query.get());

MongoDB shell aggregation does not work in Morphia

Consider the following MongoDB collection:
{
"_id" : ObjectId("..."),
"myId": 12345,
"root": {
basicData: {
code: "CODE"
}
data: [
{
descriptions: {
description: [
{
text: "...",
language: "de"
}
]
}
}
]
}}
I'm trying to get documents filtered by "myId" and "code", but with descriptions in only one specific language. In the shell, the following command seems to work properly:
db.Items.aggregate([
{ "$match" : { "myId" : 40943 , "root.basicData.code" : "A_CODE"}},
{ "$unwind" : "$root.data"},
{ "$unwind" : "$root.data.descriptions.description"},
{ "$match" : { "root.data.descriptions.description.language" : "de"}}
])
In Morphia I try to do the following to get to the same result:
AggregationPipeline pipeline = dataStore.createAggregation(Item.class);
Query<Item> matchIdAndCode = dataStore.createQuery(Item.class);
matchIdAndCode.field("myId").equal(myid);
matchIdAndCode.field("root.basicData.code").equal(code);
pipeline.match(matchIdAndCode);
pipeline.unwind("root.data");
pipeline.unwind("root.data.descriptions.description");
Query<Item> matchLanguage = dataStore.createQuery(Item.class);
matchLanguage.field("root.data.descriptions.description.language").equal(language);
pipeline.match(matchLanguage);
Iterator<Item> itemAggregate = pipeline.aggregate(Item.class);
but the iterator does not contain any items. I am not shure where to search for further errors, especially because when I copy the stages in the morphia aggregation pipeline to the shell, I get the expected result.
you are missing $ sign in following lines
pipeline.unwind("root.data");
pipeline.unwind("root.data.descriptions.description");
should be
pipeline.unwind("$root.data");
pipeline.unwind("$root.data.descriptions.description");
As a workaround, I now used the MongoDB Java Driver. My working solution:
List<DBObject> stages = new ArrayList<DBObject>();
DBCollection collection = dataStore.getCollection(Item.class);
// match
DBObject matchFields = new BasicDBObject("myId", myid);
matchFields.put("code", code);
DBObject match = new BasicDBObject("$match", matchFields );
stages.add(match);
// unwind
DBObject unwindDescriptiveData = new BasicDBObject("$unwind", "$root.data");
stages.add(unwindDescriptiveData);
DBObject unwindDescription = new BasicDBObject("$unwind", "$root.data.descriptions.description");
stages.add(unwindDescription);
// match
DBObject languageMatchFields = new BasicDBObject("root.data.descriptions.description.language", language);
DBObject languageMatch = new BasicDBObject("$match", languageMatchFields );
stages.add(languageMatch);
AggregationOutput aggregate = collection.aggregate(stages);
The mapping to the pojo can be done with Morphia again:
List<Item> items = new ArrayList<Item>();
for (Iterator<DBObject> iterator = aggregate.results().iterator(); iterator.hasNext();) {
items.add(morphia.fromDBObject(Item.class, iterator.next()));
}

Fetch mongo documents based on date

We are inserting mongo documents with identifier and there is a subarray within the documents.
insert 1 :
db.test.insert(
{
"companyId" : "123",
"persons" : [
{
"joiningDate" : NumberLong("1431674741623"),
"name" : "Rajesh"
}
],
})
insert 2 :
db.test.insert(
{
"companyId" : "123",
"persons" : [
{
"joiningDate" : NumberLong("1431674741653"),
"name" : "Rahul"
}
],
})
I would like to retreive the company details based on the company id and merge the persons into one array list, and sort the persons based on the joining date.
Currently I am able to retreive the data using QueryBuilder but I am unable to sort the person based on the date.I can use java comparator to do the same , but I am looking out if there is any API from mongo db java driver which can be used to get the same.
Thanks.
you should use mongo aggregation like first $unwind persons array and then sort persons.joiningDate and then group with push as below :
db.test.aggregate({
"$match": {
"companyId": "123" // match companyId
}
}, {
"$unwind": "$persons" //unwind person array
}, {
"$sort": {
"persons.joiningDate": -1 //sort joining date
}
}, {
"$group": {
"_id": "$companyId",
"persons": {
"$push": "$persons" //push all sorted data into persons
}
}
}).pretty()
For converting this code into java use mongo java aggregation as
// unwind persons
DBObject unwind = new BasicDBObject("$unwind", "$persons");
// create pipeline operations, with the $match companyId
DBObject match = new BasicDBObject("$match", new BasicDBObject("companyId", "123"));
// sort persons by joining date
DBObject sortDates = new BasicDBObject("$sort", new BasicDBObject("persons.joiningDate", -1)); // -1 and 1 descending or ascending resp.
// Now the $group operation
DBObject groupFields = new BasicDBObject("_id", "$companyId");
groupFields.put("persons", new BasicDBObject("$push", "$persons"));
DBObject group = new BasicDBObject("$group", groupFields);
// run aggregation
List < DBObject > pipeline = Arrays.asList(match, unwind,sortDates, group);
AggregationOutput output = test.aggregate(pipeline);
for(DBObject result: output.results()) {
System.out.println(result);
}

update a document in mongoDB using JAVA

I'm just starting to learn mongoDB using JAVA. I have the the following document in mongo
{
"_id": {
"$oid": "513fa587c5d0cf174eb9b1f8"
},
"suitename": "test_suite_name",
"testname": "test_name]",
"milestones": [
{
"milestone_id": 45
}
]
}
I have a compound key on suitename, testname, and milestone_id. I have to process a file which has these three fields. I create a simple DBObject query and check if count != 0
BasicDBObject query = new BasicDBObject();
query.put("suitename", testsuite);
query.put("testname", testcase);
query.put("milestones.milestone_id", SampleProgram.milestone_id);
If count == 0 --> add document in mongo -> this seems to work fine
What I am trying to figure out is:
If I have a new value of milestone_id in my file, I just need to add a milestone to some existing document's milestone array. Existing document is determined based on suitename AND testname.
So if milestone_id = 10, the document should look like
{
"_id": {
"$oid": "513fa587c5d0cf174eb9b1f8"
},
"suitename": "test_suite_name",
"testname": "test_name]",
"milestones": [
{
"milestone_id": 45
},
{
"milestone_id": 10
}
]
}
How can I accomplish this?
Thanks
This can be accomplished with the $push operator which appends values to an array and the update(…) method.
BasicDBObject query = new BasicDBObject();
query.put("suitename", testsuite);
query.put("testname", testcase);
BasicDBObject push = new BasicDBObject();
push.put("$push",
new BasicDBObject("milestones",
new BasicDBObject("milestone_id", SampleProgram.milestone_id)));
yourCollection.update(query, push);
BasicDBObject newDocument = new BasicDBObject();
newDocument.append("$set", new BasicDBObject().append("clients", 110));
BasicDBObject searchQuery = new BasicDBObject().append("hosting", "hostB");
collection.update(searchQuery, newDocument);