update in a nested array using C# Driver in MongoDB - asp.net-mvc-3

Here is my exact schema:
{
"_id" : ObjectId("4fb4fd04b748611ca8da0d45"),
"Name" : "Agent name",
"City" : "XXXX",
"BranchOffice" : [{
"_id" : ObjectId("4fb4fd04b748611ca8da0d46"),
"Name" : "Branch name",
"City" : "XXXX",
"SubBranch" : [{
"_id" : ObjectId("4fb4fd04b748611ca8da0d47"),
"Name" : "Sub-Branch Name",
"City" : "XXXX"
"Users" : [{
"_id" : ObjectId("4fb4fd04b748611ca8da0d48"),
"Name" : "User",
"City" : "XXXX"
}]
}]
}]
}
Its Inserted successfully in c#. insert code was below but update condition is failed .
I want to update field 3 level and 4 level of array using SubBranch and users
Insert code
IMongoQuery query = Query.And(Query.EQ("_id", new ObjectId(4fb4fd04b748611ca8da0d45)),
Query.EQ("BranchOffice._id", new ObjectId(4fb4fd04b748611ca8da0d46)));
Agent agent = dc.Collection.FindOne(query);
BsonDocument branchOffice = agent.BranchOffice.Find(objId => objId._id == new ObjectId(4fb4fd04b748611ca8da0d46)).ToBsonDocument();
subBranch I had get List object convert to BsonDocument
Files: name,city,_Id, and users for array
BsonDocument subBranchOffice = **subBranch.ToBsonDocument()**;
if (branchOffice.Contains("SubBranch"))
{
if (branchOffice["SubBranch"].IsBsonNull)
{
branchOffice["SubBranch"] = new BsonArray().Add(BsonValue.Create(subBranchOffice));
}
else
{
branchOffice["SubBranch"].AsBsonArray.Add(BsonValue.Create(subBranchOffice));
}
var update = Update.Set("BranchOffice.$.SubBranch",branchOffice["SubBranch"]);
SafeModeResult s = dc.Collection.Update(query, update, UpdateFlags.Upsert,SafeMode.True);
}
Here SafemodeResult is UpdateExisting = true
Here Inserted Option is successfully
next I try to update in else Statement. I am not get it answer
Update code
else
{
var queryEdit = Query.And(Query.EQ("_id", new ObjectId(4fb4fd04b748611ca8da0d45)),
Query.EQ("BranchOffice._id", new ObjectId(4fb4fd04b748611ca8da0d46)),
Query.EQ("SubBranchlist._id", new ObjectId(4fb4fd04b748611ca8da0d47)));
**//Index value 1 or 2 or 3**
var update = Update.Set("BranchOffice.$.SubBranch."index value".Name", "sname").
Set("BranchOffice.$.SubBranch."index value".city", "yyyyy" ?? string.Empty);
SafeModeResult s = dc.Collection.Update(queryEdit, update, UpdateFlags.None,SafeMode.True);
}
Here SafemodeResult is UpdateExisting = False
Here updated Option is fail
Please explain how to solve this probelm and how to update field 2 and 3 level of array
Please show any Example

There's a lot there, but it looks like at least part of your problem is that you've spelled BranchOffice differently between the data and the query you are using to update, also you've missed the hierarchy in SubBranch, so your queryEdit in the last code sample won't match the document. This will;
db.so.find({
_id: ObjectId("4fb4fd04b748611ca8da0d45"),
"BrancheOffice._id": ObjectId("4fb4fd04b748611ca8da0d46"),
"BrancheOffice.SubBranch._id": ObjectId("4fb4fd04b748611ca8da0d47"),
}).toArray()

Related

Spring MongoDB BasicQuery not working with projection

I have following BasicQuery
BasicQuery query2 = new BasicQuery("{status:{$in:['APPROVED','NEW','OPEN']}},{siteId:1,_id:0}");
Where BasicQuery is a class from SpringData mongoDb org.springframework.data.mongodb.core.query.BasicQuery. While doing the debug the above query get compiled into
Query: { "status" : { "$in" : [ "APPROVED" , "NEW" , "OPEN"]}}, Fields: null, Sort: { }
But it should have been compiled as below
Query: { "status" : { "$in" : [ "APPROVED" , "OPEN" , "NEW"]}}, Fields: { "siteId" : 1 , "_id" : 0}, Sort: null
If you notice, fields are still missing into compiled BasicQuery. Please help how i can have project in BasicQuery. I can have projection by using Query as below.
Query query = new Query();
query.addCriteria(Criteria.where(STATUS).in(validStatus));
query.fields().include("siteId").exclude("_id");
My query is how i can achieve the same using BasicQuery.
I guess i got the answer. Instead of using single string argument constructor of BasicQuery we need to use two String argument basic query as follow.
BasicQuery query2 = new BasicQuery("{status:{$in:['APPROVED','NEW','OPEN']}}","{siteId:1,_id:0}");
Above will compile into following query
Query: { "status" : { "$in" : [ "APPROVED" , "OPEN" , "NEW"]}}, Fields: { "siteId" : 1 , "_id" : 0}, Sort: null
BasicQuery query = new BasicQuery("{ $and: [{ studentId: { $in: "+studentIds+" } }, { status: { $ne: '"+studStatus+"'} }] }");
studentIds is an array and studStatus is a string!
Thanks to vashishth

Unwrapping a Kotlin hashmap in Thymeleaf inside of Spring Boot 2

I have a Kotlin function which creates a model with a hashmap as shown below
#GetMapping("/")
fun index(model: Model): Mono<String> {
model.addAttribute("images", imageService.findAllImages()?.flatMap { image ->
Mono.just(image)
.zipWith(repository.findByImageId(image?.id!!).collectList())
.map({ imageAndComments: Tuple2<Image?, MutableList<learningspringboot.images.Comment>> ->
hashMapOf<String, Any?>(
"id" to imageAndComments.t1?.id,
"name" to imageAndComments.t1?.name,
"comments" to imageAndComments.t2)
}).log("findAllImages")
})
model.addAttribute("extra", "DevTools can also detech code changes.")
return Mono.just("index")
}
Image.kt
package learningspringboot.images
import org.springframework.data.annotation.Id
data class Image(#Id var id: String? = null, var name: String? = null)
Comment.kt
package learningspringboot.images
import org.springframework.data.annotation.Id
data class Comment #JvmOverloads constructor(#Id private var id: String? = null, private var imageId: String? = null, private var comment: String? = null) {
}
In my Thymeleaf template I have
<ul><li th:each = "Comment :${image.comments}" th:text = "${image.comments}"></li></ul>
Which gives me this lines like
[Comment(id=5a623d5d2298352bc4929866, imageId=0d46b575-b6ce-48e2-988a-ebe62ebc2ceb, comment=test), Comment(id=5a623d8b2298352bc4929867, imageId=0d46b575-b6ce-48e2-988a-ebe62ebc2ceb, comment=test23)]
Which shows the comment record as is with the MongoDB keys/ids and everything else. This is not what I want.
I also have this in my Thymeleaf template
<ul><li th:each = "Comment :${image.comments}" th:text = "${comment == null ? 'empty' : comment.Comment}"></li></ul>
Which shows the word empty for each comment record.
The comment record in the database looks like
{ "_id" : ObjectId("5a623d5d2298352bc4929866"), "imageId" : "0d46b575-b6ce-48e2-988a-ebe62ebc2ceb", "comment" : "test", "_class" : "learningspringboot.comments.Comment" }
The image records in the database looks like
{ "_id" : ObjectId("5a623d5d2298352bc4929866"), "imageId" : "0d46b575-b6ce-48e2-988a-ebe62ebc2ceb", "comment" : "test", "_class" : "learningspringboot.comments.Comment" }
{ "_id" : ObjectId("5a623d8b2298352bc4929867"), "imageId" : "0d46b575-b6ce-48e2-988a-ebe62ebc2ceb", "comment" : "test23", "_class" : "learningspringboot.comments.Comment" }
How can I unwrap the comments records so that I only see the "comment" values and not the "_id" or "imageId" values?
The problem is that I have a hashmap<string>,arraylist<image>.
So I simply need to loop through all the image elements in the array list using thymeleaf. I'm pretty sure that this has been done before, I'll just need to find a good example and do some reading.
I was able to use the following code
<th:block th:each="Comment : ${image.comments}">
<ul th:each="comment : ${Comment}">
<li th:text="${comment.comment}"></li>
</ul>
</th:block>
And now I can move on.

LINQ to JSON - Querying an array

I need to select users that have a "3" in their json array.
{
"People":[
{
"id" : "123",
"firstName" : "Bill",
"lastName" : "Gates",
"roleIds" : {
"int" : ["3", "9", "1"]
}
},
{
"id" : "456",
"firstName" : "Steve",
"lastName" : "Jobs",
"roleIds" : {
"int" : ["3", "1"]
}
},
{
"id" : "789",
"firstName" : "Elon",
"lastName" : "Musk",
"roleIds" : {
"int" : ["3", "7"]
}
},
{
"id" : "012",
"firstName" : "Agatha",
"lastName" : "Christie",
"roleIds" : {
"int" : "2"
}
}
]}
In the end, my results should be Elon Musk & Steve Jobs. This is the code that I used (& other variations):
var roleIds = pplFeed["People"]["roleIds"].Children()["int"].Values<string>();
var resAnAssocInfo = pplFeed["People"]
.Where(p => p["roleIds"].Children()["int"].Values<string>().Contains("3"))
.Select(p => new
{
id = p["id"],
FName = p["firstName"],
LName = p["lastName"]
}).ToList();
I'm getting the following error:
"Accessed JArray values with invalid key value: "roleIds". Int32 array index expected"
I changed .Values<string>() to .Values<int>() and still no luck.
What am I doing wrong?
You are pretty close. Change your Where clause from this:
.Where(p => p["roleIds"].Children()["int"].Values<string>().Contains("3"))
to this:
.Where(p => p["roleIds"]["int"].Children().Contains("3"))
and you will get you the result you want (although there are actually three users in your sample data with a role id of "3", not two).
However, there's another issue that you might hit for which this code still won't work. You'll notice that for Agatha Christie, the value of int is not an array like the others, it is a simple string. If the value will sometimes be an array and sometimes not, then you need a where clause that can handle both. Something like this should work:
.Where(p => p["roleIds"]["int"].Children().Contains(roleId) ||
p["roleIds"]["int"].ToString() == roleId)
...where roleId is a string containing the id you are looking for.
Fiddle: https://dotnetfiddle.net/Zr1b6R
The problem is that not all objects follow the same interface. The last item in that list has a single string value in the roleIds.int property while all others has an array. You need to normalize that property and then do the check. It'll be easiest if they were all arrays.
You should be able to do this:
var roleId = "3";
var query =
from p in pplFeed["People"]
let roleIds = p.SelectToken("roleIds.int")
let normalized = roleIds.Type == JTokenType.Array ? roleIds : new JArray(roleIds)
where normalized.Values().Contains(roleId)
select new
{
id = p["id"],
FName = p["firstName"],
LName = p["lastName"],
};

how to use mongodb query option in monk nodejs

I have a collection name projects and I am trying to retrieve everything except its url like this query
db.projects.find({name:"arisha"},{url:0}).pretty()
This query is working perfectly and returning everything except url but my question is how to achieve this in
Node module for MongoDB name monk.
I am using this code but its not working and returning every field:
var projs = db.get('projects');
projs.find({creator : req.session.user._id},{url:0}, function (err,data) {
console.log(data);
if(!err) {
res.locals.projs = data;
console.log(data);
res.render("projects.ejs",{title: "Projects | Bridge"});
}
});
I did not get where the problem is, please help and thanks in advance :)
Sample document:
{
"name" : "arisha",
"date" : {
"day" : 18,
"month" : 4,
"year" : 2015
},
"creator" : "552edb6f8617322203701ad1",
"url" : "EyjPdYoW",
"members" : [
"552edb6f8617322203701ad1"
],
"_id" : ObjectId("5532994ba8ffdca31258bd1a")
}
To exclude the url field in monk, try the following syntax:
var db = require('monk')('localhost/mydb');
var projs = db.get('projects');
projs.find({ creator : req.session.user._id }, "-url", function (err, data) {
// exclude url field
});
EDIT:
To exclude multiple fields, use the following projection syntax:
projs.find({ creator : req.session.user._id }, { fields: { url: 0, creator: 0 } }, function(err, data) {
// exclude the fields url and creator
});
Alternatively (as you had discovered), you could also do:
projs.find({ creator : req.session.user._id }, "-url -creator", function (err, data) {
// exclude url and creator fields
});

Upsert Multiple Records with MongoDb

I'm trying to get MongoDB to upsert multiple records with the following query, ultimately using MongoMapper and the Mongo ruby driver.
db.foo.update({event_id: { $in: [1,2]}}, {$inc: {visit:1}}, true, true)
This works fine if all the records exist, but does not create new records for records that do not exist. The following command has the desired effect from the shell, but is probably not ideal from the ruby driver.
[1,2].forEach(function(id) {db.foo.update({event_id: id}, {$inc: {visit:1}}, true, true) });
I could loop through each id I want to insert from within ruby, but that would necessitate a trip to the database for each item. Is there a way to upsert multiple items from the ruby driver with only a single trip to the database? What's the best practice here? Using mongomapper and the ruby driver, is there a way to send multiple updates in a single batch, generating something like the following?
db.foo.update({event_id: 1}, {$inc: {visit:1}}, true); db.foo.update({event_id: 2}, {$inc: {visit:1}}, true);
Sample Data:
Desired data after command if two records exist.
{ "_id" : ObjectId("4d6babbac0d8bb8238d02099"), "event_id" : 1, "visit" : 11 }
{ "_id" : ObjectId("4d6baf56c0d8bb8238d0209a"), "event_id" : 2, "visit" : 2 }
Actual data after command if two records exist.
{ "_id" : ObjectId("4d6babbac0d8bb8238d02099"), "event_id" : 1, "visit" : 11 }
{ "_id" : ObjectId("4d6baf56c0d8bb8238d0209a"), "event_id" : 2, "visit" : 2 }
Desired data after command if only the record with event_id 1 exists.
{ "_id" : ObjectId("4d6babbac0d8bb8238d02099"), "event_id" : 1, "visit" : 2 }
{ "_id" : ObjectId("4d6baf56c0d8bb8238d0209a"), "event_id" : 2, "visit" : 1 }
Actual data after command if only the record with event_id 1 exists.
{ "_id" : ObjectId("4d6babbac0d8bb8238d02099"), "event_id" : 1, "visit" : 2 }
This - correctly - will not insert any records with event_id 1 or 2 if they do not already exist
db.foo.update({event_id: { $in: [1,2]}}, {$inc: {visit:1}}, true, true)
This is because the objNew part of the query (see http://www.mongodb.org/display/DOCS/Updating#Updating-UpsertswithModifiers) does not have a value for field event_id. As a result, you will need at least X+1 trips to the database, where X is the number of event_ids, to ensure that you insert a record if one does not exist for a particular event_id (the +1 comes from the query above, which increases the visits counter for existing records). To say it in a different way, how does MongoDB know you want to use value 2 for the event_id and not 1? And why not 6?
W.r.t. batch insertion with ruby, I think it is possible as the following link suggests - although I've only used the Java driver: Batch insert/update using Mongoid?
What you are after is the Find and Modify command with the upsert option set to true. See the example from the Mongo test suite (same one linked to in the Find and Modify docs) for an example that looks very much like what you describe in your question.
I found a way to do this using the eval operator for server-side code execution. Here is the code snippit:
def batchpush(body, item_opts = {})
#batch << {
:body => body,
:duplicate_key => item_opts[:duplicate_key] || Mongo::Dequeue.generate_duplicate_key(body),
:priority => item_opts[:priority] || #config[:default_priority]
}
end
def batchprocess()
js = %Q|
function(batch) {
var nowutc = new Date();
var ret = [];
for(i in batch){
e = batch[i];
//ret.push(e);
var query = {
'duplicate_key': e.duplicate_key,
'complete': false,
'locked_at': null
};
var object = {
'$set': {
'body': e.body,
'inserted_at': nowutc,
'complete': false,
'locked_till': null,
'completed_at': null,
'priority': e.priority,
'duplicate_key': e.duplicate_key,
'completecount': 0
},
'$inc': {'count': 1}
};
db.#{collection.name}.update(query, object, true);
}
return ret;
}
|
cmd = BSON::OrderedHash.new
cmd['$eval'] = js
cmd['args'] = [#batch]
cmd['nolock'] = true
result = collection.db.command(cmd)
#batch.clear
#pp result
end
Multiple items are added with batchpush(), and then batchprocess() is called. The data is sent as an array, and the commands are all executed. This code is used in the MongoDequeue GEM, in this file.
Only one request is made, and all the upserts happen server-side.

Resources