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 
60 		if(!goodUrl) throw new Exception("Unable to parse mongodb URL: " ~ url);
61 
62 		m_connections = new ConnectionPool!MongoConnection(() @safe {
63 				auto ret = new MongoConnection(settings);
64 				ret.connect();
65 				return ret;
66 			},
67 			settings.maxConnections
68 		);
69 
70 		// force a connection to cause an exception for wrong URLs
71 		lockConnection();
72 	}
73 
74 	package this(MongoClientSettings settings)
75 	{
76 		m_connections = new ConnectionPool!MongoConnection({
77 				auto ret = new MongoConnection(settings);
78 				ret.connect();
79 				return ret;
80 			},
81 			settings.maxConnections
82 		);
83 
84 		// force a connection to cause an exception for wrong URLs
85 		lockConnection();
86 	}
87 
88 	/** Disconnects all currently unused connections to the server.
89 	*/
90 	void cleanupConnections()
91 	{
92 		m_connections.removeUnused((conn) nothrow @safe {
93 			try conn.disconnect();
94 			catch (Exception e) {
95 				logWarn("Error thrown during MongoDB connection close: %s", e.msg);
96 				try () @trusted { logDebug("Full error: %s", e.toString()); } ();
97 				catch (Exception e) {}
98 			}
99 		});
100 	}
101 
102 	/**
103 		Accesses a collection using an absolute path.
104 
105 		The full database.collection path must be specified. To access
106 		collections using a path relative to their database, use getDatabase in
107 		conjunction with MongoDatabase.opIndex.
108 
109 		Returns:
110 			MongoCollection for the given combined database and collection name (path)
111 
112 		Examples:
113 			---
114 			auto col = client.getCollection("test.collection");
115 			---
116    */
117 	MongoCollection getCollection(string path)
118 	{
119 		return MongoCollection(this, path);
120 	}
121 
122 	/**
123 		Returns an object representing the specified database.
124 
125 		The returned object allows to access the database entity (which contains
126 		a set of collections). There are two main use cases:
127 
128 		1. Accessing collections using a relative path
129 
130 		2. Performing service commands on the database itself
131 
132 		Note that there is no performance gain in accessing collections via a
133 		relative path compared to getCollection and an absolute path.
134 
135 		Returns:
136 			MongoDatabase instance representing requested database
137 
138 		Examples:
139 			---
140 			auto db = client.getDatabase("test");
141 			auto coll = db["collection"];
142 			---
143 	*/
144 	MongoDatabase getDatabase(string dbName)
145 	{
146 		return MongoDatabase(this, dbName);
147 	}
148 
149 
150 
151 	/**
152 	 	Return a handle to all databases of the server.
153 
154 	 	Returns:
155 	 		An input range of $(D MongoDatabase) objects.
156 
157 	 	Examples:
158 	 		---
159 	 		auto names = client.getDatabaseNames();
160 	 		writeln("Current databases are: ", names);
161 	 		---
162 	 */
163 	auto getDatabases()()
164 	{
165 		import std.algorithm : map;
166 		return lockConnection.listDatabases()
167 			.map!(info => MongoDatabase(this, info.name));
168 	}
169 
170 	package auto lockConnection() { return m_connections.lockConnection(); }
171 }