mongodb.d 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. import vibe.core.core;
  2. import vibe.db.mongo.mongo;
  3. import vibe.http.router;
  4. import vibe.http.server;
  5. import vibe.web.web;
  6. import mir.random : unpredictableSeedOf;
  7. import mir.random.variable : UniformVariable;
  8. import mir.random.engine.xorshift : Xorshift;
  9. import std.conv : ConvException, to;
  10. import std.array;
  11. import std.exception : enforce;
  12. enum worldSize = 10000;
  13. void main()
  14. {
  15. runWorkerTaskDist(&runServer);
  16. runApplication();
  17. }
  18. void runServer()
  19. {
  20. auto router = new URLRouter;
  21. router.registerWebInterface(new WebInterface);
  22. router.rebuild();
  23. auto settings = new HTTPServerSettings;
  24. settings.options |= HTTPServerOption.reusePort;
  25. settings.port = 8080;
  26. listenHTTP(settings, router);
  27. }
  28. class WebInterface {
  29. private {
  30. MongoCollection _worldCollection;
  31. MongoCollection _fortuneCollection;
  32. UniformVariable!uint _uniformVariable;
  33. Xorshift _gen;
  34. }
  35. this()
  36. {
  37. import std.process : environment;
  38. auto db = connectMongoDB("tfb-database");
  39. _worldCollection = db.getCollection("hello_world.world");
  40. _fortuneCollection = db.getCollection("hello_world.fortune");
  41. _gen = Xorshift(unpredictableSeedOf!uint);
  42. _uniformVariable = UniformVariable!uint(1, worldSize);
  43. }
  44. // GET /
  45. void get()
  46. {
  47. render!"index.dt";
  48. }
  49. // GET /json
  50. void getJson(HTTPServerResponse res)
  51. {
  52. // NOTE: the status and content type parameters are optional, but we need
  53. // to specify them, because the default content type is "application/json; charset=UTF8"
  54. res.writeJsonBody(Message("Hello, World!"), HTTPStatus.ok, "application/json");
  55. }
  56. // GET /db
  57. void getDB(HTTPServerResponse res)
  58. {
  59. struct Q { int _id; }
  60. auto query = Q(_uniformVariable(_gen));
  61. auto world = _worldCollection.findOne!World(query);
  62. enforce(!world.isNull(), "expected world, found none.");
  63. auto w = WorldResponse(world.get);
  64. res.writeJsonBody(w, HTTPStatus.ok, "application/json");
  65. }
  66. // GET /queries?queries=...
  67. void getQueries(HTTPServerResponse res, string queries)
  68. {
  69. import std.algorithm : min, max;
  70. // Convert the "queries" parameter to int and ignore any conversion errors
  71. // Note that you'd usually declare queries as int instead. However, the
  72. // test required to gracefully handle errors here.
  73. int count = 1;
  74. try count = min(max(queries.to!int, 1), 500);
  75. catch (ConvException) {}
  76. // assemble the response array
  77. scope data = new WorldResponse[count];
  78. foreach (ref w; data) {
  79. static struct Q { int _id; }
  80. auto query = Q(_uniformVariable(_gen));
  81. auto world = _worldCollection.findOne!World(query);
  82. enforce(!world.isNull(), "expected world, found none.");
  83. w = WorldResponse(world.get);
  84. }
  85. // write response as JSON
  86. res.writeJsonBody(data, HTTPStatus.ok, "application/json");
  87. }
  88. // GET /fortunes
  89. void getFortunes()
  90. {
  91. import std.algorithm : map, sort;
  92. FortuneResponse[] data;
  93. data = _fortuneCollection.find!Fortune().map!(f => FortuneResponse(f)).array;
  94. data ~= FortuneResponse(Fortune(0, "Additional fortune added at request time."));
  95. data.sort!((a, b) => a.message < b.message);
  96. render!("fortunes.dt", data);
  97. }
  98. // GET /updates?queries=...
  99. void getUpdates(HTTPServerResponse res, string queries)
  100. {
  101. import std.algorithm : min, max;
  102. int count = 1;
  103. try count = min(max(queries.to!int, 1), 500);
  104. catch (ConvException e) {}
  105. scope data = new WorldResponse[count];
  106. foreach (ref w; data) {
  107. static struct Q { int _id; }
  108. auto query = Q(_uniformVariable(_gen));
  109. auto world = _worldCollection.findOne!World(query);
  110. enforce(!world.isNull(), "expected world, found none.");
  111. w = WorldResponse(world.get);
  112. // update random number
  113. w.randomNumber = _uniformVariable(_gen);
  114. // persist to DB
  115. static struct US {
  116. double randomNumber;
  117. }
  118. static struct U {
  119. @name("$set") US set;
  120. }
  121. _worldCollection.update(query, U(US(w.randomNumber)));
  122. }
  123. // write response as JSON
  124. res.writeJsonBody(data, HTTPStatus.ok, "application/json");
  125. }
  126. // GET /plaintext
  127. void getPlaintext(HTTPServerResponse res)
  128. {
  129. res.writeBody("Hello, World!", HTTPStatus.ok, "text/plain");
  130. }
  131. }
  132. struct Message {
  133. string message;
  134. }
  135. struct World {
  136. double _id;
  137. double randomNumber;
  138. }
  139. struct Fortune {
  140. double _id;
  141. string message;
  142. }
  143. struct WorldResponse {
  144. this(World w) { this.id = cast(int)w._id; this.randomNumber = cast(int)w.randomNumber; }
  145. int id;
  146. int randomNumber;
  147. }
  148. struct FortuneResponse {
  149. this(Fortune f) { this.id = cast(int)f._id; this.message = f.message; }
  150. int id;
  151. string message;
  152. }