Update the subDcoument attribute - mongodb

I have a document having schema like bellow
{
"Personal":[
{
"name":"Test_Name",
"isActive":true
}
]
}
am trying to update this as below using java driver.
collections.updateMany(new Document("Personal.name", "Test_Name"), new Document("$set", new Document("Personal.$.isActive", false)))
But unfortunately this trows an error
"The positional operator did not find the match needed from the query. Unexpanded update: Personal.$.isActive"
But if i modify the above update filter something like
collections.updateMany(new Document("Personal.name", "Test_Name"), new Document("$set", new Document("Personal.0.isActive", false)))
it works.
Can any one help me in understanding whats wrong in using "$" in my 1st update statement?
Here is some more code spinets
Creating the collection object:
collections = mongoConnection.establishConnection()
MongoConnection object:
public MongoConnection() {
StringBuilder connectionString = new StringBuilder();
connectionString.append("mongodb://url_with_port_and_server")
client = new MongoClient(new MongoClientURI(connectionString.toString()));
db = client.getDatabase("test");
collections = db.getCollection("test");
}
public MongoCollection<Document> establishConnection() {
return collections;
}
public void closeConnection() {
client.close();
}

Related

How to Unit Test AggregateIterable<User>

Struggling to Mock what to return when using AggregateIterable in Java/Mongo.
I have tried using MongoCursor but struggling to understand this. I want to cover all paths with Unit Tests for this. What are the best possible ways using mockito and testNG.
public List<User> getAggregatedList(User user){
AggregateIterable<User> agg = collection.aggregate(pipeline, User.class)
List<User> users = new ArrayList<>();
if (agg != null){
Iterator<User> it = agg.iterator();
while(it.hasNext()){
users.add(it.next());
}
}
return users;
}
I should be able to write a test such as:
...
expected = Arrays.asList(user1,user2...)
verify(result,expect)
You can test it by mocking the hasNext() and next() methods of the iterator like so:
#Test
public void testGetAggregatedList() {
MongoCollection collection = PowerMockito.mock(MongoCollection.class);
AggregateIterable<User> iterable = PowerMockito.mock(AggregateIterable.class);
MongoClient client = PowerMockito.mock(MongoClient.class);
MongoDatabase db = PowerMockito.mock(MongoDatabase.class);
MongoCursor iterator = PowerMockito.mock(MongoCursor.class);
User user1 = PowerMockito.mock(User.class);
User user2 = PowerMockito.mock(User.class);
MongoDaoDelegator.setClient(client);
PowerMockito.when(client.getDatabase(anyString())).thenReturn(db);
PowerMockito.when(db.getCollection(anyString())).thenReturn(collection);
PowerMockito.when(collection.aggregate(anyList(),any())).thenReturn(iterable);
PowerMockito.when(iterable.iterator()).thenReturn(iterator);
PowerMockito.when(iterator.hasNext()).thenReturn(true,true,false);
PowerMockito.when(iterator.next()).thenReturn(user1,user2);
getAggregatedList(new User());
}
This will give you the result you are expecting.

MongoDB: Using $sample with C# driver

I'm trying to express the following query using the MongoDB C# driver (2.4.4):
db.media.aggregate({ $sample: { size: 1 }})
This what I have so far:
BsonDocument sample = new BsonDocument
{
{ "$sample", new BsonDocument { { "size", 1 } } }
};
MongoBlob mongoBlob = await _collection
.Aggregate()
.Group<MongoBlob>(sample)
.FirstOrDefaultAsync();
I cannot put the sample to .Aggregate(AggregateOptions options = null) and putting it into the .Group(...) is obviously wrong. There is also no any like a .Sample() method.
Please, help. Thank you in advance.
Simply,
var randEl = await collection.AsQueryable().Sample(1).FirstOrDefaultAsync();
Do not forget add
using MongoDB.Driver.Linq;
I believe it should be
MongoBlob mongoBlob = await _collection
.Aggregate()
.Sample(1)
.FirstOrDefaultAsync();
If this doesn't work then let me know. Don't have windows system up right now to confirm
Edit (7-AUG):
Turns out its not that simple. The sample method doesn't exists in current driver. The classes which handle this are internal so no straight forward way to inherit. So worked out a solution based on reflection and a hack. Where you add a Stage to the pipeline and then edit it through reflection. Below is a working sample of my demo code
using System;
using MongoDB.Bson;
using MongoDB.Driver;
using System.Reflection;
namespace TestMongo
{
public static class MainClass
{
static IMongoClient _client;
static IMongoDatabase _database;
public static IAggregateFluent<BsonDocument> Sample(this IAggregateFluent<BsonDocument> agg, int count){
var new_agg = agg.Skip(10);
var stage =new_agg.Stages[new_agg.Stages.Count-1];
var newDoc = new BsonDocument {
{ "$sample", new BsonDocument {
{"size", count}
} }
};
stage.GetType().GetField("_document"
, BindingFlags.Instance | BindingFlags.NonPublic)
.SetValue(stage, newDoc);
return new_agg;
}
public static void Main(string[] args)
{
Console.WriteLine("Hello World!");
_client = new MongoClient();
_database = _client.GetDatabase("jobs");
var col = _database.GetCollection<BsonDocument>("results");
var agg = col.Aggregate().Sample(1);
var data = agg.FirstOrDefault();
data = null;
}
}
}
var sample = new BsonDocument
{
{
"$sample", new BsonDocument
{
{"size", 1000}
}
}
};
var samples = _collection.Aggregate().AppendStage(new BsonDocumentPipelineStageDefinition<MyType, MyType>(sample));
return await samples.ToListAsync();

Camel mongodb - MongoDbProducer multiple inserts

I am trying to do a multiple insert using the camel mongo db component.
My Pojo representation is :
Person {
String firstName;
String lastName;
}
I have a processor which constructs a valid List of Person pojo and is a valid json structure.
When this list of Person is sent to the mongodb producer , on invocation of createDoInsert the type conversion to BasicDBObject fails. This piece of code below looks to be the problem. Should it have more fall backs / checks in place to attempt the list conversion down further below as it fails on the very first cast itself. Debugging the MongoDbProducer the exchange object being received is a DBList which extends DBObject. This causes the singleInsert flag to remain set at true which fails the insertion below as we get a DBList instead of a BasicDBObject :
if(singleInsert) {
BasicDBObject insertObjects = (BasicDBObject)insert;
dbCol.insertOne(insertObjects);
exchange1.getIn().setHeader("CamelMongoOid", insertObjects.get("_id"));
}
The Camel MongoDbProducer code fragment
private Function<Exchange, Object> createDoInsert() {
return (exchange1) -> {
MongoCollection dbCol = this.calculateCollection(exchange1);
boolean singleInsert = true;
Object insert = exchange1.getIn().getBody(DBObject.class);
if(insert == null) {
insert = exchange1.getIn().getBody(List.class);
if(insert == null) {
throw new CamelMongoDbException("MongoDB operation = insert, Body is not conversible to type DBObject nor List<DBObject>");
}
singleInsert = false;
insert = this.attemptConvertToList((List)insert, exchange1);
}
if(singleInsert) {
BasicDBObject insertObjects = (BasicDBObject)insert;
dbCol.insertOne(insertObjects);
exchange1.getIn().setHeader("CamelMongoOid", insertObjects.get("_id"));
} else {
List insertObjects1 = (List)insert;
dbCol.insertMany(insertObjects1);
ArrayList objectIdentification = new ArrayList(insertObjects1.size());
objectIdentification.addAll((Collection)insertObjects1.stream().map((insertObject) -> {
return insertObject.get("_id");
}).collect(Collectors.toList()));
exchange1.getIn().setHeader("CamelMongoOid", objectIdentification);
}
return insert;
};
}
My route is as below :
<route id="uploadFile">
<from uri="jetty://http://0.0.0.0:9886/test"/>
<process ref="fileProcessor"/>
<unmarshal>
<csv>
<header>fname</header>
<header>lname</header>
</csv>
</unmarshal>
<process ref="mongodbProcessor" />
<to uri="mongodb:mongoBean?database=axs175&collection=insurance&operation=insert" />
and the MongoDBProcessor constructing the List of Person Pojo
#Component
public class MongodbProcessor implements Processor {
#Override
public void process(Exchange exchange) throws Exception {
ArrayList<List<String>> personlist = (ArrayList) exchange.getIn().getBody();
ArrayList<Person> persons = new ArrayList<>();
for(List<String> records : personlist){
Person person = new Person();
person.setFname(records.get(0));
person.setLname(records.get(1));
persons.add(person);
}
exchange.getIn().setBody(persons);
}
}
Also requested information here - http://camel.465427.n5.nabble.com/Problems-with-MongoDbProducer-multiple-inserts-tc5792644.html
This issue is now fixed via - https://issues.apache.org/jira/browse/CAMEL-10728

Update for MongoCollection<Documnet>

I am trying to update the document but I am not able to do it.
I tried using update() as well as updateOne() methods..None of it worked.
public boolean updateDocument(String docId, String JSONString) {
try{
MongoCollection<Document> collection = getCollection("abc");
DBObject queryObject = (DBObject)(collection.find(eq("_id","docId")).first());
if(queryObject==null)
return false;
else {
DBObject updateObject = (DBObject)JSON.parse(JSONString);
collection.update(queryObject, updateObject);
return true;
}
}catch (Exception e) {
System.out.println(e.getMessage());
return false;
}
}
The Error is coming like this : The method update(dbObject,dbObject) is undefined for the type MongoCollection
I can use updateOne is applicable for Bson arguments but I need to use for DbObjects..
Can anyone can suggest any solution?
I tried this and worked..I needed to use MongoCollection type beacuse in the whole class I have specifed with this only.
public boolean updateDocument(String docId, String JSONString) {
try{
MongoCollection<Document> collection = getCollection("123");
Document queryObject = collection.find(eq("_id",docId)).first(); //finding the document then converting to DBObject type
if(queryObject==null)
return false;
else {
Document updatedoc = new Document();
updatedoc = Document.parse(JSONString);
collection.updateOne(queryObject,updatedoc );
return true;
}
}catch (Exception e) {
System.out.println(e.getMessage());
return false;
}
}

MongoDB: Getting the list of all databases?

How do I list all databases for a connection using Mongo C# Driver?
Very easily:
var server = MongoServer.Create("mongodb://localhost/?safe=true");
var databaseNames = server.GetDatabaseNames();
The MongoServer class was deprecated in version 2.0.0.
You can use ListDatabasesAsync
using (var cursor = await client.ListDatabasesAsync())
{
await cursor.ForEachAsync(d => Console.WriteLine(d.ToString()));
}
Working Solution:
MongoClient client = new MongoClient("mongodb://localhost:27017");
using (IAsyncCursor<BsonDocument> cursor = client.ListDatabases())
{
while (cursor.MoveNext())
{
foreach (var doc in cursor.Current)
{
Console.WriteLine(doc["name"]); // database name
}
}
}
The MongoServer class was deprecated in version 2.0.0 as Juri pointed out. If you don't want to use async, here's how I do it:
var client = new MongoClient("mongodb://" + server_username + ":" + server_password + "#" + server_host + ":" + server_port);
List<MongoDB.Bson.BsonDocument> databases = client.ListDatabases();
Just one thing. It is in BsonDocument format that has 2 elements: "name" and "sizeOnDisk".
Hope this helps.
I wasn't able validate if a given DB exists or not with the existing answers, so here's my take on it:
// extension method on IMongoClient
public static IMongoClient AssertDbExists(this IMongoClient client, string dbName)
{
bool dbFound = false;
using(var cursor = client.ListDatabases())
{
var databaseDocuments = cursor.ToList();
foreach (var db in databaseDocuments)
{
if (db["name"].ToString().Equals(dbName))
{
dbFound = true;
break;
}
}
}
if (!dbFound) throw new ArgumentException("Can't connect to a specific database with the information provided", nameof(MongoSettings.ConnectionString));
return client;
}
And then use it like this:
// either you get the client with the DB validated or throws
_client = new MongoClient(settings.ConnectionString).AssertDbExists(_dbName);
Using: Mongo Official C# driver v2.4.4