blob: 77c28a51de87b283412dc20b09ffb449e3be2ee2 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.apache.solr.common;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import org.apache.solr.common.util.NamedList;
/**
* A concrete representation of a document within a Solr index. Unlike a lucene
* Document, a SolrDocument may have an Object value matching the type defined in
* schema.xml
*
* For indexing documents, use the SolrInputDocument that contains extra information
* for document and field boosting.
*
*
* @since solr 1.3
*/
public class SolrDocument extends SolrDocumentBase<Object, SolrDocument> implements Iterable<Map.Entry<String, Object>>
{
protected final Map<String,Object> _fields;
private List<SolrDocument> _childDocuments;
public SolrDocument()
{
_fields = new LinkedHashMap<>();
}
@Override
public void writeMap(EntryWriter ew) throws IOException {
_fields.forEach(ew.getBiConsumer());
}
public SolrDocument(Map<String, Object> fields) {
this._fields = fields;
}
/**
* @return a list of field names defined in this document - this Collection is directly backed by this SolrDocument.
* @see #keySet
*/
@Override
public Collection<String> getFieldNames() {
return this.keySet();
}
///////////////////////////////////////////////////////////////////
// Add / Set / Remove Fields
///////////////////////////////////////////////////////////////////
/**
* Remove all fields from the document
*/
@Override
public void clear()
{
_fields.clear();
if(_childDocuments != null) {
_childDocuments.clear();
}
}
/**
* Remove all fields with the name
*/
public boolean removeFields(String name)
{
return this.remove( name ) != null;
}
/**
* Set a field with the given object. If the object is an Array, it will
* set multiple fields with the included contents. This will replace any existing
* field with the given name
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public void setField(String name, Object value)
{
if( value instanceof Object[] ) {
value = new ArrayList(Arrays.asList( (Object[])value ));
}
else if( value instanceof Collection ) {
// nothing
}
else if( value instanceof NamedList ) {
// nothing
}
else if( value instanceof Iterable && !(value instanceof SolrDocumentBase)) {
ArrayList<Object> lst = new ArrayList<>();
for( Object o : (Iterable)value ) {
lst.add( o );
}
value = lst;
}
_fields.put(name, value);
}
/**
* This will add a field to the document. If fields already exist with this
* name it will append value to the collection. If the value is Collection,
* each value will be added independently.
*
* The class type of value and the name parameter should match schema.xml.
* schema.xml can be found in conf directory under the solr home by default.
*
* @param name Name of the field, should match one of the field names defined under "fields" tag in schema.xml.
* @param value Value of the field, should be of same class type as defined by "type" attribute of the corresponding field in schema.xml.
*/
@SuppressWarnings("unchecked")
@Override
public void addField(String name, Object value)
{
Object existing = _fields.get(name);
if (existing == null) {
if( value instanceof Collection ) {
Collection<Object> c = new ArrayList<>( 3 );
for ( Object o : (Collection<Object>)value ) {
c.add(o);
}
this.setField( name, c );
} else {
this.setField( name, value );
}
return;
}
Collection<Object> vals = null;
if( existing instanceof Collection ) {
vals = (Collection<Object>)existing;
}
else {
vals = new ArrayList<>( 3 );
vals.add( existing );
}
// Add the values to the collection
if( value instanceof Iterable && !(value instanceof SolrDocumentBase)) {
for( Object o : (Iterable<Object>)value ) {
vals.add( o );
}
}
else if( value instanceof Object[] ) {
for( Object o : (Object[])value ) {
vals.add( o );
}
}
else {
vals.add( value );
}
_fields.put( name, vals );
}
///////////////////////////////////////////////////////////////////
// Get the field values
///////////////////////////////////////////////////////////////////
/**
* returns the first value for a field
*/
public Object getFirstValue(String name) {
Object v = _fields.get( name );
if (v == null || !(v instanceof Collection)) return v;
@SuppressWarnings({"rawtypes"})
Collection c = (Collection)v;
if (c.size() > 0 ) {
return c.iterator().next();
}
return null;
}
/**
* Get the value or collection of values for a given field.
*/
@Override
public Object getFieldValue(String name) {
return _fields.get( name );
}
/**
* Get a collection of values for a given field name
*/
@SuppressWarnings("unchecked")
@Override
public Collection<Object> getFieldValues(String name) {
Object v = _fields.get( name );
if( v instanceof Collection ) {
return (Collection<Object>)v;
}
if( v != null ) {
ArrayList<Object> arr = new ArrayList<>(1);
arr.add( v );
return arr;
}
return null;
}
@Override
public String toString()
{
return "SolrDocument"+_fields;
}
/**
* Iterate of String-&gt;Object keys
*/
@Override
public Iterator<Entry<String, Object>> iterator() {
return _fields.entrySet().iterator();
}
/** Beta API; may change at will. */
// TODO SOLR-15063 reconcile SolrDocumentBase/SolrDocument/SolrInputDocument debacle
public void visitSelfAndNestedDocs(BiConsumer<String, SolrDocument> consumer) {
consumer.accept(null, this);
for (Entry<String, Object> keyVal : entrySet()) {
final Object value = keyVal.getValue();
if (value instanceof SolrDocument) {
consumer.accept(keyVal.getKey(), (SolrDocument) value);
} else if (value instanceof Collection) {
Collection<?> cVal = (Collection<?>) value;
for (Object v : cVal) {
if (v instanceof SolrDocument) {
consumer.accept(keyVal.getKey(), (SolrDocument) v);
} else {
break; // either they are all SolrDocs, or none are
}
}
}
}
if (_childDocuments != null) {
for (SolrDocument childDocument : _childDocuments) {
consumer.accept(null, childDocument);
}
}
}
//-----------------------------------------------------------------------------------------
// JSTL Helpers
//-----------------------------------------------------------------------------------------
/**
* Expose a Map interface to the solr field value collection.
*/
public Map<String,Collection<Object>> getFieldValuesMap()
{
return new Map<String,Collection<Object>>() {
/** Get the field Value */
@Override
public Collection<Object> get(Object key) {
return getFieldValues( (String)key );
}
// Easily Supported methods
@Override
public boolean containsKey(Object key) { return _fields.containsKey( key ); }
@Override
public Set<String> keySet() { return _fields.keySet(); }
@Override
public int size() { return _fields.size(); }
@Override
public boolean isEmpty() { return _fields.isEmpty(); }
// Unsupported operations. These are not necessary for JSTL
@Override
public void clear() { throw new UnsupportedOperationException(); }
@Override
public boolean containsValue(Object value) {throw new UnsupportedOperationException();}
@Override
public Set<java.util.Map.Entry<String, Collection<Object>>> entrySet() {throw new UnsupportedOperationException();}
@Override
public void putAll(Map<? extends String, ? extends Collection<Object>> t) {throw new UnsupportedOperationException();}
@Override
public Collection<Collection<Object>> values() {throw new UnsupportedOperationException();}
@Override
public Collection<Object> put(String key, Collection<Object> value) {throw new UnsupportedOperationException();}
@Override
public Collection<Object> remove(Object key) {throw new UnsupportedOperationException();}
@Override
public String toString() {return _fields.toString();}
};
}
/**
* Expose a Map interface to the solr fields. This function is useful for JSTL
*/
public Map<String,Object> getFieldValueMap() {
return new Map<String,Object>() {
/** Get the field Value */
@Override
public Object get(Object key) {
return getFirstValue( (String)key);
}
// Easily Supported methods
@Override
public boolean containsKey(Object key) { return _fields.containsKey( key ); }
@Override
public Set<String> keySet() { return _fields.keySet(); }
@Override
public int size() { return _fields.size(); }
@Override
public boolean isEmpty() { return _fields.isEmpty(); }
// Unsupported operations. These are not necessary for JSTL
@Override
public void clear() { throw new UnsupportedOperationException(); }
@Override
public boolean containsValue(Object value) {throw new UnsupportedOperationException();}
@Override
public Set<java.util.Map.Entry<String, Object>> entrySet() {throw new UnsupportedOperationException();}
@Override
public void putAll(Map<? extends String, ? extends Object> t) {throw new UnsupportedOperationException();}
@Override
public Collection<Object> values() {throw new UnsupportedOperationException();}
@Override
public Collection<Object> put(String key, Object value) {throw new UnsupportedOperationException();}
@Override
public Collection<Object> remove(Object key) {throw new UnsupportedOperationException();}
@Override
public String toString() {return _fields.toString();}
};
}
//---------------------------------------------------
// MAP interface
//---------------------------------------------------
@Override
public boolean containsKey(Object key) {
return _fields.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
return _fields.containsValue(value);
}
@Override
public Set<Entry<String, Object>> entrySet() {
return _fields.entrySet();
}
//TODO: Shouldn't the input parameter here be a String? The _fields map requires a String.
@Override
public Object get(Object key) {
return _fields.get(key);
}
@Override
public boolean isEmpty() {
return _fields.isEmpty();
}
@Override
public Set<String> keySet() {
return _fields.keySet();
}
@Override
public Object put(String key, Object value) {
return _fields.put(key, value);
}
@Override
public void putAll(Map<? extends String, ? extends Object> t) {
_fields.putAll( t );
}
@Override
public Object remove(Object key) {
return _fields.remove(key);
}
@Override
public int size() {
return _fields.size();
}
@Override
public Collection<Object> values() {
return _fields.values();
}
@Override
public void addChildDocument(SolrDocument child) {
if (_childDocuments == null) {
_childDocuments = new ArrayList<>();
}
_childDocuments.add(child);
}
@Override
public void addChildDocuments(Collection<SolrDocument> children) {
for (SolrDocument child : children) {
addChildDocument(child);
}
}
@Override
public List<SolrDocument> getChildDocuments() {
return _childDocuments;
}
@Override
public boolean hasChildDocuments() {
boolean isEmpty = (_childDocuments == null || _childDocuments.isEmpty());
return !isEmpty;
}
@Override
@Deprecated
public int getChildDocumentCount() {
if (_childDocuments == null) return 0;
return _childDocuments.size();
}
}