PageRenderTime 38ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/ucengine/src/backends/search/solr/uce_event_solr_search.erl

http://github.com/AF83/ucengine
Erlang | 258 lines | 210 code | 30 blank | 18 comment | 4 complexity | f77d262232cad58be351a6fa51a3c798 MD5 | raw file
  1. %%
  2. %% U.C.Engine - Unified Collaboration Engine
  3. %% Copyright (C) 2011 af83
  4. %%
  5. %% This program is free software: you can redistribute it and/or modify
  6. %% it under the terms of the GNU Affero General Public License as published by
  7. %% the Free Software Foundation, either version 3 of the License, or
  8. %% (at your option) any later version.
  9. %%
  10. %% This program is distributed in the hope that it will be useful,
  11. %% but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. %% GNU Affero General Public License for more details.
  14. %%
  15. %% You should have received a copy of the GNU Affero General Public License
  16. %% along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. %%
  18. -module(uce_event_solr_search).
  19. -export([add/2, commit/0, list/11, delete/2]).
  20. -include("uce.hrl").
  21. -define(DEFAULT_HOST, "http://localhost:8983/solr").
  22. -define(SOLR_UPDATE, "/update").
  23. -define(SOLR_SELECT, "/select?").
  24. -define(META_PREFIX, "metadata_").
  25. add(Domain, Event) ->
  26. [Host] = utils:get(config:get(solr), [host], [?DEFAULT_HOST]),
  27. ibrowse:send_req(Host ++ ?SOLR_UPDATE, [], post, to_solrxml(Domain, Event)),
  28. {ok, created}.
  29. commit() ->
  30. [Host] = utils:get(config:get(solr), [host], [?DEFAULT_HOST]),
  31. Commit = lists:flatten(xmerl:export_simple_element({commit, []}, xmerl_xml)),
  32. ibrowse:send_req(Host ++ ?SOLR_UPDATE, [], post, Commit),
  33. {ok, commited}.
  34. %% Encode event in solrxml format which be used to add solr index
  35. to_solrxml(Domain, #uce_event{id=Id,
  36. datetime=Datetime,
  37. location=Location,
  38. from=From,
  39. to=To,
  40. type=Type,
  41. metadata=Metadata}) ->
  42. LocationElement = [{field, [{name,"location"}], [Location]}],
  43. MetadataFlattenElement =
  44. [{field, [{name, "metadata"}], [lists:flatten([Value ++ " " || {_, Value} <- Metadata])]}],
  45. MetadataElement =
  46. lists:map(fun({Key, Value}) ->
  47. {field, [{name, "metadata_" ++ Key}], [Value]}
  48. end,
  49. Metadata),
  50. DocElements = [{field, [{name,"id"}], [Id]},
  51. {field, [{name,"domain"}], [Domain]},
  52. {field, [{name,"datetime"}], [integer_to_list(Datetime)]},
  53. {field, [{name,"type"}], [Type]},
  54. {field, [{name,"to"}], [To]},
  55. {field, [{name,"from"}], [From]}] ++
  56. LocationElement ++
  57. MetadataFlattenElement ++
  58. MetadataElement,
  59. Add = {add, [], [{doc, [], DocElements}]},
  60. lists:flatten(xmerl:export_simple_element(Add, xmerl_xml)).
  61. params_to_query([{Key, Value}|Tail]) ->
  62. Key ++ ":" ++ Value ++ case Tail of
  63. [] ->
  64. [];
  65. _ ->
  66. " AND " ++ params_to_query(Tail)
  67. end;
  68. params_to_query([Value|Tail])
  69. when is_list(Value) ->
  70. Value ++ case Tail of
  71. [] ->
  72. [];
  73. _ ->
  74. " AND " ++ params_to_query(Tail)
  75. end.
  76. list(Domain, Location, Search, From, Types, DateStart, DateEnd, Parent, Start, Rows, Order) ->
  77. [Host] = utils:get(config:get(solr), [host], [?DEFAULT_HOST]),
  78. DomainSelector = [{"domain", Domain}],
  79. LocationSelector =
  80. if
  81. Location /= "" ->
  82. [{"location", Location}];
  83. true ->
  84. []
  85. end,
  86. FromSelector =
  87. if
  88. From /= "" ->
  89. [{"from", From}];
  90. true ->
  91. []
  92. end,
  93. ParentSelector =
  94. if
  95. Parent /= "" ->
  96. [{"parent", Parent}];
  97. true ->
  98. []
  99. end,
  100. SearchSelector =
  101. if
  102. Search /= [] ->
  103. [{"metadata", string:join(Search, "+")}];
  104. true ->
  105. []
  106. end,
  107. TypesSelector =
  108. if
  109. Types == [] ->
  110. [];
  111. true ->
  112. [" (" ++ string:join(["type:" ++ Type || Type <- Types], " OR ") ++ ") "]
  113. end,
  114. TimeRange =
  115. if
  116. DateStart /= 0, DateEnd /= infinity ->
  117. "[" ++ integer_to_list(DateStart) ++ " TO " ++ integer_to_list(DateEnd) ++ "]";
  118. DateStart /= 0 ->
  119. "[" ++ integer_to_list(DateStart) ++ " TO *]";
  120. DateEnd /= infinity ->
  121. "[* TO " ++ integer_to_list(DateEnd) ++ "]";
  122. true ->
  123. []
  124. end,
  125. TimeSelector =
  126. if
  127. DateStart /= 0; DateEnd /= infinity ->
  128. [{"facet", "on"}, {"facet.field", "datetime"}, {"fq", "datetime:" ++ TimeRange}];
  129. true ->
  130. []
  131. end,
  132. Query = [{"q", params_to_query(LocationSelector ++
  133. DomainSelector ++
  134. ParentSelector ++
  135. FromSelector ++
  136. TypesSelector ++
  137. SearchSelector)}],
  138. Params = Query ++ TimeSelector ++ [{"wt", "json"},
  139. {"start", integer_to_list(Start)},
  140. {"rows", integer_to_list(Rows)},
  141. {"sort", lists:concat(["datetime ", Order])}],
  142. EncodedParams = lists:map(fun({Elem, Value}) ->
  143. lists:concat([yaws_api:url_encode(Elem),
  144. "=",
  145. yaws_api:url_encode(Value)])
  146. end,
  147. Params),
  148. case ibrowse:send_req(Host ++ ?SOLR_SELECT ++ string:join(EncodedParams, "&"), [], get) of
  149. {ok, _, _, Body} ->
  150. {NumTotal, Events} = json_to_events(Body),
  151. {ok, NumTotal, Events};
  152. {error, _} ->
  153. throw({error, bad_parameters})
  154. end.
  155. json_to_events(Body) ->
  156. {struct, JSON} = mochijson:decode(Body),
  157. {"response", {struct, ResponseJSON}} = lists:keyfind("response", 1, JSON),
  158. {"numFound", NumTotal} = lists:keyfind("numFound", 1, ResponseJSON),
  159. {"docs", {array, DocsJSON}} = lists:keyfind("docs", 1, ResponseJSON),
  160. {NumTotal, make_list_json_events(DocsJSON)}.
  161. make_list_json_events([]) ->
  162. [];
  163. make_list_json_events([{struct, Elems}|Tail]) ->
  164. case utils:get(Elems,
  165. ["id", "domain", "datetime", "location","from", "to", "type", "parent"],
  166. [none,
  167. none,
  168. none,
  169. {array, [""]},
  170. none,
  171. {array, ["all"]},
  172. none,
  173. {array, [""]}]) of
  174. [none, _, _, _, _, _, _, _, _] ->
  175. {error, bad_record};
  176. [_, none, _, _, _, _, _, _, _] ->
  177. {error, bad_record};
  178. [_, _, none, _, _, _, _, _, _] ->
  179. {error, bad_record};
  180. [_, _, _, _, _, none, _, _, _] ->
  181. {error, bad_record};
  182. [_, _, _, _, _, _, _, none, _] ->
  183. {error, bad_record};
  184. [{array, [Id]},
  185. {array, [_Domain]},
  186. Datetime,
  187. {array, [Location]},
  188. {array, [From]},
  189. {array, [To]},
  190. {array, [Type]},
  191. {array, [Parent]}] ->
  192. FlatMetadata =
  193. lists:filter(fun({Name, _}) ->
  194. if
  195. length(Name) < length(?META_PREFIX) ->
  196. false;
  197. true ->
  198. SubName = string:substr(Name, 1, length(?META_PREFIX)),
  199. if
  200. SubName == ?META_PREFIX ->
  201. true;
  202. true ->
  203. false
  204. end
  205. end
  206. end,
  207. Elems),
  208. Metadata = lists:map(fun({Name, {array, [Value]}}) ->
  209. {string:substr(Name, length(?META_PREFIX) + 1, length(Name)),
  210. Value}
  211. end,
  212. FlatMetadata),
  213. [#uce_event{id=Id,
  214. datetime=case is_list(Datetime) of
  215. true -> list_to_integer(Datetime);
  216. _ -> Datetime
  217. end,
  218. location=Location,
  219. from=From,
  220. to=To,
  221. type=Type,
  222. parent=Parent,
  223. metadata=Metadata}] ++ make_list_json_events(Tail)
  224. end.
  225. delete(_Domain, Id) ->
  226. [Host] = utils:get(config:get(solr), [host], [?DEFAULT_HOST]),
  227. ibrowse:send_req(Host ++ ?SOLR_UPDATE, [], post, "<delete><query>"++ Id ++"</query></delete>"),
  228. {ok, deleted}.