Author: bpoussin Date: 2013-02-16 16:44:22 +0100 (Sat, 16 Feb 2013) New Revision: 1528 Url: http://nuiton.org/projects/wikitty/repository/revisions/1528 Log: fixes #2531: Add DISTINT clause for SELECT query Modified: trunk/src/site/rst/user/query.rst trunk/wikitty-api/src/main/java/org/nuiton/wikitty/query/WikittyQueryMakerAbstract.java trunk/wikitty-api/src/main/java/org/nuiton/wikitty/query/WikittyQueryParser.java trunk/wikitty-api/src/main/java/org/nuiton/wikitty/query/conditions/Select.java trunk/wikitty-api/src/main/java/org/nuiton/wikitty/storage/WikittySearchEngineHelper.java Modified: trunk/src/site/rst/user/query.rst =================================================================== --- trunk/src/site/rst/user/query.rst 2013-02-15 14:38:58 UTC (rev 1527) +++ trunk/src/site/rst/user/query.rst 2013-02-16 15:44:22 UTC (rev 1528) @@ -446,7 +446,7 @@ Dans l'exemple suivant au lieu de retourner l'id des employés en CDI, on retourne leur nom. Si deux personnes avait le même -nom, ce nom ne serai retourné qu'une seule fois.:: +nom, ce nom serait retourné deux fois sauf si la clause DISTINCT a été utilisé.:: WikittyQuery q = new WikittyQueryMaker() select("Employee.name").eq("Employee.contract", "CDI").end(); @@ -467,8 +467,35 @@ Il est possible d'ajouter des fonctions d'aggrégats dans le Select. Dans ce cas le résultat retourné ne contient qu'un seul élément. L'aggrégat est fait sur les -élements retournés (de first à first+Limit) +élements retournés (de first à first+Limit). Il est aussi possible d'ajouter +la clause DISTINCT pour que les éléments retournés ou les aggrégats se fassent +seulement sur les éléments distincts. +DISTINCT +~~~~~~~~ + +Retourne une liste sans doublon:: + + WikittyQuery q = new WikittyQueryMaker() + .select("Person.age", true).eq("Person.firstname", "Jean").end(); + + WikittyQuery q = WikittyQueryParser.parse( + "SELECT DISTINCT Person.age WHERE Person.firstname=Jean"); + +Dans ces exemples si plusieurs personnes portant le prénom 'Jean' ont le même +âge, cet âge n'est retourné qu'une seule fois. Par exemple si la liste dans le +DISTINCT retourne: 12, 18, 18, 33. Avec le DISTINCT le résultat sera: 12, 18, 33 + +Il est possible de couplé DISTINCT est une fonction d'aggrégat:: + + WikittyQuery q = new WikittyQueryMaker() + .select("Person.age", Aggregate.AVG, true).eq("Person.firstname", "Jean").end(); + + WikittyQuery q = WikittyQueryParser.parse( + "SELECT DISTINCT AVG Person.age WHERE Person.firstname=Jean"); + +Dans cette exemple la moyenne est fait sur: 12, 18, 33 soit 21 + AVG ~~~ Modified: trunk/wikitty-api/src/main/java/org/nuiton/wikitty/query/WikittyQueryMakerAbstract.java =================================================================== --- trunk/wikitty-api/src/main/java/org/nuiton/wikitty/query/WikittyQueryMakerAbstract.java 2013-02-15 14:38:58 UTC (rev 1527) +++ trunk/wikitty-api/src/main/java/org/nuiton/wikitty/query/WikittyQueryMakerAbstract.java 2013-02-16 15:44:22 UTC (rev 1528) @@ -1173,6 +1173,42 @@ } /** + * Add {@link Select}, this condition must be first or + * @param element le champs dont il faut extraire les donnees + * @return {@code this} + * @see {@link Select} + */ + public M select(String element, boolean distinct) { + return select(Element.get(element), distinct); + } + + /** + * Add {@link Select}, this condition must be first or + * @param element le champs dont il faut extraire les donnees + * @return {@code this} + * @see {@link Select} + */ + public M select(String element, Aggregate aggregate, boolean distinct) { + return select(Element.get(element), aggregate, distinct); + } + + /* + * @see {@link Select} + */ + public M select(Element element, boolean distinct) { + return select(element, null, distinct); + } + + /* + * @see {@link Select} + */ + public M select(Element element, Aggregate aggregate, boolean distinct) { + Condition child = new Select(element, aggregate, distinct); + addCondition(child); + return this.asM(); + } + + /** * Close last non terminal condition (or, and, not, in). * <li>ex: WikittyQueryMaker().not().rTrue().close().and().rTrue().rFalse().close().or().rTrue().rFalse().close(); * @return Modified: trunk/wikitty-api/src/main/java/org/nuiton/wikitty/query/WikittyQueryParser.java =================================================================== --- trunk/wikitty-api/src/main/java/org/nuiton/wikitty/query/WikittyQueryParser.java 2013-02-15 14:38:58 UTC (rev 1527) +++ trunk/wikitty-api/src/main/java/org/nuiton/wikitty/query/WikittyQueryParser.java 2013-02-16 15:44:22 UTC (rev 1528) @@ -94,6 +94,7 @@ public static final String MAX = "MAX"; public static final String MIN = "MIN"; public static final String SUM = "SUM"; + public static final String DISTINCT = "DISTINCT"; public static final String SELECT = "SELECT"; public static final String WHERE = "WHERE"; public static final String IN = "IN"; @@ -144,6 +145,7 @@ public Rule icMAX = IgnoreCase(MAX); public Rule icMIN = IgnoreCase(MIN); public Rule icSUM = IgnoreCase(SUM); + public Rule icDISTINCT = IgnoreCase(DISTINCT); public Rule icSELECT = IgnoreCase(SELECT); public Rule icWHERE = IgnoreCase(WHERE); public Rule icIN = IgnoreCase(IN); @@ -480,10 +482,19 @@ } Rule select() { Var<Aggregate> aggregate = new Var<Aggregate>(); - return Sequence(icSELECT, space(), Optional(aggregate(aggregate)), space(), field(), push(match()), space(), + Var<Boolean> distinct = new Var<Boolean>(Boolean.FALSE); + return Sequence(icSELECT, space(), + Optional(distinct(distinct)), space(), + Optional(aggregate(aggregate)), space(), + field(), push(match()), space(), + FirstOf(Sequence(icWHERE, space(), term()), empty()), push(new Select(toElement(pop(1).toString()), aggregate.get(), (Condition)pop()))); } + Rule distinct(Var<Boolean> distinct) { + return Sequence(icDISTINCT, distinct.set(Boolean.TRUE)); + } + Rule aggregate(Var<Aggregate> aggregate) { return FirstOf(avg(aggregate), count(aggregate), max(aggregate), min(aggregate), sum(aggregate)); } Modified: trunk/wikitty-api/src/main/java/org/nuiton/wikitty/query/conditions/Select.java =================================================================== --- trunk/wikitty-api/src/main/java/org/nuiton/wikitty/query/conditions/Select.java 2013-02-15 14:38:58 UTC (rev 1527) +++ trunk/wikitty-api/src/main/java/org/nuiton/wikitty/query/conditions/Select.java 2013-02-16 15:44:22 UTC (rev 1528) @@ -50,27 +50,43 @@ private static final long serialVersionUID = 1L; + protected boolean distinct = false; protected Aggregate aggregate; protected Element element; public Select(Element element) { - this.element = element; + this(element, null, false, null); } + public Select(Element element, boolean distinct) { + this(element, null, distinct, null); + } + public Select(Element element, Aggregate aggregate) { - this.element = element; - this.aggregate = aggregate; + this(element, aggregate, false, null); } + public Select(Element element, Aggregate aggregate, boolean distinct) { + this(element, aggregate, distinct, null); + } + public Select(Element element, Condition restriction) { - super(restriction); - this.element = element; + this(element, null, false, restriction); } + public Select(Element element, boolean distinct, Condition restriction) { + this(element, null, distinct, restriction); + } + public Select(Element element, Aggregate aggregate, Condition restriction) { + this(element, aggregate, false, restriction); + } + + public Select(Element element, Aggregate aggregate, boolean distinct, Condition restriction) { super(restriction); this.element = element; this.aggregate = aggregate; + this.distinct = distinct; } public Aggregate getAggregate() { @@ -81,6 +97,10 @@ return element; } + public boolean isDistinct() { + return distinct; + } + @Override boolean equalsDeep(Object other) { boolean result = super.equalsDeep(other); Modified: trunk/wikitty-api/src/main/java/org/nuiton/wikitty/storage/WikittySearchEngineHelper.java =================================================================== --- trunk/wikitty-api/src/main/java/org/nuiton/wikitty/storage/WikittySearchEngineHelper.java 2013-02-15 14:38:58 UTC (rev 1527) +++ trunk/wikitty-api/src/main/java/org/nuiton/wikitty/storage/WikittySearchEngineHelper.java 2013-02-16 15:44:22 UTC (rev 1528) @@ -78,6 +78,9 @@ // n'a pas les meme limites que les autres facette Select select = (Select)query.getCondition(); + boolean isAggregate = select.getAggregate() != null; + boolean isDistinct = select.isDistinct(); + Condition newCond; if(WikittyExtension.isFqField(select.getElement().getValue())) { String extName = WikittyExtension.extractExtensionName( @@ -120,11 +123,14 @@ List<FacetTopic> topics = resultSelect.getFacets().get(select.getElement().getValue()); List<String> selectList = new ArrayList<String>(topics.size()); - if (query.getOffset() < topics.size()) { - // il faut que le premier demande soit inferieur a la taille, - // sinon on ne fait rien - for (FacetTopic topic : topics) { + for (FacetTopic topic : topics) { + if (isDistinct) { selectList.add(topic.getTopicName()); + } else { + // on ajoute la valeur le nombre de fois qu'il y a de count dans la facette + for (int i=0, max=topic.getCount(); i<max; i++) { + selectList.add(topic.getTopicName()); + } } } @@ -137,22 +143,21 @@ } // on ne garde que ce qui est demande - if (query.getOffset() < topics.size()) { - int offset = query.getOffset(); - int limit = query.getLimit(); + int offset = query.getOffset(); + int limit = query.getLimit(); + if (limit == Integer.MAX_VALUE) { + // WARNING It is necessary to substract 'offset' otherwise, + // there is a capacity overlow + limit = Integer.MAX_VALUE - offset; + } + int first = Math.min(selectList.size(), offset); + int last = Math.min(selectList.size(), offset + limit); + selectList = selectList.subList(first, last); - if (limit == Integer.MAX_VALUE) { - // WARNING It is necessary to substract 'offset' otherwise, - // there is a capacity overlow - limit = Integer.MAX_VALUE - offset; - } - int last = Math.min(topics.size(), offset + limit); - selectList = selectList.subList(offset, last); - } // gestion des agregats - if (select.getAggregate() != null) { + if (isAggregate) { switch(select.getAggregate()) { case AVG: { // convert all to number