Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
bbb64eb
First draft for precision at computation.
Mar 20, 2014
a2af593
Minor modification
Mar 20, 2014
f6c947e
Factor metric computation into separate class.
Mar 20, 2014
51eaeee
Modifications to make the test compile at least.
Mar 24, 2014
b32c483
Next increment
Apr 2, 2014
6789f7c
Stop point before kicking out everything I believe might be covered t…
Aug 13, 2014
9f4fdd4
PrecisionAt evaluation at Java API level.
Aug 14, 2014
d8299d4
Refactor precision action to be in line with spec.
Aug 28, 2014
a036469
Fix serialisation bug - cannot deal with enum
Aug 29, 2014
2f2835b
Fix action name.
Sep 12, 2014
ed5b9a5
Make tests pass. Thanks to aleph_zero for helping out.
Sep 16, 2014
ef8d4d0
Fix unit test to actually contain the spec.
Sep 16, 2014
152f637
Add docs, remove deprecation warnings.
Sep 16, 2014
847e12b
Clearer logging (refactored toString)
Sep 18, 2014
fa6197c
Switch to unknown doc per intent.
Sep 18, 2014
ffcaf07
Simplify template handling in API
Sep 18, 2014
81be9b3
Support specifying multiple target indeces.
Sep 18, 2014
4607038
Add support for configuring n in precision at n.
Sep 18, 2014
c7fa073
Major refactoring to move all non precision stuff out
Sep 20, 2014
86fdd45
Typo in class name, one class not needed. Fixing test seed.
Sep 22, 2014
3488566
Request integration
Oct 14, 2014
0212d9a
Merge branch 'master' into feature/rank-eval
May 30, 2016
39d4fa8
Setup sub module for rank evaluation
Jun 1, 2016
d5eda0d
Merge branch 'master' into feature/rank-eval
Jun 1, 2016
1b6c06a
Merge branch 'feature/rank-eval' into plugin/rank-eval
Jun 1, 2016
8e77e1c
Experiment: Module for computing prec@ on queries
Jun 9, 2016
4895361
Merge branch 'master' into plugin/rank-eval
Jun 9, 2016
5d80b79
Merge branch 'master' into plugin/rank-eval
Jun 13, 2016
09cd5a6
Get integration test over transport client to work.
Jun 14, 2016
0c6938b
Example for request json.
Jun 14, 2016
7ac09f6
Add example response
Jun 14, 2016
3438a0a
Adjust request format to have full blown query ...
Jun 15, 2016
3127a95
Rename parameters.
Jun 15, 2016
2081c8d
Remove template dependency, start REST test for API
Jun 27, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.transport.TransportService;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

Expand Down
30 changes: 30 additions & 0 deletions modules/rank-eval/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

esplugin {
description 'The Rank Eval module adds APIs to evaluate ranking quality.'
classname 'org.elasticsearch.index.rankeval.RankEvalPlugin'
}

integTest {
cluster {
setting 'script.inline', 'true'
setting 'script.stored', 'true'
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.elasticsearch.index.rankeval;

import java.util.Collection;

/** Returned for each search intent and search specification combination. Summarises the document ids found that were not
* annotated and the average precision of result sets in each particular combination based on the annotations given.
* */
public class EvalQueryQuality {
private double qualityLevel;

private Collection<String> unknownDocs;

public EvalQueryQuality (double qualityLevel, Collection<String> unknownDocs) {
this.qualityLevel = qualityLevel;
this.unknownDocs = unknownDocs;
}

public Collection<String> getUnknownDocs() {
return unknownDocs;
}

public double getQualityLevel() {
return qualityLevel;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.elasticsearch.index.rankeval;

import org.elasticsearch.common.io.stream.NamedWriteable;
import org.elasticsearch.search.SearchHit;

public interface Evaluator extends NamedWriteable {

public Object evaluate(SearchHit[] hits, RatedQuery intent);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.elasticsearch.index.rankeval;

import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.search.SearchHit;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;

import javax.naming.directory.SearchResult;

/**
* Evaluate Precision at N, N being the number of search results to consider for precision calculation.
*
* Documents of unkonwn quality are ignored in the precision at n computation and returned by document id.
* */
public class PrecisionAtN implements RankedListQualityMetric {

/** Number of results to check against a given set of relevant results. */
private int n;

public static final String NAME = "precisionatn";

public PrecisionAtN(StreamInput in) throws IOException {
n = in.readInt();
}

@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeInt(n);
}

@Override
public String getWriteableName() {
return NAME;
}

/**
* Initialises n with 10
* */
public PrecisionAtN() {
this.n = 10;
}

/**
* @param n number of top results to check against a given set of relevant results.
* */
public PrecisionAtN(int n) {
this.n= n;
}

/**
* Return number of search results to check for quality.
* */
public int getN() {
return n;
}

/** Compute precisionAtN based on provided relevant document IDs.
* @return precision at n for above {@link SearchResult} list.
**/
@Override
public EvalQueryQuality evaluate(SearchHit[] hits, RatedQuery intent) {
Map<String, Integer> ratedDocIds = intent.getRatedDocuments();

Collection<String> relevantDocIds = new ArrayList<>();
for (Entry<String, Integer> entry : ratedDocIds.entrySet()) {
if (Rating.RELEVANT.equals(RatingMapping.mapTo(entry.getValue()))) {
relevantDocIds.add(entry.getKey());
}
}

Collection<String> irrelevantDocIds = new ArrayList<>();
for (Entry<String, Integer> entry : ratedDocIds.entrySet()) {
if (Rating.IRRELEVANT.equals(RatingMapping.mapTo(entry.getValue()))) {
irrelevantDocIds.add(entry.getKey());
}
}

int good = 0;
int bad = 0;
Collection<String> unknownDocIds = new ArrayList<String>();
for (int i = 0; (i < n && i < hits.length); i++) {
String id = hits[i].getId();
if (relevantDocIds.contains(id)) {
good++;
} else if (irrelevantDocIds.contains(id)) {
bad++;
} else {
unknownDocIds.add(id);
}
}

double precision = (double) good / (good + bad);

return new EvalQueryQuality(precision, unknownDocIds);
}

public enum Rating {
RELEVANT, IRRELEVANT;
}

/**
* Needed to get the enum accross serialisation boundaries.
* */
public static class RatingMapping {
public static Integer mapFrom(Rating rating) {
if (Rating.RELEVANT.equals(rating)) {
return 0;
}
return 1;
}

public static Rating mapTo(Integer rating) {
if (rating == 0) {
return Rating.RELEVANT;
}
return Rating.IRRELEVANT;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.elasticsearch.index.rankeval;

import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.search.builder.SearchSourceBuilder;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
* Defines a QA specification: All end user supplied query intents will be mapped to the search request specified in this search request
* template and executed against the targetIndex given. Any filters that should be applied in the target system can be specified as well.
*
* The resulting document lists can then be compared against what was specified in the set of rated documents as part of a QAQuery.
* */
public class QuerySpec implements Writeable {

private int specId = 0;
private SearchSourceBuilder testRequest;
private List<String> indices = new ArrayList<>();
private List<String> types = new ArrayList<>();

public QuerySpec(
int specId, SearchSourceBuilder testRequest, List<String> indices, List<String> types) {
this.specId = specId;
this.testRequest = testRequest;
this.indices = indices;
this.types = types;
}

public QuerySpec(StreamInput in) throws IOException {
this.specId = in.readInt();
testRequest = new SearchSourceBuilder(in);
int indicesSize = in.readInt();
indices = new ArrayList<String>(indicesSize);
for (int i = 0; i < indicesSize; i++) {
this.indices.add(in.readString());
}
int typesSize = in.readInt();
types = new ArrayList<String>(typesSize);
for (int i = 0; i < typesSize; i++) {
this.types.add(in.readString());
}
}

@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeInt(specId);
testRequest.writeTo(out);
out.writeInt(indices.size());
for (String index : indices) {
out.writeString(index);
}
out.writeInt(types.size());
for (String type : types) {
out.writeString(type);
}
}

public SearchSourceBuilder getTestRequest() {
return testRequest;
}

public void setTestRequest(SearchSourceBuilder testRequest) {
this.testRequest = testRequest;
}

public List<String> getIndices() {
return indices;
}

public void setIndices(List<String> indices) {
this.indices = indices;
}

public List<String> getTypes() {
return types;
}

public void setTypes(List<String> types) {
this.types = types;
}

/** Returns a user supplied spec id for easier referencing. */
public int getSpecId() {
return specId;
}

/** Sets a user supplied spec id for easier referencing. */
public void setSpecId(int specId) {
this.specId = specId;
}
}
Loading