PageRenderTime 51ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/Raven.Database/Server/Responders/QueryStreams.cs

https://github.com/kairogyn/ravendb
C# | 230 lines | 198 code | 24 blank | 8 comment | 14 complexity | 1b93485796124ea4961e9b758690b733 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, BSD-3-Clause, CC-BY-SA-3.0
  1. // -----------------------------------------------------------------------
  2. // <copyright file="Streams.cs" company="Hibernating Rhinos LTD">
  3. // Copyright (c) Hibernating Rhinos LTD. All rights reserved.
  4. // </copyright>
  5. // -----------------------------------------------------------------------
  6. using System;
  7. using System.Collections.Generic;
  8. using System.Diagnostics;
  9. using System.Globalization;
  10. using System.IO;
  11. using System.Linq;
  12. using System.Text;
  13. using System.Threading;
  14. using Raven.Abstractions;
  15. using Raven.Abstractions.Util;
  16. using Raven.Database.Extensions;
  17. using Raven.Database.Impl;
  18. using Raven.Database.Server.Abstractions;
  19. using Raven.Imports.Newtonsoft.Json;
  20. using Raven.Imports.Newtonsoft.Json.Linq;
  21. using Raven.Json.Linq;
  22. namespace Raven.Database.Server.Responders
  23. {
  24. public class QueryStreams : AbstractRequestResponder
  25. {
  26. public override string UrlPattern
  27. {
  28. get { return @"^/streams/query/(.+)"; }
  29. }
  30. public override string[] SupportedVerbs
  31. {
  32. get { return new[] { "GET", "HEAD" }; }
  33. }
  34. public override void Respond(IHttpContext context)
  35. {
  36. using (context.Response.Streaming())
  37. {
  38. context.Response.ContentType = "application/json; charset=utf-8";
  39. var match = urlMatcher.Match(context.GetRequestUrl());
  40. var index = match.Groups[1].Value;
  41. var query = context.GetIndexQueryFromHttpContext(int.MaxValue);
  42. if (string.IsNullOrEmpty(context.Request.QueryString["pageSize"]))
  43. query.PageSize = int.MaxValue;
  44. var isHeadRequest = context.Request.HttpMethod == "HEAD";
  45. if (isHeadRequest)
  46. query.PageSize = 0;
  47. using (var writer = GetOutputWriter(context))
  48. {
  49. // we may be sending a LOT of documents to the user, and most
  50. // of them aren't going to be relevant for other ops, so we are going to skip
  51. // the cache for that, to avoid filling it up very quickly
  52. using (DocumentCacher.SkipSettingDocumentsInDocumentCache())
  53. using (var cts = new CancellationTokenSource())
  54. using(var timeout = cts.TimeoutAfter(Settings.DatbaseOperationTimeout))
  55. {
  56. Database.Query(index, query, cts.Token, information =>
  57. {
  58. context.Response.AddHeader("Raven-Result-Etag", information.ResultEtag.ToString());
  59. context.Response.AddHeader("Raven-Index-Etag", information.IndexEtag.ToString());
  60. context.Response.AddHeader("Raven-Is-Stale", information.IsStable ? "true" : "false");
  61. context.Response.AddHeader("Raven-Index", information.Index);
  62. context.Response.AddHeader("Raven-Total-Results", information.TotalResults.ToString(CultureInfo.InvariantCulture));
  63. context.Response.AddHeader("Raven-Index-Timestamp",
  64. information.IndexTimestamp.ToString(Default.DateTimeFormatsToWrite,
  65. CultureInfo.InvariantCulture));
  66. if (isHeadRequest)
  67. return;
  68. writer.WriteHeader();
  69. }, o =>
  70. {
  71. timeout.Delay();
  72. Database.WorkContext.UpdateFoundWork();
  73. writer.Write(o);
  74. });
  75. }
  76. }
  77. }
  78. }
  79. private static IOutputWriter GetOutputWriter(IHttpContext context)
  80. {
  81. var useExcelFormat = "excel".Equals(context.Request.QueryString["format"], StringComparison.InvariantCultureIgnoreCase);
  82. if (useExcelFormat)
  83. return new ExcelOutputWriter(context);
  84. return new JsonOutputWriter(context);
  85. }
  86. public interface IOutputWriter : IDisposable
  87. {
  88. void WriteHeader();
  89. void Write(RavenJObject result);
  90. }
  91. private class ExcelOutputWriter : IOutputWriter
  92. {
  93. private readonly IHttpContext context;
  94. private StreamWriter writer;
  95. public ExcelOutputWriter(IHttpContext context)
  96. {
  97. this.context = context;
  98. context.Response.ContentType = "text/csv, application/vnd.msexcel, text/anytext";
  99. }
  100. public void Dispose()
  101. {
  102. if (writer == null)
  103. return;
  104. writer.Flush();
  105. writer.Close();
  106. }
  107. public void WriteHeader()
  108. {
  109. writer = new StreamWriter(context.Response.OutputStream, Encoding.UTF8);
  110. }
  111. public void Write(RavenJObject result)
  112. {
  113. if (properties == null)
  114. {
  115. GetPropertiesAndWriteCsvHeader(result);
  116. Debug.Assert(properties != null);
  117. }
  118. foreach (var property in properties)
  119. {
  120. var token = result.SelectToken(property);
  121. if (token != null)
  122. {
  123. switch (token.Type)
  124. {
  125. case JTokenType.Null:
  126. break;
  127. case JTokenType.Array:
  128. case JTokenType.Object:
  129. OutputCsvValue(token.ToString(Formatting.None));
  130. break;
  131. default:
  132. OutputCsvValue(token.Value<string>());
  133. break;
  134. }
  135. }
  136. writer.Write(',');
  137. }
  138. writer.WriteLine();
  139. }
  140. private void GetPropertiesAndWriteCsvHeader(RavenJObject result)
  141. {
  142. properties = DocumentHelpers.GetPropertiesFromJObject(result,
  143. parentPropertyPath: "",
  144. includeNestedProperties: true,
  145. includeMetadata: false,
  146. excludeParentPropertyNames: true);
  147. foreach (var property in properties)
  148. {
  149. OutputCsvValue(property);
  150. writer.Write(',');
  151. }
  152. writer.WriteLine();
  153. }
  154. static readonly char[] requireQuotesChars = new[] { ',', '\r', '\n', '"' };
  155. private IEnumerable<string> properties;
  156. private void OutputCsvValue(string val)
  157. {
  158. var needsQuoutes = val.IndexOfAny(requireQuotesChars) != -1;
  159. if (needsQuoutes)
  160. {
  161. writer.Write('"');
  162. }
  163. writer.Write(needsQuoutes ? val.Replace("\"", "\"\"") : val);
  164. if (needsQuoutes)
  165. writer.Write('"');
  166. }
  167. }
  168. public class JsonOutputWriter : IOutputWriter
  169. {
  170. private readonly IHttpContext context;
  171. private JsonWriter writer;
  172. public JsonOutputWriter(IHttpContext context)
  173. {
  174. this.context = context;
  175. }
  176. public void WriteHeader()
  177. {
  178. writer = new JsonTextWriter(new StreamWriter(context.Response.OutputStream));
  179. writer.WriteStartObject();
  180. writer.WritePropertyName("Results");
  181. writer.WriteStartArray();
  182. }
  183. public void Dispose()
  184. {
  185. if (writer == null)
  186. return;
  187. writer.WriteEndArray();
  188. writer.WriteEndObject();
  189. writer.Flush();
  190. writer.Close();
  191. }
  192. public void Write(RavenJObject result)
  193. {
  194. result.WriteTo(writer, Default.Converters);
  195. }
  196. }
  197. }
  198. }