Mime select server-side and must/should toggle for search query

This commit is contained in:
simon987 2018-04-15 16:10:15 -04:00
parent 6c7d01dbcb
commit ba114cfa34
3 changed files with 152 additions and 70 deletions

40
run.py
View File

@ -64,7 +64,7 @@ def download(doc_id):
extension = "" if doc["extension"] is None or doc["extension"] == "" else "." + doc["extension"] extension = "" if doc["extension"] is None or doc["extension"] == "" else "." + doc["extension"]
full_path = os.path.join(directory.path, doc["path"], doc["name"] + extension) full_path = os.path.join(directory.path, doc["path"], doc["name"] + extension)
return send_file(full_path) return send_file(full_path, mimetype=doc["mime"])
@app.route("/thumb/<doc_id>") @app.route("/thumb/<doc_id>")
@ -96,31 +96,9 @@ def thumb(doc_id):
@app.route("/") @app.route("/")
def search_page(): def search_page():
mime_map = defaultdict(list) mime_map = search.get_mime_map()
mime_list = []
mime_types = search.get_mime_types()
for mime in mime_types: return render_template("search.html", directories=storage.dirs(), mime_map=mime_map)
splited_mime = os.path.split(mime["key"])
mime_map[splited_mime[0]].append(splited_mime[1])
for mime in mime_map:
category = dict()
category["text"] = mime
children = []
for m in mime_map[mime]:
child = dict()
child["text"] = m
child["id"] = mime + "/" + m
children.append(child)
if len(children) > 0:
category["children"] = children
mime_list.append(category)
return render_template("search.html", directories=storage.dirs(), mime_list=mime_list)
@app.route("/list") @app.route("/list")
@ -128,12 +106,18 @@ def search_liste_page():
return render_template("searchList.html") return render_template("searchList.html")
@app.route("/search") @app.route("/search", methods=['POST'])
def search_route(): def search_route():
query = request.args.get("q") query = request.json["q"]
query = "" if query is None else query query = "" if query is None else query
page = search.search(query)
size_min = request.json["size_min"]
size_max = request.json["size_max"]
mime_types = request.json["mime_types"]
must_match = request.json["must_match"]
page = search.search(query, size_min, size_max, mime_types, must_match)
return json.dumps(page) return json.dumps(page)

View File

@ -1,5 +1,5 @@
import json import json
import os
import elasticsearch import elasticsearch
import requests import requests
from elasticsearch import helpers from elasticsearch import helpers
@ -56,23 +56,65 @@ class Search:
query = self.es.search(body={ query = self.es.search(body={
"aggs": { "aggs": {
"mimeTypes": { "mimeTypes": {
"terms": {"field": "mime_kw"} "terms": {
"field": "mime_kw",
"size": 10000
}
} }
} }
}) })
return query["aggregations"]["mimeTypes"]["buckets"] return query["aggregations"]["mimeTypes"]["buckets"]
def search(self, query): def get_mime_map(self):
mime_map = []
for mime in self.get_mime_types():
splited_mime = os.path.split(mime["key"])
child = dict()
child["text"] = splited_mime[1] + " (" + str(mime["doc_count"]) + ")"
child["id"] = mime["key"]
mime_category_exists = False
for category in mime_map:
if category["text"] == splited_mime[0]:
category["children"].append(child)
mime_category_exists = True
break
if not mime_category_exists:
mime_map.append({"text": splited_mime[0], "children": [child]})
return mime_map
def search(self, query, size_min, size_max, mime_types, must_match):
print(query) print(query)
print(size_min)
print(size_max)
page = self.es.search(body={"query": condition = "must" if must_match else "should"
{"multi_match": {
"query": query, page = self.es.search(body={
"fields": ["name", "content", "album", "artist", "title", "genre", "album_artist"], "query": {
"operator": "and" "bool": {
}}, condition: {
"multi_match": {
"query": query,
"fields": ["name", "content", "album", "artist", "title", "genre",
"album_artist"],
"operator": "and"
}
},
"filter": [
{"range": {"size": {"gte": size_min, "lte": size_max}}},
{"terms": {"mime": mime_types}}
]
}
},
"sort": [ "sort": [
"_score" "_score"
], ],

View File

@ -143,6 +143,12 @@
overflow: auto; overflow: auto;
} }
.btn-xs {
padding: .1rem .3rem;
font-size: .875rem;
border-radius: .2rem;
}
</style> </style>
<div class="container"> <div class="container">
@ -153,7 +159,13 @@
<div class="form-group"> <div class="form-group">
<input id="pathBar" type="search" class="form-control" placeholder="Path"> <input id="pathBar" type="search" class="form-control" placeholder="Path">
</div> </div>
<div class="form-group"> <div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text">
<span >Must match&nbsp</span>
<input type="checkbox" onclick="toggleSearchBar()" checked>
</div>
</div>
<input id="searchBar" type="search" class="form-control" placeholder="Search"> <input id="searchBar" type="search" class="form-control" placeholder="Search">
</div> </div>
@ -172,7 +184,9 @@
</div> </div>
<div class="col"> <div class="col">
<label for="mime">Mime types</label> <label>Mime types</label>
<button class="btn btn-xs btn-success" onclick="toggleTree()" style="float: right">Toggle</button>
<div class="tree"></div> <div class="tree"></div>
</div> </div>
@ -183,14 +197,20 @@
selection: { selection: {
mode: 'checkbox' mode: 'checkbox'
}, },
data: {{ mime_list | tojson }} data: {{ mime_map | tojson }}
}); });
new InspireTreeDOM(tree, { new InspireTreeDOM(tree, {
target: '.tree' target: '.tree'
}); });
//Select all //Select all
tree.select() tree.select();
tree.on("node.click", function(event, node, handler) {
event.preventTreeDefault();
handler();
searchQueued = true;
})
</script> </script>
</div> </div>
@ -223,9 +243,28 @@
<script> <script>
var searchBar = document.getElementById("searchBar"); var searchBar = document.getElementById("searchBar");
var must_match = true;
var scroll_id = null; var scroll_id = null;
var coolingDown = false;
var docCount = 0; var docCount = 0;
var treeSelected = false;
var searchQueued = false;
var coolingDown = false;
function toggleSearchBar() {
must_match = !must_match;
searchQueued = true;
}
function toggleTree() {
if (treeSelected) {
tree.select();
} else {
tree.deselect();
}
treeSelected = !treeSelected;
searchQueued = true;
}
function makeStatsCard(searchResult) { function makeStatsCard(searchResult) {
@ -566,6 +605,7 @@
window.addEventListener("scroll", function () { window.addEventListener("scroll", function () {
if (!coolingDown) { if (!coolingDown) {
var threshold = 200; var threshold = 200;
if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight - threshold) { if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight - threshold) {
@ -591,9 +631,12 @@
initPopover(); initPopover();
if(hits.length !== 0) { if (hits.length !== 0) {
coolingDown = false; coolingDown = false;
} }
console.log(hits.length)
} }
}; };
xhttp.open("GET", "/scroll?scroll_id=" + scroll_id, true); xhttp.open("GET", "/scroll?scroll_id=" + scroll_id, true);
@ -603,11 +646,25 @@
} }
}); });
function getSelectedMimeTypes() {
var mimeTypes = [];
var selected = tree.selected();
for (var i = 0; i < selected.length; i++) {
//Only get children
if (selected[i].text.indexOf("(") !== -1) {
mimeTypes.push(selected[i].id);
}
}
return mimeTypes
}
function search() { function search() {
if (!coolingDown) {
coolingDown = true; if (searchQueued === true) {
searchQueued = false;
//Clear old search results //Clear old search results
var searchResults = document.getElementById("searchResults"); var searchResults = document.getElementById("searchResults");
@ -617,8 +674,6 @@
var query = searchBar.value; var query = searchBar.value;
console.log("query: " + query);
var xhttp = new XMLHttpRequest(); var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() { xhttp.onreadystatechange = function() {
if (this.readyState === 4 && this.status === 200) { if (this.readyState === 4 && this.status === 200) {
@ -648,25 +703,32 @@
//Initialise download/view button popover //Initialise download/view button popover
initPopover(); initPopover();
window.setTimeout(function() {
coolingDown = false;
}, 100);
} }
}; };
xhttp.open("GET", "/search?q=" + query, true);
xhttp.send(); xhttp.open("POST", "/search", true);
var postBody = {};
postBody.q = query;
postBody.size_min = size_min;
postBody.size_max = size_max;
postBody.mime_types = getSelectedMimeTypes();
postBody.must_match = must_match;
xhttp.setRequestHeader('content-type', 'application/json');
xhttp.send(JSON.stringify(postBody));
} }
} }
var pathAutoComplete; var pathAutoComplete;
var size_min = 0;
var size_max = 10000000000000;
searchBar.addEventListener("keyup", function () { searchBar.addEventListener("keyup", function () {
search(); searchQueued = true;
}); });
//Size slider //Size slider
$("#sizeSlider").ionRangeSlider({ var sizeSlider = $("#sizeSlider").ionRangeSlider({
type: "double", type: "double",
grid: false, grid: false,
force_edges: true, force_edges: true,
@ -687,12 +749,16 @@
return humanFileSize(num * num * num) return humanFileSize(num * num * num)
}, },
onChange: function(e) { onChange: function(e) {
var from = (e.from * e.from * e.from); size_min = (e.from * e.from * e.from);
var to = (e.to * e.to * e.to); size_max = (e.to * e.to * e.to);
search(); if (e.to >= 3684) {
size_max = 10000000000000;
}
searchQueued = true;
} }
}); })[0];
//Directories select //Directories select
document.getElementById("directories").addEventListener("change", function () { document.getElementById("directories").addEventListener("change", function () {
@ -701,20 +767,10 @@
$(selectedDirs).each(function(){ $(selectedDirs).each(function(){
selected.push($(this).val()); selected.push($(this).val());
}); });
console.log(selected);
}); });
//Mime types select window.setInterval(search, 75)
document.getElementById("mime").addEventListener("change", function () {
var selectedMimes = $('#mime').find('option:selected');
var selected = [];
$(selectedMimes).each(function(){
selected.push($(this).val());
});
console.log(selected);
});
</script> </script>
</div> </div>