C# Mongo RunCommandAsync - c#

Is it possible to run
db.Users.createIndex({"FirstName" : 1, "LastName": 1});
from the RunCommandAsync also are there examples of what can be done from the command.
As a test I tried to run this but it through an exception
var indexCommand = new BsonDocumentCommand<BsonDocument>(new BsonDocument{
{"getIndexes", "Users"}
});
var results = await database.RunCommandAsync(indexCommand);
I know I can get and create indexes from the C# drivers, however I would like to keep a script in sync between those that want to build the indexes through C# and those that want to handle it directly on the DB.

You could probably try an alternative method. Especially if you see what RunCommandAsync is expected to do with the BsonDocument. See Unit test for RunCommandAsync
Assuming you are using mongodb 3.0, can you consider the code below an alternative?
using (var results = await context.Users.Indexes.ListAsync())
{
while (await results.MoveNextAsync())
{
foreach (var current in results.Current)
{
System.Diagnostics.Debug.WriteLine(current["name"].AsString);
}
}
}

Related

Cosmos DB Mongo API ChangeStream: Is there a way to read which fields where updated?

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?

Mongo.net - BypassDocumentValidation is not working

I'm trying to insert a json like this (fieldname with a "."), in a Net Core Console Project
{"name.field" : "MongoDB", "type" : "Database"}
Using the C# code belove:
-with InsertManyOptions with BypassDocumentValidation in true
var options = new InsertManyOptions
{
BypassDocumentValidation = true,
IsOrdered = false
};
await _collection.InsertManyAsync(items, options);
But I have this exception:
Element name 'name.field' is not valid
I´m using :
C# Mongo Driver 2.5
Net Core Project
MongoDB version 4.0.3
Any idea? Thanks!
The BypassDocumentValidation can be used to bypass the JSON Schema validation. The issue you are facing, however, is due to the C# driver which explicitly prevents the use of the dot symbol . as part of a field name.
This used to be required up until MongoDB v3.6 which officially added support for fields with ".".
Looking into the internals of the C# driver you can see that the BsonWriter.WriteName method calls contains this code which throws the Exception you're seeing:
if (!_elementNameValidator.IsValidElementName(name))
{
var message = string.Format("Element name '{0}' is not valid'.", name);
throw new BsonSerializationException(message);
}
The _elementNameValidator is something that is managed internally by the driver which in fact comes with a NoOpElementNameValidator that doesn't do any validations. The driver, however, won't use this validator for "normal" collections.
All that said, I would strongly advise against the use of field names with "unusual" characters anyway because this is likely to set you up for unexpected behaviour and all sorts of other issues down the road.
In order to get around this you can do one of the following things:
a) Write your own custom serializer which is an option that I would personally steer clear off if possible - it adds complexity that most of the time shouldn't be required.
b) Use the below helper extension (copied from one of the unit testing projects inside the driver) to convert the BsonDocument into a RawBsonDocument which can then successfully written to the server:
public static class RawBsonDocumentHelper
{
public static RawBsonDocument FromBsonDocument(BsonDocument document)
{
using (var memoryStream = new MemoryStream())
{
using (var bsonWriter = new BsonBinaryWriter(memoryStream, BsonBinaryWriterSettings.Defaults))
{
var context = BsonSerializationContext.CreateRoot(bsonWriter);
BsonDocumentSerializer.Instance.Serialize(context, document);
}
return new RawBsonDocument(memoryStream.ToArray());
}
}
public static RawBsonDocument FromJson(string json)
{
return FromBsonDocument(BsonDocument.Parse(json));
}
}
And then simply write the RawBsonDocument to the server:
RawBsonDocument rawDoc = RawBsonDocumentHelper.FromJson("{\"name.field\" : \"MongoDB\", \"type\" : \"Database\"}");
collection.InsertOne(rawDoc);

mongoDB C# Driver is not returning any data

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.

How to get all installations when using Azure Notification Hubs installation model?

Using NotificationHubClient I can get all registered devices using GetAllRegistrationsAsync(). But if I do not use the registration model but the installation model instead, how can I get all installations? There are methods to retrieve a specific installation but none to get everything.
You're correct, as of July 2016 there's no way to get all installations for a hub. In the future, the product team is planning to add this feature to the installations model, but it will work in a different way. Instead of making it a runtime operation, you'll provide your storage connection string and you'll get a blob with everything associated with the hub.
Sorry for visiting an old thread... but in theory you could use the GetAllRegistrationsAsyc to get all the installations. I guess this will return everything without an installation id as well, but you could just ignore those if you choose.
Could look something like this
var allRegistrations = await _hub.GetAllRegistrationsAsync(0);
var continuationToken = allRegistrations.ContinuationToken;
var registrationDescriptionsList = new List<RegistrationDescription>(allRegistrations);
while (!string.IsNullOrWhiteSpace(continuationToken))
{
var otherRegistrations = await _hub.GetAllRegistrationsAsync(continuationToken, 0);
registrationDescriptionsList.AddRange(otherRegistrations);
continuationToken = otherRegistrations.ContinuationToken;
}
// Put into DeviceInstallation object
var deviceInstallationList = new List<DeviceInstallation>();
foreach (var registration in registrationDescriptionsList)
{
var deviceInstallation = new DeviceInstallation();
var tags = registration.Tags;
foreach(var tag in tags)
{
if (tag.Contains("InstallationId:"))
{
deviceInstallation.InstallationId = new Guid(tag.Substring(tag.IndexOf(":")+1));
}
}
deviceInstallation.PushHandle = registration.PnsHandle;
deviceInstallation.Tags = new List<string>(registration.Tags);
deviceInstallationList.Add(deviceInstallation);
}
I am not suggesting this to be the cleanest chunk of code written, but it does the trick for us. We only use this for debugging type purposes anyways

Proper way to access usersinfo using mongodb driver for .net/c#

When I write this line of code (C# using mongodb driver):
var mongoDB = ...
var result = mongoDB.FindUser("ruprecht");
I get a warning that I should "Use the new user management command "usersinfo".
But I can find no example of how to do this.
I'll just use the deprecated commands for now, but I'd like to know the right way to do it.
I realize this thread is a couple years old, but this is a question I've been looking for an answer to for quite some time with great difficulty, so I figured I'd answer for the sake of future Googlers.
Your Code:
Your attempts were close, but not quite the syntax RunCommand is looking for. If you take a look at the Reference page (https://docs.mongodb.org/manual/reference/command/usersInfo/), the syntax for the Mongo Shell looks like this:
Mongo Shell Code:
{ usersInfo: { user: <name>, db: <db> },
showCredentials: <Boolean>,
showPrivileges: <Boolean>
}
When we translate that to C#, it's a bit more complex, but it'll look something like this.
C# Code:
command = new CommandDocument
{
{"usersInfo", new BsonDocument
{
{"user", "reprecht"},
{"db", "myDatabaseName"}
}
}
};
myOpenDb.RunCommand (command)
Note the new BsonDocument part in there. That's generally what you'll need to do in cases of nested brackets.
Another Example:
For the sake of completeness, let's look at another example say you wanted to add a new user. The documentation here shows it like this:
Mongo Shell Code:
{ createUser: "<name>",
pwd: "<cleartext password>",
customData: { <any information> },
roles: [
{ role: "<role>", db: "<database>" } | "<role>",
...
],
writeConcern: { <write concern> }
}
So, ignoring the optional customData and writeConcern, we can write this in C# like this:
C# Code:
command = new CommandDocument
{
{"createUser", "myUser"},
{"pwd", "myPassword"},
{"roles", new BsonArray
{
new BsonDocument
{
{"role", "readWrite"},
{"db", "myDatabaseName"}
}
}
}
};
Again, note that we used new BsonArray to create an array like in the shell code.
Hopefully, that gives you a good place to start. You should be able to execute any command in this manner, though this is more of a guess/hope on my part than something I've extensively tested.
You use the MongoDatabase's RunCommand method and the usersInfo command documented here: http://docs.mongodb.org/manual/reference/command/usersInfo/#dbcmd.usersInfo.

Categories

Resources