The Search API module supports a handful of data alterations and processors; additional operations that can be performed on a document before it's indexed or during the display of search results. While Solr actually handles the majority of these for us already, this tutorial will look at the available options, talk about what each one does, and explain which ones are still relevant when using Solr as a backend.
Looking at data alterations in the Search API module also raises an important point about security. By default, Search API doesn't care about your content's access control settings. In order to prevent people from seeing results for their searches that contain data they shouldn't have access to we need to make sure we account for that in our configuration.
Here's a good list of the currently available data alterations and processors, though it's worth noting that not all of them are available for all search backends. Also, as we'll see, not all of them are recommended when using Solr even if they are available. Solr's tokenizer for example is much more full featured than the Search API tokenizer, so when using Solr as a backend it's best to keep the Search API tokenizer turned off and let Solr do its thing.
By the end of this lesson you should be able to use data alterations and processors to filter out specific content types from your Solr index and to highlight keywords found when displaying search results. You'll also be able to explain why some alterations and processors are better left off so that Solr can handle those tasks directly.
Additional resources
Being able to display search results using the Views module provides a huge amount of flexibility with respect to what is listed, what it looks like, and more. In this tutorial we'll look at using the Search API Views module, included in the Search API project, to create a view that allows users to search our Solr index and display the results as a table, or really, in any other way that Views can display content. We'll also cover some special considerations regarding access control and entity relationships that we need to keep in mind when using Views to display search results.
The biggest difference between creating a view that lists a bunch of nodes, and one that displays search results is that you need to use your Search API index as the base table from which you're building your view. Then, by default, the view only has access to the fields that are in the Solr index. This allows you to build the entire view without having to query the database. Or you can use the Views module's ability to define relationships to other buckets of content to query the database and pull in additional information. There's a huge amount of flexibility.
When building views from the Solr index you can optionally expose one or more filters. Essentially creating a form that allows someone to construct a search query. This can be as simple as exposing a keyword text field, or as complex as you would like to get. We'll look at using exposed filters to create a form that users can perform a search with and create a more complete search experience. We'll also look at how you can move those exposed filters into a block that can be displayed on the home page of our site, allowing us to replace the functionality provided to the Drupal core Search module with Views and Solr.
By the end of this tutorial you should be able to create a view that displays search results using the Search API Views module.
Additional resources
Using facets allows users of your search application to further narrow the results returned from a keyword search by selecting one or more attributes of the returned content and saying either show me only these, or show me everything but these. In this tutorial we'll take a look at some examples of faceted searching in practice, and then we'll use the Facet API module to expose facets for our genus and species fields.
One of the most common uses of facets is on e-commerce sites like Zappos.com that have huge collections of products that users can browse through, and narrow down, to focus in on exactly the pair of shoes they are after. In this example facets allow you do to things like narrow the results returned from your initial keyword search to just shoes for men, which are brown, size 10.5, and on sale. You can can also see faceting in action any time you perform a search on our site.
We'll use facets to allow users of the fish finder application to limit the results returned to just those of a specific species or genus. In doing so we'll also look at the options available for determining how facets should be displayed, whether or not we should show a facet that has zero documents in our result set, and how to combine multiple facets together into a single query using either AND or OR logic.
By the end of this tutorial you should be able to use the Facet API module in conjunction with Search API in order to provide facets that your users can use to further narrow and refine their search results.
Additional resources
Depending on the data that is being searched, some shorter general words, like "a", "the", or "is" can adversely effect search result relevancy. Consider the word "the", which in a standard description of a fish in our database could easily appear hundreds of times or more. When a search is performed, part of the algorithm that calculates the relevancy of any document in the index is to count the number of times a word appears in the text being searched. The more often it appears, the more relevant the document. Words like "the" however often have little to no real bearing on a document's actual relevancy. These words should instead be excluded from the ranking algorithm.
Stop words can also serve another purpose. You can filter out words that are so common in a particular set of data that the system can't handle them in a useful way. For example, consider the word "fish" in our dataset. It's probably very common. With only 500 fish being indexed it's not really going to make much difference, but what if we were indexing five million fish, and each one had the word "fish" in the description even just five times? That's 25 million occurrences of the word "fish". Eventually we might start to hit the upper limit of what Solr can handle. The word "fish" in this case is probably also not very useful in a search query. You're browsing a fish database. Are you really likely to search for the query fish and expect any meaningful results? Likely it would instead return every result. It would be like going to Drupal.org and searching for the word "drupal" and expecting to get something useful. Not going to happen.
Solr has the ability to read in a list of stop words, or words that should be ignored during indexing, so that those words do not clutter your index and are removed from influencing result relevancy. In this tutorial we'll take a look at configuring stop words for Solr.
First, we'll use the Solr web UI to see the most common terms in our index for the body field. Then, based on that list, and the list of common stop words provided by the Solr team, we'll configure our stopwords.txt file. Finally, we'll re-index all the content of our site so that it makes use of the new stop words configuration and re-examine the most common terms noting that our stop words no longer appear in the list.
By the end of this tutorial you should be able to use the Solr web UI to get a list of the most common terms in your index, and know how to add terms to Solr's stopwords.txt file to prevent them from showing up in your index.
Additional resources
Solr provides the option to configure synonyms for use during both indexing and querying of textual data. A synonym is a word or phrase that means exactly or nearly the same thing as another word or phrase in the same language. For example, shut is a synonym of close. Synonyms, if not accounted for, can cause a dilution of search result relevancy when searching for a keywords that have lots of variations in your index.
Consider for example the words, "ipod", "i-pod", and "i pod". It's pretty easy to imagine a scenario in which the content of our site could contain all three variations of the word. When someone searches though they are likely just going to search for one, but expect results for all three. In order to not break those expectations we need to make sure we account for this scenario. Another example from the the Drupal world would be the terms "CMI" and "configuration management". Chances are if you search for one you would be happy to see results for the other.
In this tutorial we'll look at using the synonyms.txt file that is part of our Solr configuration in order to account for synonyms in our data. Of course the exact words you use will depend on the content of your site, but we can at least cover how they work and how to configure them.
By the end of this tutorial you should be able to configure Solr to be aware of synonyms in your data in order to improve the quality of your search results.
Additional resources
One of the benefits of building our own search application is that we have ultimate control over the ranking of items. Combined with our superior knowledge of our own content we can use this to ensure that when someone searches for a specific keyword we bubble our best content for that term to the top of the list, regardless of whatever Solr might rank it based on its internal algorithms. This is commonly referred to as promoted, or sponsored, results; the artificial boosting of a particular document to the top of the result list for a specific query.
A similar, but not exactly the same, example would be sponsored results on Google searches, where you can pay to have your page listed at the top of the results for a specific keyword or set of keywords. We are going to be doing all of this except for the part where we let people pay to promote results, though you could certainly build that part on your own if you need that.
Solr uses a configuration file named elevate.xml, in conjunction with a processor, to elevate results at the time a query is performed. We can promote specific documents in our Solr index by figuring out the unique Solr ID for a document and then adding it to the elevate.xml file along with some information about a query, or queries, this document should be promoted for.
In this tutorial we'll learn how to find a Solr document's unique ID, and then configure Solr to use an elevate.xml file that will promote the "How to Use the Fish Finder" page to the top of the results when someone searches for the term "fish". This configuration is all within the Solr application itself and doesn't really rely on Drupal in anyway. As such, the material in this tutorial should be applicable to your Solr search applications even if you're not building them with Drupal.
By the end of this lesson you should be able to configure promoted documents in your own Solr-based search application.
Additional resources
With our interface taken care of, now we can configure multilingual support for our content. That is, we need to be able to identify which language a given piece of content is written in and then create translations. So, we already have articles, and we need to add a knowledge base to the site as well. For both of these types of content, we’ll need to identify the language they are written in, as well as provide related, linked translated versions. In this lesson we'll enable the core Book module for our knowledge base, enable multilingual content, and start translating our knowledge base.
Additional resources
Not everyone wants or needs to install yet another module, and due to the caveats we mentioned about Localization client, it is still a good idea to be familiar with all of your options. In this lesson, we'll review the Locale module translation process, then enable Localization Client and start translating some text!
Additional resources
Localization Client project
Using Drupal, 2nd edition
Using Drupal source code
So far we’ve got a nice start with getting our site translated, but everything is not quite smooth yet. We have translations for some of our content and menu items, but we're seeing all the content at the same time. You will also see some stray interface text still in English. To take our multilingual site further and really make it shine, in this lesson we're going to turn to a package of modules called Internationalization (i18n). There is a central Internationalization module, which comes packaged with a handful of other modules designed to work together to extend core’s multilingual features.
Additional resources
Internationalization (i18n) project
Using Drupal, 2nd edition
Using Drupal source code
Translated content on a Drupal is all shown at the same time by default. The Internationalization module will let you display only content that is relevant to the language currently in use, which is referred to as "content selection." In this lesson we're going to configure our site's content selection and the test it out to make sure it working as expected.
Additional resources
Before we can translate the various site-wide variables, like the site name or slogan, we need to let Drupal know which ones we want to make translatable. To do this, we’ll need to use the Variable translation module from the Internationalization package. In this lesson we'll enable a multilingual variable so that we can see how to translate the site name.
Additional resources
We’ve translated the site name, but we still have other stubborn text on the site that we couldn't select on the Variable translation page. Our content types for the site are still using English for the content type name on the “Create content” page, and for field names when making new content. In this lesson, to fix this up, we'll walk through translating content type names, the Title label on the content type, and also see how to tackle the field labels as well.
Additional resources
When using Drupal’s taxonomy system, we need to find a way to sync the terms that we create to keep the taxonomy selection limited to just the terms for a given language. We don’t want all of the different languages showing up at the same time when someone is looking at a vocabulary or individual terms on the site. We're going to work with taxonomy to create our site's forum. The Forum module’s containers and forums structure is built on Drupal’s core taxonomy, so it is also creating a new vocabulary on our site. The forums on our client’s site need to display the threads that follow the same content selection rule as the rest of the content on the site. That is, we’ll only show the forum posts for the selected language. The site will have preset containers and forums, and then users may post to them using whichever language they choose.
Additional resources
There are a lot of tools available for working with multilingual sites, and we've implemented the main pieces we needed to build the site that Blue Peak Fanatics needs. In this lesson we'll take a look at some more modules we might consider down the road.
- Language icons
- Language switcher dropdown
- Transliteration
- Translation overview
- Translation table
Additional resources
We built a nice, simple, easy-to-use site for our clients that gave them the tools they needed for discussions and a knowledge base. We set up a forum that displays only posts that are in the user’s language and a knowledge base book where all of the site members can create translations for the pages. The major need for this community was being able to use multiple languages and easily extend those languages in the future. Using Drupal’s core internationalization features with a handful of contributed modules, we have given them a very flexible multilingual solution. In this final lesson we'll tour the Blue Peak Fanatics site, discuss our implementation points, and review the modules and resources we used to build this site.
Additional resources
Using Drupal, 2nd edition
Using Drupal source code
Up next: Using Drupal Chapter 9: Online Store
The last little thing we need to do to clean up our site is to get our Home tab in the Main menu translated as well. Everything else in the menu is now translated, but the Home menu item isn't from one of the other features we've created. It's just a regular menu item. In this lesson we'll enable multilingual menus and get that home page tab translated.
Additional resources
In this lesson we will cover all the settings that are new available in the Features module from the Bean module. We will update our current feature with all of the settings we have configured from creating different Bean types. We will discuss what has been added and be reminded that content is not stored in the feature. It is good practice to setup all of your configurations and get those configurations into code, then move those settings to your final environment before you start creating content.
Testing, and quality assurance, are an important part of maintaining high-quality software. Together they ensure that the code we write functions as it is intended to, and that changes made later on do not introduce any regressions into our application. Its tedious work and generally involves coming up with a list of tasks that prove a specific feature is working, and then repeating the list of tasks every time a change is made.
Automated testing is the process of writing code to reduce the number of things we need to do manually when testing our software. That list you created to ensure your software is working correctly is likely something that can be easily automated with a testing framework like SimpleTest. Doing so can dramatically reduce the amount of time it takes to perform QA, and improve the overall stability of our applications.
The SimpleTest testing framework was introduced into Drupal core early on in the Drupal 7 development cycle. Since then it has had a profound impact on the way our community develops Drupal. Core now contains a whole suite of tests that cover somewhere in the neighborhood of 75% of the softwares functionality. Every time a new features is introduced, or a bug is fixed, this battery of tests confirms that the fix to the date formatting function didn't inadvertently break something else.
In this series we'll learn to write our own automated tests for Drupal 7 using SimpleTests. We'll walk through enabling SimpleTest, and running one or more test cases with both the SimpleTest UI, and with drush. Then we'll cover some of the related terminology like the difference between functional tests and unit tests. As well as discuss why testing is an important part of development and worth the investment.
Then we'll walk through writing a test case and a set of assertions in orders to verify that various features of our site are working. We'll use functional tests based on the DrupalWebTestCase
class, which simulates a browser navigating our site and clicking on links, and filling out forms. And the DrupalUnitTestCase
class, which allows us to write super fast unit tests for the business logic contained within our code.
After watching this series you should be able to:
- Install the SimpleTest module and run the tests included with core.
- Define and know the difference between functional tests, unit tests, test cases, and assertions.
- Create a new test case so that SimpleTest can discover and run your tests.
- Write assertions that test the validity of your application in a given state.
- Use the test browser to click on links and navigate to pages on a site.
- Fill out and submit forms and AJAX form elements using the test case browser.
- Handle file and image uploads in test cases.
- Write unit tests, and understand how the workflow for running unit tests differs from that of functional tests.
- Use the SimpleTest 7.x-2.x module from contrib to test existing websites.
This series assumes that you have basic understanding of Object Oriented PHP, and are familiar with Drupal module development. If you need to brush up on your module development watch our series on Drupal 7 Module Development. And, while not required to run tests, we do make use of drush in this series so knowing how to run drush commands is helpful.
Writing tests can be a challenging. But it doesn't have to be. And the benefits of doing so are huge. Especially as our web applications become more complex, as our teams grow, and as the multitude of ways in which people interact with our web applications changes constantly. Spending some time learning how to write tests will not only make your applications more reliable, it'll also make you a better developer.
Additional resources
Developers write tests for a variety of different reasons and understanding why, and how, writing tests can improve the quality of our code and our sanity helps motivate ourselves to write tests. The tests we write usually take one of two forms. Functional tests are used to test an applications behavior. When I click this link does it take me to the about page? Unit tests are used to verify the logic within a small segment, or unit, of code. When I call this function that is supposed to return a link with these specific parameters does it return the correct data? There are a ton of reasons that investing time and resources into writing automated tests is a big win in the long run and in this lesson we'll cover a bunch of them, including:
- Eliminate repetitive tasks.
- Improve overall stability of an application.
- Ensure critical paths & edge cases function.
- Reduce (not eliminate) the number of tests that need to be performed manually.
Types of Tests
There are are two main types of tests and we're going to be writing. Functional tests, and Unit tests.
Functional tests, also known as integration tests, test the functionality of a particular aspect of an application by simulating normal interaction with that system. In the case of web software this usually refers to simulating a user navigating and interacting with the website as if they where using their browser, keyboard, and mouse.
Unit testing is the art and practice of taking a small portion of code, a unit, and subjecting it to programmatic tests to prove its correctness. For example, testing a function that performs unit conversion by comparing a known input with an anticipated result.
UPDATE: This video references qa.drupal.org which is no longer in use. On Friday, October 23rd 2015 - qa.drupal.org received and processed it's last
test. The qa.drupal.org testbots have now been superseded by DrupalCI - and test results are now displayed directly on Drupal.org. While the mechanics are different now, the end result is still all patches to Drupal core are automatically tested.
SimpleTest is the tool that Drupal 7 core uses for discovering, and running all of it's tests. SimpleTest is also the name of the system that is used to write tests that can be run by the SimpleTest module. Understanding how SimpleTest locates the code that makes up our tests, and then executes that code, is an important part of understanding how to write tests. In this lesson we'll be discussing the pieces that make up the SimpleTest toolkit, some related terminology, and some best practices for dealing with SimpleTests in Drupal.
Methodology
Some best practices to keep in mind when working with SimpleTest and writing tests.
- Tests should always be run in a separate environment from your production site.
- Each test case sets up a completely new Drupal env, as such your test case needs to prepare the new environment as appropriate for your tests. Things like enabling any necessary modules, creating dummy content, and users etc. For this reason, it's best to have tests that require a similar setup be part of the same test case for better performance.
- After a test case is run the environment is torn down and started fresh for the next test case. This ensures there is no contamination between test cases and that each test can count on knowing exactly what to expect from the environment it's running in.
- SimpleTest is not always ideal for doing functional testing of your existing site. Drupalize.Me for example, we want to test that our About page is visible. But that's content, and doesn't live in code anywhere so creating it at install time can be tricky: We'll cover this scenario in more depth later.
- Always test both sides of the problem. If your site only allows authenticated users to make comments, and you write a test to make sure authenticated users can comment you'll also want to make sure you write a test that verifies that anon. users can not comment. If you're checking that a value exists under specific conditions, also verify that it doesn't exist when those conditions are not met
Terminology
SimpleTest: The original name of the module used for testing in Drupal. Though it's now called just "Testing", the terms are used interchangeably. Based on the PHP SimpleTest library.
Test Case: A set of conditions under which the tester will determine whether the system being tested satisfies requirements and works correctly. A Test case defines the environment in which a test or set of tests will run. Including preconditions such as what modules are enabled, what content exists, etc. It is assumed that these same preconditions are true for all individual tests within a test case.
In Drupal, all test cases are classes that extend the DrupalWebTestCase
, or the DrupalUnitTestCase
class.
Test (Test method): Each individual test checks the functionality of one discreet thing. Example, if our test case is "User cancellation" there are many variations of cancellation that we need to test. Cancel the account and keep their content, cancel the account and delete their content, don't allow cancellation of the account with UID 1, etc. Each of these individual tests is checking one piece of functionality with the test case.
Assertion: The smallest unit of a test. An assertion is used to check wether or not a given condition is true in the current context.