1 /**
2 	MongoClient class doing connection management. Usually this is a main entry point
3 	for client code.
4 
5 	Copyright: © 2012 Sönke Ludwig
6 	License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
7 	Authors: Sönke Ludwig
8 */
9 module vibe.db.mongo.client;
10 
11 public import vibe.db.mongo.collection;
12 public import vibe.db.mongo.database;
13 
14 import vibe.core.connectionpool;
15 import vibe.core.log;
16 import vibe.db.mongo.connection;
17 import vibe.db.mongo.settings;
18 
19 import core.thread;
20 
21 import std.conv;
22 import std.string;
23 import std.range;
24 
25 /**
26 	Represents a connection to a MongoDB server.
27 
28 	Note that this class uses a ConnectionPool internally to create and reuse
29 	network connections to the server as necessary. It should be reused for all
30 	fibers in a thread for optimum performance in high concurrency scenarios.
31  */
32 final class MongoClient {
33 @safe:
34 
35 	private {
36 		ConnectionPool!MongoConnection m_connections;
37 	}
38 
39 	package this(string host, ushort port)
40 	{
41 		this("mongodb://" ~ host ~ ":" ~ to!string(port) ~ "/?safe=true");
42 	}
43 
44 	/**
45 		Initializes a MongoDB client using a URL.
46 
47 		The URL must be in the form documented at
48 		$(LINK http://www.mongodb.org/display/DOCS/Connections) which is:
49 
50 		mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]
51 
52 		Throws:
53 			An exception if the URL cannot be parsed as a valid MongoDB URL.
54 	*/
55 	package this(string url)
56 	{
57 		MongoClientSettings settings;
58 		auto goodUrl = parseMongoDBUrl(settings, url);
59 		if(!goodUrl) throw new Exception("Unable to parse mongodb URL: " ~ url);
60 		this(settings);
61 	}
62 
63 	package this(MongoClientSettings settings)
64 	{
65 		m_connections = new ConnectionPool!MongoConnection({
66 				auto ret = new MongoConnection(settings);
67 				try ret.connect();
68 				catch (Exception e) {
69 					// avoid leaking the connection to the GC, which might
70 					// destroy it during shutdown when all of vibe.d has
71 					// already been destroyed, which in turn causes a null
72 					// pointer
73 					() @trusted { destroy(ret); } ();
74 					throw e;
75 				}
76 				return ret;
77 			},
78 			settings.maxConnections
79 		);
80 
81 		// force a connection to cause an exception for wrong URLs
82 		lockConnection();
83 	}
84 
85 	/** Disconnects all currently unused connections to the server.
86 	*/
87 	void cleanupConnections()
88 	{
89 		m_connections.removeUnused((conn) nothrow @safe {
90 			try conn.disconnect();
91 			catch (Exception e) {
92 				logWarn("Error thrown during MongoDB connection close: %s", e.msg);
93 				try () @trusted { logDebug("Full error: %s", e.toString()); } ();
94 				catch (Exception e) {}
95 			}
96 		});
97 	}
98 
99 	/**
100 		Accesses a collection using an absolute path.
101 
102 		The full database.collection path must be specified. To access
103 		collections using a path relative to their database, use getDatabase in
104 		conjunction with MongoDatabase.opIndex.
105 
106 		Returns:
107 			MongoCollection for the given combined database and collection name (path)
108 
109 		Examples:
110 			---
111 			auto col = client.getCollection("test.collection");
112 			---
113    */
114 	MongoCollection getCollection(string path)
115 	{
116 		return MongoCollection(this, path);
117 	}
118 
119 	/**
120 		Returns an object representing the specified database.
121 
122 		The returned object allows to access the database entity (which contains
123 		a set of collections). There are two main use cases:
124 
125 		1. Accessing collections using a relative path
126 
127 		2. Performing service commands on the database itself
128 
129 		Note that there is no performance gain in accessing collections via a
130 		relative path compared to getCollection and an absolute path.
131 
132 		Returns:
133 			MongoDatabase instance representing requested database
134 
135 		Examples:
136 			---
137 			auto db = client.getDatabase("test");
138 			auto coll = db["collection"];
139 			---
140 	*/
141 	MongoDatabase getDatabase(string dbName)
142 	{
143 		return MongoDatabase(this, dbName);
144 	}
145 
146 
147 
148 	/**
149 	 	Return a handle to all databases of the server.
150 
151 	 	Returns:
152 	 		An input range of $(D MongoDatabase) objects.
153 
154 	 	Examples:
155 	 		---
156 	 		auto names = client.getDatabaseNames();
157 	 		writeln("Current databases are: ", names);
158 	 		---
159 	 */
160 	auto getDatabases()()
161 	{
162 		import std.algorithm : map;
163 		return lockConnection.listDatabases()
164 			.map!(info => MongoDatabase(this, info.name));
165 	}
166 
167 	package auto lockConnection() { return m_connections.lockConnection(); }
168 }