PageRenderTime 56ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/ToMigrate/Raven.Database/Indexing/AnonymousObjectToLuceneDocumentConverter.cs

http://github.com/ayende/ravendb
C# | 617 lines | 524 code | 72 blank | 21 comment | 178 complexity | 2f758c36ad5872a73a13913db79459d8 MD5 | raw file
Possible License(s): GPL-3.0, MPL-2.0-no-copyleft-exception, LGPL-2.1, Apache-2.0, BSD-3-Clause, CC-BY-SA-3.0
  1. //-----------------------------------------------------------------------
  2. // <copyright file="AnonymousObjectToLuceneDocumentConverter.cs" company="Hibernating Rhinos LTD">
  3. // Copyright (c) Hibernating Rhinos LTD. All rights reserved.
  4. // </copyright>
  5. //-----------------------------------------------------------------------
  6. using System;
  7. using System.Collections;
  8. using System.Collections.Generic;
  9. using System.ComponentModel;
  10. using System.Globalization;
  11. using System.IO;
  12. using System.Linq;
  13. using Lucene.Net.Documents;
  14. using Lucene.Net.Search;
  15. using Raven.Abstractions.Extensions;
  16. using Raven.Abstractions.Logging;
  17. using Raven.Database.Linq;
  18. using Raven.Imports.Newtonsoft.Json;
  19. using Raven.Imports.Newtonsoft.Json.Linq;
  20. using Raven.Abstractions;
  21. using Raven.Abstractions.Data;
  22. using Raven.Abstractions.Indexing;
  23. using Raven.Abstractions.Linq;
  24. using Raven.Database.Extensions;
  25. using Raven.Json.Linq;
  26. using System.Runtime.CompilerServices;
  27. using Sparrow;
  28. namespace Raven.Database.Indexing
  29. {
  30. internal class AnonymousObjectToLuceneDocumentConverter
  31. {
  32. private readonly AbstractViewGenerator viewGenerator;
  33. private readonly ILog log;
  34. private readonly DocumentDatabase database;
  35. private readonly IndexDefinition indexDefinition;
  36. private readonly List<int> multipleItemsSameFieldCount = new List<int>();
  37. private readonly Dictionary<FieldCacheKey, Field> fieldsCache = new Dictionary<FieldCacheKey, Field>(Comparer);
  38. private readonly Dictionary<FieldCacheKey, NumericField> numericFieldsCache = new Dictionary<FieldCacheKey, NumericField>(Comparer);
  39. public AnonymousObjectToLuceneDocumentConverter(DocumentDatabase database, IndexDefinition indexDefinition, AbstractViewGenerator viewGenerator, ILog log)
  40. {
  41. this.database = database;
  42. this.indexDefinition = indexDefinition;
  43. this.viewGenerator = viewGenerator;
  44. this.log = log;
  45. }
  46. public IEnumerable<AbstractField> Index(object val, PropertyAccessor accessor, Field.Store defaultStorage)
  47. {
  48. return from property in accessor.Properies
  49. where property.Key != Constants.DocumentIdFieldName
  50. from field in CreateFields(property.Key, property.Value(val), defaultStorage)
  51. select field;
  52. }
  53. public IEnumerable<AbstractField> Index(RavenJObject document, Field.Store defaultStorage)
  54. {
  55. return from property in document
  56. where property.Key != Constants.DocumentIdFieldName
  57. from field in CreateFields(property.Key, GetPropertyValue(property.Value), defaultStorage)
  58. select field;
  59. }
  60. private static object GetPropertyValue(RavenJToken property)
  61. {
  62. switch (property.Type)
  63. {
  64. case JTokenType.Array:
  65. case JTokenType.Object:
  66. return property.ToString(Formatting.None);
  67. default:
  68. return property.Value<object>();
  69. }
  70. }
  71. /// <summary>
  72. /// This method generate the fields for indexing documents in lucene from the values.
  73. /// Given a name and a value, it has the following behavior:
  74. /// * If the value is enumerable, index all the items in the enumerable under the same field name
  75. /// * If the value is null, create a single field with the supplied name with the unanalyzed value 'NULL_VALUE'
  76. /// * If the value is string or was set to not analyzed, create a single field with the supplied name
  77. /// * If the value is date, create a single field with millisecond precision with the supplied name
  78. /// * If the value is numeric (int, long, double, decimal, or float) will create two fields:
  79. /// 1. with the supplied name, containing the numeric value as an unanalyzed string - useful for direct queries
  80. /// 2. with the name: name +'_Range', containing the numeric value in a form that allows range queries
  81. /// </summary>
  82. public IEnumerable<AbstractField> CreateFields(string name, object value, Field.Store defaultStorage, bool nestedArray = false, Field.TermVector defaultTermVector = Field.TermVector.NO, Field.Index? analyzed = null)
  83. {
  84. if (string.IsNullOrWhiteSpace(name))
  85. throw new ArgumentException("Field must be not null, not empty and cannot contain whitespace", "name");
  86. if (char.IsLetter(name[0]) == false && name[0] != '_')
  87. {
  88. name = "_" + name;
  89. }
  90. if (viewGenerator.IsSpatialField(name))
  91. return viewGenerator.GetSpatialField(name).CreateIndexableFields(value);
  92. return CreateRegularFields(name, value, defaultStorage, nestedArray, defaultTermVector, analyzed);
  93. }
  94. private IEnumerable<AbstractField> CreateRegularFields(string name, object value, Field.Store defaultStorage, bool nestedArray = false, Field.TermVector defaultTermVector = Field.TermVector.NO, Field.Index? analyzed = null)
  95. {
  96. var fieldIndexingOptions = analyzed ?? indexDefinition.GetIndex(name, null);
  97. var storage = indexDefinition.GetStorage(name, defaultStorage);
  98. var termVector = indexDefinition.GetTermVector(name, defaultTermVector);
  99. if (fieldIndexingOptions == Field.Index.NO && storage == Field.Store.NO && termVector == Field.TermVector.NO)
  100. {
  101. yield break;
  102. }
  103. if (fieldIndexingOptions == Field.Index.NO && storage == Field.Store.NO)
  104. {
  105. fieldIndexingOptions = Field.Index.ANALYZED; // we have some sort of term vector, forcing index to be analyzed, then.
  106. }
  107. if (value == null)
  108. {
  109. yield return CreateFieldWithCaching(name, Constants.NullValue, storage, Field.Index.NOT_ANALYZED_NO_NORMS, Field.TermVector.NO);
  110. yield break;
  111. }
  112. CheckIfSortOptionsAndInputTypeMatch(name, value);
  113. if (Equals(value, string.Empty))
  114. {
  115. yield return CreateFieldWithCaching(name, Constants.EmptyString, storage,
  116. Field.Index.NOT_ANALYZED_NO_NORMS, Field.TermVector.NO);
  117. yield break;
  118. }
  119. var dynamicNullObject = value as DynamicNullObject;
  120. if (ReferenceEquals(dynamicNullObject, null) == false)
  121. {
  122. if (dynamicNullObject.IsExplicitNull)
  123. {
  124. var sortOptions = indexDefinition.GetSortOption(name, query: null);
  125. if (sortOptions == null ||
  126. sortOptions.Value == SortOptions.None ||
  127. sortOptions.Value == SortOptions.String ||
  128. sortOptions.Value == SortOptions.StringVal ||
  129. sortOptions.Value == SortOptions.Custom)
  130. {
  131. yield return CreateFieldWithCaching(name, Constants.NullValue, storage,
  132. Field.Index.NOT_ANALYZED_NO_NORMS, Field.TermVector.NO);
  133. }
  134. foreach (var field in CreateNumericFieldWithCaching(name, GetNullValueForSorting(sortOptions), storage, termVector))
  135. yield return field;
  136. }
  137. yield break;
  138. }
  139. var boostedValue = value as BoostedValue;
  140. if (boostedValue != null)
  141. {
  142. foreach (var field in CreateFields(name, boostedValue.Value, storage, false, termVector))
  143. {
  144. field.Boost = boostedValue.Boost;
  145. field.OmitNorms = false;
  146. yield return field;
  147. }
  148. yield break;
  149. }
  150. var abstractField = value as AbstractField;
  151. if (abstractField != null)
  152. {
  153. yield return abstractField;
  154. yield break;
  155. }
  156. var bytes = value as byte[];
  157. if (bytes != null)
  158. {
  159. yield return CreateBinaryFieldWithCaching(name, bytes, storage, fieldIndexingOptions, termVector);
  160. yield break;
  161. }
  162. var itemsToIndex = value as IEnumerable;
  163. if (itemsToIndex != null && ShouldTreatAsEnumerable(itemsToIndex))
  164. {
  165. int count = 1;
  166. if (nestedArray == false)
  167. yield return new Field(name + "_IsArray", "true", storage, Field.Index.NOT_ANALYZED_NO_NORMS, Field.TermVector.NO);
  168. foreach (var itemToIndex in itemsToIndex)
  169. {
  170. if (!CanCreateFieldsForNestedArray(itemToIndex, fieldIndexingOptions))
  171. continue;
  172. multipleItemsSameFieldCount.Add(count++);
  173. foreach (var field in CreateFields(name, itemToIndex, storage, nestedArray: true, defaultTermVector: defaultTermVector, analyzed: analyzed))
  174. yield return field;
  175. multipleItemsSameFieldCount.RemoveAt(multipleItemsSameFieldCount.Count - 1);
  176. }
  177. yield break;
  178. }
  179. if (Equals(fieldIndexingOptions, Field.Index.NOT_ANALYZED) ||
  180. Equals(fieldIndexingOptions, Field.Index.NOT_ANALYZED_NO_NORMS))// explicitly not analyzed
  181. {
  182. // date time, time span and date time offset have the same structure fo analyzed and not analyzed.
  183. if (!(value is DateTime) && !(value is DateTimeOffset) && !(value is TimeSpan))
  184. {
  185. yield return CreateFieldWithCaching(name, value.ToString(), storage,
  186. indexDefinition.GetIndex(name, Field.Index.NOT_ANALYZED_NO_NORMS), termVector);
  187. yield break;
  188. }
  189. }
  190. if (value is string)
  191. {
  192. var index = indexDefinition.GetIndex(name, Field.Index.ANALYZED);
  193. yield return CreateFieldWithCaching(name, value.ToString(), storage, index, termVector);
  194. yield break;
  195. }
  196. if (value is TimeSpan)
  197. {
  198. var val = (TimeSpan)value;
  199. yield return CreateFieldWithCaching(name, val.ToString("c", CultureInfo.InvariantCulture), storage,
  200. indexDefinition.GetIndex(name, Field.Index.NOT_ANALYZED_NO_NORMS), termVector);
  201. }
  202. else if (value is DateTime)
  203. {
  204. var val = (DateTime)value;
  205. var dateAsString = val.GetDefaultRavenFormat();
  206. if (val.Kind == DateTimeKind.Utc)
  207. dateAsString += "Z";
  208. yield return CreateFieldWithCaching(name, dateAsString, storage,
  209. indexDefinition.GetIndex(name, Field.Index.NOT_ANALYZED_NO_NORMS), termVector);
  210. }
  211. else if (value is DateTimeOffset)
  212. {
  213. var val = (DateTimeOffset)value;
  214. string dtoStr;
  215. if (Equals(fieldIndexingOptions, Field.Index.NOT_ANALYZED) || Equals(fieldIndexingOptions, Field.Index.NOT_ANALYZED_NO_NORMS))
  216. {
  217. dtoStr = val.ToString(Default.DateTimeOffsetFormatsToWrite, CultureInfo.InvariantCulture);
  218. }
  219. else
  220. {
  221. dtoStr = val.UtcDateTime.GetDefaultRavenFormat(true);
  222. }
  223. yield return CreateFieldWithCaching(name, dtoStr, storage,
  224. indexDefinition.GetIndex(name, Field.Index.NOT_ANALYZED_NO_NORMS), termVector);
  225. }
  226. else if (value is bool)
  227. {
  228. yield return new Field(name, ((bool)value) ? "true" : "false", storage,
  229. indexDefinition.GetIndex(name, Field.Index.NOT_ANALYZED_NO_NORMS), termVector);
  230. }
  231. else if (value is double)
  232. {
  233. var d = (double)value;
  234. yield return CreateFieldWithCaching(name, d.ToString("r", CultureInfo.InvariantCulture), storage,
  235. indexDefinition.GetIndex(name, Field.Index.NOT_ANALYZED_NO_NORMS), termVector);
  236. }
  237. else if (value is decimal)
  238. {
  239. var d = (decimal)value;
  240. var s = d.ToString(CultureInfo.InvariantCulture);
  241. if (s.Contains('.'))
  242. {
  243. s = s.TrimEnd('0');
  244. if (s.EndsWith("."))
  245. s = s.Substring(0, s.Length - 1);
  246. }
  247. yield return CreateFieldWithCaching(name, s, storage,
  248. indexDefinition.GetIndex(name, Field.Index.NOT_ANALYZED_NO_NORMS), termVector);
  249. }
  250. else if (value is Enum)
  251. {
  252. yield return CreateFieldWithCaching(name, value.ToString(), storage,
  253. indexDefinition.GetIndex(name, Field.Index.ANALYZED_NO_NORMS), termVector);
  254. }
  255. else if (value is IConvertible) // we need this to store numbers in invariant format, so JSON could read them
  256. {
  257. var convert = ((IConvertible)value);
  258. yield return CreateFieldWithCaching(name, convert.ToString(CultureInfo.InvariantCulture), storage,
  259. indexDefinition.GetIndex(name, Field.Index.NOT_ANALYZED_NO_NORMS), termVector);
  260. }
  261. else if (value is IDynamicJsonObject)
  262. {
  263. var inner = ((IDynamicJsonObject)value).Inner;
  264. yield return CreateFieldWithCaching(name + "_ConvertToJson", "true", Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS, Field.TermVector.NO);
  265. yield return CreateFieldWithCaching(name, inner.ToString(Formatting.None), storage,
  266. indexDefinition.GetIndex(name, Field.Index.NOT_ANALYZED_NO_NORMS), termVector);
  267. }
  268. else
  269. {
  270. var jsonVal = RavenJToken.FromObject(value).ToString(Formatting.None);
  271. if (jsonVal.StartsWith("{") || jsonVal.StartsWith("["))
  272. yield return CreateFieldWithCaching(name + "_ConvertToJson", "true", Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS, Field.TermVector.NO);
  273. else if (jsonVal.StartsWith("\"") && jsonVal.EndsWith("\"") && jsonVal.Length > 1)
  274. jsonVal = jsonVal.Substring(1, jsonVal.Length - 2);
  275. yield return CreateFieldWithCaching(name, jsonVal, storage,
  276. indexDefinition.GetIndex(name, Field.Index.NOT_ANALYZED_NO_NORMS), termVector);
  277. }
  278. foreach (var numericField in CreateNumericFieldWithCaching(name, value, storage, termVector))
  279. yield return numericField;
  280. }
  281. private void CheckIfSortOptionsAndInputTypeMatch(string name, object value)
  282. {
  283. if (log == null)
  284. return;
  285. if (value == null)
  286. return;
  287. var sortOption = indexDefinition.GetSortOption(name, null);
  288. if (sortOption.HasValue == false)
  289. return;
  290. switch (sortOption.Value)
  291. {
  292. case SortOptions.Double:
  293. case SortOptions.Float:
  294. case SortOptions.Int:
  295. case SortOptions.Long:
  296. case SortOptions.Short:
  297. if (value is int || value is short || value is double || value is long || value is float || value is decimal)
  298. return;
  299. log.Warn(string.Format("Field '{1}' in index '{0}' has numerical sorting enabled, but input value '{2}' was a '{3}'.", indexDefinition.Name, name, value, value.GetType()));
  300. break;
  301. default:
  302. return;
  303. }
  304. }
  305. private static object GetNullValueForSorting(SortOptions? sortOptions)
  306. {
  307. switch (sortOptions)
  308. {
  309. case SortOptions.Short:
  310. case SortOptions.Int:
  311. return int.MinValue;
  312. case SortOptions.Double:
  313. return double.MinValue;
  314. case SortOptions.Float:
  315. return float.MinValue;
  316. // ReSharper disable RedundantCaseLabel
  317. case SortOptions.Long:
  318. // to be able to sort on timestamps
  319. case SortOptions.String:
  320. case SortOptions.StringVal:
  321. case SortOptions.None:
  322. case SortOptions.Custom:
  323. // ReSharper restore RedundantCaseLabel
  324. default:
  325. return long.MinValue;
  326. }
  327. }
  328. private IEnumerable<AbstractField> CreateNumericFieldWithCaching(string name, object value, Field.Store defaultStorage, Field.TermVector termVector)
  329. {
  330. var fieldName = name + "_Range";
  331. var storage = indexDefinition.GetStorage(name, defaultStorage);
  332. var cacheKey = new FieldCacheKey(name, null, storage, termVector, multipleItemsSameFieldCount.ToArray());
  333. NumericField numericField;
  334. if (numericFieldsCache.TryGetValue(cacheKey, out numericField) == false)
  335. {
  336. numericFieldsCache[cacheKey] = numericField = new NumericField(fieldName, storage, true);
  337. }
  338. if (value is TimeSpan)
  339. {
  340. yield return numericField.SetLongValue(((TimeSpan)value).Ticks);
  341. }
  342. else if (value is int)
  343. {
  344. var sortOption = indexDefinition.GetSortOption(name, query: null);
  345. if (sortOption == SortOptions.Long)
  346. yield return numericField.SetLongValue((int)value);
  347. else if (sortOption == SortOptions.Float)
  348. yield return numericField.SetFloatValue((int)value);
  349. else if (sortOption == SortOptions.Double)
  350. yield return numericField.SetDoubleValue((int)value);
  351. else
  352. yield return numericField.SetIntValue((int)value);
  353. }
  354. else if (value is long)
  355. {
  356. var sortOption = indexDefinition.GetSortOption(name, query: null);
  357. if (sortOption == SortOptions.Double)
  358. yield return numericField.SetDoubleValue((long)value);
  359. else if (sortOption == SortOptions.Float)
  360. yield return numericField.SetFloatValue((long)value);
  361. else if (sortOption == SortOptions.Int)
  362. yield return numericField.SetIntValue(Convert.ToInt32((long)value));
  363. else
  364. yield return numericField.SetLongValue((long)value);
  365. }
  366. else if (value is decimal)
  367. {
  368. var sortOption = indexDefinition.GetSortOption(name, query: null);
  369. if (sortOption == SortOptions.Float)
  370. yield return numericField.SetFloatValue(Convert.ToSingle((decimal)value));
  371. else if (sortOption == SortOptions.Int)
  372. yield return numericField.SetIntValue(Convert.ToInt32((decimal)value));
  373. else if (sortOption == SortOptions.Long)
  374. yield return numericField.SetLongValue(Convert.ToInt64((decimal)value));
  375. else
  376. yield return numericField.SetDoubleValue((double)(decimal)value);
  377. }
  378. else if (value is float)
  379. {
  380. var sortOption = indexDefinition.GetSortOption(name, query: null);
  381. if (sortOption == SortOptions.Double)
  382. yield return numericField.SetDoubleValue((float)value);
  383. else if (sortOption == SortOptions.Int)
  384. yield return numericField.SetIntValue(Convert.ToInt32((float)value));
  385. else if (sortOption == SortOptions.Long)
  386. yield return numericField.SetLongValue(Convert.ToInt64((float)value));
  387. else
  388. yield return numericField.SetFloatValue((float)value);
  389. }
  390. else if (value is double)
  391. {
  392. var sortOption = indexDefinition.GetSortOption(name, query: null);
  393. if (sortOption == SortOptions.Float)
  394. yield return numericField.SetFloatValue(Convert.ToSingle((double)value));
  395. else if (sortOption == SortOptions.Int)
  396. yield return numericField.SetIntValue(Convert.ToInt32((double)value));
  397. else if (sortOption == SortOptions.Long)
  398. yield return numericField.SetLongValue(Convert.ToInt64((double)value));
  399. else
  400. yield return numericField.SetDoubleValue((double)value);
  401. }
  402. }
  403. public static bool ShouldTreatAsEnumerable(object itemsToIndex)
  404. {
  405. if (itemsToIndex == null)
  406. return false;
  407. if (itemsToIndex is DynamicJsonObject)
  408. return false;
  409. if (itemsToIndex is string)
  410. return false;
  411. if (itemsToIndex is RavenJObject)
  412. return false;
  413. if (itemsToIndex is IDictionary)
  414. return false;
  415. return true;
  416. }
  417. private Field CreateBinaryFieldWithCaching(string name, byte[] value, Field.Store store, Field.Index index, Field.TermVector termVector)
  418. {
  419. if (value.Length > 1024)
  420. throw new ArgumentException("Binary values must be smaller than 1Kb");
  421. var cacheKey = new FieldCacheKey(name, null, store, termVector, multipleItemsSameFieldCount.ToArray());
  422. Field field;
  423. var stringWriter = new StringWriter();
  424. JsonExtensions.CreateDefaultJsonSerializer().Serialize(stringWriter, value);
  425. var sb = stringWriter.GetStringBuilder();
  426. sb.Remove(0, 1); // remove prefix "
  427. sb.Remove(sb.Length - 1, 1); // remove postfix "
  428. var val = sb.ToString();
  429. if (fieldsCache.TryGetValue(cacheKey, out field) == false)
  430. {
  431. fieldsCache[cacheKey] = field = new Field(name, val, store, index, termVector);
  432. }
  433. field.SetValue(val);
  434. field.Boost = 1;
  435. field.OmitNorms = true;
  436. return field;
  437. }
  438. private static FieldCacheKeyEqualityComparer Comparer = new FieldCacheKeyEqualityComparer();
  439. private class FieldCacheKeyEqualityComparer : IEqualityComparer<FieldCacheKey>
  440. {
  441. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  442. public bool Equals(FieldCacheKey x, FieldCacheKey y)
  443. {
  444. if (x.HashKey != y.HashKey)
  445. {
  446. return false;
  447. }
  448. else // We are thinking it is possible to have collisions. This may not be true ever!
  449. {
  450. if ( x.index == y.index &&
  451. x.store == y.store &&
  452. x.termVector == y.termVector &&
  453. string.Equals(x.name, y.name))
  454. {
  455. if (x.multipleItemsSameField.Length != y.multipleItemsSameField.Length)
  456. return false;
  457. int count = x.multipleItemsSameField.Length;
  458. for ( int i = 0; i < count; i++ )
  459. {
  460. if (x.multipleItemsSameField[i] != y.multipleItemsSameField[i])
  461. return false;
  462. }
  463. return true;
  464. }
  465. else return false;
  466. }
  467. }
  468. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  469. public int GetHashCode(FieldCacheKey obj)
  470. {
  471. return obj.HashKey;
  472. }
  473. }
  474. private class FieldCacheKey
  475. {
  476. internal readonly string name;
  477. internal readonly Field.Index? index;
  478. internal readonly Field.Store store;
  479. internal readonly Field.TermVector termVector;
  480. internal readonly int[] multipleItemsSameField;
  481. private int _hashKey;
  482. // We can precalculate the hash code because all fields involved are readonly.
  483. internal int HashKey
  484. {
  485. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  486. get
  487. {
  488. if (_hashKey == 0)
  489. {
  490. unsafe
  491. {
  492. int nameHash = (name != null ? name.GetHashCode() : 0);
  493. int fieldHash = (index != null ? (byte)index : -1) << 16 | ((byte)store << 8) | (byte)termVector;
  494. int hash = Hashing.CombineInline(nameHash, fieldHash);
  495. if (multipleItemsSameField.Length > 0)
  496. {
  497. fixed (int* buffer = multipleItemsSameField)
  498. {
  499. _hashKey = (int)Hashing.XXHash32.CalculateInline((byte*)buffer, multipleItemsSameField.Length * sizeof(int), (uint)hash);
  500. }
  501. }
  502. else _hashKey = hash;
  503. }
  504. }
  505. return _hashKey;
  506. }
  507. }
  508. public FieldCacheKey(string name, Field.Index? index, Field.Store store, Field.TermVector termVector, int[] multipleItemsSameField)
  509. {
  510. this.name = name;
  511. this.index = index;
  512. this.store = store;
  513. this.termVector = termVector;
  514. this.multipleItemsSameField = multipleItemsSameField;
  515. }
  516. public override int GetHashCode()
  517. {
  518. return HashKey;
  519. }
  520. }
  521. private Field CreateFieldWithCaching(string name, string value, Field.Store store, Field.Index index, Field.TermVector termVector)
  522. {
  523. var cacheKey = new FieldCacheKey(name, index, store, termVector, multipleItemsSameFieldCount.ToArray());
  524. Field field;
  525. if (fieldsCache.TryGetValue(cacheKey, out field) == false)
  526. fieldsCache[cacheKey] = field = new Field(name, value, store, index, termVector);
  527. field.SetValue(value);
  528. field.Boost = 1;
  529. field.OmitNorms = true;
  530. return field;
  531. }
  532. private bool CanCreateFieldsForNestedArray(object value, Field.Index fieldIndexingOptions)
  533. {
  534. if (!fieldIndexingOptions.IsAnalyzed())
  535. {
  536. return true;
  537. }
  538. if (value == null || value is DynamicNullObject)
  539. {
  540. return false;
  541. }
  542. return true;
  543. }
  544. }
  545. }