schema.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  1. /*
  2. Licensed to the Apache Software Foundation (ASF) under one or more
  3. contributor license agreements. See the NOTICE file distributed with
  4. this work for additional information regarding copyright ownership.
  5. The ASF licenses this file to You under the Apache License, Version 2.0
  6. (the "License"); you may not use this file except in compliance with
  7. the License. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  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. var cookie_schema_browser_autoload = 'schema-browser_autoload';
  16. solrAdminApp.controller('SchemaController',
  17. function($scope, $routeParams, $location, $cookies, $timeout, Luke, Constants, Schema, Config) {
  18. $scope.resetMenu("schema", Constants.IS_COLLECTION_PAGE);
  19. $scope.refresh = function () {
  20. Luke.schema({core: $routeParams.core}, function (schema) {
  21. Luke.raw({core: $routeParams.core}, function (index) {
  22. var data = mergeIndexAndSchemaData(index, schema.schema);
  23. $scope.fieldsAndTypes = getFieldsAndTypes(data);
  24. $scope.is = {};
  25. var search = $location.search();
  26. leftbar = {};
  27. $scope.isField = $scope.isDynamicField = $scope.isType = false;
  28. $scope.showing = true;
  29. if (search.field) {
  30. $scope.selectedType = "Field";
  31. $scope.is.field = true;
  32. $scope.name = search.field;
  33. leftbar.fields = [$scope.name];
  34. var field = data.fields[$scope.name];
  35. leftbar.types = [field.type];
  36. if (field.dynamicBase) leftbar.dynamicFields = [field.dynamicBase];
  37. if (field.copySources && field.copySources.length>0) {
  38. leftbar.copyFieldSources = sortedObjectArray(field.copySources.sort());
  39. }
  40. if (field.copyDests && field.copyDests.length>0) {
  41. leftbar.copyFieldDests = sortedObjectArray(field.copyDests.sort());
  42. }
  43. $scope.fieldOrType = "field=" + $scope.name;
  44. } else if (search["dynamic-field"]) {
  45. $scope.selectedType = "Dynamic Field";
  46. $scope.is.dynamicField = true;
  47. $scope.name = search["dynamic-field"];
  48. leftbar.dynamicFields = [$scope.name];
  49. leftbar.types = [data.dynamic_fields[$scope.name].type];
  50. $scope.fieldOrType = "dynamic-field=" + $scope.name;
  51. } else if (search.type) {
  52. $scope.selectedType = "Type";
  53. $scope.is.type = true;
  54. $scope.name = search.type;
  55. leftbar.types = [$scope.name];
  56. leftbar.fields = filterFields("fields", data, $scope.name);
  57. leftbar.dynamicFields = filterFields("dynamic_fields", data, $scope.name);
  58. $scope.fieldOrType = "type=" + $scope.name;
  59. } else {
  60. $scope.showing = false;
  61. }
  62. $scope.leftbar = leftbar;
  63. $scope.core = $routeParams.core;
  64. $scope.uniqueKeyField = data.unique_key_field;
  65. $scope.similarity = data.similarity;
  66. if ($scope.similarity && $scope.similarity.className) {
  67. $scope.similarity.className = shortenPackages($scope.similarity.className);
  68. }
  69. $scope.isUniqueKeyField = ($scope.selectedType == "Field" && $scope.name == $scope.uniqueKeyField);
  70. $scope.display = getFieldProperties(data, $routeParams.core, $scope.is, $scope.name);
  71. $scope.analysis = getAnalysisInfo(data, $scope.is, $scope.name);
  72. $scope.isAutoload = $cookies[cookie_schema_browser_autoload] == "true";
  73. if ($scope.isAutoload) {
  74. $scope.toggleTerms();
  75. }
  76. $scope.types = Object.keys(schema.schema.types);
  77. });
  78. });
  79. Config.get({core: $routeParams.core}, function(data) {
  80. $scope.isSchemaUpdatable = (data.config.hasOwnProperty('schemaFactory') == false || data.config.schemaFactory.class == "ManagedIndexSchemaFactory");
  81. });
  82. };
  83. $scope.refresh();
  84. $scope.selectFieldOrType = function() {
  85. $location.search($scope.fieldOrType);
  86. }
  87. $scope.toggleAnalyzer = function(analyzer) {
  88. analyzer.show = !analyzer.show;
  89. }
  90. $scope.loadTermInfo = function() {
  91. var params = {fl: $scope.name, core: $routeParams.core};
  92. if ($scope.topTermsCount) {
  93. params.numTerms = $scope.topTermsCount;
  94. }
  95. $scope.isLoadingTerms = true;
  96. Luke.field(params, function (data) {
  97. $scope.isLoadingTerms = false;
  98. $scope.termInfo = getTermInfo(data.fields[$scope.name]);
  99. if (!$scope.topTermsCount) {
  100. $scope.topTermsCount = $scope.termInfo.termCount;
  101. }
  102. });
  103. }
  104. $scope.toggleTerms = function() {
  105. $scope.showTerms = !$scope.showTerms;
  106. if ($scope.showTerms) {
  107. $scope.loadTermInfo();
  108. }
  109. }
  110. $scope.loadAllTerms = function() {
  111. $scope.topTermsCount = $scope.termInfo.maxTerms;
  112. $scope.loadTermInfo();
  113. }
  114. $scope.toggleAutoload = function() {
  115. $scope.isAutoload = !$scope.isAutoload;
  116. $cookies[cookie_schema_browser_autoload] = $scope.isAutoload;
  117. console.log("cookie: " + $cookies[cookie_schema_browser_autoload]);
  118. }
  119. $scope.hideAll = function() {
  120. $scope.showAddField = false;
  121. $scope.showAddDynamicField = false;
  122. $scope.showAddCopyField = false;
  123. }
  124. $scope.toggleAddField = function() {
  125. if ($scope.showAddField && $scope.adding == "field") {
  126. $scope.hideAll();
  127. } else {
  128. $scope.hideAll();
  129. $scope.showAddField = true;
  130. $scope.adding = "field";
  131. $scope.newField = {
  132. stored: "true",
  133. indexed: "true",
  134. uninvertible: "true"
  135. }
  136. delete $scope.addErrors;
  137. }
  138. }
  139. $scope.addField = function() {
  140. delete $scope.addErrors;
  141. var data = {"add-field": $scope.newField};
  142. Schema.post({core: $routeParams.core}, data, function(data) {
  143. if (data.errors) {
  144. $scope.addErrors = data.errors[0].errorMessages;
  145. if (typeof $scope.addErrors === "string") {
  146. $scope.addErrors = [$scope.addErrors];
  147. }
  148. } else {
  149. $scope.added = true;
  150. $timeout(function() {
  151. $scope.showAddField = false;
  152. $scope.added = false;
  153. $scope.refresh();
  154. }, 1500);
  155. }
  156. });
  157. }
  158. $scope.toggleAddDynamicField = function() {
  159. if ($scope.showAddField && $scope.adding == "dynamicField") {
  160. $scope.hideAll();
  161. } else {
  162. $scope.hideAll();
  163. $scope.showAddField = true;
  164. $scope.adding = "dynamicField";
  165. $scope.newField = {
  166. stored: "true",
  167. indexed: "true"
  168. }
  169. delete $scope.addErrors;
  170. }
  171. }
  172. $scope.addDynamicField = function() {
  173. delete $scope.addErrors;
  174. var data = {"add-dynamic-field": $scope.newField};
  175. Schema.post({core: $routeParams.core}, data, function(data) {
  176. if (data.errors) {
  177. $scope.addErrors = data.errors[0].errorMessages;
  178. if (typeof $scope.addErrors === "string") {
  179. $scope.addErrors = [$scope.addErrors];
  180. }
  181. } else {
  182. $scope.added = true;
  183. $timeout(function() {
  184. $scope.showAddField = false;
  185. $scope.added = false;
  186. $scope.refresh();
  187. }, 1500);
  188. }
  189. });
  190. }
  191. $scope.toggleAddCopyField = function() {
  192. if ($scope.showAddCopyField) {
  193. $scope.hideAll();
  194. } else {
  195. $scope.hideAll();
  196. $scope.showAddCopyField = true;
  197. $scope.copyField = {};
  198. delete $scope.addCopyFieldErrors;
  199. }
  200. }
  201. $scope.addCopyField = function() {
  202. delete $scope.addCopyFieldErrors;
  203. var data = {"add-copy-field": $scope.copyField};
  204. Schema.post({core: $routeParams.core}, data, function(data) {
  205. if (data.errors) {
  206. $scope.addCopyFieldErrors = data.errors[0].errorMessages;
  207. if (typeof $scope.addCopyFieldErrors === "string") {
  208. $scope.addCopyFieldErrors = [$scope.addCopyFieldErrors];
  209. }
  210. } else {
  211. $scope.showAddCopyField = false;
  212. $timeout($scope.refresh, 1500);
  213. }
  214. });
  215. }
  216. $scope.toggleDelete = function() {
  217. if ($scope.showDelete) {
  218. $scope.showDelete = false;
  219. } else {
  220. if ($scope.is.field) {
  221. $scope.deleteData = {'delete-field': {name: $scope.name}};
  222. } else if ($scope.is.dynamicField) {
  223. $scope.deleteData = {'delete-dynamic-field': {name: $scope.name}};
  224. } else {
  225. alert("TYPE NOT KNOWN");
  226. }
  227. $scope.showDelete = true;
  228. }
  229. }
  230. $scope.delete = function() {
  231. Schema.post({core: $routeParams.core}, $scope.deleteData, function(data) {
  232. if (data.errors) {
  233. $scope.deleteErrors = data.errors[0].errorMessages;
  234. if (typeof $scope.deleteErrors === "string") {
  235. $scope.deleteErrors = [$scope.deleteErrors];
  236. }
  237. } else {
  238. $scope.deleted = true;
  239. $timeout(function() {
  240. $location.search("");
  241. }, 1500
  242. );
  243. }
  244. });
  245. }
  246. $scope.toggleDeleteCopyField = function(field) {
  247. field.show = !field.show;
  248. delete field.errors;
  249. }
  250. $scope.deleteCopyField = function(field, source, dest) {
  251. data = {'delete-copy-field': {source: source, dest: dest}};
  252. Schema.post({core: $routeParams.core}, data, function(data) {
  253. if (data.errors) {
  254. field.errors = data.errors[0].errorMessages;
  255. if (typeof $scope.deleteErrors === "string") {
  256. field.errors = [field.errors];
  257. }
  258. } else {
  259. field.deleted = true;
  260. $timeout($scope.refresh, 1500);
  261. }
  262. });
  263. }
  264. }
  265. );
  266. var getFieldsAndTypes = function(data) {
  267. var fieldsAndTypes = [];
  268. var fields = Object.keys(data.fields).sort();
  269. for (var i in fields) {
  270. fieldsAndTypes.push({
  271. group: "Fields",
  272. value: "field=" + fields[i],
  273. label: fields[i]
  274. });
  275. }
  276. var dynamic_fields = Object.keys(data.dynamic_fields).sort();
  277. for (var i in dynamic_fields) {
  278. fieldsAndTypes.push({
  279. group: "Dynamic Fields",
  280. value: "dynamic-field=" + dynamic_fields[i],
  281. label: dynamic_fields[i]
  282. });
  283. }
  284. var types = Object.keys(data.types).sort();
  285. for (var i in types) {
  286. fieldsAndTypes.push({
  287. group: "Types",
  288. value: "type=" + types[i],
  289. label: types[i]
  290. });
  291. }
  292. return fieldsAndTypes;
  293. };
  294. var filterFields = function(type, data, name) {
  295. var fields = [];
  296. for (var i in data.types[name].fields) {
  297. var field = data.types[name].fields[i];
  298. if (data[type][field]) {
  299. fields.push(field)
  300. }
  301. }
  302. return fields.sort();
  303. }
  304. var mergeIndexAndSchemaData = function(index, schema) {
  305. var data = {
  306. unique_key_field: null,
  307. similarity: null,
  308. key: {},
  309. fields: {},
  310. dynamic_fields: {},
  311. types: {},
  312. relations: {
  313. f_df: {},
  314. f_t: {},
  315. df_f: {},
  316. df_t: {},
  317. t_f: {},
  318. t_df: {}
  319. }
  320. };
  321. data.fields = index.fields;
  322. data.key = index.info.key;
  323. data.unique_key_field = schema.uniqueKeyField;
  324. data.similarity = schema.similarity;
  325. data.dynamic_fields = schema.dynamicFields;
  326. data.types = schema.types;
  327. for (var field in schema.fields) {
  328. data.fields[field] =
  329. $.extend({}, data.fields[field], schema.fields[field]);
  330. }
  331. for (var field in data.fields) {
  332. var copy_dests = data.fields[field].copyDests;
  333. for (var i in copy_dests) {
  334. var copy_dest = copy_dests[i];
  335. if (!data.fields[copy_dest]) {
  336. data.fields[copy_dest] = {
  337. partial: true,
  338. copySources: []
  339. };
  340. }
  341. if (data.fields[copy_dest].partial) {
  342. data.fields[copy_dest].copySources.push(field);
  343. }
  344. }
  345. var copy_sources = data.fields[field].copySources;
  346. for (var i in copy_sources) {
  347. var copy_source = copy_sources[i];
  348. if (!data.fields[copy_source]) {
  349. data.fields[copy_source] = {
  350. partial: true,
  351. copyDests: []
  352. };
  353. }
  354. if (data.fields[copy_source].partial) {
  355. data.fields[copy_source].copyDests.push(field);
  356. }
  357. }
  358. data.relations.f_t[field] = data.fields[field].type;
  359. if (!data.relations.t_f[data.fields[field].type]) {
  360. data.relations.t_f[data.fields[field].type] = [];
  361. }
  362. data.relations.t_f[data.fields[field].type].push(field);
  363. if (data.fields[field].dynamicBase) {
  364. data.relations.f_df[field] = data.fields[field].dynamicBase;
  365. if (!data.relations.df_f[data.fields[field].dynamicBase]) {
  366. data.relations.df_f[data.fields[field].dynamicBase] = [];
  367. }
  368. data.relations.df_f[data.fields[field].dynamicBase].push(field);
  369. }
  370. }
  371. for (var dynamic_field in data.dynamic_fields) {
  372. data.relations.df_t[dynamic_field] = data.dynamic_fields[dynamic_field].type;
  373. if (!data.relations.t_df[data.dynamic_fields[dynamic_field].type]) {
  374. data.relations.t_df[data.dynamic_fields[dynamic_field].type] = [];
  375. }
  376. data.relations.t_df[data.dynamic_fields[dynamic_field].type].push(dynamic_field);
  377. }
  378. return data;
  379. };
  380. var getFieldProperties = function(data, core, is, name) {
  381. var display = {};
  382. display.partialState = is.field && !!data.fields[name].partial;
  383. display.columns = [];
  384. display.rows = [];
  385. var allFlags = "";
  386. var addRow = function(name, flags) {
  387. if (flags[0]!='(') {
  388. display.rows.push({name:name, flags:flags});
  389. for (var i in flags) {
  390. if (flags[i]!="-" && allFlags.indexOf(flags[i])<0) {
  391. allFlags+=flags[i];
  392. }
  393. }
  394. } else {
  395. display.rows.push({name:name, comment:flags});
  396. }
  397. }
  398. // Identify the rows for our field property table
  399. if (is.field && data.fields[name]) {
  400. if (data.fields[name].flags) {
  401. addRow('Properties', data.fields[name].flags);
  402. }
  403. if (data.fields[name].schema) {
  404. addRow('Schema', data.fields[name].schema);
  405. }
  406. if (data.fields[name].index) {
  407. addRow('Index', data.fields[name].index);
  408. }
  409. display.docs = data.fields[name].docs;
  410. display.docsUrl = "#/" + core + "/query?q=" + name + ":[* TO *]";
  411. display.distinct = data.fields[name].distinct;
  412. display.positionIncrementGap = data.fields[name].positionIncrementGap;
  413. if (data.types[data.fields[name].type]) {
  414. display.similarity = data.types[data.fields[name].type].similarity;
  415. } else {
  416. display.similarity = null;
  417. }
  418. } else if (is.dynamicField && data.dynamic_fields[name] && data.dynamic_fields[name].flags) {
  419. addRow('Properties', data.dynamic_fields[name].flags);
  420. display.similarity = data.types[data.dynamic_fields[name].type].similarity;
  421. } else if (is.type && data.types[name]) {
  422. display.similarity = data.types[name].similarity;
  423. }
  424. if (display.similarity && display.similarity.className) {
  425. display.similarity.className = shortenPackages(display.similarity.className);
  426. }
  427. // identify columns in field property table:
  428. for (var key in data.key) {
  429. if (allFlags.indexOf(key)>=0) {
  430. display.columns.push({key: key, name: data.key[key]});
  431. }
  432. }
  433. // identify rows and cell values in field property table:
  434. for (var i in display.rows) {
  435. var row = display.rows[i];
  436. row.cells = [];
  437. if (!row.flags) {
  438. continue; // Match the special case in the LukeRequestHandler
  439. }
  440. for (var j in display.columns) {
  441. var flag = display.columns[j].key;
  442. row.cells.push({key: flag, value: row.flags.indexOf(flag)>=0});
  443. }
  444. }
  445. return display;
  446. };
  447. var getAnalysisInfo = function(data, is, name) {
  448. var analysis = {};
  449. if (is.field) {
  450. var type = data.relations.f_t[name];
  451. analysis.query = "analysis.fieldname=" + name;
  452. }
  453. else if (is.dynamicField) {
  454. var type = data.relations.df_t[name];
  455. analysis.query = "analysis.fieldtype=" + type;
  456. }
  457. else if (is.type) {
  458. var type = name;
  459. analysis.query = "analysis.fieldtype=" + name;
  460. }
  461. var processComponentType = function (label, key, componentTypeData) {
  462. if (componentTypeData) {
  463. var components = [];
  464. for (var componentName in componentTypeData) {
  465. var componentData = componentTypeData[componentName];
  466. var component = {className: componentData.className, args:[]};
  467. if (componentData.args) {
  468. for (var argName in componentData.args) {
  469. var argValue = componentData.args[argName];
  470. if (argValue == "1" || argValue == "true") {
  471. component.args.push({name: argName, booleanValue:true});
  472. } else if (argValue == "0" || argValue == "false") {
  473. component.args.push({name: argName, booleanValue:false});
  474. } else {
  475. component.args.push({name: argName, value:argValue});
  476. }
  477. }
  478. }
  479. components.push(component);
  480. }
  481. return {label: label, key: key, components: components};
  482. } else {
  483. return {label: label, key: key};
  484. }
  485. }
  486. var buildAnalyzer = function (analyzerData) {
  487. var analyzer = {};
  488. analyzer.className = analyzerData.className;
  489. analyzer.componentTypes = [];
  490. if (analyzerData.tokenizer) {
  491. analyzer.componentTypes.push(processComponentType("Char Filters", "charFilters", analyzerData.charFilters));
  492. analyzer.componentTypes.push(processComponentType("Tokenizer", "tokenizer", {tokenizer: analyzerData.tokenizer}));
  493. analyzer.componentTypes.push(processComponentType("Token Filters", "tokenFilters", analyzerData.filters));
  494. }
  495. return analyzer;
  496. }
  497. analysis.data = data.types[type];
  498. if (analysis.data) {
  499. analysis.analyzers = [
  500. {key: "index", name: "Index", detail: buildAnalyzer(analysis.data.indexAnalyzer)},
  501. {key: "query", name: "Query", detail: buildAnalyzer(analysis.data.queryAnalyzer)}
  502. ];
  503. }
  504. return analysis;
  505. }
  506. var getTermInfo = function(data) {
  507. var termInfo = {};
  508. if (data && data.topTerms) {
  509. termInfo.topTerms = [];
  510. var currentGroup = {count: 0}
  511. for (var i = 0; i < data.topTerms.length; i += 2) {
  512. var count = data.topTerms[i + 1];
  513. if (currentGroup.count != count) {
  514. currentGroup = {count: count, terms: []};
  515. termInfo.topTerms.push(currentGroup);
  516. }
  517. currentGroup.terms.push(data.topTerms[i]);
  518. }
  519. termInfo.termCount = data.topTerms.length / 2;
  520. termInfo.maxTerms = data.distinct;
  521. }
  522. if(data && data.histogram) {
  523. termInfo.histogram = [];
  524. termInfo.histogramMax = 0;
  525. for (var i = 0; i < data.histogram.length; i += 2) {
  526. termInfo.histogram.push({key: data.histogram[i], value: data.histogram[i + 1]});
  527. termInfo.histogramMax = Math.max(termInfo.histogramMax, data.histogram[i + 1]);
  528. }
  529. }
  530. return termInfo;
  531. };
  532. var sortedObjectArray = function(list) {
  533. var objarr = [];
  534. for (var i in list) {
  535. objarr.push({"name": list[i]});
  536. }
  537. return objarr;
  538. };
  539. var shortenPackages = function(className) {
  540. return className.replace("org.apache.solr", "o.a.s").replace("org.apache.lucene", "o.a.l");
  541. };