You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

981 lines
41 KiB

/* Copyright 2010-2011 10gen Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Driver.Builders;
using MongoDB.Driver.GridFS;
using MongoDB.Driver.Internal;
namespace MongoDB.Driver {
/// <summary>
/// Represents a MongoDB database and the settings used to access it. This class is thread-safe.
/// </summary>
public class MongoDatabase {
#region private static fields
private static HashSet<char> invalidDatabaseNameChars;
#endregion
#region private fields
private object databaseLock = new object();
private MongoServer server;
private MongoDatabaseSettings settings;
private string name;
private Dictionary<MongoCollectionSettings, MongoCollection> collections = new Dictionary<MongoCollectionSettings, MongoCollection>();
private MongoCollection<BsonDocument> commandCollection;
private MongoGridFS gridFS;
#endregion
#region static constructor
static MongoDatabase() {
// MongoDB itself prohibits some characters and the rest are prohibited by the Windows restrictions on filenames
invalidDatabaseNameChars = new HashSet<char>() { '\0', ' ', '.', '$', '/', '\\' };
foreach (var c in Path.GetInvalidPathChars()) { invalidDatabaseNameChars.Add(c); }
foreach (var c in Path.GetInvalidFileNameChars()) { invalidDatabaseNameChars.Add(c); }
}
#endregion
#region constructors
/// <summary>
/// Creates a new instance of MongoDatabase. Normally you would call one of the indexers or GetDatabase methods
/// of MongoServer instead.
/// </summary>
/// <param name="server">The server that contains this database.</param>
/// <param name="settings">The settings to use to access this database.</param>
public MongoDatabase(
MongoServer server,
MongoDatabaseSettings settings
) {
ValidateDatabaseName(settings.DatabaseName);
this.server = server;
this.settings = settings.FrozenCopy();
this.name = settings.DatabaseName;
var commandCollectionSettings = new MongoCollectionSettings<BsonDocument>(this, "$cmd") {
AssignIdOnInsert = false
};
if (server.Settings.ConnectionMode == ConnectionMode.ReplicaSet) {
// make sure commands get routed to the primary server by using slaveOk false
commandCollectionSettings.SlaveOk = false;
}
commandCollection = GetCollection(commandCollectionSettings);
}
#endregion
#region factory methods
/// <summary>
/// Creates a new instance or returns an existing instance of MongoDatabase. Only one instance
/// is created for each combination of database settings. Automatically creates an instance
/// of MongoServer if needed.
/// </summary>
/// <param name="builder">Server and database settings in the form of a MongoConnectionStringBuilder.</param>
/// <returns>
/// A new or existing instance of MongoDatabase.
/// </returns>
public static MongoDatabase Create(
MongoConnectionStringBuilder builder
) {
var serverSettings = builder.ToServerSettings();
var databaseName = builder.DatabaseName;
return Create(serverSettings, databaseName);
}
/// <summary>
/// Creates a new instance or returns an existing instance of MongoDatabase. Only one instance
/// is created for each combination of database settings. Automatically creates an instance
/// of MongoServer if needed.
/// </summary>
/// <param name="serverSettings">The server settings for the server that contains this database.</param>
/// <param name="databaseName">The name of this database (will be accessed using default settings).</param>
/// <returns>
/// A new or existing instance of MongoDatabase.
/// </returns>
public static MongoDatabase Create(
MongoServerSettings serverSettings,
string databaseName
) {
if (databaseName == null) {
throw new ArgumentException("Database name is missing.");
}
var server = MongoServer.Create(serverSettings);
return server.GetDatabase(databaseName);
}
/// <summary>
/// Creates a new instance or returns an existing instance of MongoDatabase. Only one instance
/// is created for each combination of database settings. Automatically creates an instance
/// of MongoServer if needed.
/// </summary>
/// <param name="url">Server and database settings in the form of a MongoUrl.</param>
/// <returns>
/// A new or existing instance of MongoDatabase.
/// </returns>
public static MongoDatabase Create(
MongoUrl url
) {
var serverSettings = url.ToServerSettings();
var databaseName = url.DatabaseName;
return Create(serverSettings, databaseName);
}
/// <summary>
/// Creates a new instance or returns an existing instance of MongoDatabase. Only one instance
/// is created for each combination of database settings. Automatically creates an instance
/// of MongoServer if needed.
/// </summary>
/// <param name="connectionString">Server and database settings in the form of a connection string.</param>
/// <returns>
/// A new or existing instance of MongoDatabase.
/// </returns>
public static MongoDatabase Create(
string connectionString
) {
if (connectionString.StartsWith("mongodb://")) {
MongoUrl url = MongoUrl.Create(connectionString);
return Create(url);
} else {
MongoConnectionStringBuilder builder = new MongoConnectionStringBuilder(connectionString);
return Create(builder);
}
}
/// <summary>
/// Creates a new instance or returns an existing instance of MongoDatabase. Only one instance
/// is created for each combination of database settings. Automatically creates an instance
/// of MongoServer if needed.
/// </summary>
/// <param name="uri">Server and database settings in the form of a Uri.</param>
/// <returns>
/// A new or existing instance of MongoDatabase.
/// </returns>
public static MongoDatabase Create(
Uri uri
) {
return Create(MongoUrl.Create(uri.ToString()));
}
#endregion
#region public properties
/// <summary>
/// Gets the command collection for this database.
/// </summary>
public virtual MongoCollection<BsonDocument> CommandCollection {
get { return commandCollection; }
}
/// <summary>
/// Gets the credentials being used to access this database.
/// </summary>
public virtual MongoCredentials Credentials {
get { return settings.Credentials; }
}
/// <summary>
/// Gets the default GridFS instance for this database. The default GridFS instance uses default GridFS
/// settings. See also GetGridFS if you need to use GridFS with custom settings.
/// </summary>
public virtual MongoGridFS GridFS {
get {
lock (databaseLock) {
if (gridFS == null) {
gridFS = new MongoGridFS(this);
}
return gridFS;
}
}
}
/// <summary>
/// Gets the name of this database.
/// </summary>
public virtual string Name {
get { return name; }
}
/// <summary>
/// Gets the server that contains this database.
/// </summary>
public virtual MongoServer Server {
get { return server; }
}
/// <summary>
/// Gets the settings being used to access this database.
/// </summary>
public virtual MongoDatabaseSettings Settings {
get { return settings; }
}
#endregion
#region public indexers
/// <summary>
/// Gets a MongoCollection instance representing a collection on this database
/// with a default document type of BsonDocument.
/// </summary>
/// <param name="collectionName">The name of the collection.</param>
/// <returns>An instance of MongoCollection.</returns>
public virtual MongoCollection<BsonDocument> this[
string collectionName
] {
get { return GetCollection(collectionName); }
}
/// <summary>
/// Gets a MongoCollection instance representing a collection on this database
/// with a default document type of BsonDocument.
/// </summary>
/// <param name="collectionName">The name of the collection.</param>
/// <param name="safeMode">The safe mode to use when accessing this collection.</param>
/// <returns>An instance of MongoCollection.</returns>
public virtual MongoCollection<BsonDocument> this[
string collectionName,
SafeMode safeMode
] {
get { return GetCollection(collectionName, safeMode); }
}
#endregion
#region public methods
/// <summary>
/// Adds a user to this database.
/// </summary>
/// <param name="credentials">The user's credentials.</param>
public virtual void AddUser(
MongoCredentials credentials
) {
AddUser(credentials, false);
}
/// <summary>
/// Adds a user to this database.
/// </summary>
/// <param name="credentials">The user's credentials.</param>
/// <param name="readOnly">True if the user is a read-only user.</param>
public virtual void AddUser(
MongoCredentials credentials,
bool readOnly
) {
var user = new MongoUser(credentials, readOnly);
AddUser(user);
}
/// <summary>
/// Adds a user to this database.
/// </summary>
/// <param name="user">The user.</param>
public virtual void AddUser(
MongoUser user
) {
var users = GetCollection("system.users");
var document = users.FindOne(Query.EQ("user", user.Username));
if (document == null) {
document = new BsonDocument("user", user.Username);
}
document["readOnly"] = user.IsReadOnly;
document["pwd"] = user.PasswordHash;
users.Save(document);
}
/// <summary>
/// Tests whether a collection exists on this database.
/// </summary>
/// <param name="collectionName">The name of the collection.</param>
/// <returns>True if the collection exists.</returns>
public virtual bool CollectionExists(
string collectionName
) {
return GetCollectionNames().Contains(collectionName);
}
/// <summary>
/// Creates a collection. MongoDB creates collections automatically when they are first used, so
/// this command is mainly here for frameworks.
/// </summary>
/// <param name="collectionName">The name of the collection.</param>
/// <returns>A CommandResult.</returns>
public virtual CommandResult CreateCollection(
string collectionName
) {
return CreateCollection(collectionName, null);
}
/// <summary>
/// Creates a collection. MongoDB creates collections automatically when they are first used, so
/// you only need to call this method if you want to provide non-default options.
/// </summary>
/// <param name="collectionName">The name of the collection.</param>
/// <param name="options">Options for creating this collection (usually a CollectionOptionsDocument or constructed using the CollectionOptions builder).</param>
/// <returns>A CommandResult.</returns>
public virtual CommandResult CreateCollection(
string collectionName,
IMongoCollectionOptions options
) {
var command = new CommandDocument("create", collectionName);
if (options != null) {
command.Merge(options.ToBsonDocument());
}
return RunCommand(command);
}
/// <summary>
/// Creates an instance of MongoCollectionSettings for the named collection with the rest of the settings inherited.
/// You can override some of these settings before calling GetCollection.
/// </summary>
/// <typeparam name="TDefaultDocument">The default document type for this collection.</typeparam>
/// <param name="collectionName">The name of this collection.</param>
/// <returns>A MongoCollectionSettings.</returns>
public virtual MongoCollectionSettings<TDefaultDocument> CreateCollectionSettings<TDefaultDocument>(
string collectionName
) {
return new MongoCollectionSettings<TDefaultDocument>(
this,
collectionName
);
}
/// <summary>
/// Creates an instance of MongoCollectionSettings for the named collection with the rest of the settings inherited.
/// You can override some of these settings before calling GetCollection.
/// </summary>
/// <param name="defaultDocumentType">The default document type for this collection.</param>
/// <param name="collectionName">The name of this collection.</param>
/// <returns>A MongoCollectionSettings.</returns>
public virtual MongoCollectionSettings CreateCollectionSettings(
Type defaultDocumentType,
string collectionName
) {
var settingsDefinition = typeof(MongoCollectionSettings<>);
var settingsType = settingsDefinition.MakeGenericType(defaultDocumentType);
var constructorInfo = settingsType.GetConstructor(new Type[] { typeof(MongoDatabase), typeof(string) });
return (MongoCollectionSettings) constructorInfo.Invoke(
new object[] {
this,
collectionName
}
);
}
/// <summary>
/// Drops a database.
/// </summary>
public virtual void Drop() {
server.DropDatabase(name);
}
/// <summary>
/// Drops a collection.
/// </summary>
/// <param name="collectionName">The name of the collection to drop.</param>
/// <returns>A CommandResult.</returns>
public virtual CommandResult DropCollection(
string collectionName
) {
try {
var command = new CommandDocument("drop", collectionName);
var result = RunCommand(command);
server.IndexCache.Reset(name, collectionName);
return result;
} catch (MongoCommandException ex) {
if (ex.CommandResult.ErrorMessage == "ns not found") {
return ex.CommandResult;
}
throw;
}
}
/// <summary>
/// Evaluates JavaScript code at the server.
/// </summary>
/// <param name="flags">Flags that control Eval options.</param>
/// <param name="code">The code to evaluate.</param>
/// <param name="args">Optional arguments (only used when the code is a function with parameters).</param>
/// <returns>The result of evaluating the code.</returns>
public virtual BsonValue Eval(
EvalFlags flags,
BsonJavaScript code,
params object[] args
) {
var command = new CommandDocument {
{ "$eval", code },
{ "args", BsonArray.Create(args), args != null && args.Length > 0 },
{ "nolock", true, (flags & EvalFlags.NoLock) != 0 }
};
var result = RunCommand(command);
return result.Response["retval"];
}
/// <summary>
/// Evaluates JavaScript code at the server.
/// </summary>
/// <param name="code">The code to evaluate.</param>
/// <param name="args">Optional arguments (only used when the code is a function with parameters).</param>
/// <returns>The result of evaluating the code.</returns>
public virtual BsonValue Eval(
BsonJavaScript code,
params object[] args
) {
return Eval(EvalFlags.None, code, args);
}
/// <summary>
/// Fetches the document referred to by the DBRef.
/// </summary>
/// <param name="dbRef">The <see cref="MongoDBRef"/> to fetch.</param>
/// <returns>A BsonDocument (or null if the document was not found).</returns>
public virtual BsonDocument FetchDBRef(
MongoDBRef dbRef
) {
return FetchDBRefAs<BsonDocument>(dbRef);
}
/// <summary>
/// Fetches the document referred to by the DBRef, deserialized as a <typeparamref name="TDocument"/>.
/// </summary>
/// <typeparam name="TDocument">The nominal type of the document to fetch.</typeparam>
/// <param name="dbRef">The <see cref="MongoDBRef"/> to fetch.</param>
/// <returns>A <typeparamref name="TDocument"/> (or null if the document was not found).</returns>
public virtual TDocument FetchDBRefAs<TDocument>(
MongoDBRef dbRef
) {
return (TDocument) FetchDBRefAs(typeof(TDocument), dbRef);
}
/// <summary>
/// Fetches the document referred to by the DBRef.
/// </summary>
/// <param name="documentType">The nominal type of the document to fetch.</param>
/// <param name="dbRef">The <see cref="MongoDBRef"/> to fetch.</param>
/// <returns>An instance of nominalType (or null if the document was not found).</returns>
public virtual object FetchDBRefAs(
Type documentType,
MongoDBRef dbRef
) {
if (dbRef.DatabaseName != null && dbRef.DatabaseName != name) {
return server.FetchDBRefAs(documentType, dbRef);
}
var collection = GetCollection(dbRef.CollectionName);
var query = Query.EQ("_id", BsonValue.Create(dbRef.Id));
return collection.FindOneAs(documentType, query);
}
/// <summary>
/// Finds all users of this database.
/// </summary>
/// <returns>An array of users.</returns>
public virtual MongoUser[] FindAllUsers() {
var result = new List<MongoUser>();
var users = GetCollection("system.users");
foreach (var document in users.FindAll()) {
var username = document["user"].AsString;
var passwordHash = document["pwd"].AsString;
var readOnly = document["readOnly"].ToBoolean();
var user = new MongoUser(username, passwordHash, readOnly);
result.Add(user);
};
return result.ToArray();
}
/// <summary>
/// Finds a user of this database.
/// </summary>
/// <param name="username">The username.</param>
/// <returns>The user.</returns>
public virtual MongoUser FindUser(
string username
) {
var users = GetCollection("system.users");
var query = Query.EQ("user", username);
var document = users.FindOne(query);
if (document != null) {
var passwordHash = document["pwd"].AsString;
var readOnly = document["readOnly"].ToBoolean();
return new MongoUser(username, passwordHash, readOnly);
} else {
return null;
}
}
/// <summary>
/// Gets a MongoCollection instance representing a collection on this database
/// with a default document type of TDefaultDocument.
/// </summary>
/// <typeparam name="TDefaultDocument">The default document type for this collection.</typeparam>
/// <param name="collectionSettings">The settings to use when accessing this collection.</param>
/// <returns>An instance of MongoCollection.</returns>
public virtual MongoCollection<TDefaultDocument> GetCollection<TDefaultDocument>(
MongoCollectionSettings<TDefaultDocument> collectionSettings
) {
lock (databaseLock) {
MongoCollection collection;
if (!collections.TryGetValue(collectionSettings, out collection)) {
collection = new MongoCollection<TDefaultDocument>(this, collectionSettings);
collections.Add(collectionSettings, collection);
}
return (MongoCollection<TDefaultDocument>) collection;
}
}
/// <summary>
/// Gets a MongoCollection instance representing a collection on this database
/// with a default document type of TDefaultDocument.
/// </summary>
/// <param name="collectionName">The name of the collection.</param>
/// <returns>An instance of MongoCollection.</returns>
public virtual MongoCollection<TDefaultDocument> GetCollection<TDefaultDocument>(
string collectionName
) {
var collectionSettings = new MongoCollectionSettings<TDefaultDocument>(this, collectionName);
return GetCollection(collectionSettings);
}
/// <summary>
/// Gets a MongoCollection instance representing a collection on this database
/// with a default document type of TDefaultDocument.
/// </summary>
/// <param name="collectionName">The name of the collection.</param>
/// <param name="safeMode">The safe mode to use when accessing this collection.</param>
/// <returns>An instance of MongoCollection.</returns>
public virtual MongoCollection<TDefaultDocument> GetCollection<TDefaultDocument>(
string collectionName,
SafeMode safeMode
) {
var collectionSettings = new MongoCollectionSettings<TDefaultDocument>(this, collectionName) {
SafeMode = safeMode
};
return GetCollection(collectionSettings);
}
/// <summary>
/// Gets a MongoCollection instance representing a collection on this database
/// with a default document type of TDefaultDocument.
/// </summary>
/// <param name="collectionSettings">The settings to use when accessing this collection.</param>
/// <returns>An instance of MongoCollection.</returns>
public virtual MongoCollection GetCollection(
MongoCollectionSettings collectionSettings
) {
lock (databaseLock) {
MongoCollection collection;
if (!collections.TryGetValue(collectionSettings, out collection)) {
var collectionDefinition = typeof(MongoCollection<>);
var collectionType = collectionDefinition.MakeGenericType(collectionSettings.DefaultDocumentType);
var constructorInfo = collectionType.GetConstructor(new Type[] { typeof(MongoDatabase), collectionSettings.GetType() });
collection = (MongoCollection) constructorInfo.Invoke(new object[] { this, collectionSettings });
collections.Add(collectionSettings, collection);
}
return collection;
}
}
/// <summary>
/// Gets a MongoCollection instance representing a collection on this database
/// with a default document type of BsonDocument.
/// </summary>
/// <param name="collectionName">The name of the collection.</param>
/// <returns>An instance of MongoCollection.</returns>
public virtual MongoCollection<BsonDocument> GetCollection(
string collectionName
) {
var collectionSettings = new MongoCollectionSettings<BsonDocument>(this, collectionName);
return GetCollection(collectionSettings);
}
/// <summary>
/// Gets a MongoCollection instance representing a collection on this database
/// with a default document type of BsonDocument.
/// </summary>
/// <param name="collectionName">The name of the collection.</param>
/// <param name="safeMode">The safe mode to use when accessing this collection.</param>
/// <returns>An instance of MongoCollection.</returns>
public virtual MongoCollection<BsonDocument> GetCollection(
string collectionName,
SafeMode safeMode
) {
var collectionSettings = new MongoCollectionSettings<BsonDocument>(this, collectionName) {
SafeMode = safeMode
};
return GetCollection(collectionSettings);
}
/// <summary>
/// Gets a MongoCollection instance representing a collection on this database
/// with a default document type of BsonDocument.
/// </summary>
/// <param name="defaultDocumentType">The default document type.</param>
/// <param name="collectionName">The name of the collection.</param>
/// <returns>An instance of MongoCollection.</returns>
public virtual MongoCollection GetCollection(
Type defaultDocumentType,
string collectionName
) {
var collectionSettings = CreateCollectionSettings(defaultDocumentType, collectionName);
return GetCollection(collectionSettings);
}
/// <summary>
/// Gets a MongoCollection instance representing a collection on this database
/// with a default document type of BsonDocument.
/// </summary>
/// <param name="defaultDocumentType">The default document type.</param>
/// <param name="collectionName">The name of the collection.</param>
/// <param name="safeMode">The safe mode to use when accessing this collection.</param>
/// <returns>An instance of MongoCollection.</returns>
public virtual MongoCollection GetCollection(
Type defaultDocumentType,
string collectionName,
SafeMode safeMode
) {
var collectionSettings = CreateCollectionSettings(defaultDocumentType, collectionName);
collectionSettings.SafeMode = safeMode;
return GetCollection(collectionSettings);
}
/// <summary>
/// Gets a list of the names of all the collections in this database.
/// </summary>
/// <returns>A list of collection names.</returns>
public virtual IEnumerable<string> GetCollectionNames() {
List<string> collectionNames = new List<string>();
var namespaces = GetCollection("system.namespaces");
var prefix = name + ".";
foreach (var @namespace in namespaces.FindAll()) {
string collectionName = @namespace["name"].AsString;
if (!collectionName.StartsWith(prefix)) { continue; }
if (collectionName.Contains('$')) { continue; }
collectionNames.Add(collectionName.Substring(prefix.Length));
}
collectionNames.Sort();
return collectionNames;
}
/// <summary>
/// Gets the current operation.
/// </summary>
/// <returns>The current operation.</returns>
public virtual BsonDocument GetCurrentOp() {
var collection = GetCollection("$cmd.sys.inprog");
return collection.FindOne();
}
/// <summary>
/// Gets an instance of MongoGridFS for this database using custom GridFS settings.
/// </summary>
/// <param name="gridFSSettings">The GridFS settings to use.</param>
/// <returns>An instance of MongoGridFS.</returns>
public virtual MongoGridFS GetGridFS(
MongoGridFSSettings gridFSSettings
) {
return new MongoGridFS(this, gridFSSettings);
}
// TODO: mongo shell has GetPrevError at the database level?
// TODO: mongo shell has GetProfilingLevel at the database level?
// TODO: mongo shell has GetReplicationInfo at the database level?
/// <summary>
/// Gets one or more documents from the system.profile collection.
/// </summary>
/// <param name="query">A query to select which documents to return.</param>
/// <returns>A cursor.</returns>
public MongoCursor<SystemProfileInfo> GetProfilingInfo(
IMongoQuery query
) {
var collectionSettings = new MongoCollectionSettings<SystemProfileInfo>(this, "system.profile") { SlaveOk = false };
var collection = GetCollection<SystemProfileInfo>(collectionSettings);
return collection.Find(query);
}
/// <summary>
/// Gets the current profiling level.
/// </summary>
/// <returns>The profiling level.</returns>
public GetProfilingLevelResult GetProfilingLevel() {
var command = new CommandDocument("profile", -1);
return RunCommandAs<GetProfilingLevelResult>(command);
}
/// <summary>
/// Gets a sister database on the same server.
/// </summary>
/// <param name="databaseName">The name of the sister database.</param>
/// <returns>An instance of MongoDatabase.</returns>
public virtual MongoDatabase GetSisterDatabase(
string databaseName
) {
return server.GetDatabase(databaseName);
}
/// <summary>
/// Gets the current database stats.
/// </summary>
/// <returns>An instance of DatabaseStatsResult.</returns>
public virtual DatabaseStatsResult GetStats() {
return RunCommandAs<DatabaseStatsResult>("dbstats");
}
// TODO: mongo shell has IsMaster at database level?
/// <summary>
/// Removes a user from this database.
/// </summary>
/// <param name="user">The user to remove.</param>
public virtual void RemoveUser(
MongoUser user
) {
RemoveUser(user.Username);
}
/// <summary>
/// Removes a user from this database.
/// </summary>
/// <param name="username">The username to remove.</param>
public virtual void RemoveUser(
string username
) {
var users = GetCollection("system.users");
users.Remove(Query.EQ("user", username));
}
/// <summary>
/// Renames a collection on this database.
/// </summary>
/// <param name="oldCollectionName">The old name for the collection.</param>
/// <param name="newCollectionName">The new name for the collection.</param>
/// <returns>A CommandResult.</returns>
public virtual CommandResult RenameCollection(
string oldCollectionName,
string newCollectionName
) {
return RenameCollection(oldCollectionName, newCollectionName, false); // dropTarget = false
}
/// <summary>
/// Renames a collection on this database.
/// </summary>
/// <param name="oldCollectionName">The old name for the collection.</param>
/// <param name="newCollectionName">The new name for the collection.</param>
/// <param name="dropTarget">Whether to drop the target collection first if it already exists.</param>
/// <returns>A CommandResult.</returns>
public virtual CommandResult RenameCollection(
string oldCollectionName,
string newCollectionName,
bool dropTarget
) {
var command = new CommandDocument {
{ "renameCollection", string.Format("{0}.{1}", name, oldCollectionName) },
{ "to", string.Format("{0}.{1}", name, newCollectionName) },
{ "dropTarget", dropTarget, dropTarget } // only added if dropTarget is true
};
return server.RunAdminCommand(command);
}
/// <summary>
/// Lets the server know that this thread is done with a series of related operations. Instead of calling this method it is better
/// to put the return value of RequestStart in a using statement.
/// </summary>
public virtual void RequestDone() {
server.RequestDone();
}
/// <summary>
/// Lets the server know that this thread is about to begin a series of related operations that must all occur
/// on the same connection. The return value of this method implements IDisposable and can be placed in a
/// using statement (in which case RequestDone will be called automatically when leaving the using statement).
/// </summary>
/// <returns>A helper object that implements IDisposable and calls <see cref="RequestDone"/> from the Dispose method.</returns>
public virtual IDisposable RequestStart() {
return RequestStart(false); // not slaveOk
}
/// <summary>
/// Lets the server know that this thread is about to begin a series of related operations that must all occur
/// on the same connection. The return value of this method implements IDisposable and can be placed in a
/// using statement (in which case RequestDone will be called automatically when leaving the using statement).
/// </summary>
/// <param name="slaveOk">Whether queries should be sent to secondary servers.</param>
/// <returns>A helper object that implements IDisposable and calls <see cref="RequestDone"/> from the Dispose method.</returns>
public virtual IDisposable RequestStart(
bool slaveOk
) {
return server.RequestStart(this, slaveOk);
}
// TODO: mongo shell has ResetError at the database level
/// <summary>
/// Removes all entries for this database in the index cache used by EnsureIndex. Call this method
/// when you know (or suspect) that a process other than this one may have dropped one or
/// more indexes.
/// </summary>
public virtual void ResetIndexCache() {
server.IndexCache.Reset(this);
}
/// <summary>
/// Runs a command on this database.
/// </summary>
/// <param name="command">The command object.</param>
/// <returns>A CommandResult</returns>
public virtual CommandResult RunCommand(
IMongoCommand command
) {
return RunCommandAs<CommandResult>(command);
}
/// <summary>
/// Runs a command on this database.
/// </summary>
/// <param name="commandName">The name of the command.</param>
/// <returns>A CommandResult</returns>
public virtual CommandResult RunCommand(
string commandName
) {
return RunCommandAs<CommandResult>(commandName);
}
/// <summary>
/// Runs a command on this database and returns the result as a TCommandResult.
/// </summary>
/// <param name="command">The command object.</param>
/// <returns>A TCommandResult</returns>
public virtual TCommandResult RunCommandAs<TCommandResult>(
IMongoCommand command
) where TCommandResult : CommandResult, new() {
return (TCommandResult) RunCommandAs(typeof(TCommandResult), command);
}
/// <summary>
/// Runs a command on this database and returns the result as a TCommandResult.
/// </summary>
/// <param name="commandName">The name of the command.</param>
/// <returns>A TCommandResult</returns>
public virtual TCommandResult RunCommandAs<TCommandResult>(
string commandName
) where TCommandResult : CommandResult, new() {
return (TCommandResult) RunCommandAs(typeof(TCommandResult), commandName);
}
/// <summary>
/// Runs a command on this database and returns the result as a TCommandResult.
/// </summary>
/// <param name="commandResultType">The command result type.</param>
/// <param name="command">The command object.</param>
/// <returns>A TCommandResult</returns>
public virtual CommandResult RunCommandAs(
Type commandResultType,
IMongoCommand command
) {
var response = CommandCollection.FindOne(command);
if (response == null) {
var commandName = command.ToBsonDocument().GetElement(0).Name;
var message = string.Format("Command '{0}' failed. No response returned.", commandName);
throw new MongoCommandException(message);
}
var commandResult = (CommandResult) Activator.CreateInstance(commandResultType); // constructor can't have arguments
commandResult.Initialize(command, response); // so two phase construction required
if (!commandResult.Ok) {
if (commandResult.ErrorMessage == "not master") {
// TODO: figure out which instance gave the error and set its state to Unknown
server.Disconnect();
}
throw new MongoCommandException(commandResult);
}
return commandResult;
}
/// <summary>
/// Runs a command on this database and returns the result as a TCommandResult.
/// </summary>
/// <param name="commandResultType">The command result type.</param>
/// <param name="commandName">The name of the command.</param>
/// <returns>A TCommandResult</returns>
public virtual CommandResult RunCommandAs(
Type commandResultType,
string commandName
) {
var command = new CommandDocument(commandName, true);
return RunCommandAs(commandResultType, command);
}
/// <summary>
/// Sets the level of profile information to write.
/// </summary>
/// <param name="level">The profiling level.</param>
/// <returns></returns>
public virtual CommandResult SetProfilingLevel(
ProfilingLevel level
) {
return SetProfilingLevel(level, TimeSpan.Zero);
}
/// <summary>
/// Sets the level of profile information to write.
/// </summary>
/// <param name="level">The profiling level.</param>
/// <param name="slow">The threshold that defines a slow query.</param>
/// <returns></returns>
public virtual CommandResult SetProfilingLevel(
ProfilingLevel level,
TimeSpan slow
) {
var command = new CommandDocument {
{ "profile", (int) level },
{ "slowms", slow.TotalMilliseconds, slow != TimeSpan.Zero } // optional
};
return RunCommand(command);
}
/// <summary>
/// Gets a canonical string representation for this database.
/// </summary>
/// <returns>A canonical string representation for this database.</returns>
public override string ToString() {
return name;
}
#endregion
#region private methods
private void ValidateDatabaseName(
string name
) {
if (name == null) {
throw new ArgumentNullException("name");
}
if (name == "") {
throw new ArgumentException("Database name is empty.");
}
foreach (var c in name) {
if (invalidDatabaseNameChars.Contains(c)) {
var bytes = new byte[] { (byte) ((int) c >> 8), (byte) ((int) c & 255) };
var hex = BsonUtils.ToHexString(bytes);
var message = string.Format("Database name '{0}' is not valid. The character 0x{1} '{2}' is not allowed in database names.", name, hex, c);
throw new ArgumentException(message);
}
}
if (Encoding.UTF8.GetBytes(name).Length > 64) {
var message = string.Format("Database name '{0}' exceeds 64 bytes (after encoding to UTF8).", name);
throw new ArgumentException(message);
}
}
#endregion
}
}