I am currently running into an issue where I am unable to replace a Mongo document thats supposed to be updated with the following function:
public async void updateSelectedDocument(BsonDocument document)
{
var collection = mongoClient.GetDatabase("db").GetCollection<BsonDocument>("collection");
var id = document.GetElement("_id").ToString().Remove(0, 4);
var filter = Builders<BsonDocument>.Filter.Eq("_id", id);
var result = await collection.ReplaceOneAsync(filter, document, new UpdateOptions { IsUpsert = true });
}
There is no error to go off of and none of the variables are null. My connection to the mongo instance and collection works because I can perform a find and an insert. Please Let me know if you need additional information.
Related
We use mongodb as a database. In local development, the official image, but in production a Cosmos DB with Mongo API.
We use change streams to watch for changes in the database.
After we process the created or updated record, we need to make changes to the record again (set flags).
Maybe you guess it, this leads to an infinite loop.
I first designed a solution that works locally with the official mongo image. When I set the flags, I also set a Guid #changeId in the dataset and create a filter that checks if the #changeId has changed during an update, if so, the process is not triggered.
I do it like this:
public async Task Watch(CancellationToken token)
{
logger.LogInformation("Watching database for changes...");
token.Register(() => this.logger.LogInformation("Stop Polling. Stop requested."));
var filter = Builders<ChangeStreamDocument<BsonDocument>>
.Filter.Where(change =>
change.OperationType == ChangeStreamOperationType.Insert
|| change.OperationType == ChangeStreamOperationType.Update
|| change.OperationType == ChangeStreamOperationType.Replace
);
var updateExistsFilter = Builders<ChangeStreamDocument<BsonDocument>>.Filter.Exists("updateDescription.updatedFields");
var changeIdNotChangedFilter = Builders<ChangeStreamDocument<BsonDocument>>.Filter.Not(
Builders<ChangeStreamDocument<BsonDocument>>.Filter.Exists("updateDescription.updatedFields.#changeId")
);
var relevantUpdateFilter = updateExistsFilter & changeIdNotChangedFilter;
//if no updateDescription exists, its an insert so data should be processed anyway
var insertFilter = Builders<ChangeStreamDocument<BsonDocument>>.Filter.Not(
Builders<ChangeStreamDocument<BsonDocument>>.Filter.Exists("updateDescription.updatedFields")
);
filter &= relevantUpdateFilter | insertFilter;
var definition = new EmptyPipelineDefinition<ChangeStreamDocument<BsonDocument>>()
.Match(filter)
.AppendStage<ChangeStreamDocument<BsonDocument>, ChangeStreamDocument<BsonDocument>, BsonDocument>(
"{ $project: { '_id': 1, 'fullDocument': 1, 'ns': 1, 'documentKey': 1 }}"
);
var options = new ChangeStreamOptions
{
FullDocument = ChangeStreamFullDocumentOption.UpdateLookup
};
while (!token.IsCancellationRequested)
{
using (var cursor = await base.collection.WatchAsync(definition, options, token))
{
await cursor.ForEachAsync(async (doc) =>
{
ProcessData(doc);
}, token);
}
await Task.Delay(TimeSpan.FromSeconds(1), token);
}
}
Unfortunately, it wasn't until testing on QA with a Cosmos DB attached that I noticed Cosmos doesn't support UpdateDescription. see here
It goes to an infinity loop.
Is there a way to get the updated fields?
Or maybe is it possible to make sure that the changestream does not react when using the update function of the mongo driver?
Or is there any way to avoid this infinity loop?
Im trying to update a string field in specific document using Firebase Firestore for my android app but every method I see is while knowing the document refernce which I find difficult to find in my program.
Would like for some help for another method or help in finding the document refernce using a specific field value.
Thanks in advance.
(using C# btw)
private async Task<string> GetDocRefAsync(string userId)
{
Object obj = await FirestoreData.GetFirestore().Collection(DBConstants.FS_COLLECTION_USERS).
WhereEqualTo(DBConstants.FS_COLLECTION_USERS_USER_ID, userId).Get();
QuerySnapshot snapshot = (QuerySnapshot)obj;
if (snapshot.IsEmpty)
{
Log.Debug("UpdateGroupAsync", "userId: " + userId + " not found");
return null;
}
string docRef = "";
foreach (DocumentSnapshot item in snapshot.Documents)
{
//docRef = item.;
}
return docRef;
}
Firstly ive tried to find the document ref using this code but dont have a function to get the ref even after getting the correct document.
the fourth line from the bottom is where I couldnt find it.
database pic
this.groupCode = code;
string strUserRef = GetDocRefAsync(userRef).ToString();
DocumentReference docReference = database.Collection(DBConstants.FS_COLLECTION_USERS_GROUPS_CODE).Document(strUserRef);
docReference.Update(DBConstants.FS_COLLECTION_USERS_GROUPS_CODE, groupCode);
If you want to get the documents where a field has a given value, you can use a query. Then once the query returns, you can get documents IDs with the .Id field on each DocumentShapshot in the returned documents.
You will also need to add await for the returned value since it is an async method returning a Task<string> not returning a string.
private async Task<string> GetDocRefAsync(string userId) {
CollectionReference usersRef = FirestoreData.GetFirestore().Collection(DBConstants.FS_COLLECTION_USERS);
Query query = usersRef.WhereEqualTo(DBConstants.FS_COLLECTION_USERS_USER_ID, userId);
// or GetSnapshotAsync depending on the version of firebase
QuerySnapshot querySnapshot = await query.Get();
// Note: if it matches multiple documents this will just return
// the ID of the first match
foreach (DocumentSnapshot item in snapshot.Documents)
{
return item.Id;
}
Log.Debug("UpdateGroupAsync", "userId: " + userId + " not found");
return null;
}
And you can use it like this to update a document (note that you were using a different collection here - probably by mistake).
string userDocId = await GetDocRefAsync(userId);
CollectionReference userCollection = database.Collection(DBConstants.FS_COLLECTION_USERS);
DocumentReference docReference = userCollection.Document(userDocId);
// or UpdateAsync depending on your version of firebase
docReference.Update(DBConstants.FS_COLLECTION_USERS_GROUPS_CODE, groupCode);
Since collection.InsertOne(document) returns void how do i know that the document written to the database for sure? I have a function which need to be run exactly after document is written to the database.
How can I check that without running a new query?
"Since collection.InsertOne(document) returns void" - is wrong, see db.collection.insertOne():
Returns: A document containing:
A boolean acknowledged as true if the operation ran with write concern or false if write concern was disabled.
A field insertedId with the _id value of the inserted document.
So, run
ret = db.collection.insertOne({your document})
print(ret.acknowledged);
or
print(ret.insertedId);
to get directly the _id of inserted document.
The write concern can be configured on either the connection string or the MongoClientSettings which are both passed in to the MongoClient object on creation.
var client = new MongoClient(new MongoClientSettings
{
WriteConcern = WriteConcern.W1
});
More information on write concern can be found on the MongoDB documentation - https://docs.mongodb.com/manual/reference/write-concern/
If the document is not saved the C# Driver will throw an exception (MongoWriteException).
Also if you have any write concern > Acknowledged, you'll also get back the Id of the document you've just save.
var client = new MongoClient(new MongoClientSettings
{
WriteConcern = WriteConcern.W1
});
var db = client.GetDatabase("test");
var orders = db.GetCollection<Order>("orders");
var newOrder = new Order {Name = $"Order-{Guid.NewGuid()}"};
await orders.InsertOneAsync(newOrder);
Console.WriteLine($"Order Id: {newOrder.Id}");
// Output
// Order Id: 5f058d599f1f033f3507c368
public class Order
{
public ObjectId Id { get; set; }
public string Name { get; set; }
}
I am having a problem where C# Driver is not returning any data with either using async-await or synchronous method.
When trying to run in the command line, it works perfectly, here's the snippet:
db.Collection_StudentResults.aggregate([ { $unwind: "$modules" }, { $match: { "studentNumber": "", "modules.code": "" } } ])
and here's how I have it setup in C#:
public static async Task<BsonDocument> getSingleStudentData(string studentNumber)
{
var client = new MongoClient("mongodb://localhost:27017");
var db = client.GetDatabase("dbStudents");
var collection = db.GetCollection<BsonDocument>("Collection_StudentResults");
var aggregate = collection.Aggregate()
.Unwind("modules")
.Match(new BsonDocument { { "studentNumber", studentNumber } });
var result = await aggregate.ToListAsync();
return result.FirstOrDefault();
}
Drivers Used: v2.4.0
MongoDB Version: v3.2.10
In Collection_StudentResults, the first document contains the studentNumber and modules array, in the modules array each document has code field.
Please help!
Thanks
Sorry - my bad, bad bad bad...
I missed the db = db.getSiblingDB in my builder script - which caused the data to go into the root database.
All the best.
What's the new way to build indexes with the new driver 2.0?
There's no documentation whatsoever about this.
Apparently this now works with the new IndexKeysDefinitionBuilder<> interface but that's all I got so far.
You need to call and await CreateOneAsync with an IndexKeysDefinition you get by using Builders.IndexKeys:
static async Task CreateIndex()
{
var client = new MongoClient();
var database = client.GetDatabase("db");
var collection = database.GetCollection<Hamster>("collection");
await collection.Indexes.CreateOneAsync(Builders<Hamster>.IndexKeys.Ascending(_ => _.Name));
}
If you don't have a Hamster you can also create the index in a non-strongly-typed way by specifying the index's json representation:
await collection.Indexes.CreateOneAsync("{ Name: 1 }");