MongoDb C# Driver 2.0 add item to nested array - mongodb

I have a Profiles document collection with array of the following documents :
public class Profile2MailList
{
[BsonElement(elementName: "listId")]
[BsonRequired]
public int MailListId;
[BsonElement(elementName: "status")]
[BsonRequired]
public int Status;
[BsonElement(elementName: "subscriptionDate")]
[BsonRequired]
public DateTime SubscriptionDate;
}
in each Profile.
I need to add to the Profile2MailList array a new Profile2MailList document in each Profile based on Profile2MailList which already contains in a certain Profile. So i need to
Take needed profiles from Profiles collection
Update
Profile2Maillist array in each Profile
Run update command
How can i perform that action via C# 2.0 MongoDb Driver. I have MongoDb v 3.0.2.
I try to make it by the following way :
List<Profile> listProfiles = new List<Profile>();
foreach (Profile item in profiles)
{
item.MailLists.ToList().Add(new Profile2MailList(maillistId, item.MailLists.FirstOrDefault().Status));
var t = item;
listProfiles.Add(t);
}
dbCollection.UpdateManyAsync(listProfiles)

The method "UpdateManyAsync" only works if you want to perform one type of update, which doesn't seem to be your case. What you want to do is perform a Bulk-Update instead. Building on your example, you would want to do something like:
var bulk = new List<WriteModel<Profile>>();
foreach (Profile item in profiles)
{
var newProfile = new Profile2MailList(maillistId, item.MailLists.FirstOrDefault().Status);
var filter = Builders<Profile>.Filter.Eq(g => g.Id, item.Id);
var update = Builders<Profile>.Update.AddToSet(item.MailLists, newProfile);
var updatemodel = new UpdateOneModel<Profile>(filter, update);
bulk.Add(updatemodel);
}
await profileCollection.BulkWriteAsync(bulk);
The AddToSet operator adds a value to an array unless the value is already present.

Related

Looping Over a Java Object and Passing One Field to a Mongo Query Similar to SQL WHERE...IN Clause

I am trying to construct a MongoDB equivalent of an SQL WHERE IN clause by iterating over a Java Object List and using one field in that list to fill in the IN data. Using generic example, I would like to build the following command in MongoDB syntax:
SELECT title FROM albums WHERE recorded_year IN ('1967','1968','1969','1970');
The SQL command IN data is extracted from the Album object recordedYear value using the following loop:
if (albums.size() <= 1000) {
sb.append("SELECT title FROM albums WHERE recorded_year");
sb.append(" IN (");
for (int i = 0; i < albums.size(); i++) {
sb.append("'");
sb.append(albums.get(i).getRecordedYear());
sb.append("'");
if (i < albums.size() - 1) {
sb.append(",");
} else {
sb.append(")");
}
}
}
Most Mongo/Java sites I visited seem to deal with command structures using hard-coded values which, while helpful, is completely impractical in real world applications. Any help in pointing to a good tutorial, or if someone has the actual code itself would be greatly appreciated.
Thanks.
But the issue I am having is understanding how to pass a Java Object
list to the to the getAllDocuments...
Make an array of elements which you want to match with the field using the in operator. For example, If you have a someObject.year field, then the array will have the year values; int [] matchYears = { 1989, 2001, 2012 }. Instead of an array you can also use a List collection.
The query:
Bson queryFilter = in("recordedYear", matchYears);
List<Document> result = new ArrayList<>();
collection.find(queryFilter).into(result);
result.forEach(System.out::println);
The queryFilter is built using the com.mongodb.client.model.Filters factory class.
The MongoDB documentation on using the $in operator.
NOTE: The int [] matchYears = { 1989, 2001, 2012 } can be also be created as
int [] matchYears = { javaObject1.recorded_year, javaObject2.recorded_year, ... }.
I didn't quite implement it with BSON, but the logic in the method appears to be working with the code below. Thank you again for your help.
public void getAlbumYears(MongoCollection<Document> collection, List<Album> albums) {
BasicDBObject inQuery = new BasicDBObject();
List<String> year = new ArrayList<>();
for(Album album : albums) {
year.add(album.getYear());
}
inQuery.put("year", new BasicDBObject("$in", year));
for (Document document : collection.find(inQuery)) {
System.out.println(document.toJson());
}
}

MongoDB : How to find multiple documents and update at the same time?

I have mongo DB and I am using C#.Net to interact with mongo db. C# API has methods for finding a single document and updating it at the same time. For example FindOneAndUpdateAsync.
However I couldn't find any method to find multiple documents and update them at the same time asynchronously.
The code below finding and processing each document asynchronously. How do I also update that document at the same time?
public async Task<IList<IDictionary<string, string>>> DoWork()
{
var collection = _mongoDatabase.GetCollection<BsonDocument>("units");
var filterBuilder = Builders<BsonDocument>.Filter;
var filter = filterBuilder.Ne<string>("status", "INIT") &
(filterBuilder.Exists("isDone", false) |
filterBuilder.Eq<bool>("isDone", false));
// I want to pass this update filter to update the document. But not sure how
var update = Builders<BsonDocument>.Update
.CurrentDate("startTime");
var sort = Builders<BsonDocument>.Sort.Ascending("startTime");
var projection = Builders<BsonDocument>.Projection
.Include("_id")
.Include("fileName"); // for bravity i have removed other projection elements
var output = new List<IDictionary<string, string>>();
// How do i pass update filter and update the document at the same time??
await collection
.Find(filter)
.Sort(sort)
.Project(projection)
.ForEachAsync((unit) =>
{
var dictionary = new Dictionary<string, string>();
Recurse(unit, dictionary);
output.Add(dictionary);
});
return output.Count > 0 ? output : null;
}
That doesn't exist in the mongo .Net api see here.
Just use a combination of Find and UpdateManyAsync.

MongoDB C# Remove doesn't work

i have this code for removing an item froma a mongofb collation
private MongoCollection<T> GetCollection()
{
connectionString = "mongodb://localhost/?safe=true";
server = MongoServer.Create(connectionString);
database = server.GetDatabase("CSCatalog");
return database.GetCollection<T>("myCollectionName");
}
public bool Delete(T entity)
{
var id = typeof(T).GetProperty("Id").GetValue(entity,null).ToString();
var query = Query.EQ("_id",id);
var finded = GetCollection().Find(query); // return null
var result= GetCollection().Remove(query, MongoDB.Driver.RemoveFlags.Single); // no errors, but don't remove
return esito.Ok; //return true but donn't remove.
}
the GetCollection() method retrive the right collection, i have tested it width debug.
In the collection there is the item that i want remove, it have the same id that i have retrived in first line.
the entity have some fields and a Objectid filed called "Id"
the type of _id you created is ObjectId class and you are trying to equate with string so its not able to remove. use
var queryId = new ObjectId(id);
Your finded variable should not be null if the .find() has returned something from your database. That it is null means that you have not found anything, and therefore nothing is to be removed.
What it looks like is happening here is that you are querying on _id for the ObjectId, while you are storing that ObjectId in the database as Id.

MongoDb: how to return distinct field in select (find) with C# official driver

I need to select User Name from the collection of Users. I do it in a such way:
MongoCollection<Enums> coll = Db.GetCollection<Enums>("Users");
var query = Query.EQ("_id", id);
var res = coll.FindOne(query);
var name = res.Name;
var url = res.UserUrl; //or some more fields, not just Name
Assuming that User document can contain a lot of data, and there is no need to transfer the whole user document, how to select only a few distinct fields, using official C# driver?
You'll have to use a function that returns a MongoCursor.
In the MongoCursor you can specify the fields you want to return.
var result = Db.GetCollection<Enums>("Users").FindAll();
result.Fields = Fields.Include(new [] {"Name"});;
foreach (var user in result)
{
Console.WriteLine(user.Name);
}

How do I get the date a MongoDB collection was created using MongoDB C# driver?

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")