nystudio107 / craft-similar

Similar for Craft lets you find elements, Entries, Categories, Commerce Products, etc, that are similar, based on... other related elements.
https://nystudio107.com/
MIT License
26 stars 5 forks source link

Criteria limit not accurate #1

Closed forby closed 6 years ago

forby commented 6 years ago

Pretty simple setup, just trying something like this:

{% set similar = craft.similar.find({
        element: entry,
        context: entry.workTags,
        criteria: craft.entries.limit(2)
}) %}

(In this case, each entry has a tag field called 'workTag')

I have many tags used throughout my entries, but I'm only retrieving one result. I've tripple-checked and I know that my handful of entries should at least have 2 connections each. Most of them share at least two tags. Am I doing something wrong?

After looking around for answers I finally tried increasing the limit to 4, now I retrieve 2 results on almost all entries (with the occasional 3 or 4). Maybe I'm just missing something.

drifteaur commented 6 years ago

I'm having the same issue, with a limit of 3 I'm usually getting 2 entries, setting the limit to 9 gives me about 5.

forby commented 6 years ago

🤔

khalwat commented 6 years ago

hmmm. I'll have a look.

khalwat commented 6 years ago

@forby @drifteaur what happens if you pass it in like this:

{% set similar = craft.similar.find({
        element: entry,
        context: entry.workTags,
        criteria: {
            'limit': 2,
        }
}) %}
drifteaur commented 6 years ago

Still buggy for me. For some entries (about 1 in 10 or so, I'm setting the limit to 3 and getting just 2. If I set the limit higher, I can get more than 3. I'm mixing in more than one category field, but @forby had the same issue with just one. I'll try to take a look at the queries performed.

    {% set ids = entry.category.ids() | merge(entry.audience.ids()) | merge(entry.category_en.ids()) %}
    {% set similarEntries = craft.similar.find({
        element: entry,
        context: ids,
        criteria: {
            'limit': 3,
        } 
    }) %}
drifteaur commented 6 years ago

So, the limit is in a subquery, and then it gets inner joined to several tables which might filter the results.

SELECT `elements`.`id`, `elements_sites`.`siteId`, COUNT(*) as count
FROM (SELECT `elements`.`id` AS `elementsId`, `elements_sites`.`id` AS `elementsSitesId`, `content`.`id` AS `contentId`
FROM `elements` `elements`
INNER JOIN `entries` `entries` ON `entries`.`id` = `elements`.`id`
INNER JOIN `elements_sites` `elements_sites` ON `elements_sites`.`elementId` = `elements`.`id`
INNER JOIN `content` `content` ON `content`.`elementId` = `elements`.`id`
LEFT JOIN `structureelements` `structureelements` ON `structureelements`.`elementId` = `elements`.`id`
LEFT JOIN `relations` ON elements.id=`relations`.sourceId
WHERE (`elements_sites`.`siteId`='1') AND ((elements.id != '193') AND (`relations`.`targetId` IN ('34', '46'))) AND (`content`.`siteId`='1') AND (`elements`.`archived`=FALSE) AND (((`elements`.`enabled`=TRUE) AND (`elements_sites`.`enabled`=TRUE)) AND (`entries`.`postDate` <= '2018-03-21 15:05:18') AND ((`entries`.`expiryDate` IS NULL) OR (`entries`.`expiryDate` > '2018-03-21 15:05:18'))) AND (`elements_sites`.`enabled`=TRUE)
ORDER BY `structureelements`.`lft`, `entries`.`postDate` DESC
LIMIT 3) `subquery`
INNER JOIN `entries` `entries` ON `entries`.`id` = `subquery`.`elementsId`
INNER JOIN `elements` `elements` ON `elements`.`id` = `subquery`.`elementsId`
INNER JOIN `elements_sites` `elements_sites` ON `elements_sites`.`id` = `subquery`.`elementsSitesId`
INNER JOIN `content` `content` ON `content`.`id` = `subquery`.`contentId`
LEFT JOIN `structureelements` `structureelements` ON `structureelements`.`elementId` = `subquery`.`elementsId`
LEFT JOIN `relations` ON elements.id=`relations`.sourceId
GROUP BY `relations`.`sourceId`
ORDER BY `count` DESC

If I move the LIMIT to the end, it works, but I couldn't figure out how to do it in the plugin itself, modifying $query only modifies the subquery.

SELECT `elements`.`id`, `elements_sites`.`siteId`, COUNT(*) as count
FROM (SELECT `elements`.`id` AS `elementsId`, `elements_sites`.`id` AS `elementsSitesId`, `content`.`id` AS `contentId`
FROM `elements` `elements`
INNER JOIN `entries` `entries` ON `entries`.`id` = `elements`.`id`
INNER JOIN `elements_sites` `elements_sites` ON `elements_sites`.`elementId` = `elements`.`id`
INNER JOIN `content` `content` ON `content`.`elementId` = `elements`.`id`
LEFT JOIN `structureelements` `structureelements` ON `structureelements`.`elementId` = `elements`.`id`
LEFT JOIN `relations` ON elements.id=`relations`.sourceId
WHERE (`elements_sites`.`siteId`='1') AND ((elements.id != '193') AND (`relations`.`targetId` IN ('34', '46'))) AND (`content`.`siteId`='1') AND (`elements`.`archived`=FALSE) AND (((`elements`.`enabled`=TRUE) AND (`elements_sites`.`enabled`=TRUE)) AND (`entries`.`postDate` <= '2018-03-21 15:05:18') AND ((`entries`.`expiryDate` IS NULL) OR (`entries`.`expiryDate` > '2018-03-21 15:05:18'))) AND (`elements_sites`.`enabled`=TRUE)
ORDER BY `structureelements`.`lft`, `entries`.`postDate` DESC) `subquery`
INNER JOIN `entries` `entries` ON `entries`.`id` = `subquery`.`elementsId`
INNER JOIN `elements` `elements` ON `elements`.`id` = `subquery`.`elementsId`
INNER JOIN `elements_sites` `elements_sites` ON `elements_sites`.`id` = `subquery`.`elementsSitesId`
INNER JOIN `content` `content` ON `content`.`id` = `subquery`.`contentId`
LEFT JOIN `structureelements` `structureelements` ON `structureelements`.`elementId` = `subquery`.`elementsId`
LEFT JOIN `relations` ON elements.id=`relations`.sourceId
GROUP BY `relations`.`sourceId`
ORDER BY `count` DESC
LIMIT 3
khalwat commented 6 years ago

Fixed in https://github.com/nystudio107/craft-similar/commit/76cfca9bfff26fd095e4479c541e30658c147ab6

Thx to @Robin for figuring it out!

forby commented 6 years ago

👏👏👏

khalwat commented 6 years ago

Actually not fixed, and related to https://github.com/nystudio107/craft-similar/issues/6

khalwat commented 6 years ago

I think the core issue is we're using the limitCriteria we as passed in to create the Element Query to begin with. This causes it to end up in the subQuery rather than in the query, due to the way that Element Queries work.

khalwat commented 6 years ago

Fixed in https://github.com/nystudio107/craft-similar/commit/066948c78090a49280f3b658c538fa1716f20bc1 thanks to @Anubarak