I've been trying to Sort a collection using MongoDB SortDefinition but whenever I "sort" the collection via a single sort definition, all I get returned is an empty list. However, when I use more than one sort definitions, it returns values.
var TestSort1 = Builders<Scenario>.Sort.Ascending("Name");
var filtered1 = await _context
.DbCollection
.Find(_ => true)
.Sort(TestSort1)
.ToListAsync();
The code above returns an empty list. However, the code below works fine.
var TestSort2 = Builders<Scenario>.Sort.Ascending("Name").Ascending("Owner");
var filtered2 = await _context
.DbCollection
.Find(_ => true)
.Sort(TestSort2)
.ToListAsync();
Is it possible to use a single SortDefinition to sort the collection? Or maybe I am using the SortDefinition wrong?
Maybe you should try using the fluent C# syntax for creating aggregation pipelines...
var collection = database.GetCollection<FiltroCond>("dbCENTRAL");
var filter = Builders<FiltroCond>.Filter.Eq(x => x.PartnerId, cliente)
& Builders<FiltroCond>.Filter.Eq(x => x.TP_PESSOA, 3)
& Builders<FiltroCond>.Filter.Gte(x => x.FG_ATIVO, true);
var result = collection.Aggregate().Match(filter)
.Project(p => new FiltroCond { CD_CLIENTE = p.CD_CLIENTE, ID_CENTRAL = p.ID_CENTRAL, FANTASIA = p.FANTASIA })
.SortBy(p => p.ID_CENTRAL).ToList();
It works fine to me.
Related
I want to run multiple upsert queries using bulkWrite(). But the problem is, bulkWrite only returns inserted _ids not updated _ids. How can I get all
_ids in this query?
var insertCommand = Builders<myObject>.Update.SetOnInsert(d => d.Name, object.Name).SetOnInsert(d => d.Age, object.Age);
var updateCommand = Builders<myObject>.Update.Set(d => d.LastRequest, object.DateTime);
var updateQueries = Builders<myObject>.Update.Combine(new[] { updateCommand, insertCommand });
var filter = Builders<myObject>.Filter;
var findQuery = filter.And(filter.Eq(e => e.Name, object.Name), filter.Eq(e => e.City, object.City));
var upsertObject = new UpdateOneModel<myObject>(findQuery, updateQueries) { IsUpsert = true };
bulkOps.Add(upsertObject);
var result = await Collection.BulkWriteAsync(bulkOps);
Using BulkWriteAsync, I don't think this can be done. The documentation on the MongoDB bulkWrite operation states the following:
Returns:
A boolean acknowledged as true if the operation ran with
write concern or false if write concern was disabled.
A count for each write operation.
An array containing an _id for each successfully
inserted or upserted documents.
What works, however, for a single instance is this:
var result = collection.FindOneAndUpdate(findQuery, updateQueries, new FindOneAndUpdateOptions<myObject> { IsUpsert = true, ReturnDocument = ReturnDocument.After});
After running this, result will hold the modified document including its _id.
Here what I am trying to do is to convert List of Bson Document to the list of Class. The main purpose of doing so is to filter the data from the list of Json data. I am using MongoDb for my database and C# for handling data. I have stored some data in MongoDb and trying to filter the data using FilterDefinition like this:
PROCESS 1
FileContext context = new FileContext();
var collection = context.Db.GetCollection<BsonDocument>("DBCollection");
var builder = Builders<BsonDocument>.Filter;
FilterDefinition<BsonDocument> Filter = builder.Eq("FileName", fileDetail.FileName) & builder.Eq("FileNumber", fileDetail.FileNumber.ToString());
var list = await collection.Find(Filter).ToListAsync();
In this way I am not been able to get the data in list. Therefore, I tried the other way like this:
PROCESS 2
FileContext context = new FileContext();
var collection = context.Db.GetCollection<BsonDocument>("DBCollection");
var list = await collection.Find(_ => true).ToListAsync();
List<MyClass> objList = null;
foreach (var item in objList)
{
objList = new List<MyClass>();
objList.Add(BsonSerializer.Deserialize<MyClass>(item));
}
if(objList.Count >0)
{
int count = objList.Where(x => x.FileName == "SOME_FILE_NAME" && x.FileNumber == 1).Count();
}
This way I am able to get the data and able to filter data as well but I know this is not good programming at all as this will have lots of waiting time if the size of data increases in future.
Here my questions are:
What is wrong in PROCESS 1?
How to Convert List<BsonDocument> to List<MyClass>?
How to filter List<BsonDocument>?
var collection = context.Db.GetCollection<BsonDocument>("DBCollection");
should be:
var collection = context.Db.GetCollection<MyClass>("DBCollection");
That way you should be able to use the Find method to check your FileName and FileNumber using something like:
var objList = await collection.Find(x => x.FileName == "FILENAMEHERE" && x.FileNumber == 1).ToListAsync();
I'm currently upgrading my code to MongoDB C# driver 2.0 and I'm having issues upgrading the code to update documents.
using the old version I was able to do something like this:
MyType myObject; // passed in
var collection = _database.GetCollection<MyType>("myTypes");
var result = collection.Save(myObject);
I'm struggling to find a way to do this in the new version.
I have found a few examples of updating single fields like
var filter = Builders<MyType>.Filter.Eq(s => s.Id, id);
var update = Builders<MyType>.Update.Set(s => s.Description, description);
var result = await collection.UpdateOneAsync(filter, update);
I'd like to update all the fields as I was doing in the old version with the method Save.
Any ideas ?
Thanks a lot
I think you're looking for ReplaceOneAsync():
MyType myObject; // passed in
var filter = Builders<MyType>.Filter.Eq(s => s.Id, id);
var result = await collection.ReplaceOneAsync(filter, myObject)
To add to mnemosyn's answer, while a simple ReplaceOneAsync does update a document it isn't equivalent to Save as Save would also insert the document if it didn't find one to update.
To achieve the same behavior with ReplaceOneAsync you need to use the options parameter:
MyType myObject;
var result = await collection.ReplaceOneAsync(
item => item.Id == id,
myObject,
new UpdateOptions {IsUpsert = true});
you can use LINQ as following:
await context.collection.ReplaceOneAsync(b=> b.Id == item.Id,item);
use ObjectId.Parse(id)
var filter = Builders<MyType>.Filter.Eq(s => s.Id, ObjectId.Parse(id));
var update = Builders<MyType>.Update.Set(s => s.Description, description);
var result = await collection.UpdateOneAsync(filter, update);
I am not able to use Contains-filter to expanded table:
var customersData = await myClient.For<Customer>()
.Filter(t => t.name.Contains(searchCriteria) || t.PersonTable.ContactNumber.Contains(searchCriteria))
.Expand(t => new { t.PersonTable, t.AddressesTable })
.FindEntriesAsync();
I tried using new version 4.12 like below also:
var filter = new ODataExpression<Customer>(t => t.name.Contains(searchCriteria));
filter = filter || new ODataExpression<PersonTable>(x => x.ContactNumber.Contains(searchCriteria));
var result3 = await myClient.For<Customer>().Filter(filter).FindEntriesAsync();
The filter generic type is different from the type you use in For clause, i.e. you query on Customer table and send a filter on PersonTable. The library should probably detect this and throw an exception. Anyway this won't work.
EDIT:
Not looking for the javascript way of doing this. I am looking for the MongoDB C# 2.0 driver way of doing this (I know it might not be possible; but I hope somebody knows a solution).
I am trying to update the value of an item embedded in an array on the primary document in my mongodb.
I am looking for a strongly typed way to do this. I am using the Mongodb c# 2.0 driver
I can do it by popping the element, updating the value, then reinserting. This just doesn't feel right; since I am overwriting what might have been written in the meantime.
Here is what I have tried so far but with no luck:
private readonly IMongoCollection<TempAgenda> _collection;
void Main()
{
var collectionName = "Agenda";
var client = new MongoClient("mongodb://localhost:27017");
var db = client.GetDatabase("Test");
_collection = db.GetCollection<TempAgenda>(collectionName);
UpdateItemTitle(1, 1, "hello");
}
public void UpdateItemTitle(string agendaId, string itemId, string title){
var filter = Builders<TempAgenda>.Filter.Eq(x => x.AgendaId, agendaId);
var update = Builders<TempAgenda>.Update.Set(x => x.Items.Single(p => p.Id.Equals(itemId)).Title, title);
var result = _collection.UpdateOneAsync(filter, update).Result;
}
Took me a while to figure this out as it doesn't appear to be mentioned in any of the official documentation (or anywhere else). I did however find this on their issue tracker, which explains how to use the positional operator $ with the C# 2.0 driver.
This should do what you want:
public void UpdateItemTitle(string agendaId, string itemId, string title){
var filter = Builders<TempAgenda>.Filter.Where(x => x.AgendaId == agendaId && x.Items.Any(i => i.Id == itemId));
var update = Builders<TempAgenda>.Update.Set(x => x.Items[-1].Title, title);
var result = _collection.UpdateOneAsync(filter, update).Result;
}
Notice that your Item.Single() clause has been changed to Item.Any() and moved to the filter definition.
[-1] or .ElementAt(-1) is apparently treated specially (actually everything < 0) and will be replaced with the positional operator $.
The above will be translated to this query:
db.Agenda.update({ AgendaId: 1, Items.Id: 1 }, { $set: { Items.$.Title: "hello" } })
Thanks, this was helpful. I have an addition though, I've used the above for arrays, pushing to a nested array and pulling from one. The issue I have found is that if I had an int array (So not an object, just a simple int array) that the PullFilter didn't actually work - "Unable to determine the serialization information" which is strange as it's only an array of ints. What I ended up doing was making it an array of objects with only one int parameter, and it all started to work. Possibly a bug, or perhaps my lack of understanding. Anyway, as I've struggled to find information about pulling and pushing to nested object arrays with the C# 2.0 driver, I thought I should post my findings here, as they use the above syntax.
var filter = Builders<MessageDto>.Filter.Where(x => x._id == entity.ParentID && x.NestedArray.Any(i => i._id == entity._id));
var update = Builders<MessageDto>.Update.PullFilter(x => x.NestedArray.ElementAt(-1).User, Builders<User>.Filter.Eq(f => f.UserID, userID));
Collection<MessageDto>(currentUser).UpdateOneAsync(filter, update);
And also:
var filter = Builders<MessageDto>.Filter.Where(x => x._id == entity.ParentID && x.NestedArray.Any(i => i._id == entity._id));
var update = Builders<MessageDto>.Update.Push(x => x.NestedArray.ElementAt(-1).Users, new User { UserID = userID });
Collection<MessageDto>(currentUser).UpdateOneAsync(filter, update);
The correct way to update a Document or sub array is as follows:
var filter = Builders<Declaracion>.Filter.Where(x => x.Id == di && x.RemuneracionMensualActual.RemuneracionActIndustrial.Any(s => s.Id == oid));
var update = Builders<Declaracion>.Update.Set(x => x.RemuneracionMensualActual.RemuneracionActIndustrial.ElementAt(-1).Ingreso, datos.ActividadIndustrial.Ingreso)
.Set(x => x.RemuneracionMensualActual.RemuneracionActIndustrial.ElementAt(-1).RazonSocial, datos.ActividadIndustrial.RazonSocial)
.Set(x => x.RemuneracionMensualActual.RemuneracionActIndustrial.ElementAt(-1).TipoNegocio, datos.ActividadIndustrial.TipoNegocio);