'use strict';

/*
 * yield_and_hashed.js (extends yield_rooted_or.js)
 *
 * Intersperse queries which use the AND_HASH stage with updates and deletes of documents they may
 * match.
 */
load('jstests/concurrency/fsm_libs/extend_workload.js');       // for extendWorkload
load('jstests/concurrency/fsm_workloads/yield_rooted_or.js');  // for $config

var $config = extendWorkload(
    $config,
    function($config, $super) {

        /*
         * Issue a query that will use the AND_HASH stage. This is a little tricky, so use
         * stagedebug to force it to happen. Unfortunately this means it can't be batched.
         */
        $config.states.query = function andHash(db, collName) {
            var nMatches = 100;
            assertAlways.lte(nMatches, this.nDocs);
            // Construct the query plan: two ixscans under an andHashed.
            // Scan c <= nMatches
            var ixscan1 = {
                ixscan: {
                    args: {
                        name: 'stages_and_hashed',
                        keyPattern: {c: 1},
                        startKey: {'': nMatches},
                        endKey: {},
                        endKeyInclusive: true,
                        direction: -1
                    }
                }
            };

            // Scan d >= this.nDocs - nMatches
            var ixscan2 = {
                ixscan: {
                    args: {
                        name: 'stages_and_hashed',
                        keyPattern: {d: 1},
                        startKey: {'': this.nDocs - nMatches},
                        endKey: {},
                        endKeyInclusive: true,
                        direction: 1
                    }
                }
            };

            var andix1ix2 = {
                andHash: {args: {nodes: [ixscan1, ixscan2]}}
            };

            // On non-MMAP storage engines, index intersection plans will always re-filter
            // the docs to make sure we don't get any spurious matches.
            var fetch = {
                fetch: {
                    filter: {c: {$lte: nMatches}, d: {$gte: (this.nDocs - nMatches)}},
                    args: {node: andix1ix2}
                }
            };

            var res = db.runCommand({stageDebug: {plan: fetch, collection: collName}});
            assertAlways.commandWorked(res);
            for (var i = 0; i < res.results.length; i++) {
                var result = res.results[i];
                assertAlways.lte(result.c, nMatches);
                assertAlways.gte(result.d, this.nDocs - nMatches);
            }
        };

        return $config;
    });
