/**
* This software is licensed to you under the Apache License, Version 2.0 (the
* "Apache License").
*
* LinkedIn's contributions are made under the Apache License. If you contribute
* to the Software, the contributions will be deemed to have been made under the
* Apache License, unless you expressly indicate otherwise. Please do not make any
* contributions that would be inconsistent with the Apache License.
*
* You may obtain a copy of the Apache License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, this software
* distributed under the Apache License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the Apache
* License for the specific language governing permissions and limitations for the
* software governed under the Apache License.
*
* © 2012 LinkedIn Corp. All Rights Reserved.
*/
package com.senseidb.search.req;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.lang.StringUtils;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Fieldable;
import org.apache.lucene.search.Explanation;
import com.browseengine.bobo.api.BrowseFacet;
import com.browseengine.bobo.api.BrowseHit;
import com.browseengine.bobo.api.BrowseResult;
import com.browseengine.bobo.api.FacetAccessible;
public class SenseiResult extends BrowseResult implements AbstractSenseiResult
{
private static final long serialVersionUID = 1L;
private String _parsedQuery = null;
private List<SenseiError> errors;
public SenseiHit[] getSenseiHits()
{
BrowseHit[] hits = getHits();
if (hits == null || hits.length == 0)
{
return new SenseiHit[0];
}
return (SenseiHit[]) hits;
}
public void setParsedQuery(String query)
{
_parsedQuery = query;
}
public String getParsedQuery()
{
return _parsedQuery;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof SenseiResult)) return false;
SenseiResult b = (SenseiResult)o;
if(getParsedQuery() == null)
return b.getParsedQuery() == null;
if (!getParsedQuery().equals(b.getParsedQuery())) return false;
// TODO: move this into BrowseResult equals
if (!senseiHitsAreEqual(getSenseiHits(), b.getSenseiHits())) return false;
if (getTid() != b.getTid()) return false;
if (getTime() != b.getTime()) return false;
if (getNumHits() != getNumHits()) return false;
if (getNumGroups() != getNumGroups()) return false;
if (getTotalDocs() != getTotalDocs()) return false;
if (!facetMapsAreEqual(getFacetMap(), b.getFacetMap())) return false;
return true;
}
private boolean senseiHitsAreEqual(SenseiHit[] a, SenseiHit[] b) {
if (a == null)
return b == null;
if (a.length != b.length)
return false;
for (int i = 0; i < a.length; i++) {
if (a[i].getUID() != b[i].getUID())
return false;
if (a[i].getDocid() != b[i].getDocid())
return false;
if (a[i].getScore() != b[i].getScore())
return false;
if (a[i].getGroupValue() == null || b[i].getGroupValue() == null) {
if(!(a[i].getGroupValue() == null && b[i].getGroupValue() == null))
return false;
} else {
if (!a[i].getGroupValue().equals(b[i].getGroupValue()))
return false;
}
if (a[i].getRawGroupValue() == null || b[i].getRawGroupValue() == null) {
if(!(a[i].getRawGroupValue() == null && b[i].getRawGroupValue() == null))
return false;
} else {
if(a[i].getRawGroupValue() instanceof long[]) {
if(!(b[i].getRawGroupValue() instanceof long[] &&
Arrays.equals((long[]) a[i].getRawGroupValue(), (long[]) b[i].getRawGroupValue())))
return false;
} else if(!a[i].getRawGroupValue().equals(b[i].getRawGroupValue())) {
return false;
}
}
if (a[i].getGroupHitsCount() != b[i].getGroupHitsCount())
return false;
if (!senseiHitsAreEqual(a[i].getSenseiGroupHits(), b[i].getSenseiGroupHits()))
return false;
if (!explanationsAreEqual(a[i].getExplanation(), b[i].getExplanation()))
return false;
if (!storedFieldsAreEqual(a[i].getStoredFields(), b[i].getStoredFields()))
return false;
if (!fieldValuesAreEqual(a[i].getFieldValues(), b[i].getFieldValues()))
return false;
if (!rawFieldValuesAreEqual(a[i].getRawFieldValues(), b[i].getRawFieldValues()))
return false;
}
return true;
}
private boolean fieldValuesAreEqual(Map<String, String[]> fieldValuesA, Map<String, String[]> fieldValuesB) {
if(fieldValuesA == null || fieldValuesB == null) {
return fieldValuesA == null && fieldValuesB == null;
}
if(fieldValuesA.size() != fieldValuesB.size())
return false;
for(Entry<String, String[]> entryA : fieldValuesA.entrySet()) {
String keyA = entryA.getKey();
String[] valueA = entryA.getValue();
String[] valueB = fieldValuesB.get(keyA);
if(valueB == null) {
return false;
}
return Arrays.equals(valueA, valueB);
}
return true;
}
private boolean rawFieldValuesAreEqual(Map<String, Object[]> fieldValuesA, Map<String, Object[]> fieldValuesB) {
if(fieldValuesA == null || fieldValuesB == null) {
return fieldValuesA == null && fieldValuesB == null;
}
if(fieldValuesA.size() != fieldValuesB.size()) return false;
// TODO: Should we enforce iteration order?
for(Entry<String, Object[]> entryA : fieldValuesA.entrySet()) {
String keyA = entryA.getKey();
Object[] valueA = entryA.getValue();
Object[] valueB = fieldValuesB.get(keyA);
if(valueB == null) {
return false;
}
return Arrays.equals(valueA, valueB);
}
return true;
}
private boolean storedFieldsAreEqual(Document a, Document b) {
if (a == null) return b == null;
else if(b == null)
return false;
List<Fieldable> aFields = a.getFields();
List<Fieldable> bFields = b.getFields();
int aFieldSize = aFields == null ? 0 : aFields.size();
int bFieldSize = bFields == null ? 0 : bFields.size();
if(aFieldSize != bFieldSize) {
return false;
}
for(int i = 0; i < aFieldSize; i++) {
Fieldable aField = aFields.get(i);
Fieldable bField = bFields.get(i);
if(aField.getBoost() != bField.getBoost())
return false;
if(aField.isBinary() != bField.isBinary())
return false;
if(aField.isIndexed() != bField.isIndexed())
return false;
if(aField.isLazy() != bField.isLazy())
return false;
if(aField.isStored() != bField.isStored())
return false;
if(aField.getOmitNorms() != bField.getOmitNorms())
return false;
if(aField.getBinaryLength() != bField.getBinaryLength())
return false;
if(aField.getBinaryOffset() != bField.getBinaryOffset())
return false;
if(!Arrays.equals(aField.getBinaryValue(), bField.getBinaryValue()))
return false;
}
return true;
}
private boolean explanationsAreEqual(Explanation a, Explanation b) {
// TODO: is comparing the document strings adequate?
if(a == null) {
return b == null;
} else {
return StringUtils.equals(a.getDescription(), b.getDescription());
}
}
private boolean facetMapsAreEqual(Map<String, FacetAccessible> a, Map<String, FacetAccessible> b) {
if (a == null) return b == null;
if (a.size() != b.size()) return false;
for (Entry<String,FacetAccessible> entry : a.entrySet()) {
String fieldName = entry.getKey();
if (!b.containsKey(fieldName)) return false;
if (!facetAccessibleAreEqual(entry.getValue(), b.get(fieldName))) return false;
}
return true;
}
private boolean facetAccessibleAreEqual(FacetAccessible a, FacetAccessible b) {
if (a == null) return b == null;
if (a.getFacets().size() != b.getFacets().size()) return false;
List<BrowseFacet> al = a.getFacets();
List<BrowseFacet> bl = b.getFacets();
if (!Arrays.equals(al.toArray(new BrowseFacet[al.size()]), bl.toArray(new BrowseFacet[bl.size()]))) return false;
return true;
}
public List<SenseiError> getErrors() {
if (errors == null)
errors = new ArrayList<SenseiError>();
return errors ;
}
public void addError(SenseiError error) {
if (errors == null)
errors = new ArrayList<SenseiError>();
errors.add(error);
}
}