> ## Documentation Index
> Fetch the complete documentation index at: https://algolia.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Search by image

> Learn how to allow users to use images as search queries.

export const Records = () => <Tooltip tip="A record is a searchable object in an Algolia index. Each record consists of named attributes." cta="Algolia records" href="/doc/guides/sending-and-managing-data/prepare-your-data#algolia-records">
    records
  </Tooltip>;

You can let your users search for products using images, not just words.
This querying technique is commonly referred to as [reverse image search](https://wikipedia.org/wiki/Reverse_image_search).
It allows users to quickly find more information about a product.

This guide shows how to use a third-party API or platform to turn images into Algolia search queries.
It uses the all-purpose [Google Cloud Vision](https://cloud.google.com/vision) image recognition API, but you could use other providers like [Amazon Rekognition](https://aws.amazon.com/rekognition) or those made for particular use cases, such as [ViSenze](https://www.visenze.com/) for retail.
Before implementing reverse image search,
[enrich your records](/doc/guides/solutions/ecommerce/visual-image-search/tutorials/image-classification-tagging) using the same image recognition platform you plan to use for reverse image search.
Once you've tagged your <Records />, those same classifications can help retrieve the correct record when a user searches using an image.

## Before you begin

This guide assumes certain data and software requirements:

* **[Algolia records enriched using image classification](/doc/guides/solutions/ecommerce/visual-image-search/tutorials/image-classification-tagging).** It doesn't cover the UI part of this UX. Specifically, you'll need to add the ability for users to upload an image to your search box and display results accordingly.
* **Access to an image recognition platform** such as Google Cloud Vision API.

<Note>
  You can't store images directly in Algolia.
  Instead, store the image on a content delivery network (CDN) or web server and add the image URL to a field in your records.
  When you retrieve a record from Algolia, use this URL to display the image in your app.
</Note>

## Image classification

<Steps>
  <Step title="Transform your images">
    Since Algolia searches for records using a textual [`query`](/doc/api-reference/api-parameters/query) or set of [query parameters](/doc/api-reference/api-parameters),
    you need to transform your image files into a set of query parameters with an image recognition platform.
    Image recognition platforms such as [Google Cloud Vision API](https://cloud.google.com/vision) take an image and return a set of classifications, "labels", for it.

    Once your users have uploaded an image to use for search, you need to run the image through the same image recognition platform you used to [enrich your records](/doc/guides/solutions/ecommerce/visual-image-search/tutorials/image-classification-tagging).
    You can use the same function you used to classify images on your records:

    ```js JavaScript icon=code theme={"system"}
    // Import the Google Cloud client libraries
    const vision = require('@google-cloud/vision');

    // Instantiate Google Vision client
    const client = new vision.ImageAnnotatorClient();

    // Retrieve labels
    async function getImageLabels(imageURL, objectID, scoreLimit) {
      const [result] = await client.labelDetection(imageURL);
      const labels = result.labelAnnotations
        .filter((label) => label.score > scoreLimit)
        .map((label) => (
          {
            description: labels.description,
            score: label.score
          }
        ))
      return { imageURL, objectID, labels };
    }

    const classifiedImage = await getImageLabels("https://images-na.ssl-images-amazon.com/images/I/41uIVaJOLdL.jpg", "439784001", 0.5)
    ```

    This returns these labels:

    ```js JavaScript icon=code theme={"system"}
    const classifiedImage = {
      imageURL: "https://images-na.ssl-images-amazon.com/images/I/41uIVaJOLdL.jpg",
      objectID: "439784001",
      labels: [
        {
          "description": "Outerwear",
          "score": 0.9513528,
        },
        {
          "description": "Azure",
          "score": 0.89286935,
        },
        {
          "description": "Sleeve",
          "score": 0.8724504,
        },
        {
          "description": "Bag",
          "score": 0.86443543,
        },
        {
          "description": "Grey",
          "score": 0.8404184,
        }
      ]
    }
    ```
  </Step>

  <Step title="Turn classifications into an Algolia query">
    Once you've extracted classifications from an image, turn them into an Algolia query.
    For this, you can send an empty query with classifications as [`optionalFilters`](/doc/api-reference/api-parameters/optionalFilters).
    Optional filters boost results with matching values.

    To use classifications as [`optionalFilters`](/doc/api-reference/api-parameters/optionalFilters),
    you must [declare the classification attributes in `attributesForFaceting`](/doc/guides/managing-results/refine-results/faceting/how-to/declaring-attributes-for-faceting).
    Take each classification and format it properly:

    ```js JavaScript icon=code theme={"system"}
    function reduceLabelsToFilters(labels) {
      const optionalFilters = labels.map(label => `labels.description:'${label.description}'`);
      return optionalFilters;
    }
    ```

    <Note>
      In this example, image classifications are stored in the `label.descriptions` nested attribute in each product. You should update the [`optionalFilters`](/doc/api-reference/api-parameters/optionalFilters) text according to your record format.
    </Note>
  </Step>

  <Step title="Pass `optionalFilters` as a query time parameter">
    How you pass these [`optionalFilters`](/doc/api-reference/api-parameters/optionalFilters) depends on your frontend implementation.
    If you're using [InstantSearch](/doc/guides/building-search-ui/what-is-instantsearch/js),
    use the [`configure`](/doc/api-reference/widgets/configure/js) widget:

    <CodeGroup>
      ```js JavaScript theme={"system"}
      instantsearch.widgets.configure({
        optionalFilters: reduceLabelsToFilters(labels)
      });
      ```

      ```jsx React theme={"system"}
      import { liteClient as algoliasearch } from 'algoliasearch/lite';
      import { InstantSearch } from 'react-instantsearch-dom';

      const searchClient = algoliasearch(
        'undefined',
        'undefined'
      );

      const App = () => (
        <InstantSearch
          indexName="instant_search"
          searchClient={searchClient}
        >
          <Configure optionalFilters={reduceLabelsToFilters(labels)} />
        </InstantSearch>
      );
      ```

      ```vue Vue theme={"system"}
      <template>
        <ais-instant-search
          index-name="instant_search"
          :search-client="searchClient"
        >
          <ais-configure :optionalFilters="reduceLabelsToFilters(labels)" />
        </ais-instant-search>
      </template>

      <script>
        import { liteClient as algoliasearch } from 'algoliasearch/lite';

        export default {
          data() {
            return {
              searchClient: algoliasearch(
                'undefined',
                'undefined'
              ),
            };
          },
        };
      </script>
      ```
    </CodeGroup>

    If you're using an [API client](/doc/libraries/sdk),
    you can pass it as a parameter in the [`search`](/doc/libraries/sdk/methods/search) method:

    <CodeGroup>
      ```cs C# theme={"system"}
      namespace Algolia;

      using System;
      using System.Collections.Generic;
      using System.Net.Http;
      using System.Text.Json;
      using Algolia.Search.Clients;
      using Algolia.Search.Http;
      using Algolia.Search.Models.Search;

      class SearchWithOptionalFilters
      {
        private static readonly List<string> labels =
        [ /* Your labels */
        ];

        private static OptionalFilters ReduceLabelsToFilters(List<string> labels)
        {
          return new OptionalFilters([]); // Implement your logic here
        }

        async Task Main(string[] args)
        {
          var client = new SearchClient(new SearchConfig("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY"));
          var optionalFilters = ReduceLabelsToFilters(labels);
          var searchParams = new SearchParams(
            new SearchParamsObject { Query = "SEARCH_QUERY", OptionalFilters = optionalFilters }
          );

          await client.SearchSingleIndexAsync<Hit>("INDEX_NAME", searchParams);
        }
      }

      ```

      ```dart Dart theme={"system"}
      import 'package:algolia_client_search/algolia_client_search.dart';

      final List<String> labels = []; // A list of labels

      List<String> reduceLabelsToFilters(List<String> labels) {
        return []; // Implement your logic here
      }

      void searchWithOptionalFilters() async {
        final client =
            SearchClient(appId: 'ALGOLIA_APPLICATION_ID', apiKey: 'ALGOLIA_API_KEY');

        final optionalFilters = reduceLabelsToFilters(labels);
        final searchParams = SearchParamsObject(
            query: "SEARCH_QUERY", optionalFilters: optionalFilters);

        await client.searchSingleIndex(
          indexName: "INDEX_NAME",
          searchParams: searchParams,
        );
      }

      ```

      ```go Go theme={"system"}
      package main

      import (
      	"github.com/algolia/algoliasearch-client-go/v4/algolia/search"
      )

      func reduceLabelsToFilters(_ []string) (search.OptionalFilters, error) {
      	return search.OptionalFilters{}, nil // Implement your logic here
      }

      func searchWithOptionalFilters() {
      	labels := []string{ /* A list of labels */ }

      	client, err := search.NewClient("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY")
      	if err != nil {
      		// The client can fail to initialize if you pass an invalid parameter.
      		panic(err)
      	}

      	optionalFilters, err := reduceLabelsToFilters(labels)
      	if err != nil {
      		panic(err)
      	}

      	searchParams := search.SearchParamsObjectAsSearchParams(
      		search.NewSearchParamsObject().
      			SetQuery("SEARCH_QUERY").
      			SetOptionalFilters(&optionalFilters),
      	)

      	_, err = client.SearchSingleIndex(client.NewApiSearchSingleIndexRequest(
      		"INDEX_NAME").WithSearchParams(searchParams))
      	if err != nil {
      		panic(err)
      	}
      }

      ```

      ```java Java theme={"system"}
      package com.algolia;

      import com.algolia.api.SearchClient;
      import com.algolia.config.*;
      import com.algolia.model.search.*;
      import java.util.List;

      public class searchWithOptionalFilters {

        private static final List<String> labels = List.of(/* Your labels */);

        private static OptionalFilters reduceLabelsToFilters(List<String> labels) {
          return OptionalFilters.of(""); // Implement your logic here
        }

        public static void main(String[] args) throws Exception {
          try (SearchClient client = new SearchClient("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY")) {
            OptionalFilters optionalFilters = reduceLabelsToFilters(labels);
            SearchParams searchParams = new SearchParamsObject().setQuery("SEARCH_QUERY").setOptionalFilters(optionalFilters);

            client.searchSingleIndex("INDEX_NAME", searchParams, Hit.class);
          } catch (Exception e) {
            System.out.println("An error occurred: " + e.getMessage());
          }
        }
      }

      ```

      ```js JavaScript theme={"system"}
      import { algoliasearch } from 'algoliasearch';

      import type { OptionalFilters, SearchParams } from 'algoliasearch';

      const labels: string[] = []; // A list of labels

      const reduceLabelsToFilters = (_labels: string[]): OptionalFilters => {
        return []; // Implement your logic here
      };

      const client = algoliasearch('ALGOLIA_APPLICATION_ID', 'ALGOLIA_API_KEY');

      const optionalFilters = reduceLabelsToFilters(labels);
      const searchParams: SearchParams = {
        query: 'SEARCH_QUERY',
        optionalFilters: optionalFilters,
      };

      await client.searchSingleIndex({ indexName: 'indexName', searchParams: searchParams });

      ```

      ```kotlin Kotlin theme={"system"}
      import com.algolia.client.api.SearchClient
      import com.algolia.client.configuration.*
      import com.algolia.client.extensions.*
      import com.algolia.client.model.search.*
      import com.algolia.client.transport.*

      val labels: List<String> = listOf() // A list of labels

      val reduceLabelsToFilters: (List<String>) -> OptionalFilters = {
        OptionalFilters.of("") // Implement your logic here
      }

      suspend fun searchWithOptionalFilters() {
        val client = SearchClient(appId = "ALGOLIA_APPLICATION_ID", apiKey = "ALGOLIA_API_KEY")

        val optionalFilters = reduceLabelsToFilters(labels)
        val searchParams =
          SearchParamsObject(query = "SEARCH_QUERY", optionalFilters = optionalFilters)

        client.searchSingleIndex(indexName = "INDEX_NAME", searchParams = searchParams)
      }

      ```

      ```php PHP theme={"system"}
      <?php

      require __DIR__.'/../vendor/autoload.php';
      use Algolia\AlgoliaSearch\Api\SearchClient;
      use Algolia\AlgoliaSearch\Model\Search\OptionalFilters;
      use Algolia\AlgoliaSearch\Model\Search\SearchParamsObject;

      $labels = []; // A list of labels
      $reduceLabelsToFilters = function (array $labels): OptionalFilters {
          // Implement your logic here
          return new OptionalFilters();
      };

      $client = SearchClient::create('ALGOLIA_APPLICATION_ID', 'ALGOLIA_API_KEY');

      $optionalFilters = $reduceLabelsToFilters($labels);
      $searchParams = (new SearchParamsObject())
          ->setQuery('SEARCH_QUERY')
          ->setOptionalFilters($optionalFilters)
      ;

      $client->searchSingleIndex(
          'INDEX_NAME',
          $searchParams,
      );

      ```

      ```python Python theme={"system"}
      from algoliasearch.search.client import SearchClientSync

      from algoliasearch.search.models.search_params import SearchParams


      def _reduce_labels_to_filters(_labels):
          # Implement your logic here
          return []


      labels = []  # A list of labels

      _client = SearchClientSync("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY")


      optional_filters = _reduce_labels_to_filters(labels)
      search_params = SearchParams(
          query="SEARCH_QUERY",
          optional_filters=optional_filters,
      )

      _client.search_single_index(
          index_name="INDEX_NAME",
          search_params=search_params,
      )

      ```

      ```ruby Ruby theme={"system"}
      require "algolia"

      def reduce_labels_to_filters(_labels)
        # Implement your logic here
        []
      end

      # A list of labels
      labels = []

      client = Algolia::SearchClient.create("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY")

      optional_filters = reduce_labels_to_filters(labels)
      search_params = Algolia::Search::SearchParamsObject.new(
        query: "SEARCH_QUERY",
        optionalFilters: optional_filters
      )

      client.search_single_index("INDEX_NAME", search_params)

      ```

      ```scala Scala theme={"system"}
      import scala.concurrent.Future
      import scala.concurrent.ExecutionContext.Implicits.global
      import scala.concurrent.Await
      import scala.concurrent.duration.Duration

      import algoliasearch.api.SearchClient
      import algoliasearch.config.*
      import algoliasearch.extension.SearchClientExtensions
      import algoliasearch.search.OptionalFilters.SeqOfOptionalFilters
      import algoliasearch.search.SearchParamsObject

      val labels: List[String] = List() // A list of labels

      val reduceLabelsToFilters: Seq[String] => SeqOfOptionalFilters = _ => {
        SeqOfOptionalFilters(Seq()) // Implement your logic here
      }

      def searchWithOptionalFilters(): Future[Unit] = {
        val client = SearchClient(appId = "ALGOLIA_APPLICATION_ID", apiKey = "ALGOLIA_API_KEY")

        val optionalFilters = reduceLabelsToFilters(labels)
        val searchParams = SearchParamsObject(query = Some("SEARCH_QUERY"), optionalFilters = Some(optionalFilters))

        Await.result(
          client
            .searchSingleIndex(
              indexName = "INDEX_NAME",
              searchParams = Some(searchParams)
            )
            .map(_ => Future.unit),
          Duration(5, "sec")
        )
      }

      ```

      ```swift Swift theme={"system"}
      import Foundation
      #if os(Linux) // For linux interop
          import FoundationNetworking
      #endif

      import AlgoliaCore
      import AlgoliaSearch

      let labels: [String] = [] // A list of labels

      let reduceLabelsToFilters = { (_: [String]) in
          SearchOptionalFilters.arrayOfSearchOptionalFilters([]) // Implement your logic here
      }

      func searchWithOptionalFilters() async throws {
          let client = try SearchClient(appID: "ALGOLIA_APPLICATION_ID", apiKey: "ALGOLIA_API_KEY")

          let optionalFilters = reduceLabelsToFilters(labels)
          let searchParams = SearchSearchParams.searchSearchParamsObject(
              SearchSearchParamsObject(query: "SEARCH_QUERY", optionalFilters: optionalFilters)
          )

          let response: SearchResponse<Hit> = try await client.searchSingleIndex(
              indexName: "INDEX_NAME",
              searchParams: searchParams
          )
          print(response)
      }

      ```
    </CodeGroup>
  </Step>
</Steps>

## See also

* [Image classification and tagging](/doc/guides/solutions/ecommerce/visual-image-search/tutorials/image-classification-tagging)
* [Visual image search](/doc/guides/solutions/ecommerce/visual-image-search)
* [Visual search (blog)](https://www.algolia.com/blog/product/picture-search-how-does-an-image-finder-search-engine-work/)
* [Visual shopping and discovery (blog)](https://www.algolia.com/blog/ai/visual-shopping-visual-discovery-how-image-search-is-changing-online-shopping/)
