IndexKeysDefinitionBuilder.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. /* Copyright 2010-present MongoDB Inc.
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. using System;
  16. using System.Collections.Generic;
  17. using System.Linq;
  18. using System.Linq.Expressions;
  19. using MongoDB.Bson;
  20. using MongoDB.Bson.IO;
  21. using MongoDB.Bson.Serialization;
  22. using MongoDB.Driver.Core.Misc;
  23. namespace MongoDB.Driver
  24. {
  25. /// <summary>
  26. /// Extension methods for an index keys definition.
  27. /// </summary>
  28. public static class IndexKeysDefinitionExtensions
  29. {
  30. /// <summary>
  31. /// Combines an existing index keys definition with an ascending index key definition.
  32. /// </summary>
  33. /// <typeparam name="TDocument">The type of the document.</typeparam>
  34. /// <param name="keys">The keys.</param>
  35. /// <param name="field">The field.</param>
  36. /// <returns>
  37. /// A combined index keys definition.
  38. /// </returns>
  39. public static IndexKeysDefinition<TDocument> Ascending<TDocument>(this IndexKeysDefinition<TDocument> keys, FieldDefinition<TDocument> field)
  40. {
  41. var builder = Builders<TDocument>.IndexKeys;
  42. return builder.Combine(keys, builder.Ascending(field));
  43. }
  44. /// <summary>
  45. /// Combines an existing index keys definition with an ascending index key definition.
  46. /// </summary>
  47. /// <typeparam name="TDocument">The type of the document.</typeparam>
  48. /// <param name="keys">The keys.</param>
  49. /// <param name="field">The field.</param>
  50. /// <returns>
  51. /// A combined index keys definition.
  52. /// </returns>
  53. public static IndexKeysDefinition<TDocument> Ascending<TDocument>(this IndexKeysDefinition<TDocument> keys, Expression<Func<TDocument, object>> field)
  54. {
  55. var builder = Builders<TDocument>.IndexKeys;
  56. return builder.Combine(keys, builder.Ascending(field));
  57. }
  58. /// <summary>
  59. /// Combines an existing index keys definition with a descending index key definition.
  60. /// </summary>
  61. /// <typeparam name="TDocument">The type of the document.</typeparam>
  62. /// <param name="keys">The keys.</param>
  63. /// <param name="field">The field.</param>
  64. /// <returns>
  65. /// A combined index keys definition.
  66. /// </returns>
  67. public static IndexKeysDefinition<TDocument> Descending<TDocument>(this IndexKeysDefinition<TDocument> keys, FieldDefinition<TDocument> field)
  68. {
  69. var builder = Builders<TDocument>.IndexKeys;
  70. return builder.Combine(keys, builder.Descending(field));
  71. }
  72. /// <summary>
  73. /// Combines an existing index keys definition with a descending index key definition.
  74. /// </summary>
  75. /// <typeparam name="TDocument">The type of the document.</typeparam>
  76. /// <param name="keys">The keys.</param>
  77. /// <param name="field">The field.</param>
  78. /// <returns>
  79. /// A combined index keys definition.
  80. /// </returns>
  81. public static IndexKeysDefinition<TDocument> Descending<TDocument>(this IndexKeysDefinition<TDocument> keys, Expression<Func<TDocument, object>> field)
  82. {
  83. var builder = Builders<TDocument>.IndexKeys;
  84. return builder.Combine(keys, builder.Descending(field));
  85. }
  86. /// <summary>
  87. /// Combines an existing index keys definition with a 2d index key definition.
  88. /// </summary>
  89. /// <typeparam name="TDocument">The type of the document.</typeparam>
  90. /// <param name="keys">The keys.</param>
  91. /// <param name="field">The field.</param>
  92. /// <returns>
  93. /// A combined index keys definition.
  94. /// </returns>
  95. public static IndexKeysDefinition<TDocument> Geo2D<TDocument>(this IndexKeysDefinition<TDocument> keys, FieldDefinition<TDocument> field)
  96. {
  97. var builder = Builders<TDocument>.IndexKeys;
  98. return builder.Combine(keys, builder.Geo2D(field));
  99. }
  100. /// <summary>
  101. /// Combines an existing index keys definition with a 2d index key definition.
  102. /// </summary>
  103. /// <typeparam name="TDocument">The type of the document.</typeparam>
  104. /// <param name="keys">The keys.</param>
  105. /// <param name="field">The field.</param>
  106. /// <returns>
  107. /// A combined index keys definition.
  108. /// </returns>
  109. public static IndexKeysDefinition<TDocument> Geo2D<TDocument>(this IndexKeysDefinition<TDocument> keys, Expression<Func<TDocument, object>> field)
  110. {
  111. var builder = Builders<TDocument>.IndexKeys;
  112. return builder.Combine(keys, builder.Geo2D(field));
  113. }
  114. /// <summary>
  115. /// Combines an existing index keys definition with a geo haystack index key definition.
  116. /// </summary>
  117. /// <typeparam name="TDocument">The type of the document.</typeparam>
  118. /// <param name="keys">The keys.</param>
  119. /// <param name="field">The field.</param>
  120. /// <param name="additionalFieldName">Name of the additional field.</param>
  121. /// <returns>
  122. /// A combined index keys definition.
  123. /// </returns>
  124. public static IndexKeysDefinition<TDocument> GeoHaystack<TDocument>(this IndexKeysDefinition<TDocument> keys, FieldDefinition<TDocument> field, FieldDefinition<TDocument> additionalFieldName = null)
  125. {
  126. var builder = Builders<TDocument>.IndexKeys;
  127. return builder.Combine(keys, builder.GeoHaystack(field, additionalFieldName));
  128. }
  129. /// <summary>
  130. /// Combines an existing index keys definition with a geo haystack index key definition.
  131. /// </summary>
  132. /// <typeparam name="TDocument">The type of the document.</typeparam>
  133. /// <param name="keys">The keys.</param>
  134. /// <param name="field">The field.</param>
  135. /// <param name="additionalFieldName">Name of the additional field.</param>
  136. /// <returns>
  137. /// A combined index keys definition.
  138. /// </returns>
  139. public static IndexKeysDefinition<TDocument> GeoHaystack<TDocument>(this IndexKeysDefinition<TDocument> keys, Expression<Func<TDocument, object>> field, Expression<Func<TDocument, object>> additionalFieldName = null)
  140. {
  141. var builder = Builders<TDocument>.IndexKeys;
  142. return builder.Combine(keys, builder.GeoHaystack(field, additionalFieldName));
  143. }
  144. /// <summary>
  145. /// Combines an existing index keys definition with a 2dsphere index key definition.
  146. /// </summary>
  147. /// <typeparam name="TDocument">The type of the document.</typeparam>
  148. /// <param name="keys">The keys.</param>
  149. /// <param name="field">The field.</param>
  150. /// <returns>
  151. /// A combined index keys definition.
  152. /// </returns>
  153. public static IndexKeysDefinition<TDocument> Geo2DSphere<TDocument>(this IndexKeysDefinition<TDocument> keys, FieldDefinition<TDocument> field)
  154. {
  155. var builder = Builders<TDocument>.IndexKeys;
  156. return builder.Combine(keys, builder.Geo2DSphere(field));
  157. }
  158. /// <summary>
  159. /// Combines an existing index keys definition with a 2dsphere index key definition.
  160. /// </summary>
  161. /// <typeparam name="TDocument">The type of the document.</typeparam>
  162. /// <param name="keys">The keys.</param>
  163. /// <param name="field">The field.</param>
  164. /// <returns>
  165. /// A combined index keys definition.
  166. /// </returns>
  167. public static IndexKeysDefinition<TDocument> Geo2DSphere<TDocument>(this IndexKeysDefinition<TDocument> keys, Expression<Func<TDocument, object>> field)
  168. {
  169. var builder = Builders<TDocument>.IndexKeys;
  170. return builder.Combine(keys, builder.Geo2DSphere(field));
  171. }
  172. /// <summary>
  173. /// Combines an existing index keys definition with a hashed index key definition.
  174. /// </summary>
  175. /// <typeparam name="TDocument">The type of the document.</typeparam>
  176. /// <param name="keys">The keys.</param>
  177. /// <param name="field">The field.</param>
  178. /// <returns>
  179. /// A combined index keys definition.
  180. /// </returns>
  181. public static IndexKeysDefinition<TDocument> Hashed<TDocument>(this IndexKeysDefinition<TDocument> keys, FieldDefinition<TDocument> field)
  182. {
  183. var builder = Builders<TDocument>.IndexKeys;
  184. return builder.Combine(keys, builder.Hashed(field));
  185. }
  186. /// <summary>
  187. /// Combines an existing index keys definition with a hashed index key definition.
  188. /// </summary>
  189. /// <typeparam name="TDocument">The type of the document.</typeparam>
  190. /// <param name="keys">The keys.</param>
  191. /// <param name="field">The field.</param>
  192. /// <returns>
  193. /// A combined index keys definition.
  194. /// </returns>
  195. public static IndexKeysDefinition<TDocument> Hashed<TDocument>(this IndexKeysDefinition<TDocument> keys, Expression<Func<TDocument, object>> field)
  196. {
  197. var builder = Builders<TDocument>.IndexKeys;
  198. return builder.Combine(keys, builder.Hashed(field));
  199. }
  200. /// <summary>
  201. /// Combines an existing index keys definition with a text index key definition.
  202. /// </summary>
  203. /// <typeparam name="TDocument">The type of the document.</typeparam>
  204. /// <param name="keys">The keys.</param>
  205. /// <param name="field">The field.</param>
  206. /// <returns>
  207. /// A combined index keys definition.
  208. /// </returns>
  209. public static IndexKeysDefinition<TDocument> Text<TDocument>(this IndexKeysDefinition<TDocument> keys, FieldDefinition<TDocument> field)
  210. {
  211. var builder = Builders<TDocument>.IndexKeys;
  212. return builder.Combine(keys, builder.Text(field));
  213. }
  214. /// <summary>
  215. /// Combines an existing index keys definition with a text index key definition.
  216. /// </summary>
  217. /// <typeparam name="TDocument">The type of the document.</typeparam>
  218. /// <param name="keys">The keys.</param>
  219. /// <param name="field">The field.</param>
  220. /// <returns>
  221. /// A combined index keys definition.
  222. /// </returns>
  223. public static IndexKeysDefinition<TDocument> Text<TDocument>(this IndexKeysDefinition<TDocument> keys, Expression<Func<TDocument, object>> field)
  224. {
  225. var builder = Builders<TDocument>.IndexKeys;
  226. return builder.Combine(keys, builder.Text(field));
  227. }
  228. }
  229. /// <summary>
  230. /// A builder for an <see cref="IndexKeysDefinition{TDocument}"/>.
  231. /// </summary>
  232. /// <typeparam name="TDocument">The type of the document.</typeparam>
  233. public sealed class IndexKeysDefinitionBuilder<TDocument>
  234. {
  235. /// <summary>
  236. /// Creates an ascending index key definition.
  237. /// </summary>
  238. /// <param name="field">The field.</param>
  239. /// <returns>An ascending index key definition.</returns>
  240. public IndexKeysDefinition<TDocument> Ascending(FieldDefinition<TDocument> field)
  241. {
  242. return new DirectionalIndexKeyDefinition<TDocument>(field, SortDirection.Ascending);
  243. }
  244. /// <summary>
  245. /// Creates an ascending index key definition.
  246. /// </summary>
  247. /// <param name="field">The field.</param>
  248. /// <returns>An ascending index key definition.</returns>
  249. public IndexKeysDefinition<TDocument> Ascending(Expression<Func<TDocument, object>> field)
  250. {
  251. return Ascending(new ExpressionFieldDefinition<TDocument>(field));
  252. }
  253. /// <summary>
  254. /// Creates a combined index keys definition.
  255. /// </summary>
  256. /// <param name="keys">The keys.</param>
  257. /// <returns>A combined index keys definition.</returns>
  258. public IndexKeysDefinition<TDocument> Combine(params IndexKeysDefinition<TDocument>[] keys)
  259. {
  260. return Combine((IEnumerable<IndexKeysDefinition<TDocument>>)keys);
  261. }
  262. /// <summary>
  263. /// Creates a combined index keys definition.
  264. /// </summary>
  265. /// <param name="keys">The keys.</param>
  266. /// <returns>A combined index keys definition.</returns>
  267. public IndexKeysDefinition<TDocument> Combine(IEnumerable<IndexKeysDefinition<TDocument>> keys)
  268. {
  269. return new CombinedIndexKeysDefinition<TDocument>(keys);
  270. }
  271. /// <summary>
  272. /// Creates a descending index key definition.
  273. /// </summary>
  274. /// <param name="field">The field.</param>
  275. /// <returns>A descending index key definition.</returns>
  276. public IndexKeysDefinition<TDocument> Descending(FieldDefinition<TDocument> field)
  277. {
  278. return new DirectionalIndexKeyDefinition<TDocument>(field, SortDirection.Descending);
  279. }
  280. /// <summary>
  281. /// Creates a descending index key definition.
  282. /// </summary>
  283. /// <param name="field">The field.</param>
  284. /// <returns>A descending index key definition.</returns>
  285. public IndexKeysDefinition<TDocument> Descending(Expression<Func<TDocument, object>> field)
  286. {
  287. return Descending(new ExpressionFieldDefinition<TDocument>(field));
  288. }
  289. /// <summary>
  290. /// Creates a 2d index key definition.
  291. /// </summary>
  292. /// <param name="field">The field.</param>
  293. /// <returns>A 2d index key definition.</returns>
  294. public IndexKeysDefinition<TDocument> Geo2D(FieldDefinition<TDocument> field)
  295. {
  296. return new SimpleIndexKeyDefinition<TDocument>(field, "2d");
  297. }
  298. /// <summary>
  299. /// Creates a 2d index key definition.
  300. /// </summary>
  301. /// <param name="field">The field.</param>
  302. /// <returns>A 2d index key definition.</returns>
  303. public IndexKeysDefinition<TDocument> Geo2D(Expression<Func<TDocument, object>> field)
  304. {
  305. return Geo2D(new ExpressionFieldDefinition<TDocument>(field));
  306. }
  307. /// <summary>
  308. /// Creates a geo haystack index key definition.
  309. /// </summary>
  310. /// <param name="field">The field.</param>
  311. /// <param name="additionalFieldName">Name of the additional field.</param>
  312. /// <returns>
  313. /// A geo haystack index key definition.
  314. /// </returns>
  315. public IndexKeysDefinition<TDocument> GeoHaystack(FieldDefinition<TDocument> field, FieldDefinition<TDocument> additionalFieldName = null)
  316. {
  317. return new GeoHaystackIndexKeyDefinition<TDocument>(field, additionalFieldName);
  318. }
  319. /// <summary>
  320. /// Creates a geo haystack index key definition.
  321. /// </summary>
  322. /// <param name="field">The field.</param>
  323. /// <param name="additionalFieldName">Name of the additional field.</param>
  324. /// <returns>
  325. /// A geo haystack index key definition.
  326. /// </returns>
  327. public IndexKeysDefinition<TDocument> GeoHaystack(Expression<Func<TDocument, object>> field, Expression<Func<TDocument, object>> additionalFieldName = null)
  328. {
  329. FieldDefinition<TDocument> additional = additionalFieldName == null ? null : new ExpressionFieldDefinition<TDocument>(additionalFieldName);
  330. return GeoHaystack(new ExpressionFieldDefinition<TDocument>(field), additional);
  331. }
  332. /// <summary>
  333. /// Creates a 2dsphere index key definition.
  334. /// </summary>
  335. /// <param name="field">The field.</param>
  336. /// <returns>A 2dsphere index key definition.</returns>
  337. public IndexKeysDefinition<TDocument> Geo2DSphere(FieldDefinition<TDocument> field)
  338. {
  339. return new SimpleIndexKeyDefinition<TDocument>(field, "2dsphere");
  340. }
  341. /// <summary>
  342. /// Creates a 2dsphere index key definition.
  343. /// </summary>
  344. /// <param name="field">The field.</param>
  345. /// <returns>A 2dsphere index key definition.</returns>
  346. public IndexKeysDefinition<TDocument> Geo2DSphere(Expression<Func<TDocument, object>> field)
  347. {
  348. return Geo2DSphere(new ExpressionFieldDefinition<TDocument>(field));
  349. }
  350. /// <summary>
  351. /// Creates a hashed index key definition.
  352. /// </summary>
  353. /// <param name="field">The field.</param>
  354. /// <returns>A hashed index key definition.</returns>
  355. public IndexKeysDefinition<TDocument> Hashed(FieldDefinition<TDocument> field)
  356. {
  357. return new SimpleIndexKeyDefinition<TDocument>(field, "hashed");
  358. }
  359. /// <summary>
  360. /// Creates a hashed index key definition.
  361. /// </summary>
  362. /// <param name="field">The field.</param>
  363. /// <returns>A hashed index key definition.</returns>
  364. public IndexKeysDefinition<TDocument> Hashed(Expression<Func<TDocument, object>> field)
  365. {
  366. return Hashed(new ExpressionFieldDefinition<TDocument>(field));
  367. }
  368. /// <summary>
  369. /// Creates a text index key definition.
  370. /// </summary>
  371. /// <param name="field">The field.</param>
  372. /// <returns>A text index key definition.</returns>
  373. public IndexKeysDefinition<TDocument> Text(FieldDefinition<TDocument> field)
  374. {
  375. return new SimpleIndexKeyDefinition<TDocument>(field, "text");
  376. }
  377. /// <summary>
  378. /// Creates a text index key definition.
  379. /// </summary>
  380. /// <param name="field">The field.</param>
  381. /// <returns>A text index key definition.</returns>
  382. public IndexKeysDefinition<TDocument> Text(Expression<Func<TDocument, object>> field)
  383. {
  384. return Text(new ExpressionFieldDefinition<TDocument>(field));
  385. }
  386. }
  387. internal sealed class CombinedIndexKeysDefinition<TDocument> : IndexKeysDefinition<TDocument>
  388. {
  389. private readonly List<IndexKeysDefinition<TDocument>> _keys;
  390. public CombinedIndexKeysDefinition(IEnumerable<IndexKeysDefinition<TDocument>> keys)
  391. {
  392. _keys = Ensure.IsNotNull(keys, nameof(keys)).ToList();
  393. }
  394. public override BsonDocument Render(IBsonSerializer<TDocument> documentSerializer, IBsonSerializerRegistry serializerRegistry)
  395. {
  396. var document = new BsonDocument();
  397. foreach (var key in _keys)
  398. {
  399. var renderedKey = key.Render(documentSerializer, serializerRegistry);
  400. foreach (var element in renderedKey.Elements)
  401. {
  402. if (document.Contains(element.Name))
  403. {
  404. var message = string.Format(
  405. "The index keys definition contains multiple values for the field '{0}'.",
  406. element.Name);
  407. throw new MongoException(message);
  408. }
  409. document.Add(element);
  410. }
  411. }
  412. return document;
  413. }
  414. }
  415. internal sealed class DirectionalIndexKeyDefinition<TDocument> : IndexKeysDefinition<TDocument>
  416. {
  417. private readonly FieldDefinition<TDocument> _field;
  418. private readonly SortDirection _direction;
  419. public DirectionalIndexKeyDefinition(FieldDefinition<TDocument> field, SortDirection direction)
  420. {
  421. _field = Ensure.IsNotNull(field, nameof(field));
  422. _direction = direction;
  423. }
  424. public override BsonDocument Render(IBsonSerializer<TDocument> documentSerializer, IBsonSerializerRegistry serializerRegistry)
  425. {
  426. var renderedField = _field.Render(documentSerializer, serializerRegistry);
  427. BsonValue value;
  428. switch (_direction)
  429. {
  430. case SortDirection.Ascending:
  431. value = 1;
  432. break;
  433. case SortDirection.Descending:
  434. value = -1;
  435. break;
  436. default:
  437. throw new InvalidOperationException("Unknown value for " + typeof(SortDirection) + ".");
  438. }
  439. return new BsonDocument(renderedField.FieldName, value);
  440. }
  441. }
  442. internal sealed class GeoHaystackIndexKeyDefinition<TDocument> : IndexKeysDefinition<TDocument>
  443. {
  444. private readonly FieldDefinition<TDocument> _field;
  445. private readonly FieldDefinition<TDocument> _additionalFieldName;
  446. public GeoHaystackIndexKeyDefinition(FieldDefinition<TDocument> field, FieldDefinition<TDocument> additionalFieldName = null)
  447. {
  448. _field = Ensure.IsNotNull(field, nameof(field));
  449. _additionalFieldName = additionalFieldName;
  450. }
  451. public override BsonDocument Render(IBsonSerializer<TDocument> documentSerializer, IBsonSerializerRegistry serializerRegistry)
  452. {
  453. var renderedField = _field.Render(documentSerializer, serializerRegistry);
  454. var document = new BsonDocument(renderedField.FieldName, "geoHaystack");
  455. if (_additionalFieldName != null)
  456. {
  457. var additionalRenderedField = _additionalFieldName.Render(documentSerializer, serializerRegistry);
  458. document.Add(additionalRenderedField.FieldName, 1);
  459. }
  460. return document;
  461. }
  462. }
  463. internal sealed class SimpleIndexKeyDefinition<TDocument> : IndexKeysDefinition<TDocument>
  464. {
  465. private readonly FieldDefinition<TDocument> _field;
  466. private readonly string _type;
  467. public SimpleIndexKeyDefinition(FieldDefinition<TDocument> field, string type)
  468. {
  469. _field = Ensure.IsNotNull(field, nameof(field));
  470. _type = type;
  471. }
  472. public override BsonDocument Render(IBsonSerializer<TDocument> documentSerializer, IBsonSerializerRegistry serializerRegistry)
  473. {
  474. var renderedField = _field.Render(documentSerializer, serializerRegistry);
  475. return new BsonDocument(renderedField.FieldName, _type);
  476. }
  477. }
  478. }