1: /// <summary>
2: /// Simple faceter that accumulates DateTime objects.
3: /// </summary>
4: public class DateFaceter
5: {
6: #region Private Fields
7:
8: /// <summary>
9: /// Used to extract dates from documents. Exposing this as an action
10: /// allows a single type of DateFaceter to work for multiple types of
11: /// dates.
12: /// </summary>
13: private Func<InspireDocument, DateTime?> mDateSelector;
14:
15: /// <summary>
16: /// The name of the facet.
17: /// </summary>
18: private string mName;
19:
20: /// <summary>
21: /// This is used to partition dates first by year and then
22: /// by month.
23: /// </summary>
24: private HierarchicalPartition<int> mDatePartition;
25:
26: /// <summary>
27: /// The name of the Lucene field being faceted.
28: /// </summary>
29: private string mFieldName;
30:
31: #endregion
32:
33: #region Constructors
34:
35:
36: /// <summary>
37: /// Creates a faceter that will use the specified selector to
38: /// get date information from documents.
39: /// </summary>
40: /// <param name="name">The user-friendly label for the facet.</param>
41: /// <param name="fieldName">The name of the Lucene field.</param>
42: /// <param name="dateSelector">A function that returns the date from the document.</param>
43: public DateFaceter(string name, string fieldName, Func<InspireDocument, DateTime?> dateSelector)
44: {
45: mName = name;
46: mDateSelector = dateSelector;
47: mFieldName = fieldName;
48:
49: mDatePartition = new HierarchicalPartition<int>();
50: }
51:
52: #endregion
53:
54: #region Private Methods
55:
56: /// <summary>
57: /// Creates a new facet based on the year counts.
58: /// </summary>
59: /// <returns></returns>
60: private Facet GetFacetByYear()
61: {
62: //Create the facets
63: FacetDimension[] dimensions = new FacetDimension[mDatePartition.Keys.Length];
64: //This is used to sort the facets since sorting by the name won't give
65: //the intended order for months.
66: int[] sortKeys = new int[mDatePartition.Keys.Length];
67:
68: for (int i = 0; i < mDatePartition.Keys.Length; i++)
69: {
70: int year = mDatePartition.Keys[i];
71:
72: sortKeys[i] = year;
73:
74: //Create the criteria filter.
75: string criteria = string.Format("{0}:[{1} TO {2}]", mFieldName,
76: new DateTime(year, 1, 1).ToString(LuceneHelper.DateFormat),
77: new DateTime(year, 12, 31).ToString(LuceneHelper.DateFormat));
78:
79: dimensions[i] = new FacetDimension(sortKeys[i].ToString(),
80: mDatePartition.Get(mDatePartition.Keys[i]).Count,
81: criteria);
82: }
83:
84: Array.Sort(sortKeys, dimensions);
85:
86: return new Facet(mName, dimensions);
87: }
88:
89: /// <summary>
90: /// Creates a new facet based on the months counts for the only year.
91: /// </summary>
92: /// <returns></returns>
93: private Facet GetFacetByMonth()
94: {
95: int year = mDatePartition.Keys[0];
96:
97: //Get the year's partition.
98: HierarchicalPartition<int> yearPartition = mDatePartition.Get(year);
99:
100: //If it doesn't have at least two entries, then we won't even try to create a facet.
101: if (yearPartition.Keys.Length < 2)
102: {
103: return null;
104: }
105:
106: //Create the facets
107: FacetDimension[] dimensions = new FacetDimension[yearPartition.Keys.Length];
108: //This is used to sort the facets since sorting by the name won't give
109: //the intended order for months.
110: int[] sortKeys = new int[yearPartition.Keys.Length];
111:
112: for (int i = 0; i < yearPartition.Keys.Length; i++)
113: {
114: int month = yearPartition.Keys[i];
115:
116: sortKeys[i] = month;
117:
118: string monthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(month);
119:
120: //Build the filter criteria
121: string criteria = string.Format("{0}:[{1} TO {2}]", mFieldName,
122: new DateTime(year, month, 1).ToString(LuceneHelper.DateFormat),
123: //This is a handy trick to get the last day of the target month:
124: //from the first day of the target month, add one month, then
125: //subtract one day, which will then be the last day of the
126: //target month.
127: new DateTime(year, month, 1).AddMonths(1).AddDays(-1).ToString(LuceneHelper.DateFormat));
128:
129: //This creates a partition that has the name of the month instead of the month's numeric value.
130: dimensions[i] = new FacetDimension(monthName,
131: yearPartition.Get(month).Count, criteria);
132: }
133:
134: Array.Sort(sortKeys, dimensions);
135:
136: //This creates a facet named like "Start Date - YYYY"
137: return new Facet(mName + " - " + year, dimensions);
138: }
139:
140: #endregion
141:
142: /// <summary>
143: /// Accumulates facet counts from the specified document's data.
144: /// </summary>
145: /// <param name="document"></param>
146: public void Accumulate(InspireDocument document)
147: {
148: //if the document has a date, process it.
149: DateTime? date = mDateSelector(document);
150:
151: if (date != null)
152: {
153: //Partition the date based on the year.
154: HierarchicalPartition<int> yearPartition = mDatePartition.Get(date.Value.Year);
155: yearPartition.Count++;
156:
157: //Further partition by month.
158: HierarchicalPartition<int> monthPartition = yearPartition.Get(date.Value.Month);
159: monthPartition.Count++;
160: }
161: }
162:
163: /// <summary>
164: /// Creates a new facet based on the data observed by Accumulate.
165: /// </summary>
166: /// <returns></returns>
167: public Facet GetFacet()
168: {
169: int[] keys = mDatePartition.Keys;
170:
171: //If there is nothing to facet, return null
172: if (keys.Length == 0)
173: {
174: return null;
175: }
176: //If there is only one year, attempt to facet by month..
177: else if (keys.Length == 1)
178: {
179: return GetFacetByMonth();
180: }
181: //Otherwise, there are multiple years, so facet them.
182: else
183: {
184: return GetFacetByYear();
185: }
186: }
187: }