public IQueryOver <NodeVersion, NodeVersionStatusHistory> GenerateVersionedQueryPlusAttributes(Guid[] nodeIds = null, RevisionStatusType revisionStatus = null, bool limitToLatestRevision = true, IEnumerable <SortClause> sortClauses = null)
        {
            if (sortClauses == null)
            {
                sortClauses = Enumerable.Empty <SortClause>();
            }
            NodeVersion outerVersionSelect = null;
            var         outerQuery         = GenerateVersionedQuery(out outerVersionSelect, nodeIds, revisionStatus, limitToLatestRevision);

            Attribute                attribAlias              = null;
            AttributeDateValue       attributeDateValue       = null;
            AttributeDecimalValue    attributeDecimalValue    = null;
            AttributeIntegerValue    attributeIntegerValue    = null;
            AttributeStringValue     attributeStringValue     = null;
            AttributeLongStringValue attributeLongStringValue = null;

            outerQuery = outerQuery
                         .Left.JoinAlias(() => outerVersionSelect.Attributes, () => attribAlias) // Using a left join identifies to Nh that it can reuse the loaded Attributes because Nh considers them to be unaffected by the query, otherwise it issues another select from accessing NodeVersion.Attributes
                         .Left.JoinAlias(() => attribAlias.AttributeStringValues, () => attributeStringValue)
                         .Left.JoinAlias(() => attribAlias.AttributeLongStringValues, () => attributeLongStringValue)
                         .Left.JoinAlias(() => attribAlias.AttributeIntegerValues, () => attributeIntegerValue)
                         .Left.JoinAlias(() => attribAlias.AttributeDecimalValues, () => attributeDecimalValue)
                         .Left.JoinAlias(() => attribAlias.AttributeDateValues, () => attributeDateValue);

            //if (sortClauses.Any())
            //{
            //    AttributeDefinition attribDef = null;
            //    outerQuery = outerQuery.JoinAlias(() => attribAlias.AttributeDefinition, () => attribDef);
            //    foreach (var sortClause in sortClauses)
            //    {
            //        var coalesce = Projections.SqlFunction("coalesce", NHibernateUtil.String,
            //            Projections.Property(() => attributeStringValue.Value),
            //            Projections.Property(() => attributeLongStringValue.Value));
            //        if (sortClause.Direction == SortDirection.Ascending)
            //            outerQuery = outerQuery.OrderBy(coalesce).Asc;
            //        else
            //            outerQuery = outerQuery.OrderBy(coalesce).Desc;
            //    }
            //}

            return(outerQuery);
        }
Exemple #2
0
        public override QueryOver <NodeVersion> VisitFieldPredicate(FieldPredicateExpression node)
        {
            var fieldName      = node.SelectorExpression.FieldName;
            var valueKey       = node.SelectorExpression.ValueKey;
            var fieldValue     = node.ValueExpression.Value;
            var fieldValueType = fieldValue != null?fieldValue.GetType() : typeof(string);

            switch (fieldName.ToLowerInvariant())
            {
            case "id":
                Guid idValue = GetIdValue(node);

                switch (node.ValueExpression.ClauseType)
                {
                case ValuePredicateType.Equal:
                    return(QueryOver.Of <NodeVersion>().Where(x => x.Node.Id == idValue).Select(x => x.Id));

                case ValuePredicateType.NotEqual:
                    return(QueryOver.Of <NodeVersion>().Where(x => x.Node.Id != idValue).Select(x => x.Id));;

                default:
                    throw new InvalidOperationException(
                              "Cannot query an item by id by any other operator than == or !=");
                }
            }

            // First look up the types of the main field
            AttributeDefinition defAlias  = null;
            AttributeType       typeAlias = null;
            var attributeType             = _activeSession.QueryOver <AttributeDefinition>(() => defAlias)
                                            .JoinAlias(() => defAlias.AttributeType, () => typeAlias)
                                            .Where(() => defAlias.Alias == fieldName)
                                            .Select(x => typeAlias.PersistenceTypeProvider)
                                            .List <string>();

            var typesAlreadyEstablished = new List <string>();
            var typesToQuery            = new List <DataSerializationTypes>();

            foreach (var type in attributeType)
            {
                var typeName = type;
                if (typesAlreadyEstablished.Contains(typeName))
                {
                    continue;
                }
                try
                {
                    typesAlreadyEstablished.Add(typeName);
                    var persisterType = Type.GetType(typeName, false);
                    if (persisterType != null)
                    {
                        var persisterInstance = Activator.CreateInstance(persisterType) as IAttributeSerializationDefinition;
                        if (persisterInstance != null)
                        {
                            typesToQuery.Add(persisterInstance.DataSerializationType);
                        }
                    }
                }
                catch (Exception)
                {
                    throw;
                }
            }

            // U5-789
            // Workaround pending a better check of what data is actually saved
            // An issue arose because previous data had been saved in long-string,
            // but the datatype changed to be just string, therefore only string was
            // being queried despite all the data residing still in long-string
            if (typesToQuery.Contains(DataSerializationTypes.String) && !typesToQuery.Contains(DataSerializationTypes.LongString))
            {
                typesToQuery.Add(DataSerializationTypes.LongString);
            }

            NodeVersion              aliasNodeVersion         = null;
            Attribute                aliasAttribute           = null;
            AttributeDefinition      aliasAttributeDefinition = null;
            AttributeStringValue     aliasStringValue         = null;
            AttributeLongStringValue aliasLongStringValue     = null;
            AttributeIntegerValue    aliasIntegerValue        = null;
            AttributeDecimalValue    aliasDecimalValue        = null;
            NodeRelation             aliasNodeRelation        = null;
            AttributeDateValue       aliasDateValue           = null;

            QueryOver <NodeVersion, AttributeDefinition> queryExtender = QueryOver.Of <NodeVersion>(() => aliasNodeVersion)
                                                                         .JoinQueryOver <Attribute>(() => aliasNodeVersion.Attributes, () => aliasAttribute)
                                                                         .JoinQueryOver <AttributeDefinition>(() => aliasAttribute.AttributeDefinition, () => aliasAttributeDefinition);

            int numberOfMatchesEvaluated         = 0;
            AbstractCriterion restrictionBuilder = null;

            foreach (var dataSerializationTypese in typesToQuery.Distinct())
            {
                AbstractCriterion           restriction        = null;
                Expression <Func <object> > propertyExpression = null;
                Expression <Func <object> > subkeyExpression   = null;
                List <ValuePredicateType>   validClauseTypes   = null;
                var useLikeMatchForStrings = false;
                switch (dataSerializationTypese)
                {
                case DataSerializationTypes.SmallInt:
                case DataSerializationTypes.LargeInt:
                case DataSerializationTypes.Boolean:
                    queryExtender      = queryExtender.Left.JoinAlias(() => aliasAttribute.AttributeIntegerValues, () => aliasIntegerValue);
                    propertyExpression = () => aliasIntegerValue.Value;
                    subkeyExpression   = () => aliasIntegerValue.ValueKey;
                    validClauseTypes   = new List <ValuePredicateType>()
                    {
                        ValuePredicateType.Equal,
                        ValuePredicateType.GreaterThan,
                        ValuePredicateType.GreaterThanOrEqual,
                        ValuePredicateType.LessThan,
                        ValuePredicateType.LessThanOrEqual,
                        ValuePredicateType.NotEqual
                    };
                    break;

                case DataSerializationTypes.Decimal:
                    queryExtender      = queryExtender.Left.JoinAlias(() => aliasAttribute.AttributeDecimalValues, () => aliasDecimalValue);
                    propertyExpression = () => aliasDecimalValue.Value;
                    subkeyExpression   = () => aliasDecimalValue.ValueKey;
                    validClauseTypes   = new List <ValuePredicateType>()
                    {
                        ValuePredicateType.Equal,
                        ValuePredicateType.GreaterThan,
                        ValuePredicateType.GreaterThanOrEqual,
                        ValuePredicateType.LessThan,
                        ValuePredicateType.LessThanOrEqual,
                        ValuePredicateType.NotEqual
                    };
                    break;

                case DataSerializationTypes.String:
                    queryExtender      = queryExtender.Left.JoinAlias(() => aliasAttribute.AttributeStringValues, () => aliasStringValue);
                    propertyExpression = () => aliasStringValue.Value;
                    subkeyExpression   = () => aliasStringValue.ValueKey;
                    validClauseTypes   = new List <ValuePredicateType>()
                    {
                        ValuePredicateType.Equal,
                        ValuePredicateType.NotEqual,
                        ValuePredicateType.Contains,
                        ValuePredicateType.StartsWith,
                        ValuePredicateType.EndsWith,
                        ValuePredicateType.MatchesWildcard
                    };
                    break;

                case DataSerializationTypes.LongString:
                    queryExtender          = queryExtender.Left.JoinAlias(() => aliasAttribute.AttributeLongStringValues, () => aliasLongStringValue);
                    propertyExpression     = () => aliasLongStringValue.Value;
                    subkeyExpression       = () => aliasLongStringValue.ValueKey;
                    useLikeMatchForStrings = true;
                    validClauseTypes       = new List <ValuePredicateType>()
                    {
                        ValuePredicateType.Equal,
                        ValuePredicateType.NotEqual,
                        ValuePredicateType.Contains,
                        ValuePredicateType.StartsWith,
                        ValuePredicateType.EndsWith,
                        ValuePredicateType.MatchesWildcard
                    };
                    break;

                case DataSerializationTypes.Date:
                    queryExtender      = queryExtender.Left.JoinAlias(() => aliasAttribute.AttributeDateValues, () => aliasDateValue);
                    propertyExpression = () => aliasDateValue.Value;
                    subkeyExpression   = () => aliasDateValue.ValueKey;
                    validClauseTypes   = new List <ValuePredicateType>()
                    {
                        ValuePredicateType.Equal,
                        ValuePredicateType.GreaterThan,
                        ValuePredicateType.GreaterThanOrEqual,
                        ValuePredicateType.LessThan,
                        ValuePredicateType.LessThanOrEqual,
                        ValuePredicateType.NotEqual,
                        ValuePredicateType.Empty
                    };
                    break;
                }

                if (!validClauseTypes.Contains(node.ValueExpression.ClauseType))
                {
                    throw new InvalidOperationException("A field of type {0} cannot be queried with operator {1}".InvariantFormat(dataSerializationTypese.ToString(), node.ValueExpression.ClauseType.ToString()));
                }

                switch (node.ValueExpression.ClauseType)
                {
                case ValuePredicateType.Equal:
                    restriction = GetRestrictionEq(fieldValue, useLikeMatchForStrings, propertyExpression, subkeyExpression, valueKey);
                    break;

                case ValuePredicateType.NotEqual:
                    restriction = !GetRestrictionEq(fieldValue, useLikeMatchForStrings, propertyExpression);
                    break;

                case ValuePredicateType.LessThan:
                    restriction = GetRestrictionLt(fieldValue, propertyExpression);
                    break;

                case ValuePredicateType.LessThanOrEqual:
                    restriction = GetRestrictionLtEq(fieldValue, propertyExpression);
                    break;

                case ValuePredicateType.GreaterThan:
                    restriction = GetRestrictionGt(fieldValue, propertyExpression);
                    break;

                case ValuePredicateType.GreaterThanOrEqual:
                    restriction = GetRestrictionGtEq(fieldValue, propertyExpression);
                    break;

                case ValuePredicateType.Contains:
                    restriction = GetRestrictionContains(fieldValue, propertyExpression);
                    break;

                case ValuePredicateType.StartsWith:
                    restriction = GetRestrictionStarts(fieldValue, propertyExpression);
                    break;

                case ValuePredicateType.EndsWith:
                    restriction = GetRestrictionEnds(fieldValue, propertyExpression);
                    break;
                }

                if (restriction != null)
                {
                    if (numberOfMatchesEvaluated == 0)
                    {
                        restrictionBuilder = restriction;
                        numberOfMatchesEvaluated++;
                    }
                    else
                    {
                        restrictionBuilder = Restrictions.Or(restriction, restrictionBuilder);
                    }
                }
            }

            if (restrictionBuilder != null)
            {
                queryExtender = queryExtender.Where(restrictionBuilder);
            }

            queryExtender = queryExtender
                            .And(x => aliasAttributeDefinition.Alias == fieldName);

            return(queryExtender.Select(x => x.Id));
        }
        public override FieldFilterResult VisitFieldPredicate(FieldPredicateExpression node)
        {
            var fieldName      = node.SelectorExpression.FieldName;
            var valueKey       = node.SelectorExpression.ValueKey;
            var fieldValue     = node.ValueExpression.Value;
            var fieldValueType = fieldValue != null?fieldValue.GetType() : typeof(string);

            var toReturn = new FieldFilterResult();

            RdbmsModel.Attribute att            = null;
            Node outerNode                      = null;
            AttributeStringValue     stringVal  = null;
            AttributeLongStringValue longStrVal = null;
            AttributeIntegerValue    integerVal = null;
            AttributeDecimalValue    decimalVal = null;
            AttributeDateValue       dateVal    = null;
            AttributeDefinition      def        = null;

            // First check for special cases, typically stuff that can be queried
            // that isn't necessarily a value in any of the Attribute*Value tables
            switch (fieldName.ToLowerInvariant())
            {
            case "utccreated":
                DateTimeOffset createdDtoValue = ParseDateTimeOffset(fieldValue);
                if (createdDtoValue == default(DateTime))
                {
                    break;
                }

                toReturn.NhCriterion = CreateRestriction(node.ValueExpression.ClauseType,
                                                         () => outerNode.DateCreated, createdDtoValue);

                if (toReturn.NhCriterion != null)
                {
                    toReturn.Joins.Add(new Join(() => agg.Node, () => outerNode, JoinType.InnerJoin));
                }

                break;

            case "utcmodified":
                DateTimeOffset modifiedDtoValue = ParseDateTimeOffset(fieldValue);
                if (modifiedDtoValue == default(DateTime))
                {
                    break;
                }

                toReturn.NhCriterion = CreateRestriction(node.ValueExpression.ClauseType,
                                                         () => agg.StatusDate, modifiedDtoValue);

                break;

            case "id":
                Guid idValue = GetIdValue(node);

                switch (node.ValueExpression.ClauseType)
                {
                case ValuePredicateType.Equal:
                    toReturn.NhCriterion = Restrictions.Eq(Projections.Property(() => agg.Node.Id), idValue);
                    break;

                case ValuePredicateType.NotEqual:
                    toReturn.NhCriterion = !Restrictions.Eq(Projections.Property(() => agg.Node.Id), idValue);
                    break;

                default:
                    throw new InvalidOperationException("Cannot query an item by id by any other operator than == or !=");
                }
                break;

            case "system-internal-selected-template":
                //TODO Pending property editors getting involved in query modification prior to being passed to hive provider,
                //manually check for queries against a template here
                if (valueKey == "TemplateId" && fieldValue != null)
                {
                    var tryParseResult = HiveId.TryParse(fieldValue.ToString());
                    if (!tryParseResult.Success || tryParseResult.Result.ProviderGroupRoot == null || tryParseResult.Result.ProviderId == null ||
                        (tryParseResult.Result.ProviderGroupRoot.AbsoluteUri != "storage://" && tryParseResult.Result.ProviderId != "templates"))
                    {
                        var normalisedFieldValue = "/" + fieldValue.ToString().TrimStart("/").TrimEnd(".") + ".";
                        // Need to convert the value into the serialized form that a HiveId would use
                        var newValue = new HiveId("storage", "templates", new HiveIdValue(normalisedFieldValue)).ToString(HiveIdFormatStyle.UriSafe);
                        fieldValue = newValue;
                    }
                    else
                    {
                        fieldValue = tryParseResult.Result.ToString(HiveIdFormatStyle.UriSafe);
                    }
                }
                break;
            }

            if (toReturn.NhCriterion != null)
            {
                // The special-case handling above has already set the criterion,
                // so we don't have to evaluate field values in this pass and can return
                return(toReturn);
            }

            // Establish which Attribute*Value tables to query
            // First look up the types of the main field
            AttributeDefinition defAlias  = null;
            AttributeType       typeAlias = null;
            var attributeType             = _activeSession.NhSession.QueryOver <AttributeDefinition>(() => defAlias)
                                            .JoinAlias(() => defAlias.AttributeType, () => typeAlias)
                                            .Where(() => defAlias.Alias == fieldName)
                                            .Select(Projections.Distinct(Projections.Property(() => typeAlias.PersistenceTypeProvider)))
                                            .Cacheable()
                                            .List <string>();

            foreach (var type in attributeType)
            {
                var typeName = type;

                // Ensure we don't do unneccessary calls to Activator.CreateInstance,
                // (_typesAlreadyEstablished lives for the lifetime of the visitor)
                // but still make sure we populate the toReturn.ValueTypesToQuery for this
                // visit to a field predicate
                if (TypesAlreadyDiscovered.ContainsKey(typeName))
                {
                    var dst = TypesAlreadyDiscovered[typeName];
                    if (toReturn.ValueTypesToQuery.Contains(dst))
                    {
                        continue;
                    }
                    toReturn.ValueTypesToQuery.Add(dst);
                }
                try
                {
                    var persisterType = Type.GetType(typeName, false);
                    if (persisterType != null)
                    {
                        var persisterInstance = Activator.CreateInstance(persisterType) as IAttributeSerializationDefinition;
                        if (persisterInstance != null && !toReturn.ValueTypesToQuery.Contains(persisterInstance.DataSerializationType))
                        {
                            toReturn.ValueTypesToQuery.Add(persisterInstance.DataSerializationType);
                            TypesAlreadyDiscovered.TryAdd(typeName, persisterInstance.DataSerializationType);
                        }
                    }
                }
                catch (Exception ex)
                {
                    LogHelper.Error <NhCriteriaVisitor>("Error while trying to decide which value-tables to join & query", ex);
                    throw;
                }
            }

            // U5-789
            // Workaround pending a better check of what data is actually saved
            // An issue arose because previous data had been saved in long-string,
            // but the datatype changed to be just string, therefore only string was
            // being queried despite all the data residing still in long-string
            if (toReturn.ValueTypesToQuery.Contains(DataSerializationTypes.String) && !toReturn.ValueTypesToQuery.Contains(DataSerializationTypes.LongString))
            {
                toReturn.ValueTypesToQuery.Add(DataSerializationTypes.LongString);
            }

            // Now go through the types that we've found, and set up the expressions for the criteria
            int numberOfMatchesEvaluated         = 0;
            AbstractCriterion restrictionBuilder = null;

            foreach (var dataSerializationTypese in toReturn.ValueTypesToQuery.Distinct())
            {
                AbstractCriterion restriction = null;
                global::System.Linq.Expressions.Expression <Func <object> > propertyExpression = null;
                global::System.Linq.Expressions.Expression <Func <object> > subkeyExpression   = null;
                List <ValuePredicateType> validClauseTypes = null;
                var useLikeMatchForStrings = false;
                switch (dataSerializationTypese)
                {
                case DataSerializationTypes.SmallInt:
                case DataSerializationTypes.LargeInt:
                case DataSerializationTypes.Boolean:
                    propertyExpression = () => integerVal.Value;
                    subkeyExpression   = () => integerVal.ValueKey;
                    validClauseTypes   = new List <ValuePredicateType>()
                    {
                        ValuePredicateType.Equal,
                        ValuePredicateType.GreaterThan,
                        ValuePredicateType.GreaterThanOrEqual,
                        ValuePredicateType.LessThan,
                        ValuePredicateType.LessThanOrEqual,
                        ValuePredicateType.NotEqual
                    };
                    break;

                case DataSerializationTypes.Decimal:
                    propertyExpression = () => decimalVal.Value;
                    subkeyExpression   = () => decimalVal.ValueKey;
                    validClauseTypes   = new List <ValuePredicateType>()
                    {
                        ValuePredicateType.Equal,
                        ValuePredicateType.GreaterThan,
                        ValuePredicateType.GreaterThanOrEqual,
                        ValuePredicateType.LessThan,
                        ValuePredicateType.LessThanOrEqual,
                        ValuePredicateType.NotEqual
                    };
                    break;

                case DataSerializationTypes.String:
                    propertyExpression = () => stringVal.Value;
                    subkeyExpression   = () => stringVal.ValueKey;
                    validClauseTypes   = new List <ValuePredicateType>()
                    {
                        ValuePredicateType.Equal,
                        ValuePredicateType.NotEqual,
                        ValuePredicateType.Contains,
                        ValuePredicateType.StartsWith,
                        ValuePredicateType.EndsWith,
                        ValuePredicateType.MatchesWildcard
                    };
                    break;

                case DataSerializationTypes.LongString:
                    propertyExpression     = () => longStrVal.Value;
                    subkeyExpression       = () => longStrVal.ValueKey;
                    useLikeMatchForStrings = true;
                    validClauseTypes       = new List <ValuePredicateType>()
                    {
                        ValuePredicateType.Equal,
                        ValuePredicateType.NotEqual,
                        ValuePredicateType.Contains,
                        ValuePredicateType.StartsWith,
                        ValuePredicateType.EndsWith,
                        ValuePredicateType.MatchesWildcard
                    };
                    break;

                case DataSerializationTypes.Date:
                    propertyExpression = () => dateVal.Value;
                    subkeyExpression   = () => dateVal.ValueKey;
                    validClauseTypes   = new List <ValuePredicateType>()
                    {
                        ValuePredicateType.Equal,
                        ValuePredicateType.GreaterThan,
                        ValuePredicateType.GreaterThanOrEqual,
                        ValuePredicateType.LessThan,
                        ValuePredicateType.LessThanOrEqual,
                        ValuePredicateType.NotEqual,
                        ValuePredicateType.Empty
                    };
                    break;
                }

                if (!validClauseTypes.Contains(node.ValueExpression.ClauseType))
                {
                    throw new InvalidOperationException("A field of type {0} cannot be queried with operator {1}".InvariantFormat(dataSerializationTypese.ToString(), node.ValueExpression.ClauseType.ToString()));
                }

                // Based on the clause type, generate an NH criterion
                switch (node.ValueExpression.ClauseType)
                {
                case ValuePredicateType.Equal:
                    restriction = GetRestrictionEq(fieldValue, useLikeMatchForStrings, propertyExpression, subkeyExpression, valueKey);
                    break;

                case ValuePredicateType.NotEqual:
                    restriction = !GetRestrictionEq(fieldValue, useLikeMatchForStrings, propertyExpression);
                    break;

                case ValuePredicateType.LessThan:
                    restriction = GetRestrictionLt(fieldValue, propertyExpression);
                    break;

                case ValuePredicateType.LessThanOrEqual:
                    restriction = GetRestrictionLtEq(fieldValue, propertyExpression);
                    break;

                case ValuePredicateType.GreaterThan:
                    restriction = GetRestrictionGt(fieldValue, propertyExpression);
                    break;

                case ValuePredicateType.GreaterThanOrEqual:
                    restriction = GetRestrictionGtEq(fieldValue, propertyExpression);
                    break;

                case ValuePredicateType.Contains:
                    restriction = GetRestrictionContains(fieldValue, propertyExpression);
                    break;

                case ValuePredicateType.StartsWith:
                    restriction = GetRestrictionStarts(fieldValue, propertyExpression);
                    break;

                case ValuePredicateType.EndsWith:
                    restriction = GetRestrictionEnds(fieldValue, propertyExpression);
                    break;
                }

                // We might be dealing with multiple restrictions even for one field (e.g. if it's been stored once in String table, and another time in LongString)
                if (restriction != null)
                {
                    if (numberOfMatchesEvaluated == 0)
                    {
                        restrictionBuilder = restriction;
                        numberOfMatchesEvaluated++;
                    }
                    else
                    {
                        // If we're doing a second or later evaluation, turn the restriction into an Or to combine them all
                        restrictionBuilder = Restrictions.Or(restriction, restrictionBuilder);
                    }
                }
            }

            // Add the field name restriction
            var fieldNameRestriction = Restrictions.Eq(Projections.Property(() => def.Alias), fieldName);

            if (restrictionBuilder != null)
            {
                restrictionBuilder = Restrictions.And(restrictionBuilder, fieldNameRestriction);
            }
            else
            {
                restrictionBuilder = fieldNameRestriction;
            }

            // Build the query which will use the restrictions we've just generated
            var query = QueryOver.Of(() => agg);

            // Set up the basic joins (not that we're adding these to a Joins collection on our own type, not just doing
            // them on an NH query, so that we can optimise or merge the joins once the entire expression tree is evaluated
            // for example inside VisitBinary)
            toReturn.Joins.Add(new Join(() => agg.NodeVersion, () => version, JoinType.InnerJoin));
            toReturn.Joins.Add(new Join(() => version.Attributes, () => att, JoinType.InnerJoin));
            toReturn.Joins.Add(new Join(() => att.AttributeDefinition, () => def, JoinType.InnerJoin));

            // Set up the joins for the value tables - only add joins for the tables that we know we want to actually query based on
            // what VisitField might have encountered and added to toReturn.ValueTypesToQuery
            foreach (var dataSerializationTypese in toReturn.ValueTypesToQuery.Distinct())
            {
                Expression <Func <object> > path  = null;
                Expression <Func <object> > alias = null;
                switch (dataSerializationTypese)
                {
                case DataSerializationTypes.SmallInt:
                case DataSerializationTypes.LargeInt:
                case DataSerializationTypes.Boolean:
                    path  = () => att.AttributeIntegerValues;
                    alias = () => integerVal;
                    break;

                case DataSerializationTypes.Decimal:
                    path  = () => att.AttributeDecimalValues;
                    alias = () => decimalVal;
                    break;

                case DataSerializationTypes.String:
                    path  = () => att.AttributeStringValues;
                    alias = () => stringVal;
                    break;

                case DataSerializationTypes.LongString:
                    path  = () => att.AttributeLongStringValues;
                    alias = () => longStrVal;
                    break;

                case DataSerializationTypes.Date:
                    path  = () => att.AttributeDateValues;
                    alias = () => dateVal;
                    break;
                }
                toReturn.Joins.Add(new Join(path, alias));
            }

            toReturn.Subquery = query
                                .Where(restrictionBuilder)
                                .Select(x => x.NodeVersion.Id);

            toReturn.NhCriterion = restrictionBuilder;
            return(toReturn);
        }
        public override QueryOver <NodeVersion> VisitFieldPredicate(FieldPredicateExpression node)
        {
            var fieldName  = node.SelectorExpression.FieldName;
            var valueKey   = node.SelectorExpression.ValueKey;
            var fieldValue = node.ValueExpression.Value;

            switch (fieldName.ToLowerInvariant())
            {
            case "id":
                Guid idValue = GetIdValue(node);

                switch (node.ValueExpression.ClauseType)
                {
                case ValuePredicateType.Equal:
                    return(QueryOver.Of <NodeVersion>().Where(x => x.Node.Id == idValue).Select(x => x.Id));

                case ValuePredicateType.NotEqual:
                    return(QueryOver.Of <NodeVersion>().Where(x => x.Node.Id != idValue).Select(x => x.Id));;

                default:
                    throw new InvalidOperationException(
                              "Cannot query an item by id by any other operator than == or !=");
                }
            }

            NodeVersion              aliasNodeVersion         = null;
            Attribute                aliasAttribute           = null;
            AttributeDefinition      aliasAttributeDefinition = null;
            AttributeStringValue     aliasStringValue         = null;
            AttributeLongStringValue aliasLongStringValue     = null;
            NodeRelation             aliasNodeRelation        = null;
            AttributeDateValue       aliasDateValue           = null;

            //TODO: This is going to need to lookup more than string values

            var queryString = QueryOver.Of <NodeVersion>(() => aliasNodeVersion)
                              .JoinQueryOver <Attribute>(() => aliasNodeVersion.Attributes, () => aliasAttribute)
                              .JoinQueryOver <AttributeDefinition>(() => aliasAttribute.AttributeDefinition, () => aliasAttributeDefinition)
                              .Left.JoinQueryOver <AttributeStringValue>(() => aliasAttribute.AttributeStringValues, () => aliasStringValue)
                              .Left.JoinQueryOver <AttributeLongStringValue>(() => aliasAttribute.AttributeLongStringValues, () => aliasLongStringValue)
                              .Left.JoinQueryOver(() => aliasAttribute.AttributeDateValues, () => aliasDateValue)
                              //select the field name...
                              .Where(x => aliasAttributeDefinition.Alias == fieldName);

            if (!valueKey.IsNullOrWhiteSpace())
            {
                //if the value key is specified, then add that to the query
                queryString = queryString.And(() => aliasStringValue.ValueKey == valueKey || aliasLongStringValue.ValueKey == valueKey).Select(x => x.Id);
            }


            //now, select the field value....
            switch (node.ValueExpression.ClauseType)
            {
            case ValuePredicateType.Equal:
                var sqlCeCompatibleQuery =
                    Restrictions.Or(
                        Restrictions.Eq(
                            Projections.Property <AttributeStringValue>(x => aliasStringValue.Value), fieldValue),
                        Restrictions.Like(
                            Projections.Property <AttributeLongStringValue>(x => aliasLongStringValue.Value),
                            fieldValue as string,
                            MatchMode.Exact));

                return(queryString.And(sqlCeCompatibleQuery).Select(x => x.Id));

            case ValuePredicateType.NotEqual:
                return(queryString.And(x => aliasStringValue.Value != fieldValue).Select(x => x.Id));

            case ValuePredicateType.MatchesWildcard:
            case ValuePredicateType.Contains:
                return(queryString.And(Restrictions.Like(Projections.Property <AttributeStringValue>(x => aliasStringValue.Value), fieldValue as string, MatchMode.Anywhere)).Select(x => x.Id));

            case ValuePredicateType.StartsWith:
                return(queryString.And(Restrictions.Like(Projections.Property <AttributeStringValue>(x => aliasStringValue.Value), fieldValue as string, MatchMode.Start)).Select(x => x.Id));

            case ValuePredicateType.EndsWith:
                return(queryString.And(Restrictions.Like(Projections.Property <AttributeStringValue>(x => aliasStringValue.Value), fieldValue as string, MatchMode.End)).Select(x => x.Id));

            case ValuePredicateType.LessThanOrEqual:
                return
                    (queryString.And(
                         Restrictions.Le(Projections.Property <AttributeDateValue>(x => aliasDateValue.Value),
                                         (DateTimeOffset)fieldValue)));

            case ValuePredicateType.GreaterThanOrEqual:
                return
                    (queryString.And(
                         Restrictions.Ge(Projections.Property <AttributeDateValue>(x => aliasDateValue.Value),
                                         (DateTimeOffset)fieldValue)));

            default:
                throw new InvalidOperationException(
                          "This linq provider doesn't support a ClauseType of {0} for field {1}".InvariantFormat(
                              node.ValueExpression.ClauseType.ToString(), fieldName));
            }
        }