shared/generic_operator_index.js
function applyGenericOperatorIndex(r, schema, parent){
	if(r.type === 'view'){

		if(r.view === 'reducing-index') return r

		if( r.view !== 'case' && r.view !== 'default' && //do not break switch statements
			r.view !== 'map' && r.view !== 'multimap'// do not break mapValue(map()) exprs
			&& r.view !== 'mapValue'
			&& r.view !== 'property' //no point wrapping a redundant index
			&& r.view !== 'cast' //no point TODO flip this to an explicit "heavy" tag that causes indexing
			&& r.view !== 'type' //not worth it
			&& r.view !== 'timestamp' //not worth it
			&& r.view !== 'list' //not worth it
			&& r.view !== 'isa' //not worth it
			&& r.view !== 'not' //not worth it
			&& r.view !== 'generic-operator-index' //obv
			&& (!parent || parent.view !== 'generic-operator-index') //obv
			&& !schema[r.view] //don't index object(id) lookups - they're a no-op anyway
			){

			var params = getParams(r, schema)
			//console.log(r.view + ' ' + JSON.stringify(params))
			//if(r.view !== 'property' || r.params[1] !== params[0]){
				if(params.length === 1){
				
					if(canIndex(r, schema, params[0])){
						//console.log('cannot index: ')
						//if(r.view === 'tokenizeAndStemText') _.errout('TODO: ' + JSON.stringify(r, null, 2))
						//return r
					
				
						var indexExprs = getIndexes(r, params[0])
						var res = {
							type: 'view',
							view: 'generic-operator-index',
							params: [
								params[0],
								r
							].concat(indexExprs),
							schemaType: r.schemaType,
							code: r.code
						}
						//console.log('res: ' + JSON.stringify(res, null, 2))
						return res
					}
				}else if(params.length > 1 && r.params.length === 1 && r.params[0].view === 'property'){//there are multiple params in use, but they can be reduced to a single param via a 'let' externalization
				
					var name = 'extracted_for_goi_'+Math.random()
					var expr = r.params[0].params[1]
					var newParam = {type: 'param', name: name, schemaType: expr.schemaType, was: expr}
					
					var newPropertyExpr = {
						type: 'view',
						view: 'property',
						params: [
							r.params[0].params[0],
							newParam
						],
						schemaType: r.schemaType
					}
					
					var res = {
						type: 'let',
						//view: 'let',
						expr: expr,
						rest: {
							type: 'view',
							view: 'generic-operator-index',
							params: [
								newParam,
								{
									type: 'view',
									view: r.view,
									params: [newPropertyExpr],
									schemaType: r.schemaType
								},
								newPropertyExpr
							]
						},
						name: name,
						schemaType: r.schemaType,
						code: r.code
					}
					
					//res.expr = applyGenericOperatorIndex(res.expr, schema)//?
					
					return res
				}
			//}
		}
		
		if(r.view === 'tokenizeAndStemText') _.errout('TODO: ' + JSON.stringify(r, null, 2))

		if(r.view === 'generic-operator-index') return r
		
		r.params.forEach(function(p,i){
			r.params[i] = applyGenericOperatorIndex(p, schema, r)
		})
	}else if(r.type === 'let'){
		r.expr = applyGenericOperatorIndex(r.expr, schema)
		r.rest = applyGenericOperatorIndex(r.rest, schema)
	}else if(r.type === 'macro'){
		r.expr = applyGenericOperatorIndex(r.expr, schema)
	}else if(r.type === 'param' || r.type === 'value' || r.type === 'int' || r.type === 'nil'){
		//do nothing

	}
	if(r.view === 'tokenizeAndStemText') _.errout('TODO: ' + JSON.stringify(r, null, 2))
	return r
}
function applyGenericOperatorIndex(r, schema, parent){
	if(r.type === 'view'){

		if(r.view === 'reducing-index') return r

		if( r.view !== 'case' && r.view !== 'default' && //do not break switch statements
			r.view !== 'map' && r.view !== 'multimap'// do not break mapValue(map()) exprs
			&& r.view !== 'mapValue'
			&& r.view !== 'property' //no point wrapping a redundant index
			&& r.view !== 'cast' //no point TODO flip this to an explicit "heavy" tag that causes indexing
			&& r.view !== 'type' //not worth it
			&& r.view !== 'timestamp' //not worth it
			&& r.view !== 'list' //not worth it
			&& r.view !== 'isa' //not worth it
			&& r.view !== 'not' //not worth it
			&& r.view !== 'generic-operator-index' //obv
			&& (!parent || parent.view !== 'generic-operator-index') //obv
			&& !schema[r.view] //don't index object(id) lookups - they're a no-op anyway
			){

			var params = getParams(r, schema)
			//console.log(r.view + ' ' + JSON.stringify(params))
			//if(r.view !== 'property' || r.params[1] !== params[0]){
				if(params.length === 1){
				
					if(canIndex(r, schema, params[0])){
						//console.log('cannot index: ')
						//if(r.view === 'tokenizeAndStemText') _.errout('TODO: ' + JSON.stringify(r, null, 2))
						//return r
					
				
						var indexExprs = getIndexes(r, params[0])
						var res = {
							type: 'view',
							view: 'generic-operator-index',
							params: [
								params[0],
								r
							].concat(indexExprs),
							schemaType: r.schemaType,
							code: r.code
						}
						//console.log('res: ' + JSON.stringify(res, null, 2))
						return res
					}
				}else if(params.length > 1 && r.params.length === 1 && r.params[0].view === 'property'){//there are multiple params in use, but they can be reduced to a single param via a 'let' externalization
				
					var name = 'extracted_for_goi_'+Math.random()
					var expr = r.params[0].params[1]
					var newParam = {type: 'param', name: name, schemaType: expr.schemaType, was: expr}
					
					var newPropertyExpr = {
						type: 'view',
						view: 'property',
						params: [
							r.params[0].params[0],
							newParam
						],
						schemaType: r.schemaType
					}
					
					var res = {
						type: 'let',
						//view: 'let',
						expr: expr,
						rest: {
							type: 'view',
							view: 'generic-operator-index',
							params: [
								newParam,
								{
									type: 'view',
									view: r.view,
									params: [newPropertyExpr],
									schemaType: r.schemaType
								},
								newPropertyExpr
							]
						},
						name: name,
						schemaType: r.schemaType,
						code: r.code
					}
					
					//res.expr = applyGenericOperatorIndex(res.expr, schema)//?
					
					return res
				}
			//}
		}
		
		if(r.view === 'tokenizeAndStemText') _.errout('TODO: ' + JSON.stringify(r, null, 2))

		if(r.view === 'generic-operator-index') return r
		
		r.params.forEach(function(p,i){
			r.params[i] = applyGenericOperatorIndex(p, schema, r)
		})
	}else if(r.type === 'let'){
		r.expr = applyGenericOperatorIndex(r.expr, schema)
		r.rest = applyGenericOperatorIndex(r.rest, schema)
	}else if(r.type === 'macro'){
		r.expr = applyGenericOperatorIndex(r.expr, schema)
	}else if(r.type === 'param' || r.type === 'value' || r.type === 'int' || r.type === 'nil'){
		//do nothing

	}
	if(r.view === 'tokenizeAndStemText') _.errout('TODO: ' + JSON.stringify(r, null, 2))
	return r
}
/*
function shouldIndex(r){//TODO set up with .heavy = true config of impls, then start using
if(r.type === 'view'){
if(r.view === 'typeset'){
return false
}else if(r.view === 'each' || r.view === 'map' || r.view === 'eachReduce'){
return true
}
var should = false
r.params.forEach(function(p){
should = should || shouldIndex(p)
})
}else if(r.type === 'let'){
return shouldIndex(r.expr, param) || shouldIndex(r.rest)
}else if(r.type === 'macro'){
return shouldIndex(r.expr, param)
}else if(r.type === 'param'){
return false
}else if(r.type === 'value' || r.type === 'int' || r.type === 'nil'){
return false
}else{
_.errout('TODO')
}
}