Skip to content

chore(examples): add query suggestion guide #383

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion examples/android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
<application
android:name=".App"
android:allowBackup="false"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/InstantSearchExamples"
android:usesCleartextTraffic="true"
Expand Down Expand Up @@ -39,6 +39,10 @@
android:name=".guides.compose.ComposeActivity"
android:parentActivityName=".directory.DirectoryActivity" />

<activity
android:name=".guides.querysuggestion.QuerySuggestionActivity"
android:parentActivityName=".directory.DirectoryActivity" />

<!-- Showcase: Android View -->

<activity
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import com.algolia.instantsearch.examples.android.codex.voice.MainActivity as Vo
import com.algolia.instantsearch.examples.android.guides.compose.ComposeActivity
import com.algolia.instantsearch.examples.android.guides.gettingstarted.GettingStartedGuide
import com.algolia.instantsearch.examples.android.guides.insights.InsightsActivity
import com.algolia.instantsearch.examples.android.guides.querysuggestion.QuerySuggestionActivity
import com.algolia.instantsearch.examples.android.showcase.androidview.directory.AndroidViewDirectoryShowcase
import com.algolia.instantsearch.examples.android.showcase.compose.directory.ComposeDirectoryShowcase
import com.algolia.search.helper.deserialize
Expand All @@ -25,6 +26,7 @@ val guides = mapOf(
ObjectID("guide_getting_started") to GettingStartedGuide::class,
ObjectID("guide_insights") to InsightsActivity::class,
ObjectID("guide_declarative_ui") to ComposeActivity::class,
ObjectID("guide_query_suggestion") to QuerySuggestionActivity::class,
ObjectID("showcase_imperative_ui") to AndroidViewDirectoryShowcase::class,
ObjectID("showcase_declarative_ui") to ComposeDirectoryShowcase::class,
ObjectID("codex_categories_hits") to CategoriesHitsCodex::class,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.algolia.instantsearch.examples.android.guides.querysuggestion

import com.algolia.instantsearch.core.highlighting.HighlightedString
import com.algolia.instantsearch.highlighting.Highlightable
import com.algolia.search.model.Attribute
import com.algolia.search.model.ObjectID
import com.algolia.search.model.indexing.Indexable
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonObject

@Serializable
data class Product(

Check warning on line 13 in examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/guides/querysuggestion/Product.kt

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/guides/querysuggestion/Product.kt#L13

Product is missing required documentation.
val name: String,
@SerialName("image_urls") val images: List<String>,
val price: Price,
val description: String,
override val objectID: ObjectID,
override val _highlightResult: JsonObject?
) : Indexable, Highlightable {

val highlightedName: HighlightedString?
get() = getHighlight(Attribute("name"))
}

@Serializable
data class Price(

Check warning on line 27 in examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/guides/querysuggestion/Product.kt

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/guides/querysuggestion/Product.kt#L27

Price is missing required documentation.
val currency: String,
val value: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.algolia.instantsearch.examples.android.guides.querysuggestion

import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import coil.load
import com.algolia.instantsearch.android.highlighting.toSpannedString
import com.algolia.instantsearch.android.inflate
import com.algolia.instantsearch.core.hits.HitsView
import com.algolia.instantsearch.examples.android.R
import com.algolia.instantsearch.examples.android.guides.querysuggestion.ProductAdapter.ProductViewHolder

class ProductAdapter : ListAdapter<Product, ProductViewHolder>(ProductDiffUtil), HitsView<Product> {

Check warning on line 17 in examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/guides/querysuggestion/ProductAdapter.kt

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/guides/querysuggestion/ProductAdapter.kt#L17

ProductAdapter is missing required documentation.

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
ProductViewHolder(parent.inflate(R.layout.list_item_large))

override fun onBindViewHolder(holder: ProductViewHolder, position: Int) =
holder.bind(getItem(position))

override fun setHits(hits: List<Product>) = submitList(hits)

class ProductViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {

Check warning on line 27 in examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/guides/querysuggestion/ProductAdapter.kt

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/guides/querysuggestion/ProductAdapter.kt#L27

ProductViewHolder is missing required documentation.

fun bind(item: Product) {

Check warning on line 29 in examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/guides/querysuggestion/ProductAdapter.kt

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/guides/querysuggestion/ProductAdapter.kt#L29

The function bind is missing documentation.
view.findViewById<TextView>(R.id.itemTitle).text =
item.highlightedName?.toSpannedString() ?: item.name
view.findViewById<TextView>(R.id.itemSubtitle).text = item.price.value
view.findViewById<ImageView>(R.id.itemImage).load(item.images.first())
}
}

private object ProductDiffUtil : DiffUtil.ItemCallback<Product>() {
override fun areItemsTheSame(oldItem: Product, newItem: Product) = oldItem.objectID == newItem.objectID
override fun areContentsTheSame(oldItem: Product, newItem: Product) = oldItem == newItem
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.algolia.instantsearch.examples.android.guides.querysuggestion

import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.recyclerview.widget.RecyclerView
import com.algolia.instantsearch.core.connection.ConnectionHandler
import com.algolia.instantsearch.core.hits.connectHitsView
import com.algolia.instantsearch.examples.android.R
import com.algolia.instantsearch.examples.android.guides.extension.configure
import com.algolia.search.helper.deserialize

class ProductFragment : Fragment(R.layout.fragment_items) {

Check warning on line 14 in examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/guides/querysuggestion/ProductFragment.kt

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/guides/querysuggestion/ProductFragment.kt#L14

ProductFragment is missing required documentation.

private val viewModel: QuerySuggestionViewModel by activityViewModels()
private val connection = ConnectionHandler()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

// Configure products view
val productAdapter = ProductAdapter()
view.findViewById<RecyclerView>(R.id.items)
.configure(productAdapter) // Configure the RecyclerView with the adapter
connection += viewModel.productSearcher.connectHitsView(productAdapter) {
it.hits.deserialize(Product.serializer())
}

// Run initial search
viewModel.productSearcher.searchAsync()
}

override fun onDestroyView() {
super.onDestroyView()
connection.clear()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.algolia.instantsearch.examples.android.guides.querysuggestion

import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.SearchView
import androidx.fragment.app.commit
import androidx.fragment.app.replace
import com.algolia.instantsearch.android.searchbox.SearchBoxViewAppCompat
import com.algolia.instantsearch.core.connection.ConnectionHandler
import com.algolia.instantsearch.examples.android.R
import com.algolia.instantsearch.searchbox.connectView

class QuerySuggestionActivity : AppCompatActivity() {

Check warning on line 14 in examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/guides/querysuggestion/QuerySuggestionActivity.kt

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/guides/querysuggestion/QuerySuggestionActivity.kt#L14

QuerySuggestionActivity is missing required documentation.

private val viewModel by viewModels<QuerySuggestionViewModel>()
private val connection = ConnectionHandler()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_query_suggestion)

// Setup search box
val searchView = findViewById<SearchView>(R.id.searchView)
val searchBoxView = SearchBoxViewAppCompat(searchView)
connection += viewModel.searchBox.connectView(searchBoxView)

// Switch fragments on search box focus
searchView.setOnQueryTextFocusChangeListener { _, hasFocus ->
if (hasFocus) showSuggestions() else showProducts()
}

// Observe suggestions
viewModel.suggestions.observe(this) { searchBoxView.setText(it.query, true) }

// Initially show products view
showProducts()
}

private fun showSuggestions() {
supportFragmentManager.commit {
replace<SuggestionFragment>(R.id.container)
setReorderingAllowed(true)
addToBackStack("suggestions") // name can be null
}
}

private fun showProducts() {
supportFragmentManager.commit {
replace<ProductFragment>(R.id.container)
setReorderingAllowed(true)
addToBackStack("products") // name can be null
}
}

override fun onDestroy() {
super.onDestroy()
connection.clear()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.algolia.instantsearch.examples.android.guides.querysuggestion

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.algolia.instantsearch.core.connection.ConnectionHandler
import com.algolia.instantsearch.examples.android.guides.model.Suggestion
import com.algolia.instantsearch.searchbox.SearchBoxConnector
import com.algolia.instantsearch.searcher.hits.addHitsSearcher
import com.algolia.instantsearch.searcher.multi.MultiSearcher
import com.algolia.search.client.ClientSearch
import com.algolia.search.logging.LogLevel
import com.algolia.search.model.APIKey
import com.algolia.search.model.ApplicationID
import com.algolia.search.model.IndexName

class QuerySuggestionViewModel : ViewModel() {

Check warning on line 16 in examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/guides/querysuggestion/QuerySuggestionViewModel.kt

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/guides/querysuggestion/QuerySuggestionViewModel.kt#L16

QuerySuggestionViewModel is missing required documentation.

private val client = ClientSearch(
applicationID = ApplicationID("latency"),
apiKey = APIKey("927c3fe76d4b52c5a2912973f35a3077"),
logLevel = LogLevel.All
)
val multiSearcher = MultiSearcher(client)
val productSearcher = multiSearcher.addHitsSearcher(indexName = IndexName("STAGING_native_ecom_demo_products"))
val suggestionSearcher =
multiSearcher.addHitsSearcher(indexName = IndexName("STAGING_native_ecom_demo_products_query_suggestions"))
val searchBox = SearchBoxConnector(multiSearcher)
val suggestions = MutableLiveData<Suggestion>()
val connections = ConnectionHandler()

init {
searchBox.connect()
}

override fun onCleared() {
searchBox.disconnect()
connections.clear()
multiSearcher.cancel()
client.close()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.algolia.instantsearch.examples.android.guides.querysuggestion

import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.algolia.instantsearch.android.highlighting.toSpannedString
import com.algolia.instantsearch.android.inflate
import com.algolia.instantsearch.core.hits.HitsView
import com.algolia.instantsearch.examples.android.R
import com.algolia.instantsearch.examples.android.guides.model.Suggestion
import com.algolia.instantsearch.examples.android.guides.querysuggestion.SuggestionAdapter.SuggestionViewHolder

class SuggestionAdapter(private val onSuggestionClick: ((Suggestion) -> Unit)) :

Check warning on line 16 in examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/guides/querysuggestion/SuggestionAdapter.kt

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/guides/querysuggestion/SuggestionAdapter.kt#L16

SuggestionAdapter is missing required documentation.
ListAdapter<Suggestion, SuggestionViewHolder>(SuggestionAdapter),
HitsView<Suggestion> {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
SuggestionViewHolder(parent.inflate(R.layout.list_item_suggestion))

override fun onBindViewHolder(holder: SuggestionViewHolder, position: Int) {
val item = getItem(position)
holder.bind(item, onSuggestionClick)
}

override fun setHits(hits: List<Suggestion>) = submitList(hits)

class SuggestionViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {

Check warning on line 30 in examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/guides/querysuggestion/SuggestionAdapter.kt

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/guides/querysuggestion/SuggestionAdapter.kt#L30

SuggestionViewHolder is missing required documentation.

fun bind(item: Suggestion, onClick: ((Suggestion) -> Unit)) {

Check warning on line 32 in examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/guides/querysuggestion/SuggestionAdapter.kt

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/guides/querysuggestion/SuggestionAdapter.kt#L32

The function bind is missing documentation.
view.setOnClickListener { onClick(item) }
view.findViewById<TextView>(R.id.itemName).text = item.highlightedQuery?.toSpannedString() ?: item.query
}
}

companion object : DiffUtil.ItemCallback<Suggestion>() {
override fun areItemsTheSame(oldItem: Suggestion, newItem: Suggestion) =
oldItem.objectID == newItem.objectID

override fun areContentsTheSame(oldItem: Suggestion, newItem: Suggestion): Boolean =
oldItem == newItem
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.algolia.instantsearch.examples.android.guides.querysuggestion

import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.recyclerview.widget.RecyclerView
import com.algolia.instantsearch.core.connection.ConnectionHandler
import com.algolia.instantsearch.core.hits.connectHitsView
import com.algolia.instantsearch.examples.android.R
import com.algolia.instantsearch.examples.android.guides.extension.configure
import com.algolia.instantsearch.examples.android.guides.model.Suggestion
import com.algolia.search.helper.deserialize

class SuggestionFragment : Fragment(R.layout.fragment_items) {

Check warning on line 15 in examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/guides/querysuggestion/SuggestionFragment.kt

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/guides/querysuggestion/SuggestionFragment.kt#L15

SuggestionFragment is missing required documentation.

private val viewModel: QuerySuggestionViewModel by activityViewModels()
private val connection = ConnectionHandler()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Configure suggestions view
val suggestionAdapter = SuggestionAdapter {
// On suggestion click, update the
viewModel.suggestions.value = it
}
view.findViewById<RecyclerView>(R.id.items).configure(suggestionAdapter) // Configure the RecyclerView with the adapter
connection += viewModel.suggestionSearcher.connectHitsView(suggestionAdapter) {
it.hits.deserialize(Suggestion.serializer())
}

// Run initial search
viewModel.suggestionSearcher.searchAsync()
}

override fun onDestroyView() {
super.onDestroyView()
connection.clear()
}
}