nitweb: catalog refine search algorithm
[nit.git] / src / web / api_catalog.nit
index 9bca8be..4d4eac9 100644 (file)
@@ -14,7 +14,7 @@
 
 module api_catalog
 
-import web_base
+import api_model
 import catalog
 
 redef class NitwebConfig
@@ -35,11 +35,6 @@ redef class APIRouter
        redef init do
                super
                use("/catalog/packages/", new APICatalogPackages(config))
-
-               use("/catalog/highlighted", new APICatalogHighLighted(config))
-               use("/catalog/required", new APICatalogMostRequired(config))
-               use("/catalog/bytags", new APICatalogByTags(config))
-               use("/catalog/contributors", new APICatalogContributors(config))
                use("/catalog/stats", new APICatalogStats(config))
 
                use("/catalog/tags", new APICatalogTags(config))
@@ -58,30 +53,6 @@ abstract class APICatalogHandler
        #
        # Sorting is based on mpackage score.
        var mpackages_sorter = new CatalogScoreSorter(config.catalog) is lazy
-
-       # List the 10 best packages from `cpt`
-       fun list_best(cpt: Counter[MPackage]): JsonArray do
-               var res = new JsonArray
-               var best = cpt.sort
-               for i in [1..10] do
-                       if i > best.length then break
-                       res.add best[best.length-i]
-               end
-               return res
-       end
-
-       # List packages by group.
-       fun list_by(map: MultiHashMap[Object, MPackage]): JsonObject do
-               var res = new JsonObject
-               var keys = map.keys.to_a
-               alpha_comparator.sort(keys)
-               for k in keys do
-                       var projs = map[k].to_a
-                       alpha_comparator.sort(projs)
-                       res[k.to_s.html_escape] = new JsonArray.from(projs)
-               end
-               return res
-       end
 end
 
 # Get all the packages from the catalog using pagination
@@ -100,45 +71,6 @@ class APICatalogPackages
        end
 end
 
-class APICatalogHighLighted
-       super APICatalogHandler
-
-       redef fun get(req, res) do res.json list_best(config.catalog.score)
-end
-
-class APICatalogMostRequired
-       super APICatalogHandler
-
-       redef fun get(req, res) do
-               if config.catalog.deps.not_empty then
-                       var reqs = new Counter[MPackage]
-                       for p in config.model.mpackages do
-                               reqs[p] = config.catalog.deps[p].smallers.length - 1
-                       end
-                       res.json list_best(reqs)
-                       return
-               end
-               res.json new JsonArray
-       end
-end
-
-class APICatalogByTags
-       super APICatalogHandler
-
-       redef fun get(req, res) do res.json list_by(config.catalog.tag2proj)
-end
-
-class APICatalogContributors
-       super APICatalogHandler
-
-       redef fun get(req, res) do
-               var obj = new JsonObject
-               obj["maintainers"] = new JsonArray.from(config.catalog.maint2proj.keys)
-               obj["contributors"] = new JsonArray.from(config.catalog.contrib2proj.keys)
-               res.json obj
-       end
-end
-
 # Get the catalog statistics
 #
 # `GET /stats`: return the catalog statistics
@@ -274,6 +206,73 @@ class APICatalogContributing
        end
 end
 
+redef class APIEntity
+       redef fun get(req, res) do
+               var mentity = mentity_from_uri(req, res)
+               if mentity == null then return
+
+               # Special case for packages (catalog view)
+               if mentity isa MPackage then
+                       res.raw_json mentity.to_full_catalog_json(plain=true, config.catalog)
+               else
+                       res.raw_json mentity.to_full_json
+               end
+       end
+end
+
+redef class APISearch
+       super APICatalogHandler
+
+       redef fun search(query, limit) do
+               var index = config.view.index
+
+               # lookup by name prefix
+               var matches = index.find_by_name_prefix(query).uniq.
+                       sort(lname_sorter, name_sorter, kind_sorter)
+               matches = matches.rerank.sort(vis_sorter, score_sorter)
+
+               # lookup by tags
+               var malus = matches.length
+               if config.catalog.tag2proj.has_key(query) then
+                       for mpackage in config.catalog.tag2proj[query] do
+                               matches.add new IndexMatch(mpackage, malus)
+                               malus += 1
+                       end
+                       matches = matches.uniq.rerank.sort(vis_sorter, score_sorter)
+               end
+
+               # lookup by full_name prefix
+               malus = matches.length
+               var full_matches = new IndexMatches
+               for match in index.find_by_full_name_prefix(query).
+                       sort(lfname_sorter, fname_sorter) do
+                       match.score += 1
+                       full_matches.add match
+               end
+               matches = matches.uniq
+
+               # lookup by similarity
+               malus = matches.length
+               var sim_matches = new IndexMatches
+               for match in index.find_by_similarity(query).sort(score_sorter, lname_sorter, name_sorter) do
+                       if match.score > query.length then break
+                       match.score += 1
+                       sim_matches.add match
+               end
+               matches.add_all sim_matches
+               matches = matches.uniq
+               return matches.rerank.sort(vis_sorter, score_sorter).mentities
+       end
+
+       private var score_sorter = new ScoreComparator
+       private var vis_sorter = new VisibilityComparator
+       private var name_sorter = new NameComparator
+       private var lname_sorter = new NameLengthComparator
+       private var fname_sorter = new FullNameComparator
+       private var lfname_sorter = new FullNameLengthComparator
+       private var kind_sorter = new MEntityComparator
+end
+
 redef class Catalog
 
        # Build the catalog from `mpackages`