private static IMongoQuery CompositeQuery(ModelInfo.SearchParamDefinition parameterDef, Operator optor, String modifier, ValueExpression operand)
        {
            if (optor == Operator.IN)
            {
                var choices = ((ChoiceValue)operand);
                var queries = new List <IMongoQuery>();
                foreach (var choice in choices.Choices)
                {
                    queries.Add(CompositeQuery(parameterDef, Operator.EQ, modifier, choice));
                }
                return(M.Query.Or(queries));
            }
            else if (optor == Operator.EQ)
            {
                var typedOperand = (CompositeValue)operand;
                var queries      = new List <IMongoQuery>();
                var components   = typedOperand.Components;
                var subParams    = parameterDef.CompositeParams;

                if (components.Count() != subParams.Count())
                {
                    throw new ArgumentException(String.Format("Parameter {0} requires exactly {1} composite values, not the currently provided {2} values.", parameterDef.Name, subParams.Count(), components.Count()));
                }

                for (int i = 0; i < subParams.Count(); i++)
                {
                    var subCrit = new Criterium();
                    subCrit.Type      = Operator.EQ;
                    subCrit.ParamName = subParams[i];
                    subCrit.Operand   = components[i];
                    subCrit.Modifier  = modifier;
                    queries.Add(subCrit.ToFilter(parameterDef.Resource));
                }
                return(M.Query.And(queries));
            }
            throw new ArgumentException(String.Format("Invalid operator {0} on composite parameter {1}", optor.ToString(), parameterDef.Name));
        }
        private static IMongoQuery DateQuery(String parameterName, Operator optor, String modifier, ValueExpression operand)
        {
            if (optor == Operator.IN)
            {
                IEnumerable <ValueExpression> opMultiple = ((ChoiceValue)operand).Choices;
                return(M.Query.Or(opMultiple.Select(choice => DateQuery(parameterName, Operator.EQ, modifier, choice))));
            }

            string start = parameterName + ".start";
            string end   = parameterName + ".end";

            var typedOperand = ((UntypedValue)operand).AsDateValue();
            var value        = GroomDate(typedOperand.Value);

            switch (optor)
            {
            case Operator.EQ:
                return
                    (M.Query.Or(
                         M.Query.Matches(parameterName, "^" + value),
                         M.Query.And(
                             M.Query.Or(M.Query.Exists(start), M.Query.Exists(end)),
                             M.Query.Or(M.Query.LTE(start, value), M.Query.NotExists(start)),
                             M.Query.Or(M.Query.GTE(end, value), M.Query.NotExists(end))
                             )
                         ));

            case Operator.GT:
                return
                    (M.Query.Or(
                         M.Query.GT(parameterName, value),
                         M.Query.GT(start, value)
                         ));

            case Operator.GTE:
                return
                    (M.Query.Or(
                         M.Query.GTE(parameterName, value),
                         M.Query.GTE(start, value)
                         ));

            case Operator.LT:
                return
                    (M.Query.Or(
                         M.Query.LT(parameterName, value),
                         M.Query.LT(end, value)
                         ));

            case Operator.LTE:
                return
                    (M.Query.Or(
                         M.Query.LTE(parameterName, value),
                         M.Query.LTE(end, value)
                         ));

            case Operator.ISNULL:
                return(M.Query.EQ(parameterName, null));    //We don't use M.Query.NotExists, because that would exclude resources that have this field with an explicit null in it.

            case Operator.NOTNULL:
                return(M.Query.NE(parameterName, null));    //We don't use M.Query.Exists, because that would include resources that have this field with an explicit null in it.

            default:
                throw new ArgumentException(String.Format("Invalid operator {0} on date parameter {1}", optor.ToString(), parameterName));
            }
        }
        private static IMongoQuery QuantityQuery(String parameterName, Operator optor, String modifier, ValueExpression operand)
        {
            var quantity = operand.ToModelQuantity();

            Fhir.Metrics.Quantity q = quantity.ToSystemQuantity().Canonical();
            string    decimals      = Units.SearchableString(q);
            BsonValue value         = q.GetValueAsBson();

            List <IMongoQuery> queries = new List <IMongoQuery>();

            switch (optor)
            {
            case Operator.EQ:
                queries.Add(M.Query.Matches("decimals", new BsonRegularExpression("^" + decimals)));
                break;

            default:
                queries.Add(ExpressionQuery("value", optor, value));
                break;
            }

            if (quantity.System != null)
            {
                queries.Add(M.Query.EQ("system", quantity.System.ToString()));
            }

            queries.Add(M.Query.EQ("unit", q.Metric.ToString()));

            IMongoQuery query = M.Query.ElemMatch(parameterName, M.Query.And(queries));

            return(query);
        }
        private static IMongoQuery TokenQuery(String parameterName, Operator optor, String modifier, ValueExpression operand)
        {
            string systemfield  = parameterName + ".system";
            string codefield    = parameterName + ".code";
            string displayfield = parameterName + ".display";
            string textfield    = parameterName + "_text";

            switch (optor)
            {
            case Operator.EQ:
                var typedOperand = ((UntypedValue)operand).AsTokenValue();
                switch (modifier)
                {
                case Modifier.TEXT:
                    return(M.Query.Or(
                               M.Query.Matches(textfield, new BsonRegularExpression(typedOperand.Value, "i")),
                               M.Query.Matches(displayfield, new BsonRegularExpression(typedOperand.Value, "i"))));

                default:
                    if (typedOperand.AnyNamespace)
                    {
                        return(M.Query.EQ(codefield, typedOperand.Value));
                    }
                    else if (String.IsNullOrWhiteSpace(typedOperand.Namespace))
                    {
                        return(M.Query.ElemMatch(parameterName,
                                                 M.Query.And(
                                                     M.Query.NotExists("system"),
                                                     M.Query.EQ("code", typedOperand.Value)
                                                     )));
                    }
                    else
                    {
                        return(M.Query.ElemMatch(parameterName,
                                                 M.Query.And(
                                                     M.Query.EQ("system", typedOperand.Namespace),
                                                     M.Query.EQ("code", typedOperand.Value)
                                                     )));
                    }
                }

            case Operator.IN:
                IEnumerable <ValueExpression> opMultiple = ((ChoiceValue)operand).Choices;
                return(M.Query.Or(opMultiple.Select(choice => TokenQuery(parameterName, Operator.EQ, modifier, choice))));

            case Operator.ISNULL:
                return(M.Query.And(M.Query.EQ(parameterName, BsonNull.Value), M.Query.EQ(textfield, BsonNull.Value)));    //We don't use M.Query.NotExists, because that would exclude resources that have this field with an explicit null in it.

            case Operator.NOTNULL:
                return(M.Query.Or(M.Query.NE(parameterName, BsonNull.Value), M.Query.EQ(textfield, BsonNull.Value)));    //We don't use M.Query.Exists, because that would include resources that have this field with an explicit null in it.

            default:
                throw new ArgumentException(String.Format("Invalid operator {0} on token parameter {1}", optor.ToString(), parameterName));
            }
        }
        private static IMongoQuery StringQuery(String parameterName, Operator optor, String modifier, ValueExpression operand)
        {
            switch (optor)
            {
            case Operator.EQ:
                var typedOperand = ((UntypedValue)operand).AsStringValue().ToString();
                switch (modifier)
                {
                case Modifier.EXACT:
                    return(M.Query.EQ(parameterName, typedOperand));

                case Modifier.TEXT:         //the same behaviour as :phonetic in previous versions.
                    return(M.Query.Matches(parameterName + "soundex", "^" + typedOperand));

                case Modifier.NONE:
                case null:
                    //partial from begin
                    return(M.Query.Matches(parameterName, new BsonRegularExpression("^" + typedOperand, "i")));

                default:
                    throw new ArgumentException(String.Format("Invalid modifier {0} on string parameter {1}", modifier, parameterName));
                }

            case Operator.IN:     //We'll only handle choice like :exact
                IEnumerable <ValueExpression> opMultiple = ((ChoiceValue)operand).Choices;
                return(M.Query.In(parameterName, new BsonArray(opMultiple.Cast <UntypedValue>().Select(uv => uv.AsStringValue().ToString()))));

            case Operator.ISNULL:
                return(M.Query.EQ(parameterName, null));    //We don't use M.Query.NotExists, because that would exclude resources that have this field with an explicit null in it.

            case Operator.NOTNULL:
                return(M.Query.NE(parameterName, null));    //We don't use M.Query.Exists, because that would include resources that have this field with an explicit null in it.

            default:
                throw new ArgumentException(String.Format("Invalid operator {0} on string parameter {1}", optor.ToString(), parameterName));
            }
        }