Guide · Courses & memberships
Search inside a single course
If you run courses, your members search and get lessons from every course you sell, mixed together. On sites where courses cover related material, the same lesson title exists several times over and the results read like repetition. This page fixes that with one taxonomy and about fifteen lines of PHP.
Why it happens
A lesson knows which course it belongs to. That link lives in your membership plugin's own storage, as post meta or an internal table, and it is not a taxonomy.
Queryra indexes what WordPress exposes: title, content, categories, tags, and any public taxonomy attached to the post. A relationship kept in post meta is invisible to that, so nothing in the index knows the lesson belongs to a course, and no filter can narrow to it.
Checked across MemberPress Courses and LifterLMS: neither attaches a populated public taxonomy to lessons that identifies the parent course. This is not a bug in either plugin. Courses simply do not need a taxonomy to work.
The fix: give the lesson a taxonomy
Register one taxonomy on your lesson post type, and set its term to the parent course whenever a lesson is saved. Queryra picks it up on the next sync and it becomes a filter like any other.
Step 1 — register the taxonomy
add_action('init', function () {
register_taxonomy('queryra_course', ['mpcs-lesson'], [
'label' => 'Course',
'public' => true, // REQUIRED, see the note below
'publicly_queryable' => false, // no front-end archive pages
'show_ui' => false, // no extra box in wp-admin
'rewrite' => false, // no permalink clutter
'hierarchical' => false,
]);
});The one setting you cannot change: public => true. Queryra only indexes public taxonomies, so public => false means the taxonomy never reaches the index and the whole recipe silently does nothing. The other three flags are there precisely so public does not turn into archive pages, an admin metabox and rewritten permalinks you did not ask for.
Step 2 — fill it with the parent course
The two platforms differ by exactly one line: how you ask for the parent course.
MemberPress Courses
add_action('save_post_mpcs-lesson', function ($post_id) {
if (wp_is_post_revision($post_id)) return;
$lesson = new \memberpress\courses\models\Lesson($post_id);
$course = $lesson->course();
if ($course) {
wp_set_object_terms($post_id, get_the_title($course->ID), 'queryra_course');
}
}, 20);LifterLMS
add_action('save_post_lesson', function ($post_id) {
if (wp_is_post_revision($post_id)) return;
$course_id = get_post_meta($post_id, '_llms_parent_course', true);
if ($course_id) {
wp_set_object_terms($post_id, get_the_title($course_id), 'queryra_course');
}
}, 20);Register the taxonomy on the post type you actually use: mpcs-lesson for MemberPress Courses, lesson for LifterLMS. Other LMS plugins work the same way once you know where they keep the relationship.
Step 3 — apply it to existing lessons
The hook above only fires when a lesson is saved, so lessons you already have stay untagged until you touch them. Run this once, from WP-CLI, to tag everything:
wp post list --post_type=mpcs-lesson --format=ids \
| xargs -d ' ' -I{} wp eval 'do_action("save_post_mpcs-lesson", {});'Then re-sync from your Queryra dashboard. The taxonomy is read at sync time, so lessons already in the index keep their old data until they go through again.
Step 4 — narrow the search
After the sync, the plugin lists the new dimension with your real course names under Queryra → Support → Search filters. Use it in a URL, or put it in the search form on a course page so members searching from there stay inside that course:
<form role="search" method="get" action="/"> <input type="search" name="s" placeholder="Search this course"> <input type="hidden" name="filter_tax_queryra_course" value="Advanced React Patterns"> </form>
Values are the term name, not the slug, and encode spaces as %20 when you write them into a URL by hand. Full reference: Search Filters.
What changes, honestly
You get the lessons from that course, and only those. Where courses cover overlapping material, the biggest visible change is that near-identical lessons from your other courses stop competing for the same slots, so the list stops repeating itself.
What it does not do is invent depth. If a course has four lessons on a topic, a search scoped to that course returns those four. Narrowing removes noise; it does not create content that is not there.
This is relevance, not access control
A filter in a URL is something a visitor can change. It decides which slice of your content a search runs against, nothing more.
Access stays where it already is: your membership plugin decides what a member may open, and enforces it when the page renders. Changing a filter value shows different titles and never unlocks content. Use this to make search useful, and keep using your membership plugin for who may read what.
The same pattern, other content
Nothing here is specific to courses. The recipe applies whenever a dimension you want to search by exists in your data but not as a taxonomy: a company directory whose region and industry live in custom fields, a documentation site where the product version is post meta, a property listing keyed by neighbourhood.
The three steps do not change. Register the taxonomy, fill it from the data you already have, re-sync. Where the value comes from is the only part that differs, and on most sites that is a single get_post_meta() call.
If your content already carries the dimension, you need none of this. Product categories, tags and brands are indexed out of the box, which covers most shops. See Search Filters for filtering on what is already there.
Related
- Search Filters — every parameter, and the three rules that trip people up
- MemberPress integration
- WordPress integration — installing and connecting the plugin
Don't see your plugin or theme here?
Not sure it works with Queryra, or want us to verify compatibility? Get in touch — tell us the plugin or theme and we'll check, usually the same day.
WordPress Support Forum