index.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. /*
  2. * EliasDB
  3. *
  4. * Copyright 2016 Matthias Ladkau. All rights reserved.
  5. *
  6. * This Source Code Form is subject to the terms of the Mozilla Public
  7. * License, v. 2.0. If a copy of the MPL was not distributed with this
  8. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  9. */
  10. package v1
  11. import (
  12. "encoding/json"
  13. "net/http"
  14. "devt.de/krotik/eliasdb/api"
  15. "devt.de/krotik/eliasdb/graph"
  16. )
  17. /*
  18. EndpointIndexQuery is the index endpoint URL (rooted). Handles everything under index/...
  19. */
  20. const EndpointIndexQuery = api.APIRoot + APIv1 + "/index/"
  21. /*
  22. IndexEndpointInst creates a new endpoint handler.
  23. */
  24. func IndexEndpointInst() api.RestEndpointHandler {
  25. return &indexEndpoint{}
  26. }
  27. /*
  28. Handler object for search queries.
  29. */
  30. type indexEndpoint struct {
  31. *api.DefaultEndpointHandler
  32. }
  33. /*
  34. HandleGET handles a search query REST call.
  35. */
  36. func (ie *indexEndpoint) HandleGET(w http.ResponseWriter, r *http.Request, resources []string) {
  37. var err error
  38. // Check parameters
  39. if !checkResources(w, resources, 3, 3, "Need a partition, entity type (n or e) and a kind") {
  40. return
  41. }
  42. if resources[1] != "n" && resources[1] != "e" {
  43. http.Error(w, "Entity type must be n (nodes) or e (edges)", http.StatusBadRequest)
  44. return
  45. }
  46. // Check what is queried
  47. attr := r.URL.Query().Get("attr")
  48. if attr == "" {
  49. http.Error(w, "Query string for attr (attribute) is required", http.StatusBadRequest)
  50. return
  51. }
  52. phrase := r.URL.Query().Get("phrase")
  53. word := r.URL.Query().Get("word")
  54. value := r.URL.Query().Get("value")
  55. // Get the index query object
  56. var iq graph.IndexQuery
  57. if resources[1] == "n" {
  58. iq, err = api.GM.NodeIndexQuery(resources[0], resources[2])
  59. } else {
  60. iq, err = api.GM.EdgeIndexQuery(resources[0], resources[2])
  61. }
  62. if err != nil {
  63. http.Error(w, err.Error(), http.StatusInternalServerError)
  64. return
  65. } else if iq == nil {
  66. http.Error(w, "Unknown partition or node kind", http.StatusBadRequest)
  67. return
  68. }
  69. // Do the lookup
  70. var data interface{}
  71. switch {
  72. case phrase != "":
  73. data, err = iq.LookupPhrase(attr, phrase)
  74. if len(data.([]string)) == 0 {
  75. data = []string{}
  76. }
  77. case word != "":
  78. data, err = iq.LookupWord(attr, word)
  79. if len(data.(map[string][]uint64)) == 0 {
  80. data = map[string][]uint64{}
  81. }
  82. case value != "":
  83. data, err = iq.LookupValue(attr, value)
  84. if len(data.([]string)) == 0 {
  85. data = []string{}
  86. }
  87. default:
  88. http.Error(w, "Query string for either phrase, word or value is required", http.StatusBadRequest)
  89. return
  90. }
  91. // Check if there was an error
  92. if err != nil {
  93. http.Error(w, err.Error(), http.StatusInternalServerError)
  94. return
  95. }
  96. // Write data
  97. w.Header().Set("content-type", "application/json; charset=utf-8")
  98. ret := json.NewEncoder(w)
  99. ret.Encode(data)
  100. }
  101. /*
  102. SwaggerDefs is used to describe the endpoint in swagger.
  103. */
  104. func (ie *indexEndpoint) SwaggerDefs(s map[string]interface{}) {
  105. s["paths"].(map[string]interface{})["/v1/index/{partition}/{entity_type}/{kind}"] = map[string]interface{}{
  106. "get": map[string]interface{}{
  107. "summary": "Run index searches on the EliasDB datastore.",
  108. "description": "The index endpoint should be used to run index searches for either a word, phrase or a whole value. All queries must specify a kind and an node/edge attribute.",
  109. "produces": []string{
  110. "text/plain",
  111. "application/json",
  112. },
  113. "parameters": []map[string]interface{}{
  114. map[string]interface{}{
  115. "name": "partition",
  116. "in": "path",
  117. "description": "Partition to query.",
  118. "required": true,
  119. "type": "string",
  120. },
  121. map[string]interface{}{
  122. "name": "entity_type",
  123. "in": "path",
  124. "description": "Datastore entity type which should selected. " +
  125. "Either n for nodes or e for edges.",
  126. "required": true,
  127. "type": "string",
  128. },
  129. map[string]interface{}{
  130. "name": "kind",
  131. "in": "path",
  132. "description": "Node or edge kind to be queried.",
  133. "required": true,
  134. "type": "string",
  135. },
  136. map[string]interface{}{
  137. "name": "attr",
  138. "in": "query",
  139. "description": "Attribute which should contain the word, phrase or value.",
  140. "required": true,
  141. "type": "string",
  142. },
  143. map[string]interface{}{
  144. "name": "word",
  145. "in": "query",
  146. "description": "Word to search for in word queries.",
  147. "required": false,
  148. "type": "string",
  149. },
  150. map[string]interface{}{
  151. "name": "phrase",
  152. "in": "query",
  153. "description": "Phrase to search for in phrase queries.",
  154. "required": false,
  155. "type": "string",
  156. },
  157. map[string]interface{}{
  158. "name": "value",
  159. "in": "query",
  160. "description": "Value (node/edge attribute value) to search for in value queries.",
  161. "required": false,
  162. "type": "string",
  163. },
  164. },
  165. "responses": map[string]interface{}{
  166. "200": map[string]interface{}{
  167. "description": "A list of keys or when doing a word search a map with node/edge key to word positions.",
  168. },
  169. "default": map[string]interface{}{
  170. "description": "Error response",
  171. "schema": map[string]interface{}{
  172. "$ref": "#/definitions/Error",
  173. },
  174. },
  175. },
  176. },
  177. }
  178. // Add generic error object to definition
  179. s["definitions"].(map[string]interface{})["Error"] = map[string]interface{}{
  180. "description": "A human readable error mesage.",
  181. "type": "string",
  182. }
  183. }