I have two collections for example CollectionA and CollectionB both have common filed which is hostname
Collection A :
{
"hostname": "vm01",
"id": "1",
"status": "online",
}
Collection B
{
"hostname": "vm01",
"id": "string",
"installedversion": "string",
}
{
"hostname": "vm02",
"id": "string",
"installedversion": "string",
}
what i want to achieve is when i receive a post message for collection B
I want to check if the record exists in Collection B based on hostname and update all the values. if not insert the new record ( i have read it can be achieved by using upsert -- still looking how to make it work)
I want to check if the hostname is present in Collection A , if not move the record from collection B to another collection which is collection C ( as archive records).ie in the above hostname=vm02 record from collection B should be moved to collectionC
how can i achieve this using springboot mongodb anyhelp is appreciated.The code which i have to save the Collection B is as follows which i want to update to achieve the above desired result
public RscInstalltionStatusDTO save(RscInstalltionStatusDTO rscInstalltionStatusDTO) {
log.debug("Request to save RscInstalltionStatus : {}", rscInstalltionStatusDTO);
RscInstalltionStatus rscInstalltionStatus = rscInstalltionStatusMapper.toEntity(rscInstalltionStatusDTO);
rscInstalltionStatus = rscInstalltionStatusRepository.save(rscInstalltionStatus);
return rscInstalltionStatusMapper.toDto(rscInstalltionStatus);
}
Update 1 : The below works as i expected but I think there should be a better way to do this.
public RscInstalltionStatusDTO save(RscInstalltionStatusDTO rscInstalltionStatusDTO) {
log.debug("Request to save RscInstalltionStatus : {}", rscInstalltionStatusDTO);
RscInstalltionStatus rscInstalltionStatus = rscInstalltionStatusMapper.toEntity(rscInstalltionStatusDTO);
System.out.print(rscInstalltionStatus.getHostname());
Query query = new Query(Criteria.where("hostname").is(rscInstalltionStatus.getHostname()));
Update update = new Update();
update.set("configdownload",rscInstalltionStatus.getConfigdownload());
update.set("rscpkgdownload",rscInstalltionStatus.getRscpkgdownload());
update.set("configextraction",rscInstalltionStatus.getConfigextraction());
update.set("rscpkgextraction",rscInstalltionStatus.getRscpkgextraction());
update.set("rscstartup",rscInstalltionStatus.getRscstartup());
update.set("installedversion",rscInstalltionStatus.getInstalledversion());
mongoTemplate.upsert(query, update,RscInstalltionStatus.class);
rscInstalltionStatus = rscInstalltionStatusRepository.findByHostname(rscInstalltionStatus.getHostname());
return rscInstalltionStatusMapper.toDto(rscInstalltionStatus);
}
Update2 : with the below code i am able to get move the records to another collection
String query = "{$lookup:{ from: \"vmdetails\",let: {rschostname: \"$hostname\"},pipeline:[{$match:{$expr:{$ne :[\"$hostname\",\"$$rschostname\"]}}}],as: \"rscInstall\"}},{$unwind:\"$rscInstall\"},{$project:{\"_id\":0,\"rscInstall\":0}}";
AggregationOperation rscInstalltionStatusTypedAggregation = new CustomProjectAggregationOperation(query);
LookupOperation lookupOperation = LookupOperation.newLookup().from("vmdetails").localField("hostname").foreignField("hostname").as("rscInstall");
UnwindOperation unwindOperation = Aggregation.unwind("$rscInstall");
ProjectionOperation projectionOperation = Aggregation.project("_id","rscInstall").andExclude("_id","rscInstall");
OutOperation outOperation = Aggregation.out("RscInstallArchive");
Aggregation aggregation = Aggregation.newAggregation(rscInstalltionStatusTypedAggregation,unwindOperation,projectionOperation,outOperation);
List<BasicDBObject> results = mongoTemplate.aggregate(aggregation,"rsc_installtion_status",BasicDBObject.class).getMappedResults();
this issue which i have here is it returns multiple records
Found the solution , there may be other best solutions but for me this one worked
create a class customeAggregationGeneration (found in SO answers and extended to match my needs)
public class CustomProjectAggregationOperation implements AggregationOperation {
private String jsonOperation;
public CustomProjectAggregationOperation(String jsonOperation) {
this.jsonOperation = jsonOperation;
}
#Override
public Document toDocument(AggregationOperationContext aggregationOperationContext) {
return aggregationOperationContext.getMappedObject(Document.parse(jsonOperation));
}
}
String lookupquery = "{$lookup :{from:\"vmdetails\",localField:\"hostname\",foreignField:\"hostname\"as:\"rscinstall\"}}";
String matchquery = "{ $match: { \"rscinstall\": { $eq: [] } }}";
String projectquery = "{$project:{\"rscinstall\":0}}";
AggregationOperation lookupOpertaion = new CustomProjectAggregationOperation(lookupquery);
AggregationOperation matchOperation = new CustomProjectAggregationOperation(matchquery);
AggregationOperation projectOperation = new CustomProjectAggregationOperation(projectquery);
Aggregation aggregation = Aggregation.newAggregation(lookupOpertaion, matchOperation, projectOperation);
ArrayList<Document> results1 = (ArrayList<Document>) mongoTemplate.aggregate(aggregation, "rsc_installtion_status", Document.class).getRawResults().get("result");
// System.out.println(results1);
for (Document doc : results1) {
// System.out.print(doc.get("_id").toString());
mongoTemplate.insert(doc, "RscInstallArchive");
delete(doc.get("_id").toString());
Related
I am building a list view page with multiple filters using .net core as backend and mongodb as database. I want to filter data based on some condition such that if the variable is passed as blank, it should retrieve all data, otherwise only the matching data.
In mssql this can be achieved easily by
where (#SymbolCode = '' or SymbolCode = #SymbolCode)
What is the option in mongodb filter
var filter = new BsonDocument
{
{
"symbolCode", SymbolCodeSearchString // If string SymbolCodeSearchString is blank, it should retrieve all data else the search data
}
};
Sample Data
{
"_id": {
"$oid": "60ed91bc65675f966c0eec46"
},
"symbolCode": "F",
"timestamp": {
"$date": "2021-07-13T13:14:35.909Z"
}
}
you should check symboleCode before query on mongodb
first check if symbolCode is empty or ""
define filter query as
var filter = new BsonDocument
{
};
else if it was not empty
var filter = new BsonDocument
{
{
"symbolCode", SymbolCodeSearchString // If string SymbolCodeSearchString is blank, it should retrieve all data else the search data
}
};
I was able to figure out. Thanks to #Naimi and #Takis. Building a dynamic filter is the key. Posting here the answer so that it could help anyone
var filterQuery = new BsonDocument
{
{
"symbolCode", new BsonDocument
{
{ "$regex", SymbolCodeString},
{ "$options", "i"}
}
}
};
if (fromDate != "" && toDate != "")
{
BsonElement dateFilter = new("timestamp", new BsonDocument
{
{ "$gt", fromDate},
{ "$lt", toDate}
});
filterQuery.Add(dateFilter);
}
By this way, i am able to add any number of filters dynamically
After reviewing this page, specifically this query
db.scores.find(
{ results: { $elemMatch: { $gte: 80, $lt: 85 } } }
)
I used the following imports
import static com.mongodb.client.model.Filters.and;
import static com.mongodb.client.model.Filters.elemMatch;
import static com.mongodb.client.model.Filters.eq;
import static com.mongodb.client.model.Projections.excludeId;
import static com.mongodb.client.model.Projections.fields;
import static com.mongodb.client.model.Projections.include;
And came up with the following code to perform a similar operation (ARRAY_FIELD_NAME = "myArray")
MongoCollection<Document> collection = mongoDB.getCollection(COLLECTION_NAME);
Bson filters = and(eq("userId", userId), elemMatch(ARRAY_FIELD_NAME, eq("id", id)));
Bson projections = fields(include(ARRAY_FIELD_NAME), excludeId());
List<Document> results = (List<Document>) collection.find(filters).projection(projections).first().get(ARRAY_FIELD_NAME);
if (CollectionUtils.isEmpty(results)) {
return null;
}
if (results.size() > 1) {
throw new ApiException(String.format("Multiple results matched (User ID: %s, Array item ID: %s)", userId, id));
}
return results.get(0);
To filter documents that have the following structure
{
"_id": {
"$oid": "588899721bbabc26865f41cc"
},
"userId": 55,
"myArray": [
{
"id": "5888998e1bbabc26865f41d2",
"title": "ABC"
},
{
"id": "5888aaf41bbabc3200e252aa",
"title": "ABC"
}
]
}
But instead of getting a single or no item from the myArray field, I always get both items !
The only code that worked for me is the following
MongoCollection<Document> collection = mongoDB.getCollection(COLLECTION_NAME);
List<Bson> aggregationFlags = new ArrayList<>();
aggregationFlags.add(new Document("$unwind", "$" + ARRAY_FIELD_NAME));
aggregationFlags.add(new Document("$match", new Document("userId", userId).append(ARRAY_FIELD_NAME + ".id", id)));
aggregationFlags.add(new Document("$project", new Document("_id", 0).append(ARRAY_FIELD_NAME, "$" + ARRAY_FIELD_NAME)));
return (Document) collection.aggregate(aggregationFlags).first().get(ARRAY_FIELD_NAME);
So why does the first piece of code that should behave the same as the query shown at the beginning of the question, not filter results as expected ?
I do not need to "aggregate" results, I need to "filter" them using the user ID and array item id.
You need to use $elemMatch(projection). Something like below should work.
import static com.mongodb.client.model.Projections.elemMatch;
Bson filters = and(eq("userId", userId));
Bson projections = fields(elemMatch(ARRAY_FIELD_NAME, eq("id", id)), excludeId());
I am new to MongoDB so please excuse my ignorance.
I have a mongoDB which contains a bunch of documents with something like this
["field": "blah", "version":"1" ...]
["field": "blah", "version":"2" ...]
["field": "blah", "version":"1"....]
["field": "blah1", "version":"10"...]
["field": "blah2", "version":"100"...]
["field": "blah3", "version":"1"....]
["field": "blah3", "version":"2"....]
["field": "blah2", "version":"1"....
I am trying to send a list of queries and fetch all the records as a batch. Is it possible to do so?
List<Docs> fetchDocs(Map<String, String> queries)
{
CriteriaContainer cc=null;
Query<Docs> query = this.mongoDao.createQuery(MyClass.class);
for (Map.Entry<String,String >entry : queries.entrySet())
{
if(cc ==null)
{
cc= query.criteria("Field").equal(entry.getKey()).and(query.criteria("version").equal(entry.getValue()));
}
else
{
cc.or(query.criteria("Field").equal(entry.getKey()).and(query.criteria("version").equal(entry.getValue()));)
}
}
query.and(cc);
List<Docs> doc = query.asList();
return doc;
}
I am not geting the correct list back. I am not sure if I have written this query correctly.
essentially, I want to fetch results or query like this
[{"Field":"blah,"version":1 } $or {"Field":"blah1", "version":10} ]
It should return me a list containing
["field": "blah", "version":"1" ....]
["field": "blah1", "version":"10"....]
Yes, this is definitely possible to do. The final query object will look more like this, however:
{$or: [{"field": "blah,"version":1 }, {"field":"blah1", "version":10} ]}
Notice the array of subqueries for the $or expression.
There are some quirks in Morphia will mean you have to wrap it in an $and clause. So your code would be like this:
Query q = dao.createQuery();
q.and(
q.or(subQuery1),
q.or(subQuery2),
...
);
I ended up fixing my query to this which worked. Wanted to share it with the rest
List<Docs> fetchDocs(Map<String, String> queries){
Criteria [] c = new Criteria[queries.size()];
Query<TUCacheDoc> query0 = this.mongoDao.createQuery(MyClass.class);
int counter=0;
for (Map.Entry<String, String> entry : inMap.entrySet()) {
Query<TUCacheDoc> queryC = this.mongoDao.createQuery(MyClass.class);
String key= entry.getKey();
String val= entry.getValue();
Criteria cC = queryC.criteria("field").equal(key).criteria("version").equal(val);
c[counter]=cC;
counter++;
}
query0.or(c);
List<TUCacheDoc> qresult = query0.asList();
return qresult;
}
I need to create a time stamp in my mongodb collection. Am using C# in front end .My code is :
internal static void CreateStudent(string Id, string Name,string strUserId)
{
MongoServer server = MongoServer.Create(ConnectionString);
MongoDatabase mydb = server.GetDatabase("Database");
MongoCollection<BsonDocument> Student = mydb.GetCollection<BsonDocument>("Student");
BsonDocument colectionGenre = new BsonDocument {
{ "Code", Id }, //Id is Auto Generated in sql. Fetch from there using Output parameter and save it in one variable and pass that here
{ "Name", Name },
{ "Status","Y"},
{"stamps" , new BsonDocument {
{"Ins_date", DateTime.Now},
{"up_date",""},
{"createUsr", strUserId},
{"updUsr", ""},
{"Ins_Ip", GetIP()},
{"Upd_IP",""}}}
};
Student.Insert(colectionGenre);
}
internal static void UpdateStudent(string Id, string Name,string strUserId)
{
MongoServer server = MongoServer.Create(ConnectionString);
MongoDatabase mydb = server.GetDatabase("Database");
MongoCollection<BsonDocument>Student = mydb.GetCollection<BsonDocument>("Student"); ;
// Query for fetch the ID which is edited by the User...(user can only able to edit the NAME field alone)
var query = new QueryDocument {
{ "Code", Id }};
// After Fetch the correspondent ID it updates the name with the Edited one
var update = new UpdateDocument {
{ "$set", new BsonDocument("Name", Name) }
};
// Updated Query.(Id is same as previous. Name is updated with new one)
{"stamps" , new BsonDocument {
{"up_date",DateTime.Now},
{"updUsr", strUserId},
{"Upd_IP",GetIp()}}}
}}
};
Student.Update(query,update,UpdateFlags.Upsert, SafeMode.True);
}
It works fine for INSERT method with time(Stamp) once the record is created. But the problem is with update method. When user update something the insert time also changed with the updated time..
After User Updates the Name, i want my will collection looks like this
{
"_id" : ObjectId("5178aea4e6d8e401e8e51dc0"),
"Code": 12,
"Name": Sname,
"Stamps:"{
"Ins_date":03:34:00,
"up_date": 04:35:12
}
}
But my problem is both the time will same after update. That is because it takes the current date and time function..How can i achieve the above output.It needs any driver.Suggest something for me...
You're passing in a value for the Ins_date field when you're updating the document. Just remove that from the update document and it won't change it.
var update = new UpdateDocument {
{"$set", new BsonDocument {
{"State_strName", name},
{"stamps" , new BsonDocument {
{"up_date",DateTime.Now},
{"createUsr", ""},
{"updUsr", ""},
{"Ins_Ip", GetIP()},
{"Upd_IP",GetIP()}}}
};
tblmytbl.Update(query, update);
How you are updating the value in the existing document by using unique id or other unique value.Check whether the unique id or value is already exist in your database documents.If it is exist means change the update time only don't do anything..
While updating the data in mongoDB,you are passing the same values for Ins_date and up_date i.e. DateTime.Now(current system date and time).So the same values are updating in your monoDB document.
For this you can do one thing :-
Before updating your mongoDB document you take Ins_date values from your database by using sql query in C#.net and then use this value for Ins_date and for up_date use DateTime.Now then your both values will be different.
var update = new UpdateDocument {
{"$set", new BsonDocument {
{"State_strName", name},
{"stamps" , new BsonDocument {
{"Ins_date", **Ins_date values_from your database**} ,
{"up_date",DateTime.Now},
{"createUsr", ""},
{"updUsr", ""},
{"Ins_Ip", GetIP()},
{"Upd_IP",GetIP()}}}
};
tblmytbl.Update(query, update);
Sounds like what you need is the new $setOnInsert operator which was added in 2.4 for exactly this use case.
When the update with upsert flag results in an insert, you want to $set insert_date to Date.now but when it's a regular update, you don't want to set it at all. So now with your update you should use $set for regular fields you want to set whether it's an update or an insert, but use $setOnInsert for fields that should only be set during insert.
Finally I got answer...In INSERT method Simply pass the below things
{"Insert-stamps" , new BsonDocument {
{"Ins_date", DateTime.Now},
{"createUsr", strUserId},
{"Ins_Ip", GetIP()}}},
{"Update-stamps" , new BsonDocument {
{"up_date",""},
{"updUsr", ""},
{"Upd_IP",""}}}
And In UPDATE method
{"Update-stamps" , new BsonDocument {
{"up_date", DateTime.Now},
{"updUsr", StrUserId},
{"Upd_IP",GetIP()}}}
It works Fine for my standard....
I want to add a new document to the following document having an outer key "User"
{
name:himani,
User:[
{
_id:e25ffgf627627,
Name:User1
},
{
_id:fri2i2jhjh9098,
Name:User2
}
]
};
Below is my code in which I am trying to add a new document to already existing document.
My code is:
var server = MongoServer.Create("mongodb://username:password#localhost:27017/?safe=true");
SafeMode mode = new SafeMode(true);
SafeModeResult result = new SafeModeResult();
var db = server.GetDatabase("himani");
var coll = db.GetCollection("test");
BsonDocument document = new BsonDocument();
document.Add("name", "himani");
result = coll.Insert(document, mode);
BsonDocument nested = new BsonDocument();
nested.Add("1", "heena").Add("2", "divya");
BsonArray a = new BsonArray();
a.Add(2);
a.Add(5);
nested.Add("values", a);
document["3"] = new BsonArray().Add(BsonValue.Create(nested));
coll.Save(document);
var query = Query.And(
Query.EQ("name", "himani"),
Query.EQ("3.1", "heena")
);
var match = coll.FindOne(query);
var update = Update.AddToSet("3", new BsonDocument {{ "count", "2" }});
coll.Update(query, update);
I want to add a new document to the User array. I am doing this by above code but its not working.Please tell me the right way of doing it.
I don't understand your document structure at all... and the only "user" array I could find in here was a field called "3". Your code does in fact work and appends a document into the "3" array. The below is the result after running your code. Perhaps you could be more clear as to what you want your document to look like after you have "appended" a user.
{
"_id":ObjectId("4fa7d965ce48f3216c52c6c7"),
"name":"himani",
"3":[
{
"1":"heena",
"2":"divya",
"values":[ 2, 5 ]
},
{
"count":"2"
}
]
}