C# mongoDB wont update array in database - c#

I want to update array in my mongoDB collection. Posting new document works just fine, but it won't update array. Structure of my document is:
var document = new BsonDocument
{
{ "post_title", model.Title },
{ "post_text", model.Text },
{ "post_author", model.Author },
{ "post_picture", model.Picture },
{ "post_comments", new BsonArray() },
};
And my update function is:
[HttpPost]
[Route("api/PostInfo/{Comment}")]
public async Task Post(Comment comment)
{
try {
BsonObjectId oldId = new BsonObjectId(new ObjectId(comment.id.ToString()));
var mongoDbClient = new MongoClient("mongodb://127.0.0.1:27017");
var mongoDbServer = mongoDbClient.GetDatabase("nmbp");
var collection = mongoDbServer.GetCollection<PostInfo>("post");
var filter = Builders<PostInfo>.Filter.Eq(e => e._id, oldId);
var update = Builders<PostInfo>.Update.Push("post_comments", comment.comment);
await collection.FindOneAndUpdateAsync(filter, update);
var test = oldId.GetType();
}
catch
{
}
}
When debugging, i can see that post controller is triggered, and comment values are correct, but when I take a look in database, value of "post_comments" array is empty. No error is thrown in catch block. Am I doing something wrong?

It looks like the problem was in this line:
var filter = Builders<PostInfo>.Filter.Eq(e => e._id, oldId);
The correct one should look like this:
var filter = Builders<PostInfo>.Filter.Eq("_id", oldId);

Related

How to "patch" a Document in DocumentDB using Mongo commands

My code is below:
MyModel requestID = new MyModel
{
"id" = "123"
};
MyModel toUpdate = new MyModel
{
"is_cancelled" : true
};
FilterDefinition<MyModel> filter = requestID.ToBsonDocument();
UpdateDefinition<MyModel> update = toUpdate.ToBsonDocument();
collection.FindOneAndUpdate(filter, update);
My 1 Document in DocumentDB for example is:
{
"id": "123",
"delnum": "100001"
}
My toUpdate is:
{
"is_cancelled" : true
}
My expected document after FindOneAndUpdate is:
{
"id": "123",
"delnum": "100001",
"is_cancelled" : true
}
but what is happening is, it is replacing my document with id=123 to below:
{
"is_cancelled" : true
}
I would like to know if I am doing it wrong, or what my expectation outcome is not correct.
Edited Code:
MyModel requestID = new MyModel
{
"id" = "123"
};
MyModel toUpdate = new MyModel
{
"is_cancelled" : true
};
var builder = Builders<MyModel>.Update;
UpdateDefinition<MyModel> update =null;
toUpdate.GetType().GetProperties().ToList().ForEach(
x => update = builder.Set(x.Name, x.GetValue(toUpdate, null))
FilterDefinition<MyModel> filter = requestID.ToBsonDocument();
if (update == null) return;
collection.FindOneAndUpdate(filter, update);
EDIT:
code below what worked for me.
var filterData = Builders<MyModel>.Filter.Eq("id", "123"); //requestID
var updateData = new BsonDocumentUpdateDefinition<MyModel>(new BsonDocument("$set", toUpdate));
await collection.FindOneAndUpdateAsync(filter, updateData , new FindOneAndUpdateOptions<MyModel>() { IsUpsert = false });
You're not specifying what you want to update.
Mongo expects json to look like this:
{
"$set" : {
"is_cancelled" : true
}
}
You can use the update builder to make that easier:
var builder = Builders<MyModel>.Update;
var update = builder.Set("is_cancelled", true);
You can also chain multiple Set methods on the builder:
var update = builder.Set("a", 1).Set("b", 2);

Aggregate function issue with latest version of MongoC# Driver

I have .NET Application that uses MongoDB. Current driver I am using is 1.9.2. I am trying to upgrade it to 2.7.0.
I am having some difficulty in getting an Aggregate query to work in the new version:
The working code in version 1.9.2 of the driver is:
public IEnumerable<Car> GetCarsModifiedInPeriod(DateTimeOffset dateFrom, DateTimeOffset dateTo)
{
var matchRequestFromDate = new BsonDocument
{
{
"$match",
new BsonDocument
{
{
// Filter out those too recently modified
"LastUpdatedOn.0", new BsonDocument {{"$gte", dateFrom.Ticks}}
}
}
}
};
var matchRequestToDate = new BsonDocument
{
{
"$match",
new BsonDocument
{
{
// Filter out those too recently modified
"LastUpdatedOn.0", new BsonDocument {{"$lte", dateTo.Ticks}}
}
}
}
};
var cars = collection.Aggregate(new AggregateArgs
{
Pipeline = new[] { matchRequestFromDate, matchRequestToDate},
AllowDiskUse = true,
// Setting the OutputMode to Cursor allows us to return Mongo Doc Size > 16 MB - in the case when a large date
// range is used or a large number of cars were modified in a short period of time
OutputMode = AggregateOutputMode.Cursor
}).Select(r => r.Values.Select(c => c.AsObjectId.ToString()).First());
var returnData = collection.AsQueryable().Where(c => cars.Contains(c.Id)).Select(c => c);
return returnData;
}
With a breakpoint set on returnData for the two periods specified I am getting a count of 25 cars which is what I expect.
This is how I have attempted to re-write for 2.7.0 version of driver:
public IEnumerable<Car> GetCarsModifiedInPeriod(DateTimeOffset dateFrom, DateTimeOffset dateTo)
{
var matchRequestFromDate = new BsonDocument
{
{
"$match",
new BsonDocument
{
{
// Filter out those too recently modified
"LastUpdatedOn.0", new BsonDocument {{"$gte", dateFrom.Ticks}}
}
}
}
};
var matchRequestToDate = new BsonDocument
{
{
"$match",
new BsonDocument
{
{
// Filter out those too recently modified
"LastUpdatedOn.0", new BsonDocument {{"$lte", dateTo.Ticks}}
}
}
}
};
var pipeline = new[] {matchRequestFromDate, matchRequestToDate};
//var mongoPipeline = new AggregateArgs { Pipeline = pipeline, AllowDiskUse = true, OutputMode = AggregateOutputMode.Cursor };
var aggregate = collection.Aggregate(); //.Match(mongoPipeline);
aggregate.Options.AllowDiskUse = true;
aggregate.Options.UseCursor = true;
foreach (var pipe in pipeline)
{
aggregate.AppendStage<BsonDocument>(pipe);
}
var returnData = aggregate.ToList();
return returnData;
}
If I set a breakpoint in returnData in this method I am getting a count of around 10K cars so it doesnt look like I am correctly applying the same matches
Is there a reason you are doing everything in BsonDocuments? There are methods that would make your life a lot easier, for example something like this.
collection.Aggregate(new AggregateOptions() { AllowDiskUse = true, UseCursor = true })
.Match(Builders<BsonDocument>.Filter.Gte("LastUpdatedOn.0", dateFrom.Ticks) & Builders<BsonDocument>.Filter.Lte("LastUpdatedOn.0", dateFrom.Ticks))
.ToListAsync()
You could tidy the filtering up more as well by using the right class for the collection and the builders.
Looking at the query, I'm not sure you even need to be using an aggregate unless you are doing more than a match. It could simply be a find.

MongoDB Aggregate function in C#

I am trying to display/list data after using aggregation function but it isn't happening.
This code works absolutely fine.
var connectionstring = "mongodb://localhost:27017";
var client = new MongoClient(connectionstring);
var db = client.GetDatabase("school");
var col = db.GetCollection<BsonDocument>("students");
var filter = new BsonDocument("type", "homework");
var filter2 = Builders<BsonDocument>.Filter.Eq("scores.type", "homework");
var myresults = await col.Find(filter2)
.Limit(2)
.Project("{name:1,scores:1,_id:0}")
.Sort("{score:1}")
.ToListAsync();
foreach (var result in myresults)
{
Console.WriteLine(result);
}
This code fetches document as it should however when I replace
var myresults = await col.Find(filter2)
.Limit(2)
.Project("{name:1,scores:1,_id:0}")
.Sort("{score:1}")
.ToListAsync();
with this
var myresults = await col.Aggregate()
.Unwind("{$scores}")
.Group(new BsonDocument { { "_id", "$_id" }, { "lowscore", new BsonDocument("$min", "$scores.score") } })
//.Group("{_id:'$_id',lowscore:{$min:'$scores.score'}}")
.ToListAsync();
No record is being pulled.
I do not want to use Pipeline method. I simply want to display the result obtained via aggregate function.
This is my Mongo Query (I want the same result as this in C#)-
db.students.aggregate([{$sort:{_id:-1}},{$unwind:"$scores"},{$group:{_id:"$_id", lowscore:{"$min":"$scores.score"}}}])
Building aggregation pipeline is bit tricky.
Try:
var pipeline = new BsonDocument[] {
new BsonDocument{ { "$sort", new BsonDocument("_id", 1) }},
new BsonDocument{{"$unwind", "$scores"}},
new BsonDocument{{"$group", new BsonDocument{
{"_id", "$_id"},
{"lowscore",new BsonDocument{
{"$min","$scores.score"}}
}}
}}
};
var result = collection.Aggregate<BsonDocument> (pipeline).ToListAsync();
If you do pipeline.ToJson(), you'll get following JSON equivalent string which is same as of your original and tested MongoShell query.
[
{
"$sort": {
"_id": 1
}
},
{
"$unwind": "$scores"
},
{
"$group": {
"_id": "$_id",
"lowscore": {
"$min": "$scores.score"
}
}
}
]
This is wrong... {$scores} isn't even valid json. Remove the curly braces and the dollar sign from the $unwind directive.
The parameter name is field, so you need to provide a field name to it.
Try with writing only $score instead of #scores.score. may be it helpful.
db.students.aggregate([{$sort:{_id:-1}},{$unwind:"$scores"},{$group:{_id:"$_id", lowscore:{"$min":"$score"}}}])

RavenDB Collection "in" Collection query

I need to preform a query that check if a collection is in given collection, just like the regular in operation but for collections.
class Post
{
public string[] Tags {get;set;}
}
session.Queury<Post>.Where(x=>x.Tags.in(new[]{".net","c#","RavenDB"})).ToList();
so if i have in my DB:
new Post{Tags= new[]{"C#",".net"}};
it will be returned
but if i have:
new Post{Tags= new[]{"C#",".net","SQLServer"}};
it will not be returned.
Update:
what i am trying to do is this:
session.Query<Post>()
.Where(x => x.Tags.All(y => y.In(new[] { "C#", ".net", "RavenDB" })))
.ToList();
but i got System.NotSupportedException.
I manage to find a solution:
static void Main(string[] args)
{
var sessionStore = new EmbeddableDocumentStore
{
RunInMemory = true,
UseEmbeddedHttpServer = true,
Conventions =
{
DefaultQueryingConsistency = ConsistencyOptions.AlwaysWaitForNonStaleResultsAsOfLastWrite
}
};
sessionStore.Initialize();
using (var session = sessionStore.OpenSession())
{
var allTags = new[] {"C#", ".net", "RavenDB", "Linux", "Mac"};
var tagsCollection = new[] {"C#", ".net", "RavenDB"};
var complementTagsCollection = allTags.Except(tagsCollection).ToList();
session.Store(new Post
{
Tags = new List<string>{"C#",".net"}
});
session.SaveChanges();
// Posts where all their tags are in tagsCollection
var result = session.Query<Post>().Where(x => !x.Tags.In(complementTagsCollection)).ToList();
}
}
The way IN works, it matches ANY of them.
If you want to match all you have to do a separate check for each.

Elastic search with Nest

I am working on below code, and what I want to do is query by object itself.
For example: I have a search form, that populates objects fields such as below. Then what I want to do is to search Elastic search based on whatever user filled the form with.
ie: below, I want to query the index by searchItem object. How can I do it easily?
class Program
{
static void Main(string[] args)
{
var p = new Program();
var item1 = new Announcement() {Id=1, Title = "john", ContentText = "lorem", Bar = false, Num = 99, Foo = "hellow"};
//p.Index(item1, "add");
var searchItem = new Announcement() {Title="john",Num=99};
ElasticClient.Search<Announcement>();
Console.Read();
}
public void Index(Announcement announcement, String operation)
{
var uriString = "http://localhost:9200";
var searchBoxUri = new Uri(uriString);
var settings = new ConnectionSettings(searchBoxUri);
settings.SetDefaultIndex("test");
var client = new ElasticClient(settings);
if (operation.Equals("delete"))
{
client.DeleteById("test", "announcement", announcement.Id);
}
else
{
client.Index(announcement, "test", "announcement", announcement.Id);
}
}
private static ElasticClient ElasticClient
{
get
{
try
{
var uriString = "http://localhost:9200";
var searchBoxUri = new Uri(uriString);
var settings = new ConnectionSettings(searchBoxUri);
settings.SetDefaultIndex("test");
return new ElasticClient(settings);
}
catch (Exception)
{
throw;
}
}
}
}
You can't :)
NEST cannot infer how to best query only based on a partially filled POCO. Should it OR or AND should it do a nested term query or a term query wrapped in a has_child? You catch my drift.
Nest does have a slick feature called conditionless queries that allow you the write out to entire query like so:
ElasticClient.Search<Announcement>(s=>s
.Query(q=>
q.Term(p=>p.Title, searchItem.Title)
&& q.Term(p=>p.Num, searchItem.Num)
//Many more queries use () to group all you want
)
)
When NEST sees that the argument passed to Term is null or empty it simply wont render that part of the query.
Read more here on how this feature works http://nest.azurewebsites.net/concepts/writing-queries.html

Categories