I am attempting to use the $set and $inc flags to update a field and increment a field by 1 in camel ( java ). Here is the code I am using:
from("direct:mySampleRoute").routeId("update-mongo-db-entry").setBody()
.simple("{\"person\": \"${headers.personName}\", \"job\": \"computerGuy\",\"title\": \"DR\"},"
+ "{\"$set\": {\"pay\": \"${headers.newPay}\"}, \"$inc\": {\"hrPay\": 1}}")
.toD("mongodb:mongoClient?database=" + mongoDb + "&collection="
+ mongoEmplyeeCollection + "&operation=update")
.end();
The goal of the query is to find an entry where person == ${headers.personName}, job == computerGuy, and title = DR. Once it is found, it is to set the pay field to ${headers.newPay} and inc the hrPay field by positive 1. From what I can tell, I am structuring my code exactly the same as mentioned in this post: What am I doing wrong with $set and $inc in update However, once executed, my app crashes and I am unable to see any logs wrt to why that query fails. As such, I suspect I have a fundamental problem in structuring the json update. Camel documentation located here is of limited help.
Using camel, how can I achieve my goal of making a "select" query, updating some fields using $set, and incrementing a field using $inc ?
I ended up solving my problem using a Processor to create the needed body. I slved it using this syntext within the process method:
#Override
public void process(final Exchange exchange) throws Exception {
final BasicDBObject filterQuery = new BasicDBObject();
filterQuery.append("person", exchange.getIn().getHeader("personName"));
filterQuery.append("job", "computerGuy");
filterQuery.append("title", "DR");
final BasicDBObject updateQuery = new BasicDBObject();
updateQuery.append("$set", new BasicDBObject("pay", exchange.getIn().getHeader("newPay"))
.append("location", "4th floor"));
updateQuery.append("$inc", new BasicDBObject("hrPay", 1));
final List<DBObject> query = new ArrayList<>();
query.add(filterQuery);
query.add(updateQuery);
exchange.getIn().setBody(query);
}
Related
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);
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.
How to make sure that specific fields can be inserted upon creation, but can optionally be excluded when updating the object.
I'm essentially looking for something like the following:
mongoOperations.save(theObject, <fields to ignore>)
From what I see, recently introduced #ReadOnlyProperty will ignore the property both for inserts and updates.
I was able to get the desired behavior by implementing my Custom MongoTemplate and overriding its doUpdate method as follows:
#Override
protected WriteResult doUpdate(String collectionName, Query query,
Update originalUpdate, Class<?> entityClass, boolean upsert, boolean multi) {
Update updateViaSet = new Update();
DBObject dbObject = originalUpdate.getUpdateObject();
Update filteredUpdate = Update.fromDBObject(dbObject, "<fields to ignore>");
for(String key : filteredUpdate.getUpdateObject().keySet()){
Object val = filteredUpdate.getUpdateObject().get(key);
System.out.println(key + "::" + val);
updateViaSet.set(key, filteredUpdate.getUpdateObject().get(key));
}
return super
.doUpdate(collectionName, query, updateViaSet, entityClass, upsert, multi);
}
But the issue is that now it will use Mongo $set form of updates for everything, not just for specific cases.
Please advise if there is any simpler (and correct) way to achieve this.
While creating an update object, use $setOnInsert instead of $set. It is available in spring mongo as well.
If an update operation with upsert: true results in an insert of a document, then $setOnInsert assigns the specified values to the fields in the document. If the update operation does not result in an insert, $setOnInsert does nothing.
I have a list of documents with the structure:
{
from:"string"
to:"string"
payload:{
projectid:10000
}
}
I want to delete all documents with payload.projectid set to 10000 using the java API.
From the command-line I write:
db.notifications.find({"payload":{"projectid":10000}})
Error:
java.lang.RuntimeException: An operator that starts with $ is expected for projectid.
A similar error arises if I delete, whether through cli or programmatically.
This works in normal mongoDB.
How do I delete in db2 nosql?
Java code (for your reference!):
BasicDBObject query = new BasicDBObject();
query.append("to", "username");
query.append("from", "username2");
query.append("payload", new BasicDBObject().append("projectid", 10000));
System.out.println(query); // prints out okay
col.remove(query); //error
You should be using dot notation to reference the sub-document field:
Link
In your case it would be:
db.notifications.find({"payload.projectid":10000})
In Java the find should look something like this:
DBCollection notifications = db.getCollection("notifications")
DBObject subdocumentQuery = new BasicDBObject("payload.projectid", 10000);
DBCursor subdocumentCursor = notifications.find(subdocumentQuery);
I need to iterate through all of the collections in my MongoDB database and get the time when each of the collections was created (I understand that I could get the timestamp of each object in the collection, but I would rather not go that route if a simpler/faster method exists).
This should give you an idea of what I'm trying to do:
MongoDatabase _database;
// code elided
var result = _database.GetAllCollectionNames().Select(collectionName =>
{
_database.GetCollection( collectionName ) //.{GetCreatedDate())
});
As far as I know, MongoDB doesn't keep track of collection creation dates. However, it's really easy to do this yourself. Add a simple method, something like this, and use it whenever you create a new collection:
public static void CreateCollectionWithMetadata(string collectionName)
{
var result = _db.CreateCollection(collectionName);
if (result.Ok)
{
var collectionMetadata = _db.GetCollection("collectionMetadata");
collectionMetadata.Insert(new { Id = collectionName, Created = DateTime.Now });
}
}
Then whenever you need the information just query the collectionMetadata collection. Or, if you want to use an extension method like in your example, do something like this:
public static DateTime GetCreatedDate(this MongoCollection collection)
{
var collectionMetadata = _db.GetCollection("collectionMetadata");
var metadata = collectionMetadata.FindOneById(collection.Name);
var created = metadata["Created"].AsDateTime;
return created;
}
The "creation date" is not part of the collection's metadata. A collection does not "know" when it was created. Some indexes have an ObjectId() which implies a timestamp, but this is not consistent and not reliable.
Therefore, I don't believe this can be done.
Like Mr. Gates VP say, there is no way using the metadata... but you can get the oldest document in the collection and get it from the _id.
Moreover, you can insert an "empty" document in the collection for that purpose without recurring to maintain another collection.
And it's very easy get the oldest document:
old = db.collection.find({}, {_id}).sort({_id: 1}).limit(1)
dat = old._id.getTimestamp()
By default, all collection has an index over _id field, making the find efficient.
(I using MongoDb 3.6)
Seems like it's some necroposting but anyway: I tried to find an answer and got it:
Checked it in Mongo shell, don't know how to use in C#:
// db.payload_metadata.find().limit(1)
ObjectId("60379be2bec7a3c17e6b662b").getTimestamp()
ISODate("2021-02-25T12:45:22Z")