I have a large number of NIDs and I need one field value from each node. Is there any way to avoid the overhead of loading the entire node to get one field value?
3 Answers
I don't think there's anything built in to the API but in a pinch you could just query the database directly:
$entity_type = 'node';
$bundle = 'page';
$nids = array(1, 2, 3);
$field_values = db_select('field_revision_FIELD_NAME', 'f')
->fields('f', array('entity_id', 'FIELD_NAME_value'))
->condition('entity_type', $entity_type)
->condition('bundle', $bundle)
->condition('entity_id', $nids, 'IN')
->condition('deleted', 0)
->execute()
->fetchAllKeyed();
After running that you should have an array of field values, keyed by their respective node's nid.
It's worth remembering the column name won't necessarily be FIELD_NAME_value
; for example a node reference field would have a column name of FIELD_NAME_nid
. Which one you use will depend on your field type.
UPDATE
It seems there is a way to do it with the API, but it's not pretty and still involves a manual query:
// Get the field meta data for the field_id.
$field_name = 'field_something';
$field_info = field_info_field($field_name);
$field_id = $field_info['id'];
// Load up the properties from the node table.
$nids = array(1, 2, 3);
$sql = 'SELECT * FROM {node} WHERE nid IN (:nids)';
$nodes = db_query($sql, array(':nids' => $nids))->fetchAllAssoc('nid');
// Attach the single field to all nodes.
field_attach_load('node', $nodes, FIELD_LOAD_CURRENT, array('field_id' => $field_id));
That method takes advantage of the $options
parameter in field_attach_load()
by specifying a field ID to load data for. It's worth noting, per the docs:
Note that returned entities may contain data for other fields, for example if they are read from a cache.
So the code might have appeared to load extra field data, but anything other than the field you've specified will come from cache.
-
Awesome. Worked perfectly and MUCH faster than loading the whole node.– JorenCommented May 12, 2012 at 1:47
-
I have used this approach with the user entity to load a single field's value but this method loads all the fields (and their values) associated with the user object. Am I missing something?– W.M.Commented Apr 12, 2016 at 18:08
-
Warning: The first example requires to store fields in SQL database (default) and is not compatible with alternative field storages. The second should work (because it use field_attach_load and it will work with storages abstraction).– BobíkCommented Nov 17, 2016 at 17:36
-
@Clive , is there any reason you used field_revision_FIELD_NAME instead of field_data_FIELD_NAME ? Can you please explain? Thanks. Commented Apr 20, 2019 at 15:05
I find a bit cleaner way using an entityCondition and field attach load.
$query = new EntityFieldQuery();
$query->entityCondition('entity_type', 'node')
->entityCondition('bundle', 'story')
->propertyCondition('status', 1)
->fieldCondition('field_story_image', 'fid', 'NULL', '!=');
$result = $query->execute();
if (isset($result['node'])) {
$stories = $result['node'];
// At first we need to get field's id. If you already know field id, you can ommit this step
// Get all fields attached to a given node type
$fields = field_info_instances('node', 'story');
// Get id of body field
$field_id = $fields['field_story_image']['field_id'];
// Attach a field of selected id only to get value for it
field_attach_load('node', $stories, FIELD_LOAD_CURRENT, array('field_id' => $field_id));
// Get values of our node field
$output = field_get_items('node', $stories, 'field_story_image');
}
From the blog post http://timonweb.com/loading-only-one-field-from-an-entity-or-node
To avoid loading node one by one having large number of NIDs, you can use node_load_multiple()
which will load multiple nodes at one go:
node_load_multiple($nids = array(), $conditions = array(), $reset = FALSE)
Normally loading the nodes are cached and it's fast if you're using memory caching (like memcached), but could be slow if you've too many modules installed (like Pathauto, etc)..
Other way is to reuse the existing object, so check whether you can load it directly from cache (e.g. via form_get_cache
if it's part of the form) or from $_POST
request.
Another way is to use EntityFieldQuery
with multiple NIDs, e.g.
$query->entityCondition('entity_id', array(17, 21, 422), 'IN')
which will fetch the values directly from the database.