
검색 기능을 구현할 때 보통 쿼리문의 Like절을 사용하는데,
NoSql에서는 관계형데이터베이스와 같은 Like절이 없다.
따라서 게시글을 모두 불러온 후 검색어에 해당되는 게시글을 체크하는 과정이 필요하다.
따로 찾아보니 like절 대신 정규표현식을 사용하는 방법도 있다고 한다.
https://stackoverflow.com/questions/3305561/how-to-query-mongodb-with-like
// MainFragment.kt
// 검색 결과의 데이터를 가져와 검색 화면의 리사이클러뷰를 갱신한다.
fun gettingSearchData(){
// 검색어를 가져온다.
// SearchView에 있는 입력요소(editText)를 추출하여 사용자가 입력한 내용을 가져온다.
val keyword = fragmentMainBinding.searchViewMain.editText.text.toString()
// 검색 결과를 가지고 있는 리스트를 비워준다.
searchList.clear()
CoroutineScope(Dispatchers.Main).launch {
// 현재 게시판에 해당하는 게시글을 모두 가져온다.
val tempList = ContentDao.gettingContentList(contentType)
// 사용자 정보를 가져온다.
userList = UserDao.getUserAll()
// 가져온 게시글 데이터 중에서 검색어를 포함하는 제목의 글 데이터만 담는다.
tempList.forEach {
if(it.contentSubject.contains(keyword)){
// 검색 결과를 담는 리스트에 담아준다.
searchList.add(it)
}
}
// 리사이클러뷰를 갱신한다.
fragmentMainBinding.recyclerViewMainSearch.adapter?.notifyDataSetChanged()
}
}
// MainFragment.kt
// 검색 화면의 RecyclerView의 어댑터
inner class SearchRecyclerViewAdapter : RecyclerView.Adapter<SearchRecyclerViewAdapter.SearchViewHolder>(){
inner class SearchViewHolder(rowMainBinding: RowMainBinding) : RecyclerView.ViewHolder(rowMainBinding.root){
val rowMainBinding:RowMainBinding
init {
this.rowMainBinding = rowMainBinding
this.rowMainBinding.root.layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchViewHolder {
val rowMainBinding = RowMainBinding.inflate(layoutInflater)
val mainViewHolder = SearchViewHolder(rowMainBinding)
return mainViewHolder
}
override fun getItemCount(): Int {
return searchList.size
}
override fun onBindViewHolder(holder: SearchViewHolder, position: Int) {
holder.rowMainBinding.textViewRowMainSubject.text = searchList[position].contentSubject
// 사용자의 수 만큼 반복한다.
userList.forEach {
// 사용자 번호와 작성자 번호가 같으면 출력하고 중단한다.
if(it.userIdx == searchList[position].contentWriterIdx){
holder.rowMainBinding.textViewRowMainNickName.text = it.userNickName
return@forEach
}
}
// 항목을 눌렀을 때 동작할 리스너를 연결해준다.
holder.rowMainBinding.root.setOnClickListener {
// 필요한 데이터를 담아준다.
val readBundle = Bundle()
readBundle.putInt("contentIdx", searchList[position].contentIdx)
// 글 읽는 화면으로 이동한다.
contentActivity.replaceFragment(ContentFragmentName.READ_CONTENT_FRAGMENT, true, true, readBundle)
}
}
}
// MainFragment.kt
// SearchView 설정
fun settingSearchBar(){
fragmentMainBinding.apply {
searchBarMain.apply {
// SearchBar에 보여줄 메시지
hint = "여기를 눌러 검색해주세요"
// 메뉴
inflateMenu(R.menu.menu_main_search_menu)
setOnMenuItemClickListener {
// 글 작성 화면이 나타나게 한다.
contentActivity.replaceFragment(ContentFragmentName.ADD_CONTENT_FRAGMENT, true, true, null)
true
}
}
searchViewMain.apply {
// SearchView에 보여줄 메시지
hint = "검색어를 입력해주세요"
// 검색 시 사용하는 키보드의 엔터키를 누르면 동작하는 리스너
editText.setOnEditorActionListener { view, actionId, event ->
if(event != null && event.action == KeyEvent.ACTION_DOWN){
// 검색 결과를 가져와 보여주는 메서드를 호출한다.
gettingSearchData()
}
// 반환값이 true면 키보드가 고정되고, false면 키보드가 내려간다.
false
}
}
}
}

// ReadContentFragment.kt
// 툴바 설정
fun settingToolbarReadContent(){
fragmentReadContentBinding.apply {
toolbarReadContent.apply {
// 타이틀
title = "글 읽기"
// 네비게이션
setNavigationIcon(R.drawable.arrow_back_24px)
setNavigationOnClickListener {
backProcesss()
}
// 메뉴
inflateMenu(R.menu.menu_read_content)
// 모든 메뉴를 보이지 않는 상태로 둔다.
// 글 정보를 가져온 다음 메뉴를 노출시킨다.
menu.findItem(R.id.menuItemReadContentReply).isVisible = false
menu.findItem(R.id.menuItemReadContentModify).isVisible = false
menu.findItem(R.id.menuItemReadContentDelete).isVisible = false
// ReadContentFragment.kt
// 입력 요소에 값을 넣어준다.
fun settingInputForm(){
// 이미지뷰를 안보이게 한다.
fragmentReadContentBinding.imageViewReadContent.visibility = View.INVISIBLE
// 입력 요소에 띄어쓰기를 넣어준다.
readContentViewModel.textFieldReadContentSubject.value = " "
readContentViewModel.textFieldReadContentText.value = " "
readContentViewModel.textFieldReadContentDate.value = " "
readContentViewModel.textFieldReadContentType.value = " "
readContentViewModel.textFieldReadContentNickName.value = " "
CoroutineScope(Dispatchers.Main).launch {
// 현재 글 번호에 해당하는 글 데이터를 가져온다.
val contentModel = ContentDao.selectContentData(contentIdx)
// 로그인한 사용자가 글을 작성한 사용자와 같다면 수정과 삭제 메뉴를 보여준다.
if(contentActivity.loginUserIdx == contentModel?.contentWriterIdx){
fragmentReadContentBinding.toolbarReadContent.menu.findItem(R.id.menuItemReadContentModify).isVisible = true
fragmentReadContentBinding.toolbarReadContent.menu.findItem(R.id.menuItemReadContentDelete).isVisible = true
}
// 다르다면
else {
fragmentReadContentBinding.toolbarReadContent.menu.findItem(R.id.menuItemReadContentReply).isVisible = true
}
글작성자 계정으로 로그인 한 경우 수정,삭제 메뉴만 보인다.

다른 계정으로 로그인 한 경우 댓글 메뉴만 보인다.

실제 데이터를 삭제하지 않고 글의 상태를 변경해준다.
특정 필드의 값만 수정하고 싶을때 객체를 전달하면 모든 필드가 업데이트가 되고,
객체가 아닌 맵을 셋팅하여 전달하면 특정 필드만 업데이트 할 수 있다.
// ContentDao.kt
// 글의 상태를 변경하는 메서드
suspend fun updateContentState(contentIdx:Int, newState:ContentState){
val job1 = CoroutineScope(Dispatchers.IO).launch {
// 컬렉션에 접근할 수 있는 객체를 가져온다.
val collectionReference = Firebase.firestore.collection("ContentData")
// 컬렉션이 가지고 있는 문서들 중에 contentIdx 필드가 지정된 글 번호값하고 같은 Document들을 가져온다.
val query = collectionReference.whereEqualTo("contentIdx", contentIdx).get().await()
// 저장할 데이터를 담을 HashMap을 만들어준다.
val map = mutableMapOf<String, Long>()
map["contentState"] = newState.number.toLong()
// 저장한다.
// 가져온 문서 중 첫 번째 문서에 접근하여 데이터를 수정한다.
query.documents[0].reference.set(map)
}
job1.join()
}
// ReadContentFragment.kt
// 툴바 설정
fun settingToolbarReadContent(){
fragmentReadContentBinding.apply {
toolbarReadContent.apply {
// 타이틀
title = "글 읽기"
// 네비게이션
setNavigationIcon(R.drawable.arrow_back_24px)
setNavigationOnClickListener {
backProcesss()
}
// 메뉴
inflateMenu(R.menu.menu_read_content)
// 모든 메뉴를 보이지 않는 상태로 둔다.
// 글 정보를 가져온 다음 메뉴를 노출시킨다.
menu.findItem(R.id.menuItemReadContentReply).isVisible = false
menu.findItem(R.id.menuItemReadContentModify).isVisible = false
menu.findItem(R.id.menuItemReadContentDelete).isVisible = false
setOnMenuItemClickListener {
// 메뉴의 id로 분기한다.
when(it.itemId){
// 댓글
R.id.menuItemReadContentReply -> {
// 댓글을 보여줄 BottomSheet를 띄운다.
showReplyBottomSheet()
}
// 수정하기
R.id.menuItemReadContentModify -> {
// 수정 화면이 보이게 한다.
contentActivity.replaceFragment(ContentFragmentName.MODIFY_CONTENT_FRAGMENT, true, true, null)
}
// 삭제하기
R.id.menuItemReadContentDelete -> {
// 삭제 확인을 받기 위한 다이얼로그를 띄워준다.
MaterialAlertDialogBuilder(contentActivity).apply {
setTitle("삭제하기")
setMessage("삭제하면 복원할 수 없습니다")
setNegativeButton("취소", null)
setPositiveButton("삭제"){ dialogInterface: DialogInterface, i: Int ->
CoroutineScope(Dispatchers.Main).launch {
// 글 상태를 삭제 상태로 변경한다.
ContentDao.updateContentState(contentIdx, ContentState.CONTENT_STATE_REMOVE)
// 글 목록 화면으로 돌아간다.
backProcesss()
}
}
show()
}
}
}
true
}
}
}
}


