Add chips to improve precision
This commit is contained in:
parent
3cd7b78d81
commit
9839634e1b
6 changed files with 84 additions and 25 deletions
|
@ -9,7 +9,6 @@ import click
|
||||||
from .question import Question
|
from .question import Question
|
||||||
from .question_search import QuestionSearch
|
from .question_search import QuestionSearch
|
||||||
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
connections.create_connection(hosts=["localhost"])
|
connections.create_connection(hosts=["localhost"])
|
||||||
|
@ -39,6 +38,15 @@ def index():
|
||||||
"category": {
|
"category": {
|
||||||
"terms": {"field": "category"},
|
"terms": {"field": "category"},
|
||||||
},
|
},
|
||||||
|
"suggestions": {
|
||||||
|
"significant_terms": {
|
||||||
|
"field": "body",
|
||||||
|
"mutual_information": {
|
||||||
|
"include_negatives": True,
|
||||||
|
},
|
||||||
|
"size": 40,
|
||||||
|
},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -51,6 +59,9 @@ def index():
|
||||||
for bucket in response.aggregations.category.buckets
|
for bucket in response.aggregations.category.buckets
|
||||||
]
|
]
|
||||||
|
|
||||||
|
suggestions = [{"key": bucket.key, "count": bucket.doc_count}
|
||||||
|
for bucket in response.aggregations.suggestions.buckets]
|
||||||
|
|
||||||
date_facets = []
|
date_facets = []
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
|
@ -59,14 +70,17 @@ def index():
|
||||||
url = Question.url(hit)
|
url = Question.url(hit)
|
||||||
|
|
||||||
results.append({
|
results.append({
|
||||||
"id": hit.meta.id, "score": hit.meta.score,
|
"id": hit.meta.id, "score": hit.meta.score, "title": hit.title,
|
||||||
"title": hit.title, "body": summary,
|
"body": summary, "category": hit.category, "date": hit.date,
|
||||||
"category": hit.category, "date": hit.date,
|
|
||||||
"url": url,
|
"url": url,
|
||||||
})
|
})
|
||||||
|
|
||||||
return jsonify(
|
return jsonify(
|
||||||
facets={"months": date_facets, "categories": category_facets},
|
facets={
|
||||||
|
"months": date_facets,
|
||||||
|
"categories": category_facets,
|
||||||
|
},
|
||||||
|
suggestions=suggestions,
|
||||||
results=results,
|
results=results,
|
||||||
hits=round_sigfig(response.hits.total, 4),
|
hits=round_sigfig(response.hits.total, 4),
|
||||||
took=response.took / 1000,
|
took=response.took / 1000,
|
||||||
|
|
|
@ -3,7 +3,10 @@ from elasticsearch_dsl import Document, Date, Keyword, Text
|
||||||
|
|
||||||
class Question(Document):
|
class Question(Document):
|
||||||
title = Text(analyzer="snowball")
|
title = Text(analyzer="snowball")
|
||||||
body = Text(analyzer="snowball")
|
body = Text(
|
||||||
|
analyzer="snowball",
|
||||||
|
fielddata=True,
|
||||||
|
)
|
||||||
category = Keyword()
|
category = Keyword()
|
||||||
date = Date()
|
date = Date()
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
|
<div class="suggestions">
|
||||||
|
<div v-for="suggestion in json.suggestions" class="suggestion"
|
||||||
|
@click="appendToQuery(suggestion.key)">
|
||||||
|
{{suggestion.key}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="result-count">
|
<div class="result-count">
|
||||||
Ongeveer {{hits}} resultaten ({{responseTime.toFixed(2)}} seconden)
|
Ongeveer {{hits}} resultaten ({{responseTime.toFixed(2)}} seconden)
|
||||||
</div>
|
</div>
|
||||||
|
@ -30,6 +37,8 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!--pre>{{JSON.stringify(json, null, 2)}}</pre-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -40,19 +49,25 @@ import {Component, Prop, Watch} from "vue-property-decorator";
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
export default class ResultBody extends Vue {
|
export default class ResultBody extends Vue {
|
||||||
@Prop() query;
|
@Prop() value;
|
||||||
|
|
||||||
|
json = null;
|
||||||
activeCategories = [];
|
activeCategories = [];
|
||||||
hits = 0;
|
hits = 0;
|
||||||
results = [];
|
results = [];
|
||||||
responseTime = 0;
|
responseTime = 0;
|
||||||
facets = {};
|
facets = {};
|
||||||
|
|
||||||
@Watch("query")
|
@Watch("value")
|
||||||
async onQueryChanged(value) {
|
async onQueryChanged(value) {
|
||||||
await this.search();
|
await this.search();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Watch("$route")
|
||||||
|
async onRouteChanged() {
|
||||||
|
this.$emit("input", this.$route.query.q);
|
||||||
|
}
|
||||||
|
|
||||||
async toggleCategory(category) {
|
async toggleCategory(category) {
|
||||||
const name = category.category;
|
const name = category.category;
|
||||||
|
|
||||||
|
@ -67,7 +82,7 @@ export default class ResultBody extends Vue {
|
||||||
}
|
}
|
||||||
|
|
||||||
async search() {
|
async search() {
|
||||||
const query = encodeURIComponent(this.query);
|
const query = encodeURIComponent(this.value);
|
||||||
let queryString = `?q=${query}`;
|
let queryString = `?q=${query}`;
|
||||||
|
|
||||||
if (this.activeCategories.length > 0) {
|
if (this.activeCategories.length > 0) {
|
||||||
|
@ -79,12 +94,16 @@ export default class ResultBody extends Vue {
|
||||||
const url = `/api/${queryString}`;
|
const url = `/api/${queryString}`;
|
||||||
|
|
||||||
let response = await fetch(url);
|
let response = await fetch(url);
|
||||||
let json = await response.json();
|
this.json = await response.json();
|
||||||
|
|
||||||
this.results = json.results;
|
this.results = this.json.results;
|
||||||
this.hits = json.hits;
|
this.hits = this.json.hits;
|
||||||
this.responseTime = json.took;
|
this.responseTime = this.json.took;
|
||||||
this.facets = json.facets;
|
this.facets = this.json.facets;
|
||||||
|
}
|
||||||
|
|
||||||
|
appendToQuery(value) {
|
||||||
|
this.$emit("input", `${this.value} AND ${value}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -171,4 +190,25 @@ export default class ResultBody extends Vue {
|
||||||
.category__count {
|
.category__count {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.suggestions {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row wrap;
|
||||||
|
max-width: 1000px;
|
||||||
|
margin: 12px 0;
|
||||||
|
|
||||||
|
height: 70px;
|
||||||
|
overflow-y: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestion {
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||||
|
border-radius: 12px;
|
||||||
|
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
padding: 7px 16px;
|
||||||
|
margin: 2px;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<top-bar v-model="query" />
|
<top-bar v-model="query" />
|
||||||
<result-body :query="query" />
|
<result-body v-model="query" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -37,8 +37,10 @@ import {Component, Prop} from "vue-property-decorator";
|
||||||
export default class TopBar extends Vue {
|
export default class TopBar extends Vue {
|
||||||
@Prop() value = "";
|
@Prop() value = "";
|
||||||
|
|
||||||
search(event) {
|
search (event) {
|
||||||
this.$emit("input", event.target.value)
|
if (event.target.value.trim().length > 0) {
|
||||||
|
this.$emit("input", event.target.value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1763,9 +1763,9 @@ copy-descriptor@^0.1.0:
|
||||||
integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
|
integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
|
||||||
|
|
||||||
copy-webpack-plugin@^4.0.1:
|
copy-webpack-plugin@^4.0.1:
|
||||||
version "4.5.3"
|
version "4.5.4"
|
||||||
resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-4.5.3.tgz#14a224d205e46f7a79f7956028e1da6df2225ff2"
|
resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-4.5.4.tgz#f2b2782b3cd5225535c3dc166a80067e7d940f27"
|
||||||
integrity sha512-VKCiNXQcc8zyznaepXfKpCH2cZD+/j3T3B+gsFY97P7qMlEsj34wr/sI9OCG7QPUUh7gAHVx3q8Q1rdQIDM4bA==
|
integrity sha512-0lstlEyj74OAtYMrDxlNZsU7cwFijAI3Ofz2fD6Mpo9r4xCv4yegfa3uHIKvZY1NSuOtE9nvG6TAhJ+uz9gDaQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
cacache "^10.0.4"
|
cacache "^10.0.4"
|
||||||
find-cache-dir "^1.0.0"
|
find-cache-dir "^1.0.0"
|
||||||
|
@ -4538,12 +4538,12 @@ node-libs-browser@^2.0.0:
|
||||||
vm-browserify "0.0.4"
|
vm-browserify "0.0.4"
|
||||||
|
|
||||||
node-notifier@^5.1.2:
|
node-notifier@^5.1.2:
|
||||||
version "5.2.1"
|
version "5.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.2.1.tgz#fa313dd08f5517db0e2502e5758d664ac69f9dea"
|
resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.3.0.tgz#c77a4a7b84038733d5fb351aafd8a268bfe19a01"
|
||||||
integrity sha512-MIBs+AAd6dJ2SklbbE8RUDRlIVhU8MaNLh1A9SUZDUHPiZkWLFde6UNwG41yQHZEToHgJMXqyVZ9UcS/ReOVTg==
|
integrity sha512-AhENzCSGZnZJgBARsUjnQ7DnZbzyP+HxlVXuD0xqAnvL8q+OqtSX7lGg9e8nHzwXkMMXNdVeqq4E2M3EUAqX6Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
growly "^1.3.0"
|
growly "^1.3.0"
|
||||||
semver "^5.4.1"
|
semver "^5.5.0"
|
||||||
shellwords "^0.1.1"
|
shellwords "^0.1.1"
|
||||||
which "^1.3.0"
|
which "^1.3.0"
|
||||||
|
|
||||||
|
@ -5971,7 +5971,7 @@ selfsigned@^1.9.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
node-forge "0.7.5"
|
node-forge "0.7.5"
|
||||||
|
|
||||||
"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1:
|
"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0:
|
||||||
version "5.6.0"
|
version "5.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
|
||||||
integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==
|
integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==
|
||||||
|
|
Loading…
Reference in a new issue