Mongodb C# driver - Update document then select collection with updated document - c#

I'm updating a document in Mongodb with the C# driver. I've verified the update is successfully completing, but if I select the collection that contains the updated document immediately after the update I'm not seeing the new values immediately. If I put a breakpoint in my code after the update but before the select, I will see the new values in the results from the Select. If I let the code run straight through, I get the old values in my names collection. I tried changing the write concern, but I don't think that's it. Is there some way to chain these two operations together so the select won't happen until the update has completed?
var qry = Query.EQ("_id", new ObjectId(id));
var upd = Update.Set("age", BsonValue.Create(newAge));
db.GetCollection<MongoTest>("mongotest").Update(qry,upd);
... would like to pause here until update is complete ...
var names = db.GetCollection<MongoTest>("mongotest")
.FindAll().SetSortOrder(SortBy.Ascending("name"))
.ToList<MongoTest>();
if (names.Count() > 0)
{
return View(names);
}

One clarification. The official MongoDB .Net driver defaults to Acknowledged write (write concern 1) when you start with using MongoClient. If you start with the old style of using MongoServer.Create (now obsoleted), the default is Unacknowledged.
Also ensure that you are not using a read preference that could route your reads to a secondary.

Related

CosmosDB SQL API: Change property Name - (Partial Updates .Net SDK or loops)

Consider the following data
I am trying to update the property name from givenName to PetName. Below are the things I tried.
Option 1:
List<PatchOperation> patchOperations = new List<PatchOperation>();
dynamic data = new
{
PetName= "Dummy"
};
//Remove the 0th element from the pet array
patchOperations.Add(PatchOperation.Remove("/children/0/pets/0"));
//insert the new entity
patchOperations.Add(PatchOperation.Add<dynamic>("/children/0/pets/0", data));
await Container.PatchItemAsync<dynamic>("AndersenFamily", new
PartitionKey("AndersenFamily"), patchOperations);
This works on the first element (as I have specified the index as 0).
But I couldn't find a way to update all the elements in the array. Something like /children/*/pets (I get an error "Invalid navigation for array(*)").
Option 2:
Read all the data from the database, remove the existing pets and upload the new object with the property PetName. But I guess it will take lot of time to loop through all the objects and make these changes.
Can someone suggest a better solution or a way to fix the partial updates so that it updates all the elements in the array?
The actual dataset that I am working on is huge and has more nested arrays.
Yes you are right, Current version of Patch does not support patching nested array. You need to know the index in order to replace/add values as you mentioned in the first question.
One way to think about this would be to write your own logic to replace the keys , there are libraries available in dotnet and then using the bulk insert using the SDK to insert those documents.

How do I read a single item from CosmosDb using Microsoft.Azure.Cosmos v3.9.1

I'm using CosmosDB for the first time, and I am attempting to read a single document by id, according to this documentation:
https://learn.microsoft.com/en-us/dotnet/api/microsoft.azure.cosmos.container.readitemasync?view=azure-dotnet
According to this documentation, my container object should have a ReadItemAsync method that I can use to find and read a single item. However, when I import the "Microsoft.Azure.Cosmos" nuget package to my project and attempt to use this code snippet, there is no function on the container for ReadItemAsync. Here is a snippet of my code:
using Microsoft.Azure.Cosmos;
...
var databaseId = "{databaseId}";
var containerId = "{containerId}";
var client = new CosmosClient(model.IndexDatabaseUri, model.IndexDatabasePrimaryKey);
var database = client.GetDatabase(databaseId);
var container = database.GetContainer(containerId);
var call = container... // NO ReadItemAsync HERE
I do see these functions on the container:
GetItemLinqQueryable
GetItemQueryIterator
GetItemQueryStreamIterator
From the looks of it, I could probably create a query iterator with a custom SELECT statement, and retrieve the first item in the iterator. But that seems overkill for what I am wanting to do. I will always only want a single item in this instance. Why is there no ReadItemAsync method here, when it shows in the documentation that there should be one? And, if the "Iterator" is the new, preferred method, are there any performance considerations in using QueryIterator vs retrieving a single document? This function will be run millions of times over the span of one day.
p.s. - I am using the "Microsoft.Azure.Cosmos" nuget package v3.9.1
If you are reading 1 item and you know the id and Partition Key, then that's the fastest and cheapest (in terms of RU/s) way to read the information. Doing a Query with a WHERE clause with the id would be more expensive.
I just created a NET Core Console Application with 3.9.1 and the method is there, and that documentation you pointed at gets automatically generated based off the DLLs in the Nuget packages.

Lucene.NET is not deleting docs?

I've probably gone through numerous S.O. posts on this issue, but I'm at a loss and can't figure out what the problem is.
I can add and update docs to the index, but I cannot seem to successfully delete them.
I'm using Lucene.NET v3.0.3
I read one suggestion was to do a query using the same conditions and ensure I'm getting a result back. Well, I did so:
First, I have a method that returns items in my database that have been marked as deleted
var deletedItems = VehicleController.GetDeleted(DateTime lastcheck);
Right now during testing, this includes a single item. I then iterate:
// This method returns my writer
var indexWriter = LuceneController.GetWriter();
// And my searcher
var searcher = new IndexSearcher(indexWriter.GetReader());
// And iterate over my items (just one for testing)
foreach(var c in deletedItems) {
// Here I'm testing by doing a query
var query = new BooleanQuery();
query.Add(new TermQuery(new Term("key", c.Guid.ToString())), Occur.MUST);
// Let's see if it can find the record based on this
var docs = searcher.Search(query, 1);
var foundDoc = docs.FirstOrDefault();
// Yep, we have one... let's get the full doc to be sure
var actualDoc = searcher.Doc(foundDoc.Doc);
// If I inspect actualDoc, it's the right one... I want to delete it.
indexWriter.DeleteDocuments(query);
indexWriter.Commit();
}
I've tried to smash all the logic above so it's easier to read, but I've tried all kinds of methods...
indexWriter.Optimize();
indexWriter.Flush(true, true, true);
If I watch the actual folder where everything is being stored, I can see filenames like 0_1.del and stuff like that popup, which seems promising.
I then read somewhere about a merge policy, but isn't that what Flush is supposed to do?
Then read to try setting the optimize method to 1 max, and that still didn't work (i.e. indexWriter.Optimize(1)).
So using the same query to fetch works, but deleting does not. Why? What else can I check? Does delete actually remove the item permanently or does it live on in some other manner until I completely delete the directory that's being used? Not understanding.
Index segment files in Lucene are immutable they never change once written. So when a deletion is recorded, the deleted record is not actually removed from the index files immediately, the record is simply marked as deleted. The record will eventually be removed from the index once that index segment is merged to produce a new segment. i.e. the deleted record won't be in the new segment that is the result of the merge.
Theoritically, once commit is called the deletion should be removed from the reader's view since you are getting the reader from the writer (i.e. it's a real time reader) This is documented here:
Note that flushing just moves the internal buffered state in IndexWriter into the index, but these changes are not visible to IndexReader until either commit() or close() is called.
source: https://lucene.apache.org/core/3_0_3/api/core/org/apache/lucene/index/IndexWriter.html
But you might want to try closing the reader after the deletion takes place and then getting a new reader from the writer to see if that new reader now has the record removed from visibility.

Mongo Update Unintentionally Inserting Document

I have a .NET application written in C#, and use Mongo for my database backend. One of my collections, UserSearchTerms, repeatedly (and unintentionally) has duplicate documents created.
I've teased out the problem to an update function that gets called asynchronously, and can be called multiple times simultaneously. In order to avoid problems with concurrent runs, I've implemented this code using an update which I trigger on any documents that match a specific query (unique on user and program), upserting if no documents are found.
Initially, I can guarantee that no duplicates exist and so expect that only the following two cases can occur:
No matching documents exist, triggering an upsert to add a new document
One matching document exists, and so an update is triggered only on that one document
Given these two cases, I expect that there would be no way for duplicate documents to be inserted through this function - the only time a new document should be inserted is if there are none to begin with. Yet over an hour or so, I've found that even though documents for a particular user/program pair exist, new documents for them are created.
Am I implementing this update correctly to guarantee that duplicate documents will not be created? If not, what is the proper way to implement an update in order to assure this?
This is the function in question:
public int UpdateSearchTerm(UserSearchTerm item)
{
_userSearches = _uow.Db.GetCollection<UserSearchTerm>("UserSearchTerms");
var query = Query.And(Query<UserSearchTerm>.EQ(ust => ust.UserId, item.UserId), Query<UserSearchTerm>.EQ(ust => ust.ProgramId, item.ProgramId));
_userSearches.Update(query, Update<UserSearchTerm>.Replace(item), new MongoUpdateOptions { Flags = UpdateFlags.Upsert });
return (int)_userSearches.Count(query);
}
Additional Information:
I'm using mongod version 2.6.5
The mongocsharpdriver version I'm using is 1.9.2
I'm running .NET 4.5
UserSearchTerms is the collection I store these documents in.
The query is intended to match users on both userId AND programId - my definition of a 'unique' document.
I return a count after the fact for debugging purposes.
You could add a unique index on userId and programId to ensure that no duplicate will be inserted
Doc : https://docs.mongodb.org/v2.4/tutorial/create-a-unique-index/

MongoDB - Inserting the result of a query in one round-trip

Consider this hypothetical snippet:
using (mongo.RequestStart(db))
{
var collection = db.GetCollection<BsonDocument>("test");
var insertDoc = new BsonDocument { { "currentCount", collection.Count() } };
WriteConcernResult wcr = collection.Insert(insertDoc);
}
It inserts a new document with "currentCount" set to the value returned by collection.Count().
This implies two round-trips to the server. One to calculate collection.Count() and one to perform the insert. Is there a way to do this in one round-trip?
In other words, can the value assigned to "currentCount" be calculated on the server at the time of the insert?
Thanks!
There is no way to do this currently (Mongo 2.4).
The upcoming 2.6 version should have batch operations support but I don't know if it will support batching operations of different types and using the results of one operation from another operation.
What you can do, however, is execute this logic on the server by expressing it in JavaScript and using eval:
collection.Database.Eval(new BsonJavaScript(#"
var count = db.test.count();
db.test.insert({ currentCount: count });
");
But this is not recommended, because of several reasons: you lose the write concern, it is very unsafe in terms of security, it requires admin permissions, it holds a global write lock, and it won't work on sharded clusters :)
I think your best route at the moment would be to do this in two queries.
If you're looking for atomic updates or counters (which don't exactly match your example but seem somewhat related), take a look at findAndModify and the $inc operator of update.
If you've got a large collection and you're looking to save CPU, it's recommended that you create another collection called counters that only has one document per collection that you want to count and increment the document pertaining to you're collection each time you insert a document.
See the guidance here.
It appears that you can place a JavaScript function inside your query, so perhaps it can be done in one trip, but I haven't implemented this in my own app, so I can't confirm that.

Categories