Filtering search results using tags AFTER the initial search using keyword input

Hello! I’m a newborn at coding, and I can’t find what I’m needing despite trying many different things. I’m using Wix Studio. I have a search bar that searches my database using keywords and returns the results in a repeater. I then want to be able to filter those search results based on my tags field. Every tutorial and article that I have come across and all the code I have tried appears to just filter the database based on the tag(s) selected and not a keyword plus tag(s). When I have my tags connected to my database, I can run an initial search of the keyword and get the results I want with that key word, but if I click on a tag after that, it gives me ALL items associated with that tag, not just those associated with keyword and tag. Here’s the code I have for my search bar. This is the first code I’ve ever written, so feel free to suggest improvements. Thanks!

import wixData from ‘wix-data’;

$w.onReady(function () {
$w(‘#repeater1’).collapse()
$w(‘#results’).hide()

    $w("#searchInput").onKeyPress((event) => {
        if (event.key === "Enter")

            wixData.query('MyGraphics')
            .contains('keywords', $w('#searchInput').value)
            .find()
            .then(results => {

                $w('#repeater1').data = results.items;
                $w('#repeater1').expand()
                $w('#results').show()

                let totalCount = results.totalCount;

                if (results.totalCount > 1)
                    $w('#results').text = (totalCount + ' Graphics Found')

                if (results.totalCount == 1)
                    $w('#results').text = ('1 Graphic Found.')

                if (results.totalCount == 0)
                    $w('#results').text = ('No Graphics Found 😬. Try another keyword.')
            })


            });
    });

    $w("#clearsearch").onClick((event) => {
        $w("#searchInput").value = undefined;
        $w("#selectionTags1").value = undefined;
        $w('#repeater1').collapse()
        $w('#results').hide()

    })

I feel like I’m getting closer. I’ve added the code below. I need to use something other tha ‘hasAll’. That code will render graphics with the keyword that have all of those tags. I want code that will render graphics with the keyword that have any of the selected tags. I’ve tried ‘hasSome’, but can’t get that to work. Also, this code filters the search with just a click on the tag without having to hit enter in the search bar which is exactly what I want. It also removes the results for the selected tag if I unselect the tag which is what I want, but it’s doing these things inconsistently.

$w(‘#selectionTags1’).onClick(() => {
search();

function search() {
const keywords= $w(‘#searchInput’).value
const selectedTags= $w(‘#selectionTags1’).value
wixData.query(“MyGraphics”)
.contains(‘keywords’, keywords)
.hasAll(‘tags’, selectedTags)
.find()
.then(results => {

CODE-UPGRADE-1.0:

import wixData from 'wix-data';

//----------USER-INTERFACE---------------------
const DATABASE = 'MyGraphics';
const REPEATER = 'repeater1';
const dbFIELDS=[];
	  dbFIELDS[0] = 'keywords';
	  dbFIELDS[1] = 'tags';
	  dbFIELDS[2] = '';
	  dbFIELDS[3] = '';
	  dbFIELDS[4] = '';
	  dbFIELDS[5] = '';
//----------USER-INTERFACE---------------------

$w.onReady(()=> {
	$w(`#${REPEATER}`).collapse();
	$w(`#${REPEATER}`).hide();
	$w('#results').hide();
	//--------------------------------------------
	
	$w("#searchInput").onKeyPress(async(e)=> {console.log(e.target.id+'-clicked');
    	if (e.key==="Enter") {
			let keywords = $w('#searchInput').value;
			let res = await search_Keywords(keywords, dbFIELDS[0]);
			let totalCount = res.totalCount;
			if (res.totalCount>=1) {
				$w('#results').text=(totalCount+' Graphic(s) Found.');
			}
			else {$w('#results').text = ('No Graphics Found 😬.');}
			//------------------------------------
			feedRepeater(REPEATER, res.items);
		}
		else{ }
	});
	//------------------------------------

	$w('#selectionTags1').onClick(async()=> {
		let selectedTags = $w('#selectionTags1').value
		let res = await search_selectedTags(selectedTags, dbFIELDS[0]);
		//continue your code..........
		//continue your code..........
		//continue your code..........
		//continue your code..........
	});

	//--------------------------------------------
	$w("#clearsearch").onClick((e)=> {console.log(e.target.id+'-clicked');
		$w("#searchInput").value = undefined;
		$w("#selectionTags1").value = undefined;
		$w(`#${REPEATER}`).collapse();
		$w('#results').hide();
	});
});


function search_Keywords(keywords, dbFIELD) {
	return wixData.query(DATABASE)
	.contains(dbFIELD, keywords).find()
	.then((res)=> {return res;})
	.catch((err)=>{
		console.log('Something went wrong!'); 
		console.log(err);
	});
}

function search_selectedTags(selectedTags, dbFIELD) {
	return wixData.query(DATABASE)
	.hasAll(dbFIELD, selectedTags).find()
	.then((res)=> {return res;})
	.catch((err)=>{
		console.log('Something went wrong!'); 
		console.log(err);
	});
}

function feedRepeater(REPEATER, repDATA) {
	$w(`#${REPEATER}`).data = repDATA;
	$w(`#${REPEATER}`).expand(); 
	$w(`#${REPEATER}`).show('fade');
	$w('#results').show('fade');
}

First you should get a better CODE-STRUCTURE → so you understand your code better!
Some further steps infront of you… continue…

This blew my mind at first :exploding_head: , very intimidating lol. After studying it tho, it mostly makes sense. Gonna try it out, and look up the elements I haven’t used before. Thanks!

First get it to work, because further optimizations will be needed.

Step by step.

Now your process-flow is divided into several parts including a USER_INTERFACE.
Once your whole code is written and complete, all you will have to change, will be inside the USER-INTERFACE, if you would want to reuse the code for another PROJECT.

The functions are now → RETURNING-FUNCTIONS → This will make your main-code-block be better readable.

ALWAYS TRY TO CODE AS FLEXIBLE AS POSSIBLE (this is what we are going to do).

At this line, there was of course a COPY-PASTE-ERROR…
let res = await search_selectedTags(selectedTags, dbFIELDS[0]);

You already found the error? —> dbFIELDS[0]

I did catch that :grin: but there is a red line under the ‘totalcount’ in res.totalCount and under ‘items’ in res.items. It says these do not exist on void | wixquerydata and property doesn’t exist. I haven’t been able to figure out why.

Forget about that error message…
2024-03-14 18_04_07-Wix Studio _ My Site 10

As you can clearly see → totalCount does exist, the only error was a typo…

…instead of totalCount you need → _totalCount

And it will give you also the proper result inside of console…

ITEMS also exists inside of the RESULT-PACKAGE, but here the same problem.

…instead of → items ← —> you will have to use —> _items.

Wix changed the ID of the ITEMS result-property, in the past it was just → items ← now it is → _items, the same for total-count → now it is → _totalCount.

To eleminate such errors just open your CONSOLE and take a look onto the given RESULTS.

All properties are included inside the RESULT-OBJECT and are working fine…

The only thing what is not working is the CODE-AUTOCOMPLETION by WIX :joy: :joy: :joy:
Which will mark it in RED, even if it is working well. :blush: :wink:

CODE-UPGRADE-2.0:
Now you put both functions together…
-making your code shorter and more compact.
-making your search-engine interacting with both elements (input + tags)

import wixData from 'wix-data';

//----------USER-INTERFACE---------------------
const DATABASE = 'DATABASE';
const REPEATER = 'repeater1';
const dbFIELDS=[];
	  dbFIELDS[0] = 'keywords';
	  dbFIELDS[1] = 'tags';
	  dbFIELDS[2] = '';
	  dbFIELDS[3] = '';
	  dbFIELDS[4] = '';
	  dbFIELDS[5] = '';
//----------USER-INTERFACE---------------------

$w.onReady(()=> {console.log('Page ready...');
	$w(`#${REPEATER}`).collapse();
	$w(`#${REPEATER}`).hide();
	$w('#results').hide();
    //--------------------------------------------

    $w("#searchInput").onKeyPress(async(e)=> {console.log(e.target.id + '-clicked');
		console.log('Pressed-Key: ', e.key);
        if (e.key==="Enter") {console.log('start search...');
            let keywords = $w('#searchInput').value; console.log('Selected-Keywords: ', keywords);
            let selectedTags = $w('#selectionTags1').value; console.log('Selected-Tags: ', selectedTags);
            let res = await search(keywords, selectedTags); console.log('RESULTS: ', res);
            let items = res._items; console.log('Items: ', items);
            let totalCount = res._totalCount; console.log('Total-Count: ', totalCount);
            if (totalCount>=1) {
                $w('#results').text = (totalCount + ' Graphic(s) Found.');
            } else {
                $w('#results').text = ('No Graphics Found 😬.');
            }
            //------------------------------------
            feedRepeater(REPEATER, items);
        } else {console.log('else');}
    });
    //------------------------------------

    $w('#selectionTags1').onClick(async () => {
        // This functionality is now integrated into the keypress event of searchInput
    });

    //--------------------------------------------
    $w("#clearsearch").onClick((e)=> {console.log(e.target.id + '-clicked');
        $w("#searchInput").value = undefined;
        $w("#selectionTags1").value = undefined;
        $w(`#${REPEATER}`).collapse();
        $w('#results').hide();
    });

	$w(`#${REPEATER}`).onItemReady(($i, iData, i)=>{
	 //$i(`#txt1`).text = iData.keyword
         //...add here your repeater-elements (code)...
         //...add here your repeater-elements (code)...
         //...add here your repeater-elements (code)...
         //...add here your repeater-elements (code)...
    });
});


function search(keywords, selectedTags) {
    return wixData.query(DATABASE)
    .contains(dbFIELDS[0], keywords) 
    .hasAll(dbFIELDS[1], selectedTags) 
    .find()
    .then((res)=> {
        return res;
    })
    .catch((err) => {
        console.log('Something went wrong!');
        console.log(err);
    });
}

function feedRepeater(rep, repDATA) {
    $w(`#${rep}`).data = repDATA;
    $w(`#${rep}`).expand();
    $w(`#${rep}`).show('fade');
    $w('#results').show('fade');
}

So, call logs are a really nice thing to have lol.

I corrected the const DATABASE=‘DATABASE’ to =‘MyGraphics’. The code works great for what it’s written to do which is have the user select one or more tags and search a keyword in the keywords field that contains the selected tags. Each time a tag is selected or changed the user has to press enter for the new results. However, this isn’t how I want it to work. I’m wanting someone to enter a search word, hit enter and get all of the items containing that search word. Then the user can filter those results by selecting one or more tags. If multiple tags are selected the results should show items that have one or more of the selected tags. When a tag is unseleted the results go back to those that are selected.

The ugly code I wrote does this for one exeption. If all tags are unselected, I want the results to go back to the results of the original serach. It doesn’t do that. I’ve been trying to convert my code to your format or rework your code to do what I want it to for two days with no luck :face_with_diagonal_mouth: :woozy_face: I understand the intergration of my tag search with my keyword search, but I don’t think that’s what I’m looking for. I’m gonna keep working on it. Thanks for all your help!!! I feel I’m learning a lot.

1 Like