/* * 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.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 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>> { private final Map<String,Object> _fields; private List<SolrDocument> _childDocuments; public SolrDocument() { _fields = new LinkedHashMap<>(); } 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") 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 ) { 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 ) { 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; 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->Object keys */ @Override public Iterator<Entry<String, Object>> iterator() { return _fields.entrySet().iterator(); } //----------------------------------------------------------------------------------------- // 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); } } /** Returns the list of child documents, or null if none. */ @Override public List<SolrDocument> getChildDocuments() { return _childDocuments; } @Override public boolean hasChildDocuments() { boolean isEmpty = (_childDocuments == null || _childDocuments.isEmpty()); return !isEmpty; } @Override public int getChildDocumentCount() { return _childDocuments.size(); } }