All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.nuiton.wikitty.storage.WikittySearchEngineInMemory Maven / Gradle / Ivy

The newest version!
/*
 * #%L
 * Wikitty :: api
 * %%
 * Copyright (C) 2009 - 2010 CodeLutin, Benjamin Poussin
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as 
 * published by the Free Software Foundation, either version 3 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public 
 * License along with this program.  If not, see
 * .
 * #L%
 */
package org.nuiton.wikitty.storage;

import com.google.common.collect.Multiset;
import com.google.common.collect.TreeMultiset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Factory;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuiton.wikitty.WikittyException;
import org.nuiton.wikitty.WikittyUtil;
import org.nuiton.wikitty.entities.ElementExtension;
import org.nuiton.wikitty.entities.ElementField;
import org.nuiton.wikitty.entities.ElementId;
import org.nuiton.wikitty.entities.FieldType;
import org.nuiton.wikitty.entities.Wikitty;
import org.nuiton.wikitty.entities.WikittyExtension;
import org.nuiton.wikitty.entities.WikittyTypes;
import org.nuiton.wikitty.query.FacetQuery;
import org.nuiton.wikitty.query.FacetSortType;
import org.nuiton.wikitty.query.FacetTopic;
import org.nuiton.wikitty.query.WikittyQuery;
import org.nuiton.wikitty.query.WikittyQueryResult;
import org.nuiton.wikitty.query.WikittyQueryResultTreeNode;
import org.nuiton.wikitty.query.WikittyQueryVisitor;
import org.nuiton.wikitty.query.conditions.Condition;
import org.nuiton.wikitty.query.conditions.ConditionValue;
import org.nuiton.wikitty.query.conditions.ConditionValueString;
import org.nuiton.wikitty.query.conditions.ContainsAll;
import org.nuiton.wikitty.query.conditions.ContainsOne;
import org.nuiton.wikitty.query.conditions.Equals;
import org.nuiton.wikitty.query.conditions.Greater;
import org.nuiton.wikitty.query.conditions.GreaterOrEquals;
import org.nuiton.wikitty.query.conditions.Less;
import org.nuiton.wikitty.query.conditions.LessOrEquals;
import org.nuiton.wikitty.query.conditions.Like;
import org.nuiton.wikitty.query.conditions.NotEquals;
import org.nuiton.wikitty.query.conditions.NotNull;
import org.nuiton.wikitty.query.conditions.Select;
import org.nuiton.wikitty.query.conditions.Unlike;
import org.nuiton.wikitty.query.function.FunctionValue;
import org.nuiton.wikitty.query.function.WikittyQueryFunction;
import org.nuiton.wikitty.search.Criteria;
import org.nuiton.wikitty.search.PagedResult;
import org.nuiton.wikitty.search.TreeNodeResult;
import org.nuiton.wikitty.search.operators.And;
import org.nuiton.wikitty.search.operators.AssociatedRestriction;
import org.nuiton.wikitty.search.operators.Between;
import org.nuiton.wikitty.search.operators.BinaryOperator;
import org.nuiton.wikitty.search.operators.Contains;
import org.nuiton.wikitty.search.operators.Element;
import org.nuiton.wikitty.search.operators.False;
import org.nuiton.wikitty.search.operators.In;
import org.nuiton.wikitty.search.operators.Keyword;
import org.nuiton.wikitty.search.operators.Not;
import org.nuiton.wikitty.search.operators.Null;
import org.nuiton.wikitty.search.operators.Or;
import org.nuiton.wikitty.search.operators.Restriction;
import org.nuiton.wikitty.search.operators.RestrictionName;
import org.nuiton.wikitty.search.operators.True;
import org.nuiton.wikitty.services.WikittyTransaction;

public class WikittySearchEngineInMemory implements WikittySearchEngine {

    /** to use log facility, just put in your code: log.info(\"...\"); */
    static private Log log = LogFactory.getLog(WikittySearchEngineInMemory.class);

    protected WikittyStorageInMemory wikittyStorage;

    public WikittySearchEngineInMemory(WikittyStorageInMemory wikittyStorage) {
        this.wikittyStorage = wikittyStorage;
    }

    @Override
    public void clear(WikittyTransaction transaction) {
        // do nothing
    }

    @Override
    public void store(WikittyTransaction transaction, Collection wikitties, boolean force) {
        // do nothing
    }

    @Override
    public void delete(WikittyTransaction transaction, Collection idList) throws WikittyException {
        // do nothing
    }

    static private boolean checkRestriction(WikittySearchEngine searchEngine,
            WikittyTransaction transaction, Condition condition, Wikitty w) {
        WikittyQueryVisitorCheckCondition v =
                new WikittyQueryVisitorCheckCondition(searchEngine, transaction, w);
        condition.accept(v);
        boolean result = v.getResult();
        return result;
    }

    /** comparateur generic qui accept deux objets de meme type en argument */
    static public Comparator genericComparator = new Comparator() {
        public int compare(Object v1, Object v2) {
            if (v1 == null && v2 == null) {
                return 0;
            } else if (v1 == null) {
                return -1;
            } else if (v2 == null) {
                return 1;
            }

            if (v1 == v2 || v1.equals(v2)) {
                return 0;
            }

            if (v1 instanceof Collection) {
                v1 = CollectionUtils.get(v1, 0);
            }
            if (v2 instanceof Collection) {
                v2 = CollectionUtils.get(v2, 0);
            }
            Comparable c1;
            Comparable c2;
            if (v1 instanceof Comparable) {
                c1 = (Comparable)v1;
            } else {
                return 0; // non comparable on retourne 0
            }
            if (v2 instanceof Comparable) {
                c2 = (Comparable)v2;
            } else {
                return 0; // non comparable on retourne 0
            }

            int result = c1.compareTo(c2);
            return result;
        }
    };

    static public class WikittyComparator implements Comparator {

        protected List asc;
        protected List desc;

        public WikittyComparator(List asc, List desc) {
            this.asc = asc;
            this.desc = desc;
        }

        /** compare un champs de deux objets Wikitty */
        protected int compareValue(Wikitty o1, Wikitty o2, org.nuiton.wikitty.entities.Element e, boolean asc) {
            Object v1 = o1.getFqField(e.getValue());
            Object v2 = o2.getFqField(e.getValue());

            int result = genericComparator.compare(v1, v2);
            if (!asc) {
                result = result * -1;
            }
            return result;
        }

        public int compare(Wikitty o1, Wikitty o2) {
            int result = 0;

            for (org.nuiton.wikitty.entities.Element e : asc) {
                result = compareValue(o1, o2, e, true);
                if (result != 0) {
                    return result;
                }
            }
            
            for (org.nuiton.wikitty.entities.Element e : desc) {
                result = compareValue(o1, o2, e, false);
                if (result != 0) {
                    return result;
                }
            }
            return result;
        }

    }

    static public class FacetPredicate {
        WikittyTransaction tx;
        WikittySearchEngine searchEngine;
        WikittyQuery query;
        Map topic;
        
        public FacetPredicate(WikittySearchEngine searchEngine, 
                WikittyTransaction tx, WikittyQuery query) {
            this.searchEngine = searchEngine;
            this.tx = tx;
            this.query = query;

            topic = LazyMap.decorate(new HashMap(), new Factory() {
                public Object create() {
                    return TreeMultiset.create(genericComparator);
                }
            });

        }

        public Map> getFacets() {
            Map> result = new HashMap>();
            for (String facetName : topic.keySet()) {
                List list = new ArrayList();
                Multiset b = topic.get(facetName);
                for (Object topicName : b.elementSet()) {
                    int count = b.count(topicName);
                    // pour ajouter le topic il faut un minimum indique dans la query
                    if (count >= query.getFacetMinCount()) {
                        FacetTopic ft = new FacetTopic(facetName, String.valueOf(topicName), count);
                        list.add(ft);
                    }
                }

                // on re-tri si besoin, sinon l'ordre par defaut est sur la valeur des topics
                if (query.getFacetSort() == FacetSortType.count) {
                    Collections.sort(list, query.getFacetSort().compartor);
                }

                // ... et on en prend que le nombre demande
                if (list.size() > query.getFacetLimit()) {
                    list = list.subList(0, query.getFacetLimit());
                }
                result.put(facetName, list);
            }
            return result;
        }

        public boolean add(Wikitty w) {
            boolean result = false;
            
            // create facet extension
            if (query.isFacetExtension()) {
                String facetName = ElementExtension.EXTENSION.getValue();
                for (String extName : w.getExtensionNames()) {
                    topic.get(facetName).add(extName);
                    result = true;
                }
            }

            // create facet field
            for (org.nuiton.wikitty.entities.Element e : query.getFacetField()) {
                if (ElementExtension.EXTENSION.equals(e)) {
                    String facetName = ElementExtension.EXTENSION.getValue();
                    for (String extName : w.getExtensionNames()) {
                        topic.get(facetName).add(extName);
                        result = true;
                    }
                } else {
                    String fqf = e.getValue();
                    Object value = w.getFqField(fqf);
                    if (value != null) {
                        if (value instanceof Collection) {
                            topic.get(fqf).addAll((Collection)value);
                        } else {
                            topic.get(fqf).add(value);
                        }
                        result = true;
                    }
                }
            }

            // create facet query
            for (FacetQuery q : query.getFacetQuery()) {
                if (checkRestriction(searchEngine, tx, q.getCondition(), w)) {
                    String facetName = q.getName();
                    if (facetName == null) {
                        facetName = q.getCondition().toString();
                    }
                    topic.get(facetName).add(facetName);
                    result = true;
                }
            }

            return result;
        }
    }
    
    @Override
    public WikittyQueryResult> findAllByQuery(WikittyTransaction transaction, WikittyQuery query) {

        // la condition select du query, sera traite a la fin
        Select select = null;
        WikittyQuery queryWithoutSelect = query;

            if (query.isSelectQuery()) {
                select = query.getSelect();
                // copy de la query sans le select
                queryWithoutSelect = query.getWhereQuery();
                if (queryWithoutSelect.getCondition() == null) {
                    queryWithoutSelect.setCondition(new org.nuiton.wikitty.query.conditions.True());
                }

            }


        int offset = queryWithoutSelect.getOffset();
        int limit = queryWithoutSelect.getLimit();
        List ids = new LinkedList();
        FacetPredicate facets = new FacetPredicate(this, transaction, queryWithoutSelect);

        ArrayList wikitties =
                new ArrayList(wikittyStorage.getWikitties().values());

        // on tri les wikitties selon l'ordre demande ...
        if (!queryWithoutSelect.getSortAscending().isEmpty()
                || !queryWithoutSelect.getSortDescending().isEmpty() ) {
            Collections.sort(wikitties, new WikittyComparator(
                    queryWithoutSelect.getSortAscending(), queryWithoutSelect.getSortDescending()));
        }

        int totalResult = 0;
        for (Wikitty w : wikitties) {
            String id = w.getWikittyId();
            Condition c = queryWithoutSelect.getCondition();
            if (!w.isDeleted()) {
                if (checkRestriction(this, transaction, c, w)) {
                    totalResult++;
                    if (totalResult > offset && ids.size() < limit) {
                        // ajout en tant que resultat
                        ids.add(id);
                    }
                    facets.add(w);
                }
            }
        }

        List> values = new ArrayList>(ids.size());
        List> selectResult = null;
        if (select == null) {
            String idTag = org.nuiton.wikitty.entities.Element.ID.getValue();
            for (Object id : ids) {
                values.add(WikittyUtil.singletonMap(idTag, id));
            }
        } else {
            // Extract data
            for (String id : ids) {
                Wikitty w = wikittyStorage.getWikitties().get(id);
                Map map = new LinkedHashMap(w.getFieldValue());
                map.put(org.nuiton.wikitty.entities.Element.ID.getValue(), id);
                values.add(map);
            }

            values = select.getFunction().call(query, values);
            selectResult = values;
        }


        WikittyQueryResult> result =
                new WikittyQueryResult>(
                queryWithoutSelect.getName(), offset, totalResult,
                query, queryWithoutSelect.getCondition().toString(),
                values, selectResult, ids, facets.getFacets(), 0, 0);

        return result;
    }

    @Override
    public WikittyQueryResultTreeNode findAllChildrenCount(WikittyTransaction transaction,
            String wikittyId, int depth, boolean count, WikittyQuery filter) {
        // FIXME
        throw new UnsupportedOperationException("Not supported yet.");
    }

    static public class WikittyQueryVisitorCheckCondition extends WikittyQueryVisitor {

        protected WikittySearchEngine searchEngine;
        /** transaction used to check wikitty */
        protected WikittyTransaction tx;
        /** wikitty to check */
        protected Wikitty w;
        /** la pile d'evaluation des differencetes contraintes */
        protected Deque evalStack = new LinkedList();

        public boolean getResult() {
            Boolean result = evalStack.poll();
            if (result == null) {
                // s'il n'y avait pas de condition, la stack est vide, donc
                // c'est vrai
                result = Boolean.TRUE;
            }
            return result;
        }

        public WikittyQueryVisitorCheckCondition(
                WikittySearchEngine searchEngine, WikittyTransaction tx, Wikitty w) {
            this.searchEngine = searchEngine;
            this.tx = tx;
            this.w = w;
        }

        /**
         * Interface permettant de verifier une condition sur deux collections
         */
        static private interface Predicate {
            /**
             * retourne vrai si la condition est validee
             * @param type type des elements des collections. Si null alors les
             * collection contiennent des String (id ou nom extension)
             * @param value l'ensemble des valeur du champs
             * @param expected l'ensemble des valeurs attendue
             * @return vrai si la condition est validee
             */
            boolean check(FieldType type, Collection values, Collection expected);
        }

        static private Predicate BetweenPredicate = new Predicate() {
                public boolean check(FieldType type, Collection values, Collection expected) {
                    boolean result =  false;

                    if (values != null && expected.size() == 2) {
                        // si on a pas deux valeur, c'est qu'on a pas min et max
                        Iterator i = expected.iterator();
                        Object min = i.next();
                        Object max = i.next();
                        for (Object fieldValue : values) {
                            // fieldValue doit etre comparable
                            if (fieldValue instanceof Comparable) {
                                // recupere le type de la valeur

                                result = ((Comparable) fieldValue).compareTo(min) >= 0
                                        && ((Comparable) fieldValue).compareTo(max) <= 0;
                                if (result) {
                                    // si une des valeurs correspond, on retourne true
                                    break;
                                }
                            }
                        }
                    }
                    return result;
                }
            };

        static private Predicate ContainsAllPredicate = new Predicate() {
                public boolean check(FieldType type, Collection values, Collection expected) {
                    boolean result = false;
                    // si expected est vide alors rien ne doit matcher
                    // http://www.nuiton.org/issues/3735
                    // Anomalie #3735: containsOne and containsAll with an empty collection returns all objects but no object should have been returned
                    if (values != null && CollectionUtils.isNotEmpty(expected)) {
                        expected = CollectionUtils.subtract(expected, values);
                        // si lorsqu'on retire tous les elements en commun avec la valeur
                        // du champs il ne reste plus rien, c'est que le containsAll est
                        // vrai
                        result = expected.isEmpty();
                    }
                    return result;

                }
            };

        static private Predicate ContainsOnePredicate = new Predicate() {
                public boolean check(FieldType type, Collection values, Collection expected) {
                    boolean result = false;
                    if (values != null) {
                        expected = CollectionUtils.retainAll(expected, values);
                        // si lorsqu'on retient tous les elements en commun avec la valeur
                        // du champs il ne reste plus rien, c'est que le containsOne est
                        // faux, donc il faut qu'il en reste
                        result = !expected.isEmpty();
                    }
                    return result;
                }
            };

        static private Predicate EqualsPredicate = new Predicate() {
                public boolean check(FieldType type, Collection values, Collection expected) {
                    boolean result = false;
                    if (values != null && expected.size() > 0) {
                        // gestion des type STRING differement car il peut y avoir des '*'
                        if (type != null && type.getType() == WikittyTypes.STRING) {
                            Iterator i = expected.iterator();
                            String exp = String.valueOf(i.next());
                            for (Object fieldValue : values) {
                                String val = String.valueOf(fieldValue);
                                result = matchString(val, exp, false);
                                if (result) {
                                    // si une des valeurs correspond, on retourne true
                                    break;
                                }
                            }
                        } else {
                            expected = CollectionUtils.subtract(expected, values);
                            // si lorsqu'on retire tous les elements en commun avec la valeur
                            // du champs il ne reste plus rien, c'est que le equals est
                            // vrai
                            result = expected.isEmpty();
                        }
                    }
                    return result;

                }
            };

        static private Predicate EqualsIgnoreCaseAndAccentPredicate = new Predicate() {
                public boolean check(FieldType type, Collection values, Collection expected) {
                    boolean result = false;
                    if (values != null && expected.size() > 0) {
                        // gestion des type STRING differement car il peut y avoir des '*'
                        if (type != null && type.getType() == WikittyTypes.STRING) {
                            Iterator i = expected.iterator();
                            String exp = String.valueOf(i.next());
                            for (Object fieldValue : values) {
                                String val = String.valueOf(fieldValue);
                                result = matchString(val, exp, true);
                                if (result) {
                                    // si une des valeurs correspond, on retourne true
                                    break;
                                }
                            }
                        } else {
                            expected = CollectionUtils.subtract(expected, values);
                            // si lorsqu'on retire tous les elements en commun avec la valeur
                            // du champs il ne reste plus rien, c'est que le equals est
                            // vrai
                            result = expected.isEmpty();
                        }
                    }
                    return result;

                }
            };

        static private Predicate GreaterPredicate = new Predicate() {
                public boolean check(FieldType type, Collection values, Collection expected) {
                    boolean result =  false;

                    if (values != null && expected.size() > 0) {
                        // si on a pas une valeur, c'est pas bon :(
                        Iterator i = expected.iterator();
                        Object val = i.next();
                        for (Object fieldValue : values) {
                            // fieldValue doit etre comparable
                            if (fieldValue instanceof Comparable) {
                                // recupere le type de la valeur
                                result = ((Comparable) fieldValue).compareTo(val) > 0;
                                if (result) {
                                    // si une des valeurs correspond, on retourne true
                                    break;
                                }
                            }
                        }
                    }
                    return result;
                }
            };

        static private Predicate GreaterOrEqualsPredicate = new Predicate() {
                public boolean check(FieldType type, Collection values, Collection expected) {
                    boolean result =  false;

                    if (values != null && expected.size() > 0) {
                        // si on a pas une valeur, c'est pas bon :(
                        Iterator i = expected.iterator();
                        Object val = i.next();
                        for (Object fieldValue : values) {
                            // fieldValue doit etre comparable
                            if (fieldValue instanceof Comparable) {
                                // recupere le type de la valeur
                                result = ((Comparable) fieldValue).compareTo(val) >= 0;
                                if (result) {
                                    // si une des valeurs correspond, on retourne true
                                    break;
                                }
                            }
                        }
                    }
                    return result;
                }
            };

        static private Predicate LessPredicate = new Predicate() {
                public boolean check(FieldType type, Collection values, Collection expected) {
                    boolean result =  false;

                    if (values != null && expected.size() > 0) {
                        // si on a pas une valeur, c'est pas bon :(
                        Iterator i = expected.iterator();
                        Object val = i.next();
                        for (Object fieldValue : values) {
                            // fieldValue doit etre comparable
                            if (fieldValue instanceof Comparable) {
                                // recupere le type de la valeur
                                result = ((Comparable) fieldValue).compareTo(val) < 0;
                                if (result) {
                                    // si une des valeurs correspond, on retourne true
                                    break;
                                }
                            }
                        }
                    }
                    return result;
                }
            };

        static private Predicate LessOrEqualsPredicate = new Predicate() {
                public boolean check(FieldType type, Collection values, Collection expected) {
                    boolean result =  false;

                    if (values != null && expected.size() > 0) {
                        // si on a pas une valeur, c'est pas bon :(
                        Iterator i = expected.iterator();
                        Object val = i.next();
                        for (Object fieldValue : values) {
                            // fieldValue doit etre comparable
                            if (fieldValue instanceof Comparable) {
                                // recupere le type de la valeur
                                result = ((Comparable) fieldValue).compareTo(val) <= 0;
                                if (result) {
                                    // si une des valeurs correspond, on retourne true
                                    break;
                                }
                            }
                        }
                    }
                    return result;
                }
            };

        static private Predicate KeywordPredicate = new Predicate() {
                public boolean check(FieldType type, Collection values, Collection expected) {
                    boolean result =  false;

                    if (values != null && expected.size() > 0) {
                        // si on a pas une valeur, c'est pas bon :(
                        Iterator i = expected.iterator();
                        String exp = String.valueOf(i.next());
                        for (Object fieldValue : values) {
                            String val = String.valueOf(fieldValue);
                            result = matchString(val, exp, true);
                            if (result) {
                                // si une des valeurs correspond, on retourne true
                                break;
                            }
                        }
                    }
                    return result;
                }
            };

        static private Predicate LikePredicate = new Predicate() {
            public boolean check(FieldType type, Collection values, Collection expected) {
                    boolean result =  false;

                    if (values != null && expected.size() > 0) {
                        // si on a pas une valeur, c'est pas bon :(
                        Iterator i = expected.iterator();
                        String exp = String.valueOf(i.next());
                        for (Object fieldValue : values) {
                            String val = String.valueOf(fieldValue);
                            result = matchString(val, exp, true);
                            if (result) {
                                // si une des valeurs correspond, on retourne true
                                break;
                            }
                        }
                    }
                    return result;
                }
            };

        static private Predicate NullPredicate = new Predicate() {
                public boolean check(FieldType type, Collection values, Collection expected) {
                    boolean result =  values == null || values.contains(null);
                    return result;
                }
            };

        static private Predicate NotNullPredicate = new Predicate() {
                public boolean check(FieldType type, Collection values, Collection expected) {
                    boolean result =  values != null && !values.contains(null);
                    return result;
                }
            };



        /**
         * check if string match other string.
         *
         * @param s string
         * @param sub string to match with 's' parameter. This string can start
         * or and with star '*'
         * @param ignoreCaseAndAccent if true match is done in ingore case mode
         * @return true if sub match s
         */
        static private boolean matchString(String s, String sub, boolean ignoreCaseAndAccent) {
            if (ignoreCaseAndAccent) {
                s = StringUtils.stripAccents(s);
                s = s.toLowerCase();

                sub = StringUtils.stripAccents(sub);
                sub = sub.toLowerCase();
            }
            sub = sub.replaceAll("\\*", ".*");
            sub = sub.replaceAll("\\?", ".");
            
            boolean result = s.matches(sub);
            return result;
        }

        /**
         * Collecte les valeur possible de l'element demande
         *
         * @param e l'element demande. Peut etre l'id, l'extension ou un champs.
         * le champs pouvant contenir des * pour remplacer le nom de extension
         * ou le nom du champs (ex: *.name, myext.*)
         * @param values une map avec en cle le nom du champs et en valeur
         * la valeur du champs
         */
        protected void collectFieldValue(
                org.nuiton.wikitty.entities.Element e,
                Map values) {
            if (e instanceof ElementId) {
                values.put(e.getValue(), Collections.singleton(w.getWikittyId()));
            } else if (e instanceof ElementExtension) {
                values.put(e.getValue(), w.getExtensionNames());
            } else {
                String fqf = e.getValue();
                if (w.hasField(fqf)) {
                    Object v = w.getFqField(fqf);
                    values.put(fqf, convert(null, v));
                } else {
                    String extName = WikittyExtension.extractExtensionName(fqf);
                    String fieldName = WikittyExtension.extractFieldName(fqf);
                    if ("*".equals(extName) && "*".equals(fieldName)) {
                        // on ajoute tous les champs de toutes les extensions
                        for (String f : w.getAllFieldNames()) {
                            Object v = w.getFqField(f);
                            values.put(f, convert(null, v));
                        }
                    } else if ("*".equals(fieldName)){
                        // on ajoute tous les champs de l'extension demandee
                        for (String f : w.getExtension(extName).getFieldNames()) {
                            String fq = WikittyUtil.getFQFieldName(extName, f);
                            Object v = w.getFqField(fq);
                            values.put(fq, convert(null, v));
                        }
                    } else if ("*".equals(extName)){
                        // on ajoute tous les champs ayant le nom demande
                        // quelque soit l'extension
                        for (String ext : w.getExtensionNames()) {
                            for (String f : w.getExtension(ext).getFieldNames()) {
                                if (f.equals(fieldName)) {
                                    String fq = WikittyUtil.getFQFieldName(ext, f);
                                    Object v = w.getFqField(fq);
                                    values.put(fq, convert(null, v));
                                }
                            }
                        }
                    }
                }
            }
        }

        /**
         * Converti o en une list de valeur compatible avec type
         * @param type le type dans lequel doit etre converti o
         * @param o la valeur a convertir, si o est une collection, chaque
         * element de la collection est converti
         *
         * @return une nouvelle collection avec les elements dans le bon type
         */
        protected Collection convert(FieldType type, Object o) {
            Collection result;
            try {
                if (o instanceof Collection) {
                    // order of collection must be maintained, for that use LinkedHashSet
                    if (type == null) {
                        // if type is null don't change type (in case of id or extension
                        // or when we know object is already in right type)
                        result = new LinkedHashSet((Collection)o);
                    } else {
                        result = new LinkedHashSet();
                        for (Object v : (Collection)o) {
                            result.add(type.getContainedValidObject(v));
                        }
                    }
                } else {
                    if (type != null) {
                        // if type is null don't change type (in case of id or extension
                        // or when we know object is already in right type)
                        o = type.getContainedValidObject(o);
                    }
                    result = Collections.singleton(o);
                }
            } catch (Exception eee) {
                // si on arrive pas a convertir on retourne null
                result = null;
                if (log.isTraceEnabled()) {
                    log.trace("not an error but can't convert string to wanted type", eee);
                }
            }
            return result;
        }

        /**
         * Verifie qu'un predicat est vrai pour un element et une valeur attendu
         * @param predicate le predicat a verifier
         * @param element l'element mis en cause (id, extension, field)
         * @param expected la/les valeur(s) attendu(s)
         * @return vrai si le predicat est verifie
         */
        protected boolean check(Predicate predicate,
                org.nuiton.wikitty.entities.Element element, Object expected) {
            if (element instanceof ElementField) {
                String fqf = element.getValue();
                // suppression du type dans l'element, car on en a pas besoin
                String extName = WikittyUtil.getExtensionNameFromFQFieldName(fqf);
                String fieldName = WikittyUtil.getFieldNameFromFQFieldName(fqf);
                // on reconstruit fqf, sans l'optionnel 3eme composantes (Type)
                fqf = WikittyUtil.getFQFieldName(extName, fieldName);
                element = org.nuiton.wikitty.entities.Element.get(fqf);
            }

            boolean result = false;

            boolean isIndexed = true;
            Map fieldValues = new HashMap();
            collectFieldValue(element, fieldValues);
            for (String fqf : fieldValues.keySet()) {
                FieldType type = null;
                if (w.hasField(fqf)) {
                    type = w.getFieldType(fqf);
                    isIndexed = type.isIndexed();
                }
                Collection contained = convert(type, expected);
                if (isIndexed && contained != null) {
                    // si on a reussi a convertir dans le bon type on fait la verif
                    Collection values;
                    values = fieldValues.get(fqf);
                    
                    result = predicate.check(type, values, contained);
                    if (result) {
                        // on a reussi a verifie le predicat pour au moins un champs
                        // on renvoie donc true
                        break;
                    }
                }
            }
            return result;
        }

        @Override
        public void visit(ConditionValueString o) {
            // do nothing
        }

        @Override
        public boolean visitEnter(WikittyQuery o) {
            // nothing to do
            return true;
        }

        @Override
        public void visitLeave(WikittyQuery o, boolean enterOrMiddleResult) {
            // nothing to do
        }

        @Override
        public boolean visitEnter(org.nuiton.wikitty.query.conditions.And o) {
            evalStack.push(Boolean.TRUE);
            return true;
        }

        @Override
        public boolean visitMiddle(org.nuiton.wikitty.query.conditions.And o) {
            // l'evaluation du dernier element du and
            Boolean last = evalStack.pop();
            // la valeur courante du and
            Boolean currentValue = evalStack.pop();

            Boolean result = currentValue && last;
            evalStack.push(result);

            // si le and est deja faut ca ne sert a rien de d'evaluer la suite
            return result;
        }

        @Override
        public void visitLeave(org.nuiton.wikitty.query.conditions.And o, boolean enterOrMiddleResult) {
            Boolean last = Boolean.TRUE;
            if (enterOrMiddleResult && o.getConditions().size()>0) {
                // si enter ou middle on renvoye false, alors on a pas
                // de nouvelle condition a prendre dans la pile sinon
                // l'evaluation du dernier element du and
                last = evalStack.pop();
            }
            // la valeur courante du and
            Boolean currentValue = evalStack.pop();

            Boolean result = currentValue && last;
            evalStack.push(result);
        }

        @Override
        public boolean visitEnter(org.nuiton.wikitty.query.conditions.Or o) {
            // si le or n'a aucun element, alors il est vrai
            if (o.getConditions().isEmpty()) {
                evalStack.push(Boolean.TRUE);
            } else {
                // sinon, il faut qu'un de ses elements soit vrai pour etre vrai
                evalStack.push(Boolean.FALSE);
            }
            return true;
        }

        @Override
        public boolean visitMiddle(org.nuiton.wikitty.query.conditions.Or o) {
            // l'evaluation du dernier element du or
            Boolean last = evalStack.pop();
            // la valeur courante du or
            Boolean currentValue = evalStack.pop();

            Boolean result = currentValue || last;
            evalStack.push(result);

            // si le or est deja vrai, ca ne sert a rien d'evaluer la suite
            return !result;
        }

        @Override
        public void visitLeave(org.nuiton.wikitty.query.conditions.Or o, boolean enterOrMiddleResult) {
            Boolean last = Boolean.TRUE;
            if (enterOrMiddleResult && o.getConditions().size()>0) {
                // si enter ou middle on renvoye false, alors on a pas
                // de nouvelle condition a prendre dans la pile sinon
                // l'evaluation du dernier element du or
                last = evalStack.pop();
            }
            // la valeur courante du or
            Boolean currentValue = evalStack.pop();

            Boolean result = currentValue || last;
            evalStack.push(result);
        }

        @Override
        public boolean visitEnter(Select o) {
            // do nothing
            return true;
        }

        @Override
        public boolean visitMiddle(Select o) {
            // do nothing
            return true;
        }


        @Override
        public void visitLeave(Select o, boolean enterOrMiddleResult) {
            // do nothing
        }

        protected List evalConditionValueAsList(List o) {
            List result = new ArrayList(o.size());
            for (ConditionValue c : o) {
                result.addAll(evalConditionValueAsList(c));
            }
            return result;
        }

        protected List evalConditionValueAsList(ConditionValue o) {
            List result = new ArrayList();
            if (o instanceof Select) {
                WikittyQuery query = new WikittyQuery(o);
                // eval select
                WikittyQueryResult selectResult =
                        searchEngine.findAllByQuery(tx, query).convertMapToSimpleString();
                result.addAll(selectResult.getAll());
            } else if (o instanceof ConditionValueString) {
                result.add(((ConditionValueString)o).getValue());
            } else {
                throw new WikittyException(String.format(
                        "ConditionValue type unsupported %s",
                        ClassUtils.getShortCanonicalName(o, "null")));
            }
            return result;

        }
        protected String evalConditionValue(ConditionValue o) {
            String result;
            if (o instanceof Select) {
                WikittyQuery query = new WikittyQuery(o);
                // eval select
                WikittyQueryResult selectResult =
                        searchEngine.findAllByQuery(tx, query).convertMapToSimpleString();
                if (selectResult.size() == 0) {
                    throw new WikittyException(String.format(
                            "Select return no result query was '%s' transformed to '%s'",
                            o.toString(),
                            selectResult.getQueryString()));
                } else if (selectResult.size() > 1) {
                    if (log.isWarnEnabled()) {
                        log.warn(String.format(
                                "Select return more than one result, only first"
                                + " is used. Query was '%s' transformed to '%s'",
                                o.toString(),
                                selectResult.getQueryString()));
                    }
                }
                result = selectResult.peek();
            } else if (o instanceof ConditionValueString) {
                result = ((ConditionValueString)o).getValue();
            } else {
                throw new WikittyException(String.format(
                        "ConditionValue type unsupported %s",
                        ClassUtils.getShortCanonicalName(o, "null")));
            }
            return result;
        }

        @Override
        public boolean visitEnter(org.nuiton.wikitty.query.conditions.Not o) {
            // nothing to do
            return true;
        }

        @Override
        public void visitLeave(org.nuiton.wikitty.query.conditions.Not o, boolean enterOrMiddleResult) {
            Boolean val = evalStack.pop();
            Boolean result = !val;
            evalStack.push(result);
        }

        @Override
        public boolean visitEnter(org.nuiton.wikitty.query.conditions.Between o) {
            boolean result = false;
            Collection expected = new ArrayList(2);
            expected.add(evalConditionValue(o.getMin()));
            expected.add(evalConditionValue(o.getMax()));
            result = check(BetweenPredicate, o.getElement(), expected);

            evalStack.push(result);
            return false;
        }

        @Override
        public boolean visitMiddle(org.nuiton.wikitty.query.conditions.Between o) {
            // do nothing
            return false;
        }

        @Override
        public void visitLeave(org.nuiton.wikitty.query.conditions.Between o, boolean enterOrMiddleResult) {
            // do nothing
        }

        @Override
        public boolean visitEnter(ContainsAll o) {
            boolean result = false;

            result = check(ContainsAllPredicate, o.getElement(), 
                    evalConditionValueAsList(o.getValues()));

            evalStack.push(result);
            return false;
        }

        @Override
        public boolean visitMiddle(ContainsAll o) {
            // do nothing
            return false;
        }

        @Override
        public void visitLeave(ContainsAll o, boolean enterOrMiddleResult) {
            // do nothing
        }

        @Override
        public boolean visitEnter(ContainsOne o) {
            boolean result = false;

            result = check(ContainsOnePredicate, o.getElement(),
                    evalConditionValueAsList(o.getValues()));

            evalStack.push(result);
            return false;
        }

        @Override
        public boolean visitMiddle(ContainsOne o) {
            // do nothing
            return false;
        }

        @Override
        public void visitLeave(ContainsOne o, boolean enterOrMiddleResult) {
            // do nothing
        }

        @Override
        public boolean visitEnter(Equals o) {
            boolean result = false;

            if (o.isIgnoreCaseAndAccent()) {
                result = check(EqualsIgnoreCaseAndAccentPredicate, o.getElement(),
                        evalConditionValue(o.getValue()));
            } else {
                result = check(EqualsPredicate, o.getElement(),
                        evalConditionValue(o.getValue()));
            }

            evalStack.push(result);
            return false;
        }

        @Override
        public void visitLeave(Equals o, boolean enterOrMiddleResult) {
            // do nothing
        }

        @Override
        public boolean visitEnter(NotEquals o) {
            boolean result = false;

            if (o.isIgnoreCaseAndAccent()) {
                result = !check(EqualsIgnoreCaseAndAccentPredicate, o.getElement(),
                        evalConditionValue(o.getValue()));
            } else {
                result = !check(EqualsPredicate, o.getElement(),
                        evalConditionValue(o.getValue()));
            }

            evalStack.push(result);
            return false;
        }

        @Override
        public void visitLeave(NotEquals o, boolean enterOrMiddleResult) {
            // do nothing
        }

        @Override
        public void visit(org.nuiton.wikitty.query.conditions.False o) {
            evalStack.push(Boolean.FALSE);
        }

        @Override
        public void visit(org.nuiton.wikitty.query.conditions.True o) {
            evalStack.push(Boolean.TRUE);
        }

        @Override
        public boolean visitEnter(Greater o) {
            boolean result = false;

            result = check(GreaterPredicate, o.getElement(),
                    evalConditionValue(o.getValue()));

            evalStack.push(result);
            return false;
        }

        @Override
        public void visitLeave(Greater o, boolean enterOrMiddleResult) {
            // do nothing
        }

        @Override
        public boolean visitEnter(GreaterOrEquals o) {
            boolean result = false;

            result = check(GreaterOrEqualsPredicate, o.getElement(),
                    evalConditionValue(o.getValue()));

            evalStack.push(result);
            return false;
        }

        @Override
        public void visitLeave(GreaterOrEquals o, boolean enterOrMiddleResult) {
            // do nothing
        }

        @Override
        public boolean visitEnter(org.nuiton.wikitty.query.conditions.Keyword o) {
            boolean result = false;

            result = check(KeywordPredicate, o.getElement(),
                    "*" + evalConditionValue(o.getValue()) + "*");

            evalStack.push(result);
            return false;
        }

        @Override
        public void visitLeave(org.nuiton.wikitty.query.conditions.Keyword o, boolean enterOrMiddleResult) {
            // do nothing
        }

        @Override
        public boolean visitEnter(Less o) {
            boolean result = false;

            result = check(LessPredicate, o.getElement(),
                    evalConditionValue(o.getValue()));

            evalStack.push(result);
            return false;
        }

        @Override
        public void visitLeave(Less o, boolean enterOrMiddleResult) {
            // do nothing
        }

        @Override
        public boolean visitEnter(LessOrEquals o) {
            boolean result = false;

            result = check(LessOrEqualsPredicate, o.getElement(),
                    evalConditionValue(o.getValue()));

            evalStack.push(result);
            return false;
        }

        @Override
        public void visitLeave(LessOrEquals o, boolean enterOrMiddleResult) {
            boolean result = false;

            result = check(LessOrEqualsPredicate, o.getElement(),
                    evalConditionValue(o.getValue()));

            evalStack.push(result);
        }

        @Override
        public boolean visitEnter(Like o) {
            boolean result = false;

            result = check(LikePredicate, o.getElement(),
                    evalConditionValue(o.getValue()));

            evalStack.push(result);
            return false;
        }

         @Override
        public void visitLeave(Like o, boolean enterOrMiddleResult) {
            // do nothing
        }

        @Override
        public boolean visitEnter(Unlike o) {
            boolean result = false;

            result = !check(LikePredicate, o.getElement(),
                    evalConditionValue(o.getValue()));

            evalStack.push(result);
            return false;
        }

       @Override
        public void visitLeave(Unlike o, boolean enterOrMiddleResult) {
            // do nothing
        }

        @Override
        public void visit(org.nuiton.wikitty.query.conditions.Null o) {
            boolean result = false;

            result = check(NullPredicate, o.getElement(), null);

            evalStack.push(result);
        }

        @Override
        public void visit(NotNull o) {
            boolean result = false;

            result = check(NotNullPredicate, o.getElement(), null);

            evalStack.push(result);
        }

        @Override
        public boolean visitEnter(WikittyQueryFunction function) {
            // do nothing
            return true;
        }

        @Override
        public boolean visitMiddle(WikittyQueryFunction function) {
            // do nothing
            return true;
        }

        @Override
        public void visitLeave(WikittyQueryFunction function, boolean enterOrMiddleResult) {
            // do nothing
        }

        @Override
        public void visit(FunctionValue function) {
            // do nothing
        }

        @Override
        public void defaultVisit(Object o) {
            throw new UnsupportedOperationException("Not supported yet.");
        }

        @Override
        public boolean defaultVisitEnter(Object o) {
            throw new UnsupportedOperationException("Not supported yet.");
        }

        @Override
        public boolean defaultVisitMiddle(Object o) {
            throw new UnsupportedOperationException("Not supported yet.");
        }

        @Override
        public void defaultVisitLeave(Object o, boolean enterOrMiddleResult) {
            throw new UnsupportedOperationException("Not supported yet.");
        }

    }



    @Override
    public PagedResult findAllByCriteria(WikittyTransaction transaction, Criteria criteria) {
        // throw new UnsupportedOperationException("Not supported yet.");
        int firstIndex = criteria.getFirstIndex();
        int endIndex = criteria.getEndIndex();
        List ids = new LinkedList();
        int currentIndex = 0;
        for (Entry entry : wikittyStorage.getWikitties().entrySet()) {
            Wikitty w = entry.getValue();
            String id = entry.getKey();
            Restriction dto = criteria.getRestriction();
            if (!w.isDeleted() && checkRestriction(transaction, dto, w)) {
                currentIndex++;
                if (currentIndex > firstIndex) {
                    ids.add(id);
                }
                if (endIndex >= 0 && currentIndex >= endIndex) {
                    break;
                }
            }
        }
        return new PagedResult(criteria.getName(),
                firstIndex, ids.size(), criteria.getRestriction().toString(), null, ids);
    }

    @Override
    public TreeNodeResult findAllChildrenCount(WikittyTransaction transaction,
            String wikittyId, int depth, boolean count, Criteria filter) {
        // FIXME
        throw new UnsupportedOperationException("Not supported yet.");
    }

    private boolean checkRestriction(WikittyTransaction transaction,
                                    Restriction restriction, Wikitty w) {
        if (restriction instanceof BinaryOperator) {
            BinaryOperator binOp = (BinaryOperator) restriction;

            String fqfieldName = binOp.getElement().getName();

            //Checks on extensions
            if (Element.ELT_EXTENSION.equals(fqfieldName)) {
                boolean checked = false;

                switch (restriction.getName()) {
                    case NOT_EQUALS:
                        checked = !w.getExtensionNames().contains(binOp.getValue());
                        break;
                    case EQUALS:
                        checked = w.getExtensionNames().contains(binOp.getValue());
                        break;
                }

                return checked;

            //Checks on id
            } else if (Element.ELT_ID.equals(fqfieldName)) {

                boolean checked = false;

                switch (restriction.getName()) {
                    case NOT_EQUALS:
                        checked = !w.getWikittyId().equals(binOp.getValue());
                        break;
                    case EQUALS:
                        checked = w.getWikittyId().equals(binOp.getValue());
                        break;
                }

                return checked;
            }

            // si les wikitty n'ont meme pas l'extension concerné
            // Le check restriction, ne doit pas tester les champs
            // si les wikitty n'ont meme pas l'extension concerné
            String[] extName = fqfieldName.split("\\.");
            if (!w.hasField(extName[0], extName[1])) {

                //return true in case of not equals
                if (RestrictionName.NOT_EQUALS == restriction.getName()) {
                    return true;
                }

                return false;
            }
            // recupere la valeur dans le wikitty
            Object o = w.getFqField(fqfieldName);
            // recupere le type de la valeur
            FieldType t = w.getFieldType(fqfieldName);
            // convertie la valeur a verifier dans le meme type que la valeur
            // du wikitty
            Object value = binOp.getValue();
            if ( !(value instanceof Collection) && t.isCollection()) {
                // on doit encapsuler dans une collection, car la creation
                // de la requete ajoute autant de v == o && ... que de valeurs
                // dans la collection (champs multi-value solr). Mais
                // dans le inmemory on doit retrouve des collections et non pas
                // des objets seuls :(
                value = Collections.singleton(value);
            }
            value = t.getValidValue(value);
            boolean checked = false;

            switch (restriction.getName()) {
                case EQUALS:
                    if (value instanceof String && o instanceof String) {

                        String pattern = (String)value;
                        pattern = pattern.replace("*","\\p{ASCII}*");
                        pattern = pattern.replace("?","\\p{ASCII}");

                        Pattern p = Pattern.compile(pattern);
                        Matcher m = p.matcher((String)o);
                        checked = m.matches();
                    } else {
                        checked = value.equals(o);
                    }
                    break;
                case LESS:
                    checked = ((Comparable) o).compareTo(value) < 0;
                    break;
                case LESS_OR_EQUAL:
                    checked = ((Comparable) o).compareTo(value) <= 0;
                    break;
                case GREATER:
                    checked = ((Comparable) o).compareTo(value) > 0;
                    break;
                case GREATER_OR_EQUAL:
                    checked = ((Comparable) o).compareTo(value) >= 0;
                    break;
                case NOT_EQUALS:
                    checked = !value.equals(o);
                    break;
                case ENDS_WITH:
                    if (t.getType() != WikittyTypes.STRING) {
                        throw new WikittyException("Can't search for contents that 'ends with' on attribute type different of String. " + "Attribute " + fqfieldName + " is " + t.getType().name());
                    }
                    checked = ((String) o).endsWith((String) value);
                    break;
                case STARTS_WITH:
                    if (t.getType() != WikittyTypes.STRING) {
                        throw new WikittyException("Can't search for contents that 'starts with' on attribute type different of String. " + "Attribute " + fqfieldName + " is " + t.getType().name());
                    }
                    checked = ((String) o).startsWith((String) value);
                    break;
            }
            return checked;
        } else if (restriction instanceof Null) {
            Null nullRes = (Null) restriction;

            String fqfieldName = nullRes.getFieldName();

            //check my wikitty got the right extension before doing anything.
            String[] extName = fqfieldName.split("\\.");
            if (!w.hasField(extName[0], extName[1])) {
                return false;
            }
            // get the value in the wikitty
            Object o = w.getFqField(fqfieldName);

            //No null on extensions, always return false
            if (fqfieldName.equals(Element.ELT_EXTENSION)) {
                return false;
            }

            //No null on ids, always return false
            if (fqfieldName.equals(Element.ELT_ID)) {
                return false;
            }

            boolean checked = false;

            switch (nullRes.getName()) {
                case IS_NULL:
                    checked = (o == null);
                    break;
                case IS_NOT_NULL:
                    checked = (o != null);
                    break;
            }

            return checked;

        } else if (restriction instanceof In) {
            In in = (In) restriction;
            String fqfieldName = in.getElement().getName();
            String testedValue = String.valueOf(w.getFqField(fqfieldName));
            for (String value : in.getValue()){
                if (testedValue.equals(value)) {
                    return true;
                }
            }

            return false;

        } else if (restriction instanceof True) {
            return true;
        } else if (restriction instanceof False) {
            return false;
        }  else if (restriction instanceof Contains) {
            Contains contains = (Contains) restriction;

            String fqfieldName = contains.getElement().getName();
            List values = contains.getValue();

            String extension = WikittyUtil.getExtensionNameFromFQFieldName(fqfieldName);
            String fieldName = WikittyUtil.getFieldNameFromFQFieldName(fqfieldName);

            if (!w.hasField(extension, fieldName)) {
                return false;
            }

            // Get field as string and then split it to take into account not
            // multivalued fields.
            String testedValuesAsString = w.getFieldAsString(extension, fieldName);

            if('[' == testedValuesAsString.charAt(0)){
                testedValuesAsString = testedValuesAsString.substring(1, testedValuesAsString.length());
            }

            List testedValues = Arrays.asList(testedValuesAsString.split(","));


            for (Object value : values){
                if (!testedValues.contains(String.valueOf(value))) {
                    return false;
                }
            }

            return true;

        } else if (restriction instanceof And) {
            And and = (And) restriction;
            for (Restriction sub : and.getRestrictions()) {
                if (!checkRestriction(transaction, sub, w)) {
                    return false;
                }
            }
            return true;
        } else if (restriction instanceof Or) {
            Or or = (Or) restriction;
            for (Restriction sub : or.getRestrictions()) {
                if (checkRestriction(transaction, sub, w)) {
                    return true;
                }
            }
            return false;
        }  else if (restriction instanceof Keyword) {
            Keyword keyword = (Keyword) restriction;

            String value = keyword.getValue();

            for(String fieldName : w.getAllFieldNames()) {
                String testedValue = String.valueOf(w.getFqField(fieldName));
                if (testedValue.contains(value)) {
                    return true;
                }
            }
            return false;
        } else if (restriction instanceof Not) {
            Not or = (Not) restriction;
            Restriction sub = or.getRestriction();
            return !checkRestriction(transaction, sub, w);
        } else if (restriction instanceof AssociatedRestriction) {

            AssociatedRestriction ass = (AssociatedRestriction) restriction;

            String fqfieldName = ass.getElement().getName();

            //check my wikitty got the right extension before doing anything.
            String[] extName = fqfieldName.split("\\.");
            if (!w.hasField(extName[0], extName[1])) {
                return false;
            }
            // get the value in the wikitty, it is a wikitty's id
            Object o = w.getFqField(fqfieldName);

            //Get sub-restriction
            Restriction sub = ass.getRestriction();

            Criteria associatedSearch = new Criteria();
            associatedSearch.setRestriction(sub);

            //find everything that validate the sub-restriction
            PagedResult associatedResult = findAllByCriteria(transaction, associatedSearch);

            List associatedList = associatedResult.getAll();

            //Check that my field is contained in the sub-restriction results.
            return associatedList.contains(String.valueOf(o));
        } else if (restriction instanceof Between) {

            Between op = (Between) restriction;

            Object max = op.getMax();
            Object min = op.getMin();

            //No between on extensions, always return false
            if (op.getElement().getName().equals(Element.ELT_EXTENSION)) {
                return false;
            }

            //No between on ids, always return false
            if (op.getElement().getName().equals(Element.ELT_ID)) {
                return false;
            }

            String fqfieldName = op.getElement().getName();

            // si les wikitty n'ont meme pas l'extension concerné
            // Le check restriction, ne doit pas tester les champs
            // si les wikitty n'ont meme pas l'extension concerné
            String[] extName = fqfieldName.split("\\.");
            if (!w.hasField(extName[0], extName[1])) {
                return false;
            }

            // recupere la valeur dans le wikitty
            Object o = w.getFqField(fqfieldName);

            // recupere le type de la valeur
            FieldType t = w.getFieldType(fqfieldName);

            if (!(min instanceof Collection) && t.isCollection()) {
                // on doit encapsuler dans une collection, car la creation
                // de la requete ajoute autant de v == o && ... que de valeurs
                // dans la collection (champs multi-value solr). Mais
                // dans le inmemory on doit retrouve des collections et non pas
                // des objets seuls :(
                min = Collections.singleton(min);
            }
            min = t.getValidValue(min);

            if (!(max instanceof Collection) && t.isCollection()) {
                // on doit encapsuler dans une collection, car la creation
                // de la requete ajoute autant de v == o && ... que de valeurs
                // dans la collection (champs multi-value solr). Mais
                // dans le inmemory on doit retrouve des collections et non pas
                // des objets seuls :(
                max = Collections.singleton(max);
            }
            max = t.getValidValue(max);

            return ((Comparable) o).compareTo(min) >= 0
                    && ((Comparable) o).compareTo(max) <= 0;
        } else{
            throw new UnsupportedOperationException(restriction.getName() + " Search Not yet implemented");
        }
    }



}