Bulk save to mongo db using Morphia - mongodb

I have a list of data object which has to be saved in mongodb.
if datastore.save(ListofObject) is used will it degrade the performance.Morphia version is 1.1.0 and java jdk 1.8

You can do this. Suppose if entity name is User,
Query<User> query = MongoUtils.getDataStore()
.createQuery(User.class)
.filter("active", Boolean.TRUE)
.filter("suspensionDate <", new Date());
UpdateOperations<User> updateOperations = MongoUtils.getDataStore()
.createUpdateOperations(User.class)
.set("active", false);
UpdateResults updateResults = MongoUtils.getDataStore()
.update(query, updateOperations, false, null);
In the above case the all user with flag active true and suspension date less that today will be set with flag active false.
If total records updated need to know then below lines are useful.
WriteResult writeResult = updateResults.getWriteResult();
int totalRecordsUpdated = writeResult.getN();

Related

MongoDB bulkwrite with upsert option is taking more than a minute for 100k+ records. Is it possible to improve?

I have read the following two questions but did not see a satisfactory solution or suggestion:
Performance degrade with Mongo when using bulkwrite with upsert
Mongodb/Mongoose bulkwrite(upsert) performance issues
I have an API that will be receiving ~80k objects every 5 - 6 seconds.
These objects will need to be inserted if they do not exist and updated if they do exist.
The collection has a unique index (of type asc 1), which I use in the filter expression below.
Upserting 100k documents using the code below takes more than a minute, which is too long for my requirement (instantiating the objects takes less than a second).
MongoClient _mongoClient = new MongoClient("connection string");
IMongoDatabase _hotsauceOdds = _mongoClient.GetDatabase("DatabaseName");
var collection = _hotsauceOdds.GetCollection<Person>("TestMongo");
List<Person> peeps = new List<Person>();
for (int i = 0; i < 100000; i++)
{
peeps.Add(new Person { Age = i, Name = Guid.NewGuid().ToString() });
}
var models = peeps.Select(item => new ReplaceOneModel<Person>(new ExpressionFilterDefinition<Person>(doc => doc.Name == item.Name), item) { IsUpsert = true });
await collection.BulkWriteAsync(models);
I am testing this on the free tier of mongo atlas.
Is it possible to make this operation run more quickly, or is this as good as it gets for mongodb?

update multiple documents in mongodb from spring mongo

In my use case I want to update multiple documents at once, documents that match a query, using spring-data-mongo.
Here is what I have been trying,
Criteria filterCriteria = new Criteria().andOperator(Criteria.where("bac").is("def"));
Update update = new Update();
update.set("status", status);
Query query = new Query();
query.addCriteria(filterCriteria);
mongoOperations.findAndModify(query, update, MyClass.class);
But this is not updating any document.
Plus I have looked up in the mongo documentation but have not anything useful
https://docs.mongodb.com/manual/reference/method/db.collection.findAndModify/#comparisons-with-the-update-method
Here is the version that I am using
Mongodb - 3.6
spring-data-mongodb - 1.5.5.RELEASE
findAndModify(...) method can update a document and return either the old or newly updated document in a single operation.
To update all document that matches the given query use updateMulti(...).
https://docs.spring.io/spring-data/mongodb/docs/current/api/org/springframework/data/mongodb/core/MongoOperations.html#updateMulti-org.springframework.data.mongodb.core.query.Query-org.springframework.data.mongodb.core.query.UpdateDefinition-java.lang.Class-
visit the link and there you will find it.
#Autowire
MongoTemplate mongoTemplate;
Query query = new Query();
Criteria filterCriteria = Criteria.where("bac").is("def");
query.addCriteria(filterCriteria);
Update update = new Update();
update.set("status", status);
mongoTemplate.updateMulti(query, update, MyClass.class);

MongoDB Morphia Update multi=true VS bulk update

I want to update 10,000 documents in a single web request. I intend to update only one field (which is indexed) in all the documents matched with some criteria with same value.
I see morphia 1.3.2 always set multi=true parameter in update call. Is it enough for updating 10,000 document? Or there are any bulk update functionality in morphia.
The following code should work for you.
Query<Entity> query = datastore.createQuery(Entity.class);
query.filter("name = ", "xxx");
UpdateOperations<Entity> updateOperations = datastore.createUpdateOperations(Entity.class).set
("yyy", 200);
UpdateResults updateResults = datastore.update(query, updateOperations, false, null);
All the documents in the collection with name = 'xxx' will now have all their 'yyy' attribute equal to 200.

Index hint with mongodb csharp

I am migrating from the mongodb csharp driver 1.10.0 to 2.0.0.
One of the collection I am using is very big and has to fulfill many queries with different filter attributes. That is why I was relying on some index hint statements. With the v1.10 driver it looks like
myCollection.Find(query).SetHint("myIndexName");
I searched the v2 driver api but this hint method seems to be completly removed in the v2 driver. Is there an alternative? How should I do index hints with the v2 driver?
Note: The Solutions provided works for latest mongodb csharp drivers as well
You can use the FindOptions.Modifiers property.
var modifiers = new BsonDocument("$hint", "myIndexName");
await myCollection.Find(query, new FindOptions { Modifiers = modifiers }).ToListAsync();
May I ask why you are using the hint? Was the server consistently choosing the wrong index? You shouldn't need to do this except in exceptional cases.
Craig
Ideally, try to make the query in a way that mongodb optimizer can use the index automatically.
If you are using FindAsync then you will have a property named Hint. Use it like this:
If you have index named "myIndexName" which you want your query should use forcefully, then use like this:.
BsonString bsonString = new BsonString("myIndexName");
cursor = await collection.FindAsync(y => y.Population > 400000000,
new FindOptions<Person, Person>()
{
BatchSize = 200,
NoCursorTimeout = true,
AllowPartialResults = true,
Projection = "{'_id':1,'Name':1,'Population':1}"
Hint = bsonString.AsBsonValue,
}).ConfigureAwait(false);
You can fine BsonString class in MongoDB.Bson
With agregate you can force indice like this:
BsonString bsonString = new BsonString("ix_indice");
var query = this.collection.Aggregate(new AggregateOptions() { Hint = bsonString }).Match(new BsonDocument {..});
If you are using the Linq IQueryable, you can specify the hint (and other options) like this:
BsonDocument hint = new BsonDocument("myFieldName", 1);
// or
BsonDocument hint = new BsonString("myIndexName");
await collection.AsQueryable(new AggregateOptions { Hint = hint })
myFieldName can also reference a complex field, e.g. Metadata.FileName
myIndexName is the name of an index. I prefer to reference the field (first option) directly, instead of an index, for simple cases.

OrientDB - How do I insert a document with connections to multiple other documents?

Using OrientDB 1.7-rc and Scala, I would like to insert a document (ODocument), into a document (not graph) database, with connections to other documents. How should I do this?
I've tried the following, but it seems to insert an embedded list of documents into the Package document, rather than connect the package to a set of Version documents (which is what I want):
val doc = new ODocument("Package")
.field("id", "MyPackage")
.field("versions", List(new ODocument("Version").field("id", "MyVersion")))
EDIT:
I've tried inserting a Package with connections to Versions through SQL, and that seems to produce the desired result:
insert into Package(id, versions) values ('MyPackage', [#10:3, #10:4] )
However, I need to be able to do this from Scala, which has yet to produce the correct results when loading the ODocument back. How can I do it (from Scala)?
You need to create the individual documents first and then inter-link them using below SQL commands.
Some examples given in OrientDB documentation
insert into Profile (name, friends) values ('Luca', [#10:3, #10:4] )
OR
insert into Profile SET name = 'Luca', friends = [#10:3, #10:4]
Check here for more details.
I tried posting in comments above, but somehow the code is not readable, so posting the response separately again.
Here is an example of linking two documents in OrientDB. This is take from documentation. Here we are adding new user in DB and connecting it to give role:
var db = orient.getDatabase();
var role = db.query("select from ORole where name = ?", roleName);
if( role == null ){
response.send(404, "Role not found", "text/plain", "Error: role name not found" );
} else {
db.begin();
try{
var result = db.save({ "#class" : "OUser", name : "Gaurav", password : "gauravpwd", roles : role});
db.commit();
return result;
}catch ( err ){
db.rollback();
response.send(500, "Error: Server", "text/plain", err.toString() );
}
}
Hope it helps you and others.
This is how to insert a Package with a linkset referring to an arbitrary number of Versions:
val version = new ODocument("Version")
.field("id", "1.0")
version.save()
val versions = new java.util.HashSet[ODocument]()
versions.add(version)
val package = new ODocument("Package")
.field("id", "MyPackage")
.field("versions", versions)
package.save()
When inserting a Java Set into an ODocument field, OrientDB understands this to mean one wants to insert a linkset, which is an unordered, unique, collection of references.
When reading the Package back out of the database, you should get hold of its Versions like this:
val versions = doc.field[java.util.HashSet[ODocument]]("versions").asScala.toSeq
As when the linkset of versions is saved, a HashSet should be used when loading the referenced ODocument instances.
Optionally, to enforce that Package.versions is in fact a linkset of Versions, you may encode this in the database schema (in SQL):
create property Package.versions linkset Version