I have an unlimited field attaching a field collection to a node.

I have a view that is sorting those nodes based on fields in the field collection (using a relationship).

Everything works fine with one exposed filter. What I would like to know is if there is a way that I could have two+ exposed filter forms for the same field (contained in the field collection), and return nodes who have field collections that can match all of the criteria?

A more visual example:

For the sake of simplicity, say I have a fruit basket node that has fruits attached to it via a field collection. The fruits have a type and a weight associated with them. In a view of fruit baskets, I want to expose a filter that has a fruit type and minimum weight:

Fruit Type: ?
Min. Weight: ?

That works just fine with my current setup. The key is that I want the user to be able to specify a few fruits and their minimum weight:

Fruit Type: Apple
Min. Weight: 5

Fruit Type: Orange
Min. Weight: 3

If I expose two filters on the same field and "AND" them together, I get no results because there isn't a field collection that has an "Apple" and "Orange." If I "OR" the filters, I get results that have either an apple with a weight of 5+, OR an orange with a weight of 3+.

Can I accomplish a view that would show only the results of fruit baskets that fit both criteria?

Please excuse me if the answer is obvious and I sound like a fruit basket, I am very new to Drupal. Any help is greatly appreciated!

CommentFileSizeAuthor
#3 Screenshot from 2013-03-12 17:21:59.png95.08 KBskywalk

Comments

merlinofchaos’s picture

Status: Active » Fixed

The short answer is: No, you won't be able to do this with Views.

The long answer is to explain why this is hard in SQL; once you understand that, you'll likely see why this is hard in Views.

First, let's start with a simpler example than a node to fields, but one that represents the same kind of data structure. I have a table called organizations It has an organization id (we'll call it oid) some amount of data.

I then have another other table members which contains an oid (the organization the user is a member of) and some other data. An organization can have 0..infinity members (though fitting infinite members into your database is another problem!).

Now, I want to find every organization that 'Dave' and 'Jerad' are members of. (Apologies if to Dave and Jerad if they read this :). In order to find the organizations, I need to actually put my constraints in the relationships because of the way SQL works:

SELECT o.* FROM organizations o 
  LEFT JOIN members m1 ON m1.oid = o.oid AND m1.name = 'Dave' 
  LEFT JOIN members m2 ON m2.oid = o.oid AND m2.name = 'Jerad'
WHERE m1.oid IS NOT NULL AND m2.oid IS NOT NULL;

Sidenote: We could also do this with INNER JOINs which would be simpler, but I think this method does a better job of explaining what is going on.

What we're doing is joining the members table twice and telling SQL to only join the member record for a particular person. In SQL, if you perform a LEFT join and no records match in the right table, you still get a record and all the fields in the right table are NULL. So we filter those out. (This is what the INNER join would've done for us).

Sidenote 2: This query will fail if you can have Dave or Jerad as a member of the organization twice. Or rather, it will potentially produce duplicates. At that point you'll need a subquery. The subquery is less efficient, but more stable, and slightly more straightforward.

The subquery looks like this:

SELECT o.* FROM organizations o
WHERE o.oid IN (SELECT m.oid FROM members m WHERE m.name = 'Dave') AND o.oid IN (SELECT m2.oid FROM members m2 WHERE m2.name = 'Jerad');

As you can hopefully see, both of these queries are both non-trivial and don't follow the standard sort of template for the way Views generates queries. Views tries to avoid subqueries wherever possible (they're inefficient) and Views is simply incapable of creating the kind of relationship needed for the first query using field api.

You could, potentially, write a custom filter handler that would accomplish the second query. But Views can't do this on its own and is unlikely to be able to without custom coding.

skywalk’s picture

@merlinofchaos- Thank you for your great (and fast!) response. I should also thank you for such an EXCELLENT module!

It makes much more sense when you put it in that context. Thanks for spelling it out for me. I didn't want to resort to it, but it seems I am going to have to create a field for each fruit (going back to my crude visualization) to fill in the weights. I may write a custom filter, but I'm trying to stay as stock as possible on this project.

My actual use case is a product selector for Ethernet switches. There are vast possibilities of port quantity and port type combinations (e.g., 10/100, Gigabit, PoE, RJ-45, M12, etc.) for each switch. So, a switch might have something like 2-10/100/1000 RJ-45 PoE and 14-10/100 M12 ports (fruit baskets would be much more fun). The user will specify what types of ports they need, and the selector will recommend products.

On a hopefully still somewhat related topic: If anyone has any ideas or feedback on the best way to organize data like this to work nicely with Views and exposed filters, I'd love to hear it. Obviously field collections aren't going to be my best friend here.

skywalk’s picture

Status: Fixed » Active
StatusFileSize
new95.08 KB

I hate to re-open this, but I think this actually can be accomplished with Views and I would like to verify.

Maybe I am way off base, but after playing around today it seems like I can accomplish this task by creating multiple relationships to the same field-collection field, and then using a different relationship for each each exposed filter (same filters, different relationships).

I've done some initial testing, and it seems to give me what I want... but I'm left wondering if that makes any sense?? Should that work, or have I hit my head a few too many times? I lean to the latter, but I can't seem to prove it.

Excuse the fact that this is using serial ports, not Ethernet like mentioned above (these fruit baskets have quite the variety!).

Here is a screenshot of the view (didn't see any use in exporting it):

screenshot

mustanggb’s picture

Status: Active » Closed (outdated)