diff --git a/distribution/pom.xml b/distribution/pom.xml index cbfc489b4fd4..e24b456d95e3 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -358,6 +358,8 @@ org.apache.druid.extensions.contrib:druid-moving-average-query -c org.apache.druid.extensions.contrib:druid-tdigestsketch + -c + org.apache.druid.extensions.contrib:accurate-cardinality @@ -367,4 +369,4 @@ - \ No newline at end of file + diff --git a/docs/content/development/extensions-contrib/accurate-cardinality.md b/docs/content/development/extensions-contrib/accurate-cardinality.md new file mode 100644 index 000000000000..2446401731f9 --- /dev/null +++ b/docs/content/development/extensions-contrib/accurate-cardinality.md @@ -0,0 +1,87 @@ +--- +layout: doc_page +title: "AccurateCardinality Aggregator" +--- + + + +# AccurateCardinality Aggregator + +In many case, we do need exactly count distinct. +Now, Druid offers the ability by nested group by query. +Its logic can be described as follows: + +For a sql like: +```sql +select count(distinct pid) from DATASOURCE where col="val" +``` +the exactly query will be like: +```sql +select count(*) from ( + select pid from DATASOURCE_segments_in_historical1 where col="val" group by pid + UNION ALL + select pid from DATASOURCE_segments_in_historical2 where col="val" group by pid + UNION ALL + select pid from DATASOURCE_segments_in_historical3 where col="val" group by pid + ... +) group by pid +``` + +For high cardinality case, the size of result transfered from historical node to broker node can be really large and leads to poor performance. +So this extension try to use bitmap as the container for the result data from the historical. +The performance can be 10 times better the nested group by method. + +To use this extension, make sure to [include](../../operations/including-extensions.html) the `accurate-cardinality` extension. + +DSL query example: + +```json +{ + "queryType": "timeseries", + "dataSource": "sample_datasource", + "granularity": "day", + "aggregations": [ + { + "type": "accurateCardinality", + "name": "uv", + "fieldName": "clientid" + } + ], + "intervals": [ + "2018-12-01T00:00:00.000/2018-12-31T00:00:00.000" + ] +} +``` + +SQL query example: + +```json +{ + "query": "select ACCURATE_CARDINALITY(clientid) as uv from sample_datasource where __time >= '2018-12-01 00:00:00' and __time < '2018-12-31 00:00:00'", + "context": { + "sqlTimeZone":"Asia/Shanghai" + } +} +``` + +There are some limitations: + +1. `accurateCardinality` aggregator can only support dimension which is long type. +2. `ACCURATE_CARDINALITY` can only have one operand \ No newline at end of file diff --git a/extensions-contrib/accurate-cardinality/pom.xml b/extensions-contrib/accurate-cardinality/pom.xml new file mode 100644 index 000000000000..1fd2a450aac3 --- /dev/null +++ b/extensions-contrib/accurate-cardinality/pom.xml @@ -0,0 +1,162 @@ + + + + + + druid + org.apache.druid + 0.16.0-incubating-SNAPSHOT + ../../pom.xml + + 4.0.0 + + org.apache.druid.extensions.contrib + accurate-cardinality + accurate-cardinality + accurate-cardinality + + http://www.example.com + + + + + org.apache.druid + druid-core + ${project.parent.version} + provided + + + org.apache.druid + druid-processing + ${project.parent.version} + provided + + + org.apache.druid + druid-sql + ${project.parent.version} + provided + + + + com.google.code.findbugs + jsr305 + provided + + + org.apache.calcite + calcite-core + provided + + + com.fasterxml.jackson.core + jackson-annotations + provided + + + com.google.inject + guice + provided + + + com.fasterxml.jackson.core + jackson-databind + provided + + + it.unimi.dsi + fastutil + provided + + + com.fasterxml.jackson.core + jackson-core + provided + + + org.roaringbitmap + RoaringBitmap + provided + + + com.google.guava + guava + provided + + + org.apache.druid + druid-server + ${project.parent.version} + provided + + + + + org.apache.druid + druid-processing + ${project.parent.version} + test + test-jar + + + org.apache.druid + druid-core + ${project.parent.version} + test + test-jar + + + junit + junit + test + + + org.easymock + easymock + test + + + org.codehaus.jackson + jackson-mapper-asl + test + + + com.google.caliper + caliper + test + + + org.apache.druid + druid-sql + ${project.parent.version} + test-jar + test + + + org.apache.druid + druid-server + ${project.parent.version} + test + test-jar + + + + diff --git a/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/AccurateCardinalityAggregatorFactory.java b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/AccurateCardinalityAggregatorFactory.java new file mode 100644 index 000000000000..b42ff0bd7aac --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/AccurateCardinalityAggregatorFactory.java @@ -0,0 +1,293 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.query.aggregation.cardinality.accurate; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.Function; +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import org.apache.druid.java.util.common.IAE; +import org.apache.druid.java.util.common.StringUtils; +import org.apache.druid.query.aggregation.Aggregator; +import org.apache.druid.query.aggregation.AggregatorFactory; +import org.apache.druid.query.aggregation.AggregatorUtil; +import org.apache.druid.query.aggregation.BufferAggregator; +import org.apache.druid.query.aggregation.cardinality.accurate.collector.LongBitmapCollector; +import org.apache.druid.query.aggregation.cardinality.accurate.collector.LongBitmapCollectorFactory; +import org.apache.druid.query.aggregation.cardinality.accurate.collector.LongRoaringBitmapCollectorFactory; +import org.apache.druid.query.dimension.DefaultDimensionSpec; +import org.apache.druid.query.dimension.DimensionSpec; +import org.apache.druid.segment.ColumnSelectorFactory; +import org.apache.druid.segment.ColumnValueSelector; +import org.apache.druid.segment.NilColumnValueSelector; +import org.apache.druid.segment.column.ColumnCapabilities; +import org.apache.druid.segment.column.ValueType; + +import java.nio.ByteBuffer; +import java.util.Comparator; +import java.util.List; + + +public class AccurateCardinalityAggregatorFactory extends AggregatorFactory +{ + private static final LongBitmapCollectorFactory DEFAULT_BITMAP_FACTORY = new LongRoaringBitmapCollectorFactory(); + + private final String name; + private final DimensionSpec field; + private final LongBitmapCollectorFactory longBitmapCollectorFactory; + + @JsonCreator + public AccurateCardinalityAggregatorFactory( + @JsonProperty("name") String name, + @JsonProperty("field") final DimensionSpec field, + @JsonProperty("longBitmapCollectorFactory") LongBitmapCollectorFactory longBitmapCollectorFactory + ) + { + this.name = name; + this.field = field; + this.longBitmapCollectorFactory = longBitmapCollectorFactory == null + ? DEFAULT_BITMAP_FACTORY + : longBitmapCollectorFactory; + } + + public AccurateCardinalityAggregatorFactory( + String name, + DimensionSpec field + ) + { + this(name, field, DEFAULT_BITMAP_FACTORY); + } + + public AccurateCardinalityAggregatorFactory( + String name, + String field + ) + { + this(name, field, DEFAULT_BITMAP_FACTORY); + } + + + public AccurateCardinalityAggregatorFactory( + String name, + String field, + LongBitmapCollectorFactory longBitmapCollectorFactory + ) + { + this(name, new DefaultDimensionSpec(field, field, ValueType.LONG), longBitmapCollectorFactory); + } + + @JsonProperty + public DimensionSpec getField() + { + return field; + } + + @JsonProperty + public LongBitmapCollectorFactory getLongBitmapCollectorFactory() + { + return longBitmapCollectorFactory; + } + + @Override + public Aggregator factorize(ColumnSelectorFactory columnFactory) + { + return factorizeInternal(columnFactory, true); + } + + private BaseAccurateCardinalityAggregator factorizeInternal(ColumnSelectorFactory columnFactory, boolean onHeap) + { + if (field == null || field.getDimension() == null) { + return new NoopAccurateCardinalityAggregator(longBitmapCollectorFactory, onHeap); + } + ColumnCapabilities capabilities = columnFactory.getColumnCapabilities(field.getDimension()); + + if (capabilities != null) { + ValueType type = capabilities.getType(); + switch (type) { + case LONG: + return new LongAccurateCardinalityAggregator( + columnFactory.makeColumnValueSelector(field.getDimension()), + longBitmapCollectorFactory, + onHeap + ); + default: + throw new IAE( + "Cannot create accurate cardinality %s for invalid column type [%s]", + onHeap ? "aggregator" : "buffer aggregator", + type + ); + } + } else { + ColumnValueSelector columnValueSelector = columnFactory.makeColumnValueSelector(field.getDimension()); + if (columnValueSelector instanceof NilColumnValueSelector) { + return new NoopAccurateCardinalityAggregator(longBitmapCollectorFactory, onHeap); + } + return new ObjectAccurateCardinalityAggregator( + columnFactory.makeColumnValueSelector(field.getDimension()), + longBitmapCollectorFactory, + onHeap + ); + } + } + + @Override + public BufferAggregator factorizeBuffered(ColumnSelectorFactory columnFactory) + { + return factorizeInternal(columnFactory, false); + } + + @Override + public Comparator getComparator() + { + return new Comparator() + { + @Override + public int compare(LongBitmapCollector c1, LongBitmapCollector c2) + { + return c1.compareTo(c2); + } + }; + } + + @Override + public Object combine(Object lhs, Object rhs) + { + if (rhs == null) { + return lhs; + } + if (lhs == null) { + return rhs; + } + LongBitmapCollector lhsLongBitmapCollector = (LongBitmapCollector) lhs; + return lhsLongBitmapCollector.fold((LongBitmapCollector) rhs); + } + + @Override + public AggregatorFactory getCombiningFactory() + { + return new BitmapAggregatorFactory(name, name); + } + + @Override + public List getRequiredColumns() + { + return Lists.transform( + ImmutableList.of(field), + new Function() + { + @Override + public AggregatorFactory apply(DimensionSpec input) + { + return new AccurateCardinalityAggregatorFactory(input.getOutputName(), input, longBitmapCollectorFactory); + } + } + ); + } + + @Override + public Object deserialize(Object object) + { + final ByteBuffer buffer; + + if (object instanceof byte[]) { + buffer = ByteBuffer.wrap((byte[]) object); + } else if (object instanceof ByteBuffer) { + // Be conservative, don't assume we own this buffer. + buffer = ((ByteBuffer) object).duplicate(); + } else if (object instanceof String) { + buffer = ByteBuffer.wrap(StringUtils.decodeBase64(StringUtils.toUtf8((String) object))); + } else { + return object; + } + return longBitmapCollectorFactory.makeCollector(buffer); + } + + @Override + public Object finalizeComputation(Object object) + { + if (object == null) { + return 0; + } + return ((LongBitmapCollector) object).getCardinality(); + } + + @JsonProperty + @Override + public String getName() + { + return name; + } + + @Override + public List requiredFields() + { + return ImmutableList.of(field.getOutputName()); + } + + @Override + public String getTypeName() + { + return AccurateCardinalityModule.BITMAP_COLLECTOR; + } + + @Override + public int getMaxIntermediateSize() + { + /* LongAccurateCardinalityAggregator and BitmapAggregator actrually use onheap LongBitmapCollector to collect long-type dimension value. + It just use the buffer an offset in buffer to locate according LongBitmapCollector, but not store data in it. + So here just return 1. + */ + return 1; + } + + @Override + public byte[] getCacheKey() + { + byte[] dimSpecKey = field.getCacheKey(); + ByteBuffer retBuf = ByteBuffer.allocate(2 + dimSpecKey.length); + retBuf.put(AggregatorUtil.ACCURATE_CARDINALITY_CACHE_TYPE_ID); + retBuf.put(dimSpecKey); + + return retBuf.array(); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AccurateCardinalityAggregatorFactory factory = (AccurateCardinalityAggregatorFactory) o; + return Objects.equal(name, factory.name) && + Objects.equal(field, factory.field) && + Objects.equal(longBitmapCollectorFactory, factory.longBitmapCollectorFactory); + } + + @Override + public int hashCode() + { + return Objects.hashCode(name, field, longBitmapCollectorFactory); + } +} diff --git a/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/AccurateCardinalityModule.java b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/AccurateCardinalityModule.java new file mode 100644 index 000000000000..61155425e554 --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/AccurateCardinalityModule.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.query.aggregation.cardinality.accurate; + +import com.fasterxml.jackson.databind.Module; +import com.fasterxml.jackson.databind.jsontype.NamedType; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.google.common.collect.ImmutableList; +import com.google.inject.Binder; +import org.apache.druid.initialization.DruidModule; +import org.apache.druid.query.aggregation.cardinality.accurate.collector.LongBitmapCollectorFactory; +import org.apache.druid.query.aggregation.cardinality.accurate.collector.LongRoaringBitmapCollector; +import org.apache.druid.query.aggregation.cardinality.accurate.collector.LongRoaringBitmapCollectorComplexMetricSerde; +import org.apache.druid.query.aggregation.cardinality.accurate.collector.LongRoaringBitmapCollectorFactory; +import org.apache.druid.query.aggregation.cardinality.accurate.collector.LongRoaringBitmapCollectorJsonSerializer; +import org.apache.druid.query.aggregation.cardinality.accurate.sql.AccurateCardinalitySqlAggregator; +import org.apache.druid.segment.serde.ComplexMetrics; +import org.apache.druid.sql.guice.SqlBindings; + +import java.util.List; + +public class AccurateCardinalityModule implements DruidModule +{ + public static final String ACCURATE_CARDINALITY = "accurateCardinality"; + public static final String BITMAP_COLLECTOR = "bitmapCollector"; + private static final String BITMPA_AGG = "bitmapAgg"; + + private static final LongBitmapCollectorFactory DEFAULT_BITMAP_FACTORY = new LongRoaringBitmapCollectorFactory(); + + @Override + public List getJacksonModules() + { + return ImmutableList.of( + new SimpleModule("AccurateCardinalityModule") + .registerSubtypes( + new NamedType(AccurateCardinalityAggregatorFactory.class, ACCURATE_CARDINALITY), + new NamedType(BitmapAggregatorFactory.class, BITMPA_AGG) + ) + .addSerializer( + LongRoaringBitmapCollector.class, + new LongRoaringBitmapCollectorJsonSerializer() + ) + ); + } + + @Override + public void configure(Binder binder) + { + if (ComplexMetrics.getSerdeForType(BITMAP_COLLECTOR) == null) { + ComplexMetrics.registerSerde( + BITMAP_COLLECTOR, + new LongRoaringBitmapCollectorComplexMetricSerde(DEFAULT_BITMAP_FACTORY) + ); + } + + if (binder != null) { + SqlBindings.addAggregator(binder, AccurateCardinalitySqlAggregator.class); + } + } +} diff --git a/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/BaseAccurateCardinalityAggregator.java b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/BaseAccurateCardinalityAggregator.java new file mode 100644 index 000000000000..f24521201757 --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/BaseAccurateCardinalityAggregator.java @@ -0,0 +1,184 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.query.aggregation.cardinality.accurate; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import org.apache.druid.query.aggregation.Aggregator; +import org.apache.druid.query.aggregation.BufferAggregator; +import org.apache.druid.query.aggregation.cardinality.accurate.collector.LongBitmapCollector; +import org.apache.druid.query.aggregation.cardinality.accurate.collector.LongBitmapCollectorFactory; + +import javax.annotation.Nullable; +import java.nio.ByteBuffer; +import java.util.IdentityHashMap; + +public abstract class BaseAccurateCardinalityAggregator + implements BufferAggregator, Aggregator +{ + protected final TSelector selector; + protected final IdentityHashMap> collectors = new IdentityHashMap<>(); + + protected final LongBitmapCollectorFactory longBitmapCollectorFactory; + private final ByteBuffer defaultByteBuffer; + + public BaseAccurateCardinalityAggregator( + TSelector selector, + LongBitmapCollectorFactory longBitmapCollectorFactory, + boolean onHeap + ) + { + this.selector = selector; + this.longBitmapCollectorFactory = longBitmapCollectorFactory; + if (onHeap) { + defaultByteBuffer = ByteBuffer.allocate(1); + } else { + defaultByteBuffer = null; + } + } + + abstract void collectorAdd(LongBitmapCollector longBitmapCollector); + + @Override + public void aggregate() + { + aggregate(defaultByteBuffer, 0); + } + + @Override + public void init(ByteBuffer buf, int position) + { + createNewCollector(buf, position); + } + + @Override + public void aggregate(ByteBuffer buf, int position) + { + final int oldPosition = buf.position(); + try { + buf.position(position); + LongBitmapCollector longBitmapCollector = getOrCreateCollector(buf, position); + collectorAdd(longBitmapCollector); + } + finally { + buf.position(oldPosition); + } + } + + @Nullable + @Override + public Object get() + { + return getOrCreateCollector(defaultByteBuffer, 0); + } + + @Override + public long getLong() + { + throw new UnsupportedOperationException("AccurateCardinalityAggregator does not support getLong()"); + } + + @Override + public float getFloat() + { + throw new UnsupportedOperationException("BloomFilterAggregator does not support getFloat()"); + } + + @Override + public double getDouble() + { + throw new UnsupportedOperationException("AccurateCardinalityAggregator does not support getDouble()"); + } + + @Override + public Object get(ByteBuffer buf, int position) + { + return getOrCreateCollector(buf, position); + } + + @Override + public long getLong(ByteBuffer buf, int position) + { + throw new UnsupportedOperationException("AccurateCardinalityAggregator does not support getLong()"); + } + + @Override + public double getDouble(ByteBuffer buf, int position) + { + throw new UnsupportedOperationException("AccurateCardinalityAggregator does not support getDouble()"); + } + + @Override + public float getFloat(ByteBuffer buf, int position) + { + throw new UnsupportedOperationException("AccurateCardinalityAggregator does not support getFloat()"); + } + + @Override + public void close() + { + + } + + @Override + public void relocate(int oldPosition, int newPosition, ByteBuffer oldBuffer, ByteBuffer newBuffer) + { + createNewCollector(newBuffer, newPosition); + LongBitmapCollector collector = collectors.get(oldBuffer).get(oldPosition); + putCollectors(newBuffer, newPosition, collector); + Int2ObjectMap collectorMap = collectors.get(oldBuffer); + if (collectorMap != null) { + collectorMap.remove(oldPosition); + if (collectorMap.isEmpty()) { + collectors.remove(oldBuffer); + } + } + } + + private void putCollectors(final ByteBuffer buffer, final int position, final LongBitmapCollector collector) + { + Int2ObjectMap map = collectors.computeIfAbsent(buffer, buf -> new Int2ObjectOpenHashMap<>()); + map.put(position, collector); + } + + private LongBitmapCollector getOrCreateCollector(ByteBuffer buf, int position) + { + Int2ObjectMap collectMap = collectors.get(buf); + LongBitmapCollector longBitmapCollector = collectMap != null ? collectMap.get(position) : null; + if (longBitmapCollector != null) { + return longBitmapCollector; + } + + return createNewCollector(buf, position); + } + + private LongBitmapCollector createNewCollector(ByteBuffer buf, int position) + { + buf.position(position); + LongBitmapCollector longBitmapCollector = longBitmapCollectorFactory.makeEmptyCollector(); + Int2ObjectMap collectorMap = collectors.get(buf); + if (collectorMap == null) { + collectorMap = new Int2ObjectOpenHashMap<>(); + collectors.put(buf, collectorMap); + } + collectorMap.put(position, longBitmapCollector); + return longBitmapCollector; + } +} diff --git a/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/BitmapAggregator.java b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/BitmapAggregator.java new file mode 100644 index 000000000000..9b6f5020ee8d --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/BitmapAggregator.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.query.aggregation.cardinality.accurate; + + +import org.apache.druid.query.aggregation.cardinality.accurate.collector.LongBitmapCollector; +import org.apache.druid.query.aggregation.cardinality.accurate.collector.LongBitmapCollectorFactory; +import org.apache.druid.segment.BaseObjectColumnValueSelector; + +public class BitmapAggregator extends BaseAccurateCardinalityAggregator +{ + public BitmapAggregator( + BaseObjectColumnValueSelector selector, + LongBitmapCollectorFactory longBitmapCollectorFactory, + boolean onHeap + ) + { + super(selector, longBitmapCollectorFactory, onHeap); + } + + @Override + void collectorAdd(LongBitmapCollector longBitmapCollector) + { + Object object = selector.getObject(); + if (object == null) { + return; + } + longBitmapCollector.fold((LongBitmapCollector) object); + } +} diff --git a/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/BitmapAggregatorCombiner.java b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/BitmapAggregatorCombiner.java new file mode 100644 index 000000000000..14f6d5a3c9ae --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/BitmapAggregatorCombiner.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.query.aggregation.cardinality.accurate; + +import org.apache.druid.query.aggregation.ObjectAggregateCombiner; +import org.apache.druid.query.aggregation.cardinality.accurate.collector.LongBitmapCollector; +import org.apache.druid.query.aggregation.cardinality.accurate.collector.LongBitmapCollectorFactory; +import org.apache.druid.query.aggregation.cardinality.accurate.collector.LongRoaringBitmapCollector; +import org.apache.druid.segment.ColumnValueSelector; + +import javax.annotation.Nullable; + +public class BitmapAggregatorCombiner extends ObjectAggregateCombiner +{ + + @Nullable + private LongBitmapCollector collector; + private LongBitmapCollectorFactory collectorFactory; + + public BitmapAggregatorCombiner( + LongBitmapCollectorFactory collectorFactory + ) + { + this.collectorFactory = collectorFactory; + } + + @Override + public void reset(ColumnValueSelector selector) + { + collector = null; + fold(selector); + } + + @Override + public void fold(ColumnValueSelector selector) + { + Object object = selector.getObject(); + if (object == null) { + return; + } + if (collector == null) { + collector = collectorFactory.makeEmptyCollector(); + } + collector.fold((LongBitmapCollector) object); + } + + @Nullable + @Override + public LongBitmapCollector getObject() + { + return collector; + } + + @Override + public Class classOfObject() + { + return LongRoaringBitmapCollector.class; + } +} diff --git a/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/BitmapAggregatorFactory.java b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/BitmapAggregatorFactory.java new file mode 100644 index 000000000000..46b8653196f2 --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/BitmapAggregatorFactory.java @@ -0,0 +1,221 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.query.aggregation.cardinality.accurate; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.apache.druid.java.util.common.IAE; +import org.apache.druid.java.util.common.StringUtils; +import org.apache.druid.java.util.common.guava.Comparators; +import org.apache.druid.query.aggregation.AggregateCombiner; +import org.apache.druid.query.aggregation.Aggregator; +import org.apache.druid.query.aggregation.AggregatorFactory; +import org.apache.druid.query.aggregation.AggregatorUtil; +import org.apache.druid.query.aggregation.BufferAggregator; +import org.apache.druid.query.aggregation.cardinality.accurate.collector.LongBitmapCollector; +import org.apache.druid.query.aggregation.cardinality.accurate.collector.LongBitmapCollectorFactory; +import org.apache.druid.query.aggregation.cardinality.accurate.collector.LongRoaringBitmapCollector; +import org.apache.druid.query.aggregation.cardinality.accurate.collector.LongRoaringBitmapCollectorFactory; +import org.apache.druid.query.cache.CacheKeyBuilder; +import org.apache.druid.query.dimension.DefaultDimensionSpec; +import org.apache.druid.query.dimension.DimensionSpec; +import org.apache.druid.segment.ColumnSelectorFactory; +import org.apache.druid.segment.ColumnValueSelector; +import org.apache.druid.segment.NilColumnValueSelector; +import org.apache.druid.segment.column.ColumnCapabilities; +import org.apache.druid.segment.column.ValueType; + +import java.nio.ByteBuffer; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + + +public class BitmapAggregatorFactory extends AggregatorFactory +{ + private static final LongBitmapCollectorFactory DEFAULT_BITMAP_FACTORY = new LongRoaringBitmapCollectorFactory(); + + private final String name; + private final DimensionSpec field; + + public BitmapAggregatorFactory(String name, String field) + { + this(name, DefaultDimensionSpec.of(field)); + } + + @JsonCreator + public BitmapAggregatorFactory( + @JsonProperty("name") String name, + @JsonProperty("fieldName") DimensionSpec field + ) + { + this.name = name; + this.field = field; + } + + @Override + public Aggregator factorize(ColumnSelectorFactory columnFactory) + { + return factorizeInternal(columnFactory, true); + } + + @Override + public BufferAggregator factorizeBuffered(ColumnSelectorFactory columnFactory) + { + return factorizeInternal(columnFactory, false); + } + + private BaseAccurateCardinalityAggregator factorizeInternal(ColumnSelectorFactory columnFactory, boolean onHeap) + { + if (field == null || field.getDimension() == null) { + return new NoopAccurateCardinalityAggregator(DEFAULT_BITMAP_FACTORY, onHeap); + } + ColumnCapabilities capabilities = columnFactory.getColumnCapabilities(field.getDimension()); + if (capabilities != null) { + ValueType type = capabilities.getType(); + switch (type) { + case COMPLEX: + return new BitmapAggregator( + columnFactory.makeColumnValueSelector(field.getDimension()), + DEFAULT_BITMAP_FACTORY, + onHeap + ); + default: + throw new IAE( + "Cannot create bitmap aggregator %s for invalid column type [%s]", + onHeap ? "aggregator" : "buffer aggregator", + type + ); + } + } else { + ColumnValueSelector columnValueSelector = columnFactory.makeColumnValueSelector(field.getDimension()); + if (columnValueSelector instanceof NilColumnValueSelector) { + return new NoopAccurateCardinalityAggregator(DEFAULT_BITMAP_FACTORY, onHeap); + } + return new ObjectAccurateCardinalityAggregator( + columnFactory.makeColumnValueSelector(field.getDimension()), + DEFAULT_BITMAP_FACTORY, + onHeap + ); + } + } + + @Override + public Comparator getComparator() + { + return Comparators.naturalNullsFirst(); + } + + @Override + public Object combine(Object lhs, Object rhs) + { + if (rhs == null) { + return lhs; + } + if (lhs == null) { + return rhs; + } + return ((LongRoaringBitmapCollector) lhs).fold((LongRoaringBitmapCollector) rhs); + } + + @Override + public AggregatorFactory getCombiningFactory() + { + return new BitmapAggregatorFactory(name, name); + } + + @Override + public AggregateCombiner makeAggregateCombiner() + { + return new BitmapAggregatorCombiner(DEFAULT_BITMAP_FACTORY); + } + + @Override + public List getRequiredColumns() + { + return Collections.singletonList(new BitmapAggregatorFactory(name, field)); + } + + @Override + public Object deserialize(Object object) + { + final ByteBuffer buffer; + + if (object instanceof byte[]) { + buffer = ByteBuffer.wrap((byte[]) object); + } else if (object instanceof ByteBuffer) { + // Be conservative, don't assume we own this buffer. + buffer = ((ByteBuffer) object).duplicate(); + } else if (object instanceof String) { + buffer = ByteBuffer.wrap(StringUtils.decodeBase64(StringUtils.toUtf8((String) object))); + } else { + return object; + } + return DEFAULT_BITMAP_FACTORY.makeCollector(buffer); + } + + @Override + public Object finalizeComputation(Object object) + { + if (object == null) { + return 0; + } + return ((LongBitmapCollector) object).getCardinality(); + } + + @JsonProperty + @Override + public String getName() + { + return this.name; + } + + @JsonProperty + public DimensionSpec getField() + { + return field; + } + + @Override + public List requiredFields() + { + return Collections.singletonList(field.getDimension()); + } + + @Override + public String getTypeName() + { + return AccurateCardinalityModule.BITMAP_COLLECTOR; + } + + @Override + public int getMaxIntermediateSize() + { + return 1; + } + + @Override + public byte[] getCacheKey() + { + return new CacheKeyBuilder(AggregatorUtil.BITMAP_AGG_CACHE_TYPE_ID) + .appendCacheable(field) + .build(); + } +} diff --git a/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/LongAccurateCardinalityAggregator.java b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/LongAccurateCardinalityAggregator.java new file mode 100644 index 000000000000..35bb39077d4f --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/LongAccurateCardinalityAggregator.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.query.aggregation.cardinality.accurate; + + +import org.apache.druid.query.aggregation.cardinality.accurate.collector.LongBitmapCollector; +import org.apache.druid.query.aggregation.cardinality.accurate.collector.LongBitmapCollectorFactory; +import org.apache.druid.segment.BaseLongColumnValueSelector; + +public class LongAccurateCardinalityAggregator extends BaseAccurateCardinalityAggregator +{ + public LongAccurateCardinalityAggregator( + BaseLongColumnValueSelector selector, + LongBitmapCollectorFactory longBitmapCollectorFactory, + boolean onHeap + ) + { + super(selector, longBitmapCollectorFactory, onHeap); + } + + @Override + void collectorAdd(LongBitmapCollector longBitmapCollector) + { + longBitmapCollector.add(selector.getLong()); + } +} + + diff --git a/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/NoopAccurateCardinalityAggregator.java b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/NoopAccurateCardinalityAggregator.java new file mode 100644 index 000000000000..eb24adbea734 --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/NoopAccurateCardinalityAggregator.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.query.aggregation.cardinality.accurate; + + +import org.apache.druid.query.aggregation.cardinality.accurate.collector.LongBitmapCollector; +import org.apache.druid.query.aggregation.cardinality.accurate.collector.LongBitmapCollectorFactory; +import org.apache.druid.segment.NilColumnValueSelector; + +import java.nio.ByteBuffer; + +public class NoopAccurateCardinalityAggregator extends BaseAccurateCardinalityAggregator +{ + public NoopAccurateCardinalityAggregator( + LongBitmapCollectorFactory longBitmapCollectorFactory, + boolean onHeap + ) + { + super(NilColumnValueSelector.instance(), longBitmapCollectorFactory, onHeap); + } + + @Override + void collectorAdd(LongBitmapCollector longBitmapCollector) + { + // nothing to do + } + + @Override + public void aggregate(ByteBuffer buf, int position) + { + // nothing to do + } + + @Override + public void aggregate() + { + // nothing to do + } +} diff --git a/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/ObjectAccurateCardinalityAggregator.java b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/ObjectAccurateCardinalityAggregator.java new file mode 100644 index 000000000000..13fce6977aca --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/ObjectAccurateCardinalityAggregator.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.query.aggregation.cardinality.accurate; + + +import org.apache.druid.java.util.common.IAE; +import org.apache.druid.query.aggregation.cardinality.accurate.collector.LongBitmapCollector; +import org.apache.druid.query.aggregation.cardinality.accurate.collector.LongBitmapCollectorFactory; +import org.apache.druid.segment.ColumnValueSelector; + +public class ObjectAccurateCardinalityAggregator extends BaseAccurateCardinalityAggregator +{ + public ObjectAccurateCardinalityAggregator( + ColumnValueSelector selector, + LongBitmapCollectorFactory longBitmapCollectorFactory, + boolean onHeap + ) + { + super(selector, longBitmapCollectorFactory, onHeap); + } + + @Override + void collectorAdd(LongBitmapCollector longBitmapCollector) + { + final Object object = selector.getObject(); + if (object == null) { + return; + } + if (object instanceof LongBitmapCollector) { + longBitmapCollector.fold((LongBitmapCollector) object); + } else if (object instanceof Long) { + longBitmapCollector.add(selector.getLong()); + } else { + throw new IAE( + "Cannot aggregat accurate cardinality for invalid column type" + ); + } + } +} diff --git a/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/bitmap64/LongBitmapFactory.java b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/bitmap64/LongBitmapFactory.java new file mode 100644 index 000000000000..fa305fee7056 --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/bitmap64/LongBitmapFactory.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.query.aggregation.cardinality.accurate.bitmap64; + +import java.nio.ByteBuffer; + +public interface LongBitmapFactory +{ + LongMutableBitmap makeEmptyMutableBitmap(); + + LongImmutableBitmap makeEmptyImmutableBitmap(); + + LongImmutableBitmap makeImmutableBitmap(LongMutableBitmap mutableBitmap); + + LongMutableBitmap mapMutableBitmap(ByteBuffer b); +} diff --git a/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/bitmap64/LongImmutableBitmap.java b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/bitmap64/LongImmutableBitmap.java new file mode 100644 index 000000000000..55396f98f692 --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/bitmap64/LongImmutableBitmap.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.query.aggregation.cardinality.accurate.bitmap64; + +import org.roaringbitmap.longlong.LongIterator; + +/** + * This class is meant to represent a simple wrapper around an immutable bitmap + * class. + */ +public interface LongImmutableBitmap +{ + /** + * @return an iterator over the set bits of this bitmap + */ + LongIterator iterator(); + + /** + * @return The number of bits set to true in this bitmap + */ + long size(); + + byte[] toBytes(); + + /** + * @return True if this bitmap is empty (contains no set bit) + */ + boolean isEmpty(); + + /** + * Returns true if the bit at position value is set + * + * @param value the position to check + * + * @return true if bit is set + */ + boolean get(long value); +} diff --git a/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/bitmap64/LongImmutableRoaringBitmap.java b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/bitmap64/LongImmutableRoaringBitmap.java new file mode 100644 index 000000000000..fba7d0b45059 --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/bitmap64/LongImmutableRoaringBitmap.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.query.aggregation.cardinality.accurate.bitmap64; + +import com.google.common.base.Throwables; +import org.roaringbitmap.BitmapDataProviderSupplier; +import org.roaringbitmap.RoaringBitmapSupplier; +import org.roaringbitmap.longlong.LongIterator; +import org.roaringbitmap.longlong.Roaring64NavigableMap; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; + + +public class LongImmutableRoaringBitmap implements LongImmutableBitmap +{ + protected InnerRoaringBitmap64 underlyingBitmap; + + public LongImmutableRoaringBitmap() + { + this(new InnerRoaringBitmap64()); + } + + public LongImmutableRoaringBitmap(InnerRoaringBitmap64 underlyingBitmap) + { + this.underlyingBitmap = underlyingBitmap; + } + + @Override + public LongIterator iterator() + { + return underlyingBitmap.getLongIterator(); + } + + @Override + public long size() + { + return underlyingBitmap.getLongCardinality(); + } + + @Override + public byte[] toBytes() + { + try { + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + underlyingBitmap.serialize(new DataOutputStream(out)); + return out.toByteArray(); + } + catch (Exception e) { + throw Throwables.propagate(e); + } + } + + @Override + public boolean isEmpty() + { + return underlyingBitmap.isEmpty(); + } + + @Override + public boolean get(long value) + { + return underlyingBitmap.contains(value); + } + + static class InnerRoaringBitmap64 extends Roaring64NavigableMap + { + private static BitmapDataProviderSupplier supplier = new RoaringBitmapSupplier(); + + public InnerRoaringBitmap64() + { + super(supplier); + } + + public InnerRoaringBitmap64 copy() + { + InnerRoaringBitmap64 copy = new InnerRoaringBitmap64(); + LongIterator iter = getLongIterator(); + while (iter.hasNext()) { + copy.addLong(iter.next()); + } + return copy; + } + } +} + diff --git a/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/bitmap64/LongMutableBitmap.java b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/bitmap64/LongMutableBitmap.java new file mode 100644 index 000000000000..7cfaa5a88670 --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/bitmap64/LongMutableBitmap.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.query.aggregation.cardinality.accurate.bitmap64; + +/** + * This class is meant to represent a simple wrapper around a bitmap class. + */ +public interface LongMutableBitmap extends LongImmutableBitmap +{ + /** + * Empties the content of this bitmap. + */ + void clear(); + + /** + * Compute the bitwise-or of this bitmap with another bitmap. The current + * bitmap is modified whereas the other bitmap is left intact. + *

+ * Note that the other bitmap should be of the same class instance. + * + * @param mutableBitmap other bitmap + */ + void or(LongMutableBitmap mutableBitmap); + + /** + * Compute the bitwise-and of this bitmap with another bitmap. The current + * bitmap is modified whereas the other bitmap is left intact. + *

+ * Note that the other bitmap should be of the same class instance. + * + * @param mutableBitmap other bitmap + */ + void and(LongMutableBitmap mutableBitmap); + + + /** + * Compute the bitwise-xor of this bitmap with another bitmap. The current + * bitmap is modified whereas the other bitmap is left intact. + *

+ * Note that the other bitmap should be of the same class instance. + * + * @param mutableBitmap other bitmap + */ + void xor(LongMutableBitmap mutableBitmap); + + /** + * Compute the bitwise-andNot of this bitmap with another bitmap. The current + * bitmap is modified whereas the other bitmap is left intact. + *

+ * Note that the other bitmap should be of the same class instance. + * + * @param mutableBitmap other bitmap + */ + void andNot(LongMutableBitmap mutableBitmap); + + /** + * Return the size in bytes for the purpose of serialization to a ByteBuffer. + * Note that this is distinct from the memory usage. + * + * @return the total set in bytes + */ + long getSizeInBytes(); + + /** + * Add the specified integer to the bitmap. This is equivalent to setting the + * ith bit to the value 1. + * + * @param entry integer to be added + */ + void add(long entry); + + /** + * Remove the specified integer to the bitmap. This is equivalent to setting the + * ith bit to the value 1. + * + * @param entry integer to be remove + */ + void remove(long entry); + +} diff --git a/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/bitmap64/LongRoaringBitmap.java b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/bitmap64/LongRoaringBitmap.java new file mode 100644 index 000000000000..8a35af7652e1 --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/bitmap64/LongRoaringBitmap.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.query.aggregation.cardinality.accurate.bitmap64; + +import com.google.common.base.Throwables; +import org.roaringbitmap.longlong.Roaring64NavigableMap; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; + +public class LongRoaringBitmap extends LongImmutableRoaringBitmap implements LongMutableBitmap +{ + public LongRoaringBitmap() + { + super(); + } + + public LongRoaringBitmap(InnerRoaringBitmap64 underlyingBitmap) + { + super(underlyingBitmap); + } + + @Override + public void clear() + { + this.underlyingBitmap.clear(); + } + + @Override + public void or(LongMutableBitmap mutableBitmap) + { + LongRoaringBitmap other = (LongRoaringBitmap) mutableBitmap; + Roaring64NavigableMap unwrappedOtherBitmap = other.underlyingBitmap; + this.underlyingBitmap.or(unwrappedOtherBitmap); + } + + @Override + public void and(LongMutableBitmap mutableBitmap) + { + LongRoaringBitmap other = (LongRoaringBitmap) mutableBitmap; + Roaring64NavigableMap unwrappedOtherBitmap = other.underlyingBitmap; + this.underlyingBitmap.and(unwrappedOtherBitmap); + } + + @Override + public void xor(LongMutableBitmap mutableBitmap) + { + LongRoaringBitmap other = (LongRoaringBitmap) mutableBitmap; + Roaring64NavigableMap unwrappedOtherBitmap = other.underlyingBitmap; + this.underlyingBitmap.xor(unwrappedOtherBitmap); + } + + @Override + public void andNot(LongMutableBitmap mutableBitmap) + { + LongRoaringBitmap other = (LongRoaringBitmap) mutableBitmap; + Roaring64NavigableMap unwrappedOtherBitmap = other.underlyingBitmap; + this.underlyingBitmap.andNot(unwrappedOtherBitmap); + } + + @Override + public long getSizeInBytes() + { + return this.underlyingBitmap.getLongSizeInBytes(); + } + + @Override + public void add(long entry) + { + this.underlyingBitmap.add(entry); + } + + @Override + public void remove(long entry) + { + this.underlyingBitmap.removeLong(entry); + } + + public LongImmutableRoaringBitmap toImmutableBitmap() + { + InnerRoaringBitmap64 mrb = this.underlyingBitmap.copy(); + return new LongImmutableRoaringBitmap(mrb); + } + + public static LongRoaringBitmap deserializeFromByteArray(byte[] bytes) + { + InnerRoaringBitmap64 innerRoaringBitmap64 = new InnerRoaringBitmap64(); + try { + innerRoaringBitmap64.deserialize(new DataInputStream(new ByteArrayInputStream(bytes))); + } + catch (IOException e) { + throw Throwables.propagate(e); + } + return new LongRoaringBitmap(innerRoaringBitmap64); + } + +} diff --git a/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/bitmap64/LongRoaringBitmapFactory.java b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/bitmap64/LongRoaringBitmapFactory.java new file mode 100644 index 000000000000..357d889cf7ea --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/bitmap64/LongRoaringBitmapFactory.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.query.aggregation.cardinality.accurate.bitmap64; + + +import com.google.common.base.Throwables; +import org.apache.druid.java.util.common.StringUtils; + +import java.nio.ByteBuffer; + +public class LongRoaringBitmapFactory implements LongBitmapFactory +{ + private static final LongImmutableRoaringBitmap EMPTY_IMMUTABLE_BITMAP = new LongImmutableRoaringBitmap(); + + @Override + public LongMutableBitmap makeEmptyMutableBitmap() + { + return new LongRoaringBitmap(); + } + + @Override + public LongImmutableBitmap makeEmptyImmutableBitmap() + { + return EMPTY_IMMUTABLE_BITMAP; + } + + @Override + public LongImmutableBitmap makeImmutableBitmap(LongMutableBitmap mutableBitmap) + { + if (!(mutableBitmap instanceof LongRoaringBitmap)) { + throw new IllegalStateException(StringUtils.nonStrictFormat("Cannot convert [%s]", mutableBitmap.getClass())); + } + try { + return ((LongRoaringBitmap) mutableBitmap).toImmutableBitmap(); + } + catch (Exception e) { + throw Throwables.propagate(e); + } + } + + @Override + public LongMutableBitmap mapMutableBitmap(ByteBuffer b) + { + return LongRoaringBitmap.deserializeFromByteArray(b.array()); + } +} diff --git a/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/collector/LongBitmapCollector.java b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/collector/LongBitmapCollector.java new file mode 100644 index 000000000000..62ddb160f842 --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/collector/LongBitmapCollector.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.query.aggregation.cardinality.accurate.collector; + +import java.nio.ByteBuffer; + +public interface LongBitmapCollector extends Comparable +{ + void add(long value); + + long getCardinality(); + + LongBitmapCollector fold(LongBitmapCollector rhs); + + ByteBuffer toByteBuffer(); +} diff --git a/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/collector/LongBitmapCollectorFactory.java b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/collector/LongBitmapCollectorFactory.java new file mode 100644 index 000000000000..45989595e58a --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/collector/LongBitmapCollectorFactory.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.query.aggregation.cardinality.accurate.collector; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +import java.nio.ByteBuffer; + +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = LongRoaringBitmapCollectorFactory.class) +@JsonSubTypes(value = { + @JsonSubTypes.Type(name = "roaring", value = LongRoaringBitmapCollectorFactory.class) +}) +public interface LongBitmapCollectorFactory +{ + LongBitmapCollector makeEmptyCollector(); + + LongBitmapCollector makeCollector(ByteBuffer buffer); +} diff --git a/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/collector/LongRoaringBitmapCollector.java b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/collector/LongRoaringBitmapCollector.java new file mode 100644 index 000000000000..f06d0f43de4f --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/collector/LongRoaringBitmapCollector.java @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.query.aggregation.cardinality.accurate.collector; + + +import org.apache.druid.java.util.common.StringUtils; +import org.apache.druid.query.aggregation.cardinality.accurate.bitmap64.LongMutableBitmap; +import org.apache.druid.query.aggregation.cardinality.accurate.bitmap64.LongRoaringBitmap; + +import java.nio.ByteBuffer; + +public class LongRoaringBitmapCollector implements LongBitmapCollector +{ + public final LongMutableBitmap bitmap; + + public LongRoaringBitmapCollector(LongMutableBitmap mutableBitmap) + { + this.bitmap = mutableBitmap; + } + + @Override + public void add(long value) + { + this.bitmap.add(value); + } + + @Override + public long getCardinality() + { + return this.bitmap.size(); + } + + @Override + public LongBitmapCollector fold(LongBitmapCollector other) + { + if (other == null) { + return this; + } + bitmap.or(((LongRoaringBitmapCollector) other).bitmap); + return this; + } + + @Override + public int compareTo(LongBitmapCollector other) + { + return Long.compare(this.getCardinality(), other.getCardinality()); + } + + public static LongRoaringBitmapCollector of(Object obj) + { + return new LongRoaringBitmapCollector((LongMutableBitmap) obj); + } + + public static LongRoaringBitmapCollector deserialize(Object serializedCollector) + { + if (serializedCollector instanceof String) { + return LongRoaringBitmapCollector.of(deserializeFromBase64EncodedString((String) serializedCollector)); + } else if (serializedCollector instanceof byte[]) { + return LongRoaringBitmapCollector.of(deserializeFromByteArray((byte[]) serializedCollector)); + } else if (serializedCollector instanceof LongRoaringBitmapCollector) { + return (LongRoaringBitmapCollector) serializedCollector; + } else { + return null; + } + } + + private static LongMutableBitmap deserializeFromBase64EncodedString(String str) + { + return deserializeFromByteArray(StringUtils.decodeBase64(StringUtils.toUtf8(str))); + } + + private static LongMutableBitmap deserializeFromByteArray(byte[] bytes) + { + return LongRoaringBitmap.deserializeFromByteArray(bytes); + } + + @Override + public ByteBuffer toByteBuffer() + { + return ByteBuffer.wrap(bitmap.toBytes()); + } +} diff --git a/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/collector/LongRoaringBitmapCollectorComplexMetricSerde.java b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/collector/LongRoaringBitmapCollectorComplexMetricSerde.java new file mode 100644 index 000000000000..dd2d753b83de --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/collector/LongRoaringBitmapCollectorComplexMetricSerde.java @@ -0,0 +1,139 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.query.aggregation.cardinality.accurate.collector; + +import com.google.common.collect.Ordering; +import org.apache.druid.data.input.InputRow; +import org.apache.druid.query.aggregation.cardinality.accurate.AccurateCardinalityModule; +import org.apache.druid.segment.column.ColumnBuilder; +import org.apache.druid.segment.data.GenericIndexed; +import org.apache.druid.segment.data.ObjectStrategy; +import org.apache.druid.segment.serde.ComplexColumnPartSupplier; +import org.apache.druid.segment.serde.ComplexMetricExtractor; +import org.apache.druid.segment.serde.ComplexMetricSerde; + +import java.nio.ByteBuffer; + +public class LongRoaringBitmapCollectorComplexMetricSerde extends ComplexMetricSerde +{ + private LongBitmapCollectorFactory longBitmapCollectorFactory; + + public LongRoaringBitmapCollectorComplexMetricSerde(LongBitmapCollectorFactory longBitmapCollectorFactory) + { + this.longBitmapCollectorFactory = longBitmapCollectorFactory; + } + + private static Ordering comparator = new Ordering() + { + @Override + public int compare( + LongBitmapCollector arg1, + LongBitmapCollector arg2 + ) + { + return arg1.toByteBuffer().compareTo(arg2.toByteBuffer()); + } + }.nullsFirst(); + + @Override + public String getTypeName() + { + return AccurateCardinalityModule.BITMAP_COLLECTOR; + } + + @Override + public ComplexMetricExtractor getExtractor() + { + return new ComplexMetricExtractor() + { + @Override + public Class extractedClass() + { + return LongRoaringBitmapCollector.class; + } + + @Override + public LongRoaringBitmapCollector extractValue(InputRow inputRow, String metricName) + { + final Object object = inputRow.getRaw(metricName); + if (object instanceof LongRoaringBitmapCollector) { + return (LongRoaringBitmapCollector) object; + } + LongRoaringBitmapCollector collector = (LongRoaringBitmapCollector) longBitmapCollectorFactory.makeEmptyCollector(); + collector.add(Long.valueOf(object.toString())); + return collector; + } + }; + } + + @Override + public void deserializeColumn(ByteBuffer buffer, ColumnBuilder builder) + { + final GenericIndexed column = GenericIndexed.read(buffer, getObjectStrategy(), builder.getFileMapper()); + builder.setComplexColumnSupplier(new ComplexColumnPartSupplier(getTypeName(), column)); + } + + @Override + public ObjectStrategy getObjectStrategy() + { + return new ObjectStrategy() + { + + @Override + public int compare( + LongRoaringBitmapCollector o1, + LongRoaringBitmapCollector o2 + ) + { + return comparator.compare(o1, o2); + } + + @Override + public Class getClazz() + { + return LongRoaringBitmapCollector.class; + } + + @Override + public LongRoaringBitmapCollector fromByteBuffer(ByteBuffer buffer, int numBytes) + { + final ByteBuffer readOnlyBuffer = buffer.asReadOnlyBuffer(); + + readOnlyBuffer.limit(readOnlyBuffer.position() + numBytes); + byte[] bytes = new byte[readOnlyBuffer.remaining()]; + readOnlyBuffer.get(bytes, 0, numBytes); + + return LongRoaringBitmapCollector.deserialize(bytes); + } + + @Override + public byte[] toBytes(LongRoaringBitmapCollector collector) + { + if (collector == null) { + return new byte[]{}; + } + ByteBuffer val = collector.toByteBuffer(); + byte[] retVal = new byte[val.remaining()]; + val.asReadOnlyBuffer().get(retVal); + return retVal; + } + }; + } +} diff --git a/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/collector/LongRoaringBitmapCollectorFactory.java b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/collector/LongRoaringBitmapCollectorFactory.java new file mode 100644 index 000000000000..8cd961773f4e --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/collector/LongRoaringBitmapCollectorFactory.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.query.aggregation.cardinality.accurate.collector; + + +import org.apache.druid.query.aggregation.cardinality.accurate.bitmap64.LongBitmapFactory; +import org.apache.druid.query.aggregation.cardinality.accurate.bitmap64.LongRoaringBitmapFactory; + +import java.nio.ByteBuffer; + +public class LongRoaringBitmapCollectorFactory implements LongBitmapCollectorFactory +{ + private static final LongBitmapFactory LONG_BITMAP_FACTORY = new LongRoaringBitmapFactory(); + + public LongRoaringBitmapCollectorFactory() + { + } + + @Override + public LongBitmapCollector makeEmptyCollector() + { + return new LongRoaringBitmapCollector(LONG_BITMAP_FACTORY.makeEmptyMutableBitmap()); + } + + @Override + public LongBitmapCollector makeCollector(ByteBuffer buffer) + { + return new LongRoaringBitmapCollector(LONG_BITMAP_FACTORY.mapMutableBitmap(buffer)); + } + + @Override + public String toString() + { + return "LongRoaringBitmapCollectorFactory"; + } + + @Override + public boolean equals(Object o) + { + return this == o || o instanceof LongRoaringBitmapCollectorFactory; + } + + @Override + public int hashCode() + { + return 0; + } +} diff --git a/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/collector/LongRoaringBitmapCollectorJsonSerializer.java b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/collector/LongRoaringBitmapCollectorJsonSerializer.java new file mode 100644 index 000000000000..3929e91b3f94 --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/collector/LongRoaringBitmapCollectorJsonSerializer.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.query.aggregation.cardinality.accurate.collector; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; + +public class LongRoaringBitmapCollectorJsonSerializer extends JsonSerializer +{ + @Override + public void serialize(LongRoaringBitmapCollector collector, JsonGenerator jgen, SerializerProvider provider) + throws IOException + { + jgen.writeBinary(collector.toByteBuffer().array()); + } +} diff --git a/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/sql/AccurateCardinalitySqlAggregator.java b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/sql/AccurateCardinalitySqlAggregator.java new file mode 100644 index 000000000000..4de900826f82 --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/main/java/org/apache/druid/query/aggregation/cardinality/accurate/sql/AccurateCardinalitySqlAggregator.java @@ -0,0 +1,186 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.query.aggregation.cardinality.accurate.sql; + +import com.google.common.collect.Iterables; +import org.apache.calcite.rel.core.AggregateCall; +import org.apache.calcite.rel.core.Project; +import org.apache.calcite.rex.RexBuilder; +import org.apache.calcite.rex.RexNode; +import org.apache.calcite.sql.SqlAggFunction; +import org.apache.calcite.sql.SqlFunctionCategory; +import org.apache.calcite.sql.SqlKind; +import org.apache.calcite.sql.type.OperandTypes; +import org.apache.calcite.sql.type.ReturnTypes; +import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.druid.java.util.common.StringUtils; +import org.apache.druid.query.aggregation.AggregatorFactory; +import org.apache.druid.query.aggregation.cardinality.accurate.AccurateCardinalityAggregatorFactory; +import org.apache.druid.query.dimension.DefaultDimensionSpec; +import org.apache.druid.query.dimension.DimensionSpec; +import org.apache.druid.query.dimension.ExtractionDimensionSpec; +import org.apache.druid.segment.VirtualColumn; +import org.apache.druid.segment.column.ValueType; +import org.apache.druid.segment.virtual.ExpressionVirtualColumn; +import org.apache.druid.sql.calcite.aggregation.Aggregation; +import org.apache.druid.sql.calcite.aggregation.SqlAggregator; +import org.apache.druid.sql.calcite.expression.DruidExpression; +import org.apache.druid.sql.calcite.expression.Expressions; +import org.apache.druid.sql.calcite.planner.Calcites; +import org.apache.druid.sql.calcite.planner.PlannerContext; +import org.apache.druid.sql.calcite.rel.VirtualColumnRegistry; +import org.apache.druid.sql.calcite.table.RowSignature; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; + +public class AccurateCardinalitySqlAggregator implements SqlAggregator +{ + private static final SqlAggFunction FUNCTION_INSTANCE = new AccurateCardinalitySQLAggFunction(); + private static final String NAME = "ACCURATE_CARDINALITY"; + + @Override + + public SqlAggFunction calciteFunction() + { + return FUNCTION_INSTANCE; + } + + @Nullable + @Override + public Aggregation toDruidAggregation( + PlannerContext plannerContext, + RowSignature rowSignature, + VirtualColumnRegistry virtualColumnRegistry, + RexBuilder rexBuilder, + String name, + AggregateCall aggregateCall, + Project project, + List existingAggregations, + boolean finalizeAggregations + ) + { + final RexNode rexNode = Expressions.fromFieldAccess( + rowSignature, + project, + Iterables.getOnlyElement(aggregateCall.getArgList()) + ); + + final DruidExpression input = Expressions.toDruidExpression(plannerContext, rowSignature, rexNode); + if (input == null) { + return null; + } + + final AggregatorFactory aggregatorFactory; + final String aggName = StringUtils.format("%s:agg", name); + + // Look for existing matching aggregatorFactory. + for (final Aggregation existing : existingAggregations) { + for (AggregatorFactory factory : existing.getAggregatorFactories()) { + if (factory instanceof AccurateCardinalityAggregatorFactory) { + final AccurateCardinalityAggregatorFactory theFactory = (AccurateCardinalityAggregatorFactory) factory; + + // Check input for equivalence. + final boolean inputMatches; + final VirtualColumn virtualInput = + existing.getVirtualColumns() + .stream() + .filter(virtualColumn -> + virtualColumn.getOutputName().equals(theFactory.getField().getOutputName()) + ) + .findFirst() + .orElse(null); + if (virtualInput == null) { + if (input.isDirectColumnAccess()) { + inputMatches = + input.getDirectColumn().equals(theFactory.getField().getDimension()); + } else { + inputMatches = + input.getSimpleExtraction().getColumn().equals(theFactory.getField().getDimension()) && + input.getSimpleExtraction().getExtractionFn().equals(theFactory.getField().getExtractionFn()); + } + } else { + inputMatches = ((ExpressionVirtualColumn) virtualInput).getExpression().equals(input.getExpression()); + } + + if (inputMatches) { + // Found existing one. Use this. + return Aggregation.create( + theFactory + ); + } + } + } + } + // No existing match found. Create a new one. + final List virtualColumns = new ArrayList<>(); + + final ValueType inputType = Calcites.getValueTypeForSqlTypeName(rexNode.getType().getSqlTypeName()); + final DimensionSpec dimensionSpec; + if (input.isDirectColumnAccess()) { + dimensionSpec = new DefaultDimensionSpec( + input.getSimpleExtraction().getColumn(), + StringUtils.format("%s:%s", name, input.getSimpleExtraction().getColumn()), + inputType + ); + } else if (input.isSimpleExtraction()) { + dimensionSpec = new ExtractionDimensionSpec( + input.getSimpleExtraction().getColumn(), + StringUtils.format("%s:%s", name, input.getSimpleExtraction().getColumn()), + inputType, + input.getSimpleExtraction().getExtractionFn() + ); + } else { + VirtualColumn virtualColumn = virtualColumnRegistry.getOrCreateVirtualColumnForExpression( + plannerContext, + input, + rexNode.getType().getSqlTypeName() + ); + dimensionSpec = new DefaultDimensionSpec(virtualColumn.getOutputName(), virtualColumn.getOutputName()); + virtualColumns.add(virtualColumn); + } + + aggregatorFactory = new AccurateCardinalityAggregatorFactory( + aggName, + dimensionSpec + ); + + return Aggregation.create(virtualColumns, aggregatorFactory); + } + + private static class AccurateCardinalitySQLAggFunction extends SqlAggFunction + { + AccurateCardinalitySQLAggFunction() + { + super( + NAME, + null, + SqlKind.OTHER_FUNCTION, + ReturnTypes.explicit(SqlTypeName.BIGINT), + null, + OperandTypes.NUMERIC, + SqlFunctionCategory.NUMERIC, + false, + false + ); + } + } +} diff --git a/extensions-contrib/accurate-cardinality/src/main/resources/META-INF/services/org.apache.druid.initialization.DruidModule b/extensions-contrib/accurate-cardinality/src/main/resources/META-INF/services/org.apache.druid.initialization.DruidModule new file mode 100644 index 000000000000..6ac494a7f49b --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/main/resources/META-INF/services/org.apache.druid.initialization.DruidModule @@ -0,0 +1,16 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +org.apache.druid.query.aggregation.cardinality.accurate.AccurateCardinalityModule diff --git a/extensions-contrib/accurate-cardinality/src/test/java/org/apache/druid/query/aggregation/cardinality/accurate/AccurateCardinalityAggGroupbyTest.java b/extensions-contrib/accurate-cardinality/src/test/java/org/apache/druid/query/aggregation/cardinality/accurate/AccurateCardinalityAggGroupbyTest.java new file mode 100644 index 000000000000..1e22802d5ca6 --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/test/java/org/apache/druid/query/aggregation/cardinality/accurate/AccurateCardinalityAggGroupbyTest.java @@ -0,0 +1,192 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.query.aggregation.cardinality.accurate; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.io.Files; +import org.apache.druid.data.input.MapBasedRow; +import org.apache.druid.data.input.Row; +import org.apache.druid.java.util.common.DateTimes; +import org.apache.druid.java.util.common.granularity.Granularities; +import org.apache.druid.java.util.common.guava.Sequence; +import org.apache.druid.query.aggregation.AggregationTestHelper; +import org.apache.druid.query.aggregation.AggregatorFactory; +import org.apache.druid.query.aggregation.cardinality.accurate.collector.LongRoaringBitmapCollectorFactory; +import org.apache.druid.query.groupby.GroupByQueryConfig; +import org.apache.druid.query.groupby.GroupByQueryRunnerTest; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + + +@RunWith(Parameterized.class) +public class AccurateCardinalityAggGroupbyTest +{ + private final AggregationTestHelper helper; + @Rule + public final TemporaryFolder tempFolder = new TemporaryFolder(); + + public AccurateCardinalityAggGroupbyTest(final GroupByQueryConfig config) + { + AccurateCardinalityModule acm = new AccurateCardinalityModule(); + acm.configure(null); + helper = AggregationTestHelper.createGroupByQueryAggregationTestHelper( + acm.getJacksonModules(), + config, + tempFolder + ); + } + + @Parameterized.Parameters(name = "{0}") + public static Collection constructorFeeder() throws IOException + { + final List constructors = new ArrayList<>(); + for (GroupByQueryConfig config : GroupByQueryRunnerTest.testConfigs()) { + constructors.add(new Object[]{config}); + } + return constructors; + } + + @Test + public void testAccurateCardinalityAggregatorFactorySerde() throws Exception + { + Sequence seq = helper.createIndexAndRunQueryOnSegment( + new File(AccurateCardinalityAggGroupbyTest.class.getClassLoader() + .getResource("simple_test_data.tsv") + .getFile()), + readFileFromClasspathAsString("simple_test_data_record_parser.json"), + readFileFromClasspathAsString("simple_test_data_aggregators.json"), + 0, + Granularities.NONE, + 1000, + readFileFromClasspathAsString("simple_test_data_group_by_query.json") + ); + + List results = seq.toList(); + Assert.assertEquals(5, results.size()); + Assert.assertEquals( + ImmutableList.of( + new MapBasedRow( + DateTimes.of("2014-10-19T00:00:00.000Z"), + ImmutableMap.builder() + .put("uv", 291L) + .put("product", "product_1") + .build() + ), + new MapBasedRow( + DateTimes.of("2014-10-19T00:00:00.000Z"), + ImmutableMap.builder() + .put("uv", 297L) + .put("product", "product_2") + .build() + ), + new MapBasedRow( + DateTimes.of("2014-10-19T00:00:00.000Z"), + ImmutableMap.builder() + .put("uv", 309L) + .put("product", "product_3") + .build() + ), + new MapBasedRow( + DateTimes.of("2014-10-19T00:00:00.000Z"), + ImmutableMap.builder() + .put("uv", 300L) + .put("product", "product_4") + .build() + ), + new MapBasedRow( + DateTimes.of("2014-10-19T00:00:00.000Z"), + ImmutableMap.builder() + .put("uv", 297L) + .put("product", "product_5") + .build() + ) + ), + results + ); + + } + + public static String readFileFromClasspathAsString(String fileName) throws IOException + { + return Files.asCharSource( + new File(AccurateCardinalityAggGroupbyTest.class.getClassLoader().getResource(fileName).getFile()), + Charset.forName("UTF-8") + ).read(); + } + + @Test + public void testAccurateCardinalityAggFactorySerde() throws Exception + { + assertAggregatorFactorySerde(new AccurateCardinalityAggregatorFactory( + "name", + "fieldName", + new LongRoaringBitmapCollectorFactory() + )); + } + + private void assertAggregatorFactorySerde(AggregatorFactory agg) throws Exception + { + Assert.assertEquals( + agg, + helper.getObjectMapper().readValue( + helper.getObjectMapper().writeValueAsString(agg), + AggregatorFactory.class + ) + ); + } + + @Test + public void testCacheKey() + { + LongRoaringBitmapCollectorFactory collectorFactory = new LongRoaringBitmapCollectorFactory(); + final AccurateCardinalityAggregatorFactory factory1 = new AccurateCardinalityAggregatorFactory( + "name", + "fieldName", + collectorFactory + ); + final AccurateCardinalityAggregatorFactory factory2 = new AccurateCardinalityAggregatorFactory( + "name", + "fieldName", + collectorFactory + ); + final AccurateCardinalityAggregatorFactory factory3 = new AccurateCardinalityAggregatorFactory( + "name", + "fieldName1", + collectorFactory + ); + Assert.assertTrue(Arrays.equals(factory1.getCacheKey(), factory2.getCacheKey())); + Assert.assertFalse(Arrays.equals(factory1.getCacheKey(), factory3.getCacheKey())); + } + + +} diff --git a/extensions-contrib/accurate-cardinality/src/test/java/org/apache/druid/query/aggregation/cardinality/accurate/AccurateCardinalityAggregatorTest.java b/extensions-contrib/accurate-cardinality/src/test/java/org/apache/druid/query/aggregation/cardinality/accurate/AccurateCardinalityAggregatorTest.java new file mode 100644 index 000000000000..e97b3df34da6 --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/test/java/org/apache/druid/query/aggregation/cardinality/accurate/AccurateCardinalityAggregatorTest.java @@ -0,0 +1,688 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.query.aggregation.cardinality.accurate; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterators; +import com.google.common.collect.Lists; +import org.apache.druid.jackson.DefaultObjectMapper; +import org.apache.druid.js.JavaScriptConfig; +import org.apache.druid.query.aggregation.Aggregator; +import org.apache.druid.query.aggregation.BufferAggregator; +import org.apache.druid.query.aggregation.cardinality.accurate.collector.LongRoaringBitmapCollectorFactory; +import org.apache.druid.query.dimension.DefaultDimensionSpec; +import org.apache.druid.query.dimension.DimensionSpec; +import org.apache.druid.query.extraction.ExtractionFn; +import org.apache.druid.query.extraction.JavaScriptExtractionFn; +import org.apache.druid.query.filter.ValueMatcher; +import org.apache.druid.query.monomorphicprocessing.RuntimeShapeInspector; +import org.apache.druid.segment.BaseLongColumnValueSelector; +import org.apache.druid.segment.DimensionDictionarySelector; +import org.apache.druid.segment.DimensionSelector; +import org.apache.druid.segment.DimensionSelectorUtils; +import org.apache.druid.segment.IdLookup; +import org.apache.druid.segment.data.IndexedInts; +import org.apache.druid.segment.data.ZeroIndexedInts; +import org.junit.Assert; +import org.junit.Test; + +import javax.annotation.Nullable; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + + +public class AccurateCardinalityAggregatorTest +{ + + private LongRoaringBitmapCollectorFactory roaringBitmapCollectorFactory = new LongRoaringBitmapCollectorFactory(); + + interface ISeek + { + void increment(); + + void reset(); + } + + abstract static class AbstractDimensionSelector implements DimensionSelector, ISeek + { + protected int pos = 0; + + @Override + public void increment() + { + pos++; + } + + @Override + public void reset() + { + pos = 0; + } + } + + public static class TestStringDimensionSelector extends AbstractDimensionSelector + { + + private final List column; + private final Map ids; + private final Map lookup; + private final ExtractionFn exFn; + + + public TestStringDimensionSelector(Iterable values, ExtractionFn exFn) + { + this.lookup = new HashMap<>(); + this.ids = new HashMap<>(); + this.exFn = exFn; + + int index = 0; + for (String[] multiValue : values) { + for (String value : multiValue) { + if (!ids.containsKey(value)) { + ids.put(value, index); + lookup.put(index, value); + index++; + } + } + } + + this.column = Lists.newArrayList( + StreamSupport.stream(values.spliterator(), false).map(input -> Iterators.toArray( + Iterators.transform( + Iterators.forArray(input), new Function() + { + @Nullable + @Override + public Integer apply(@Nullable String input) + { + return ids.get(input); + } + } + ), Integer.class + )).collect(Collectors.toList()) + ); + } + + @Override + public IndexedInts getRow() + { + final int p = this.pos; + return new IndexedInts() + { + @Override + public int size() + { + return column.get(p).length; + } + + @Override + public int get(int i) + { + return column.get(p)[i]; + } + + @Override + public void inspectRuntimeShape(RuntimeShapeInspector inspector) + { + // Don't care about runtime shape in tests + } + }; + } + + @Override + public ValueMatcher makeValueMatcher(@Nullable String value) + { + return DimensionSelectorUtils.makeValueMatcherGeneric(this, value); + } + + @Override + public ValueMatcher makeValueMatcher(Predicate predicate) + { + return DimensionSelectorUtils.makeValueMatcherGeneric(this, predicate); + } + + @Override + public int getValueCardinality() + { + return 1; + } + + @Nullable + @Override + public String lookupName(int id) + { + String val = lookup.get(id); + return exFn == null ? val : exFn.apply(val); + } + + @Override + public boolean nameLookupPossibleInAdvance() + { + return true; + } + + @Nullable + @Override + public IdLookup idLookup() + { + return new IdLookup() + { + @Override + public int lookupId(String s) + { + return ids.get(s); + } + }; + } + + @Override + public void inspectRuntimeShape(RuntimeShapeInspector inspector) + { + // Don't care about runtime shape in tests + } + + @Nullable + @Override + public Object getObject() + { + return null; + } + + @Override + public Class classOfObject() + { + return null; + } + } + + + public static class TestLongDimensionSelector extends AbstractDimensionSelector implements BaseLongColumnValueSelector + { + private final List column; + private final ExtractionFn exFn; + + public TestLongDimensionSelector(Iterable values, ExtractionFn exFn) + { + this.exFn = exFn; + this.column = Lists.newArrayList( + StreamSupport.stream(values.spliterator(), false).map(input -> Iterators.toArray( + Iterators.transform( + Iterators.forArray(input), new Function() + { + @Nullable + @Override + public Long apply(@Nullable Long input) + { + return input; + } + } + ), Long.class + )).collect(Collectors.toList()) + ); + } + + @Override + public IndexedInts getRow() + { + return ZeroIndexedInts.instance(); + } + + @Override + public ValueMatcher makeValueMatcher(@Nullable String value) + { + return new ValueMatcher() + { + @Override + public boolean matches() + { + return Objects.equals(getValue(), value); + } + + @Override + public void inspectRuntimeShape(RuntimeShapeInspector inspector) + { + + } + }; + } + + protected String getValue() + { + final int p = this.pos; + if (exFn == null) { + return column.get(p)[0].toString(); + } else { + return exFn.apply(column.get(p)[0]); + } + } + + @Override + public ValueMatcher makeValueMatcher(Predicate predicate) + { + return new ValueMatcher() + { + @Override + public boolean matches() + { + return predicate.apply(getValue()); + } + + @Override + public void inspectRuntimeShape(RuntimeShapeInspector inspector) + { + } + }; + } + + @Override + public int getValueCardinality() + { + return DimensionDictionarySelector.CARDINALITY_UNKNOWN; + } + + @Nullable + @Override + public String lookupName(int id) + { + return getValue(); + } + + @Override + public boolean nameLookupPossibleInAdvance() + { + return false; + } + + @Nullable + @Override + public IdLookup idLookup() + { + return null; + } + + @Override + public void inspectRuntimeShape(RuntimeShapeInspector inspector) + { + + } + + @Override + public long getLong() + { + final int p = this.pos; + if (exFn == null) { + return column.get(p)[0]; + } else { + return Long.valueOf(exFn.apply(column.get(p)[0])); + } + } + + @Nullable + @Override + public Object getObject() + { + return null; + } + + @Override + public Class classOfObject() + { + return null; + } + } + + + /** + * 字符串类型的数据 + */ + private static final List values1 = stringDimensionValues( + "a", "b", "c", "a", "a", null, "b", "b", "b", "b", "a", "a", "d" + ); + + /** + * 11 个值 + * 基数 6; + */ + private static final List values2 = longDimensionValues( + 1L, + 2L, + 2L, + 2L, + 3L, + 4L, + 5L, + 5L, + 6L, + 6L, + 6L + ); + + private static final List values3 = longDimensionValues( + 5L, + 5L, + 6L, + 7L, + 7L + ); + + private static List stringDimensionValues(Object... values) + { + return Lists.transform( + Lists.newArrayList(values), new Function() + { + @Nullable + @Override + public String[] apply(@Nullable Object input) + { + if (input instanceof String[]) { + return (String[]) input; + } else { + return new String[]{(String) input}; + } + } + } + ); + } + + private static List longDimensionValues(Object... values) + { + return Lists.transform( + Lists.newArrayList(values), new Function() + { + @Nullable + @Override + public Long[] apply(@Nullable Object input) + { + if (input instanceof Long[]) { + return (Long[]) input; + } else { + return new Long[]{(Long) input}; + } + } + } + ); + } + + private static void aggregate(List selectorList, Aggregator agg) + { + agg.aggregate(); + + for (DimensionSelector selector : selectorList) { + AbstractDimensionSelector _selector = (AbstractDimensionSelector) selector; + _selector.increment(); + } + } + + private static void bufferAggregate( + List selectorList, + BufferAggregator agg, + ByteBuffer buf, + int pos + ) + { + agg.aggregate(buf, pos); + + for (DimensionSelector selector : selectorList) { + AbstractDimensionSelector _selector = (AbstractDimensionSelector) selector; + _selector.increment(); + } + } + + List selectorList; + AccurateCardinalityAggregatorFactory strAggregatorFactory; + AccurateCardinalityAggregatorFactory longAggregatorFactory2; + AccurateCardinalityAggregatorFactory longAggregatorFactory3; + + final TestStringDimensionSelector dim1; + final TestLongDimensionSelector dim2; + final TestLongDimensionSelector dim3; + + + List selectorListWithExtraction; + final TestStringDimensionSelector dim1WithExtraction; + final TestLongDimensionSelector dim2WithExtraction; + + List selectorListConstantVal; + final TestStringDimensionSelector dim1ConstantVal; + final TestLongDimensionSelector dim2ConstantVal; + + final DimensionSpec dimSpec1 = new DefaultDimensionSpec("dim1", "dim1"); + final DimensionSpec dimSpec2 = new DefaultDimensionSpec("dim2", "dim2"); + final DimensionSpec dimSpec3 = new DefaultDimensionSpec("dim3", "dim3"); + + public AccurateCardinalityAggregatorTest() + { + dim1 = new TestStringDimensionSelector(values1, null); + dim2 = new TestLongDimensionSelector(values2, null); + dim3 = new TestLongDimensionSelector(values3, null); + + selectorList = Lists.newArrayList( + (DimensionSelector) dim1, + dim2 + ); + + strAggregatorFactory = new AccurateCardinalityAggregatorFactory( + "billy", + dimSpec1, + roaringBitmapCollectorFactory + ); + + longAggregatorFactory2 = new AccurateCardinalityAggregatorFactory( + "UV2", + dimSpec2, + roaringBitmapCollectorFactory + ); + + longAggregatorFactory3 = new AccurateCardinalityAggregatorFactory( + "UV3", + dimSpec3, + roaringBitmapCollectorFactory + ); + + String superJsFn1 = "function(str) { return 'super-' + str; }"; + String superJsFn2 = "function(str) { return str + 10; }"; + ExtractionFn superFn1 = new JavaScriptExtractionFn(superJsFn1, false, JavaScriptConfig.getEnabledInstance()); + ExtractionFn superFn2 = new JavaScriptExtractionFn(superJsFn2, false, JavaScriptConfig.getEnabledInstance()); + + dim1WithExtraction = new TestStringDimensionSelector(values1, superFn1); + dim2WithExtraction = new TestLongDimensionSelector(values2, superFn2); + + selectorListWithExtraction = Lists.newArrayList( + dim1WithExtraction, + dim2WithExtraction + ); + + String helloJsFn = "function(str) { return 'hello' }"; + String helloJsFn2 = "function(str) { return 100 }"; + ExtractionFn helloFn = new JavaScriptExtractionFn(helloJsFn, false, JavaScriptConfig.getEnabledInstance()); + ExtractionFn helloFn2 = new JavaScriptExtractionFn(helloJsFn2, false, JavaScriptConfig.getEnabledInstance()); + dim1ConstantVal = new TestStringDimensionSelector(values1, helloFn); + dim2ConstantVal = new TestLongDimensionSelector(values2, helloFn2); + selectorListConstantVal = Lists.newArrayList( + dim1ConstantVal, + dim2ConstantVal + ); + } + + @Test + public void testAggregateLong() throws Exception + { + LongAccurateCardinalityAggregator agg = new LongAccurateCardinalityAggregator( + selectorList.get(1), + roaringBitmapCollectorFactory, + true + ); + for (int i = 0; i < values2.size(); ++i) { + aggregate(selectorList, agg); + } + Assert.assertEquals(6L, longAggregatorFactory2.finalizeComputation(agg.get())); + } + + @Test + public void testBufferAggregateLong() + { + LongAccurateCardinalityAggregator agg = new LongAccurateCardinalityAggregator( + selectorList.get(1), + roaringBitmapCollectorFactory, + false + ); + int maxSize = longAggregatorFactory2.getMaxIntermediateSize(); + + ByteBuffer buf = ByteBuffer.allocate(maxSize + 64); + int pos = 10; + buf.limit(pos + maxSize); + + agg.init(buf, pos); + for (int i = 0; i < values2.size(); ++i) { + bufferAggregate(selectorList, agg, buf, pos); + } + Assert.assertEquals(6L, longAggregatorFactory2.finalizeComputation(agg.get(buf, pos))); + } + + @Test + public void testCombinRows() + { + List selector2 = ImmutableList.of(dim2); + List selector3 = ImmutableList.of(dim3); + + LongAccurateCardinalityAggregator agg2 = new LongAccurateCardinalityAggregator( + dim2, + roaringBitmapCollectorFactory, + true + ); + LongAccurateCardinalityAggregator agg3 = new LongAccurateCardinalityAggregator( + dim3, + roaringBitmapCollectorFactory, + true + ); + + for (int i = 0; i < values2.size(); ++i) { + aggregate(selector2, agg2); + } + for (int i = 0; i < values3.size(); ++i) { + aggregate(selector3, agg3); + } + + Assert.assertEquals(6L, longAggregatorFactory2.finalizeComputation(agg2.get())); + Assert.assertEquals(3L, longAggregatorFactory3.finalizeComputation(agg3.get())); + + Assert.assertEquals( + 7L, + longAggregatorFactory2.finalizeComputation( + longAggregatorFactory2.combine( + agg2.get(), + agg3.get() + ) + ) + ); + } + + @Test + public void testAggregateLongWithExtraction() + { + LongAccurateCardinalityAggregator agg = new LongAccurateCardinalityAggregator( + selectorListWithExtraction.get(1), + roaringBitmapCollectorFactory, + true + ); + for (int i = 0; i < values2.size(); ++i) { + aggregate(selectorListWithExtraction, agg); + } + Assert.assertEquals(6L, longAggregatorFactory2.finalizeComputation(agg.get())); + + LongAccurateCardinalityAggregator agg2 = new LongAccurateCardinalityAggregator( + selectorListConstantVal.get(1), + roaringBitmapCollectorFactory, + true + ); + for (int i = 0; i < values2.size(); ++i) { + aggregate(selectorListConstantVal, agg2); + } + Assert.assertEquals(1L, longAggregatorFactory2.finalizeComputation(agg2.get())); + } + + @Test + public void testBufferAggregateLongWithExtraction() + { + LongAccurateCardinalityAggregator agg = new LongAccurateCardinalityAggregator( + selectorListWithExtraction.get(1), + roaringBitmapCollectorFactory, + false + ); + + int maxSize = longAggregatorFactory2.getMaxIntermediateSize(); + + ByteBuffer buf = ByteBuffer.allocate(maxSize + 64); + int pos = 10; + buf.limit(pos + maxSize); + + agg.init(buf, pos); + for (int i = 0; i < values2.size(); ++i) { + bufferAggregate(selectorListWithExtraction, agg, buf, pos); + } + + Assert.assertEquals(6L, longAggregatorFactory2.finalizeComputation(agg.get(buf, pos))); + + LongAccurateCardinalityAggregator agg2 = new LongAccurateCardinalityAggregator( + selectorListConstantVal.get(1), + roaringBitmapCollectorFactory, + false + ); + pos = buf.limit(); + buf.limit(buf.limit() + maxSize); + agg2.init(buf, pos); + for (int i = 0; i < values2.size(); ++i) { + bufferAggregate(selectorListConstantVal, agg2, buf, pos); + } + + Assert.assertEquals(1L, longAggregatorFactory2.finalizeComputation(agg2.get(buf, pos))); + } + + @Test + public void testSerde() throws IOException + { + AccurateCardinalityAggregatorFactory expectedFactory = new AccurateCardinalityAggregatorFactory( + "UV", + "UV", + roaringBitmapCollectorFactory + ); + + ObjectMapper objectMapper = new DefaultObjectMapper(); + AccurateCardinalityModule acm = new AccurateCardinalityModule(); + acm.getJacksonModules().forEach(objectMapper::registerModule); + + String jsonStr = "{\"type\":\"accurateCardinality\",\"name\":\"UV\",\"field\":\"UV\"}"; + + AccurateCardinalityAggregatorFactory factory; + factory = objectMapper.readValue(jsonStr, AccurateCardinalityAggregatorFactory.class); + Assert.assertEquals("", expectedFactory.getLongBitmapCollectorFactory(), factory.getLongBitmapCollectorFactory()); + Assert.assertEquals("", expectedFactory.getName(), factory.getName()); + } + +} diff --git a/extensions-contrib/accurate-cardinality/src/test/java/org/apache/druid/query/aggregation/cardinality/accurate/BitmapAggGroupbyTest.java b/extensions-contrib/accurate-cardinality/src/test/java/org/apache/druid/query/aggregation/cardinality/accurate/BitmapAggGroupbyTest.java new file mode 100644 index 000000000000..8bbb23b38942 --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/test/java/org/apache/druid/query/aggregation/cardinality/accurate/BitmapAggGroupbyTest.java @@ -0,0 +1,193 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +package org.apache.druid.query.aggregation.cardinality.accurate; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.io.Files; +import org.apache.druid.data.input.MapBasedRow; +import org.apache.druid.data.input.Row; +import org.apache.druid.java.util.common.DateTimes; +import org.apache.druid.java.util.common.granularity.Granularities; +import org.apache.druid.java.util.common.guava.Sequence; +import org.apache.druid.query.aggregation.AggregationTestHelper; +import org.apache.druid.query.aggregation.AggregatorFactory; +import org.apache.druid.query.aggregation.cardinality.accurate.collector.LongRoaringBitmapCollectorFactory; +import org.apache.druid.query.groupby.GroupByQueryConfig; +import org.apache.druid.query.groupby.GroupByQueryRunnerTest; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + + +@RunWith(Parameterized.class) +public class BitmapAggGroupbyTest +{ + private final AggregationTestHelper helper; + @Rule + public final TemporaryFolder tempFolder = new TemporaryFolder(); + + public BitmapAggGroupbyTest(final GroupByQueryConfig config) + { + AccurateCardinalityModule acm = new AccurateCardinalityModule(); + acm.configure(null); + helper = AggregationTestHelper.createGroupByQueryAggregationTestHelper( + acm.getJacksonModules(), + config, + tempFolder + ); + } + + @Parameterized.Parameters(name = "{0}") + public static Collection constructorFeeder() throws IOException + { + final List constructors = new ArrayList<>(); + for (GroupByQueryConfig config : GroupByQueryRunnerTest.testConfigs()) { + constructors.add(new Object[]{config}); + } + return constructors; + } + + @Test + public void testBitmapAggregatorFactorySerde() throws Exception + { + Sequence seq = helper.createIndexAndRunQueryOnSegment( + new File(BitmapAggGroupbyTest.class.getClassLoader().getResource("simple_test_data.tsv").getFile()), + readFileFromClasspathAsString("bitmap_test_data_record_parser.json"), + readFileFromClasspathAsString("bitmap_test_data_aggregators.json"), + 0, + Granularities.NONE, + 1000, + readFileFromClasspathAsString("bitmap_test_data_group_by_query.json") + ); + + List results = seq.toList(); + System.out.println(helper.getObjectMapper().writeValueAsString(results)); + + Assert.assertEquals(5, results.size()); + Assert.assertEquals( + ImmutableList.of( + new MapBasedRow( + DateTimes.of("2014-10-19T00:00:00.000Z"), + ImmutableMap.builder() + .put("uv", 291L) + .put("product", "product_1") + .build() + ), + new MapBasedRow( + DateTimes.of("2014-10-19T00:00:00.000Z"), + ImmutableMap.builder() + .put("uv", 297L) + .put("product", "product_2") + .build() + ), + new MapBasedRow( + DateTimes.of("2014-10-19T00:00:00.000Z"), + ImmutableMap.builder() + .put("uv", 309L) + .put("product", "product_3") + .build() + ), + new MapBasedRow( + DateTimes.of("2014-10-19T00:00:00.000Z"), + ImmutableMap.builder() + .put("uv", 300L) + .put("product", "product_4") + .build() + ), + new MapBasedRow( + DateTimes.of("2014-10-19T00:00:00.000Z"), + ImmutableMap.builder() + .put("uv", 297L) + .put("product", "product_5") + .build() + ) + ), + results + ); + + } + + public static String readFileFromClasspathAsString(String fileName) throws IOException + { + return Files.asCharSource( + new File(BitmapAggGroupbyTest.class.getClassLoader().getResource(fileName).getFile()), + Charset.forName("UTF-8") + ).read(); + } + + @Test + public void testAccurateCardinalityAggFactorySerde() throws Exception + { + assertAggregatorFactorySerde(new AccurateCardinalityAggregatorFactory( + "name", + "fieldName", + new LongRoaringBitmapCollectorFactory() + )); + } + + private void assertAggregatorFactorySerde(AggregatorFactory agg) throws Exception + { + Assert.assertEquals( + agg, + helper.getObjectMapper().readValue( + helper.getObjectMapper().writeValueAsString(agg), + AggregatorFactory.class + ) + ); + } + + @Test + public void testCacheKey() + { + LongRoaringBitmapCollectorFactory collectorFactory = new LongRoaringBitmapCollectorFactory(); + final AccurateCardinalityAggregatorFactory factory1 = new AccurateCardinalityAggregatorFactory( + "name", + "fieldName", + collectorFactory + ); + final AccurateCardinalityAggregatorFactory factory2 = new AccurateCardinalityAggregatorFactory( + "name", + "fieldName", + collectorFactory + ); + final AccurateCardinalityAggregatorFactory factory3 = new AccurateCardinalityAggregatorFactory( + "name", + "fieldName1", + collectorFactory + ); + Assert.assertTrue(Arrays.equals(factory1.getCacheKey(), factory2.getCacheKey())); + Assert.assertFalse(Arrays.equals(factory1.getCacheKey(), factory3.getCacheKey())); + } + + +} diff --git a/extensions-contrib/accurate-cardinality/src/test/java/org/apache/druid/query/aggregation/cardinality/accurate/sql/AccurateCardinalityAggregatorSqlTest.java b/extensions-contrib/accurate-cardinality/src/test/java/org/apache/druid/query/aggregation/cardinality/accurate/sql/AccurateCardinalityAggregatorSqlTest.java new file mode 100644 index 000000000000..60e59bdc159f --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/test/java/org/apache/druid/query/aggregation/cardinality/accurate/sql/AccurateCardinalityAggregatorSqlTest.java @@ -0,0 +1,301 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.query.aggregation.cardinality.accurate.sql; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import org.apache.calcite.tools.ValidationException; +import org.apache.druid.data.input.InputRow; +import org.apache.druid.data.input.impl.DimensionSchema; +import org.apache.druid.data.input.impl.DimensionsSpec; +import org.apache.druid.data.input.impl.InputRowParser; +import org.apache.druid.data.input.impl.LongDimensionSchema; +import org.apache.druid.data.input.impl.MapInputRowParser; +import org.apache.druid.data.input.impl.TimeAndDimsParseSpec; +import org.apache.druid.data.input.impl.TimestampSpec; +import org.apache.druid.java.util.common.Pair; +import org.apache.druid.java.util.common.io.Closer; +import org.apache.druid.query.QueryRunnerFactoryConglomerate; +import org.apache.druid.query.aggregation.CountAggregatorFactory; +import org.apache.druid.query.aggregation.DoubleSumAggregatorFactory; +import org.apache.druid.query.aggregation.cardinality.accurate.AccurateCardinalityModule; +import org.apache.druid.segment.IndexBuilder; +import org.apache.druid.segment.QueryableIndex; +import org.apache.druid.segment.incremental.IncrementalIndexSchema; +import org.apache.druid.segment.writeout.OffHeapMemorySegmentWriteOutMediumFactory; +import org.apache.druid.server.security.AuthTestUtils; +import org.apache.druid.server.security.NoopEscalator; +import org.apache.druid.sql.calcite.planner.Calcites; +import org.apache.druid.sql.calcite.planner.DruidOperatorTable; +import org.apache.druid.sql.calcite.planner.DruidPlanner; +import org.apache.druid.sql.calcite.planner.PlannerConfig; +import org.apache.druid.sql.calcite.planner.PlannerFactory; +import org.apache.druid.sql.calcite.planner.PlannerResult; +import org.apache.druid.sql.calcite.schema.DruidSchema; +import org.apache.druid.sql.calcite.schema.SystemSchema; +import org.apache.druid.sql.calcite.util.CalciteTests; +import org.apache.druid.sql.calcite.util.QueryLogHook; +import org.apache.druid.sql.calcite.util.SpecificSegmentsQuerySegmentWalker; +import org.apache.druid.timeline.DataSegment; +import org.apache.druid.timeline.partition.LinearShardSpec; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + + +public class AccurateCardinalityAggregatorSqlTest +{ + private static final String DATA_SOURCE = "foo"; + private static final String TIMESTAMP_COLUMN = "t"; + private static final Map QUERY_CONTEXT_DEFAULT = ImmutableMap.of( + "remoterAddr", "127.0.0.1", "realIP", "127.0.0.1" + ); + + private static final List dimensions = new ArrayList(); + + private static QueryRunnerFactoryConglomerate conglomerate; + private static Closer resourceCloser; + + static { + dimensions.add(new LongDimensionSchema("clientid")); + dimensions.addAll(DimensionsSpec.getDefaultSchemas(ImmutableList.of("dim1", "dim2"))); + } + + private static final InputRowParser> PARSER = new MapInputRowParser( + new TimeAndDimsParseSpec( + new TimestampSpec(TIMESTAMP_COLUMN, "iso", null), + new DimensionsSpec( + dimensions, + null, + null + ) + ) + ); + + public static InputRow createRow(final ImmutableMap map) + { + return PARSER.parseBatch((Map) map).get(0); + } + + public static final List ROWS1 = ImmutableList.of( + createRow( + ImmutableMap.of("t", "2000-01-01", "m1", "1.0", "clientid", 1000L, "dim1", "", "dim2", ImmutableList.of("a")) + ), + createRow( + ImmutableMap.of( + "t", + "2000-01-02", + "m1", + "2.0", + "clientid", + 1001L, + "dim1", + "10.1", + "dim2", + ImmutableList.of() + ) + ), + createRow( + ImmutableMap.of("t", "2000-01-03", "m1", "3.0", "clientid", 1001L, "dim1", "2", "dim2", ImmutableList.of("")) + ), + createRow( + ImmutableMap.of( + "t", + "2001-01-01", + "m1", + "4.0", + "clientid", + 1002L, + "dim1", + "1", + "dim2", + ImmutableList.of("a") + ) + ), + createRow( + ImmutableMap.of( + "t", + "2001-01-02", + "m1", + "5.0", + "clientid", + 1003L, + "dim1", + "def", + "dim2", + ImmutableList.of("abc") + ) + ), + createRow( + ImmutableMap.of("t", "2001-01-03", "m1", "6.0", "clientid", 1003L, "dim1", "abc") + ) + ); + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Rule + public QueryLogHook queryLogHook = QueryLogHook.create(); + + private SpecificSegmentsQuerySegmentWalker walker; + private PlannerFactory plannerFactory; + + @BeforeClass + public static void setUpClass() + { + final Pair conglomerateCloserPair = CalciteTests + .createQueryRunnerFactoryConglomerate(); + conglomerate = conglomerateCloserPair.lhs; + resourceCloser = conglomerateCloserPair.rhs; + } + + @AfterClass + public static void tearDownClass() throws IOException + { + resourceCloser.close(); + } + + @Before + public void setUp() throws Exception + { + Calcites.setSystemProperties(); + + // Note: this is needed in order to properly register the serde for Histogram. + new AccurateCardinalityModule().configure(null); + + final QueryableIndex index = IndexBuilder.create() + .tmpDir(temporaryFolder.newFolder()) + .segmentWriteOutMediumFactory(OffHeapMemorySegmentWriteOutMediumFactory.instance()) + .schema( + new IncrementalIndexSchema.Builder() + .withDimensionsSpec( + new DimensionsSpec( + ImmutableList.of(new LongDimensionSchema("clientid")), + null, + null + ) + ) + .withMetrics( + new CountAggregatorFactory("cnt"), + new DoubleSumAggregatorFactory("m1", "m1") + ) + .withRollup(true) + .build() + ) + .rows(ROWS1) + .buildMMappedIndex(); + + walker = new SpecificSegmentsQuerySegmentWalker(conglomerate).add( + DataSegment.builder() + .dataSource(DATA_SOURCE) + .interval(index.getDataInterval()) + .version("1") + .shardSpec(new LinearShardSpec(0)) + .build(), + index + ); + + final PlannerConfig plannerConfig = new PlannerConfig(); + final DruidSchema druidSchema = CalciteTests.createMockSchema( + conglomerate, + walker, + plannerConfig + ); + final SystemSchema systemSchema = CalciteTests.createMockSystemSchema(druidSchema, walker, plannerConfig); + final DruidOperatorTable operatorTable = new DruidOperatorTable( + ImmutableSet.of(new AccurateCardinalitySqlAggregator()), + ImmutableSet.of() + ); + + plannerFactory = new PlannerFactory( + druidSchema, + systemSchema, + CalciteTests.createMockQueryLifecycleFactory(walker, conglomerate), + operatorTable, + CalciteTests.createExprMacroTable(), + plannerConfig, + AuthTestUtils.TEST_AUTHORIZER_MAPPER, + CalciteTests.getJsonMapper() + ); + } + + @After + public void tearDown() throws Exception + { + walker.close(); + walker = null; + } + + @Test + public void testAccurateCardinalityAggregatorOnLongs() throws Exception + { + final DruidPlanner planner = plannerFactory.createPlanner( + QUERY_CONTEXT_DEFAULT, + NoopEscalator.getInstance() + .createEscalatedAuthenticationResult() + ); + final String sql = "SELECT\n" + + "ACCURATE_CARDINALITY(clientid),\n" + + "ACCURATE_CARDINALITY(clientid) FILTER(WHERE dim1 = 'abc')\n" + + "FROM foo"; + + final PlannerResult plannerResult = planner.plan(sql); + final List results = plannerResult.run().toList(); + final List expectedResults = ImmutableList.of( + new Object[]{ + 4L, + 1L + } + ); + + Assert.assertEquals(expectedResults.size(), results.size()); + + for (int i = 0; i < expectedResults.size(); i++) { + Assert.assertArrayEquals(expectedResults.get(i), results.get(i)); + } + } + + @Test(expected = ValidationException.class) + public void testAccurateCardinalityAggregatorOnString() throws Exception + { + final DruidPlanner planner = plannerFactory.createPlanner( + QUERY_CONTEXT_DEFAULT, + NoopEscalator.getInstance() + .createEscalatedAuthenticationResult() + ); + final String sql = "SELECT\n" + + "ACCURATE_CARDINALITY(dim1)\n" + + "FROM foo"; + + final PlannerResult plannerResult = planner.plan(sql); + plannerResult.run().toList(); + } +} diff --git a/extensions-contrib/accurate-cardinality/src/test/resources/bitmap_test_data_aggregators.json b/extensions-contrib/accurate-cardinality/src/test/resources/bitmap_test_data_aggregators.json new file mode 100644 index 000000000000..ec73c5a4729d --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/test/resources/bitmap_test_data_aggregators.json @@ -0,0 +1,11 @@ +[ + { + "type": "count", + "name": "count" + }, + { + "type": "bitmapAgg", + "name": "duration", + "fieldName": "duration" + } +] diff --git a/extensions-contrib/accurate-cardinality/src/test/resources/bitmap_test_data_group_by_query.json b/extensions-contrib/accurate-cardinality/src/test/resources/bitmap_test_data_group_by_query.json new file mode 100644 index 000000000000..14ef2d994c1d --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/test/resources/bitmap_test_data_group_by_query.json @@ -0,0 +1,18 @@ +{ + "queryType": "groupBy", + "dataSource": "test_datasource", + "granularity": "ALL", + "dimensions": [ + "product" + ], + "aggregations": [ + { + "type": "bitmapAgg", + "fieldName": "duration", + "name": "uv" + } + ], + "intervals": [ + "2014-10-19T00:00:00.000Z/2014-11-03T00:00:00.000Z" + ] +} diff --git a/extensions-contrib/accurate-cardinality/src/test/resources/bitmap_test_data_record_parser.json b/extensions-contrib/accurate-cardinality/src/test/resources/bitmap_test_data_record_parser.json new file mode 100644 index 000000000000..29eb2ebba65b --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/test/resources/bitmap_test_data_record_parser.json @@ -0,0 +1,22 @@ +{ + "type": "string", + "parseSpec": { + "format": "tsv", + "timestampSpec": { + "column": "timestamp", + "format": "yyyyMMddHH" + }, + "dimensionsSpec": { + "dimensions": [ + "product" + ], + "dimensionExclusions": [], + "spatialDimensions": [] + }, + "columns": [ + "timestamp", + "duration", + "product" + ] + } +} diff --git a/extensions-contrib/accurate-cardinality/src/test/resources/simple_test_data.tsv b/extensions-contrib/accurate-cardinality/src/test/resources/simple_test_data.tsv new file mode 100644 index 000000000000..9f4ee0d1084b --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/test/resources/simple_test_data.tsv @@ -0,0 +1,3360 @@ +2014102000 14102000 product_1 pty_country_34 +2014102000 14102000 product_4 pty_country_8 +2014102000 14102000 product_5 pty_country_39 +2014102000 14102000 product_2 pty_country_6 +2014102000 14102000 product_5 pty_country_28 +2014102000 14102000 product_4 pty_country_44 +2014102000 14102000 product_1 pty_country_16 +2014102000 14102000 product_3 pty_country_12 +2014102000 14102000 product_5 pty_country_31 +2014102000 14102000 product_2 pty_country_9 +2014102001 14102001 product_5 pty_country_29 +2014102001 14102001 product_1 pty_country_23 +2014102001 14102001 product_4 pty_country_5 +2014102001 14102001 product_5 pty_country_17 +2014102001 14102001 product_3 pty_country_8 +2014102001 14102001 product_1 pty_country_13 +2014102001 14102001 product_3 pty_country_12 +2014102001 14102001 product_1 pty_country_34 +2014102001 14102001 product_4 pty_country_6 +2014102001 14102001 product_3 pty_country_34 +2014102002 14102002 product_2 pty_country_44 +2014102002 14102002 product_2 pty_country_16 +2014102002 14102002 product_5 pty_country_1 +2014102002 14102002 product_5 pty_country_8 +2014102002 14102002 product_4 pty_country_43 +2014102002 14102002 product_1 pty_country_17 +2014102002 14102002 product_3 pty_country_43 +2014102002 14102002 product_5 pty_country_8 +2014102002 14102002 product_4 pty_country_37 +2014102002 14102002 product_3 pty_country_4 +2014102003 14102003 product_5 pty_country_42 +2014102003 14102003 product_4 pty_country_34 +2014102003 14102003 product_2 pty_country_14 +2014102003 14102003 product_4 pty_country_18 +2014102003 14102003 product_5 pty_country_39 +2014102003 14102003 product_4 pty_country_8 +2014102003 14102003 product_3 pty_country_17 +2014102003 14102003 product_1 pty_country_12 +2014102003 14102003 product_4 pty_country_49 +2014102003 14102003 product_3 pty_country_5 +2014102004 14102004 product_3 pty_country_35 +2014102004 14102004 product_4 pty_country_4 +2014102004 14102004 product_2 pty_country_5 +2014102004 14102004 product_5 pty_country_16 +2014102004 14102004 product_5 pty_country_4 +2014102004 14102004 product_4 pty_country_43 +2014102004 14102004 product_2 pty_country_20 +2014102004 14102004 product_5 pty_country_3 +2014102004 14102004 product_3 pty_country_48 +2014102004 14102004 product_2 pty_country_29 +2014102005 14102005 product_2 pty_country_19 +2014102005 14102005 product_5 pty_country_6 +2014102005 14102005 product_3 pty_country_1 +2014102005 14102005 product_4 pty_country_39 +2014102005 14102005 product_5 pty_country_42 +2014102005 14102005 product_4 pty_country_14 +2014102005 14102005 product_3 pty_country_41 +2014102005 14102005 product_4 pty_country_34 +2014102005 14102005 product_4 pty_country_42 +2014102005 14102005 product_2 pty_country_34 +2014102006 14102006 product_2 pty_country_6 +2014102006 14102006 product_1 pty_country_36 +2014102006 14102006 product_4 pty_country_27 +2014102006 14102006 product_3 pty_country_4 +2014102006 14102006 product_5 pty_country_14 +2014102006 14102006 product_2 pty_country_48 +2014102006 14102006 product_1 pty_country_21 +2014102006 14102006 product_3 pty_country_26 +2014102006 14102006 product_3 pty_country_1 +2014102006 14102006 product_4 pty_country_31 +2014102007 14102007 product_4 pty_country_5 +2014102007 14102007 product_1 pty_country_18 +2014102007 14102007 product_3 pty_country_48 +2014102007 14102007 product_2 pty_country_1 +2014102007 14102007 product_1 pty_country_12 +2014102007 14102007 product_1 pty_country_37 +2014102007 14102007 product_4 pty_country_43 +2014102007 14102007 product_3 pty_country_35 +2014102007 14102007 product_2 pty_country_18 +2014102007 14102007 product_2 pty_country_19 +2014102008 14102008 product_4 pty_country_30 +2014102008 14102008 product_3 pty_country_22 +2014102008 14102008 product_1 pty_country_46 +2014102008 14102008 product_5 pty_country_25 +2014102008 14102008 product_5 pty_country_32 +2014102008 14102008 product_1 pty_country_8 +2014102008 14102008 product_4 pty_country_7 +2014102008 14102008 product_1 pty_country_28 +2014102008 14102008 product_2 pty_country_26 +2014102008 14102008 product_4 pty_country_21 +2014102009 14102009 product_5 pty_country_23 +2014102009 14102009 product_2 pty_country_3 +2014102009 14102009 product_1 pty_country_17 +2014102009 14102009 product_2 pty_country_27 +2014102009 14102009 product_3 pty_country_11 +2014102009 14102009 product_1 pty_country_30 +2014102009 14102009 product_2 pty_country_25 +2014102009 14102009 product_4 pty_country_43 +2014102009 14102009 product_3 pty_country_44 +2014102009 14102009 product_2 pty_country_17 +2014102010 14102010 product_1 pty_country_16 +2014102010 14102010 product_4 pty_country_41 +2014102010 14102010 product_2 pty_country_44 +2014102010 14102010 product_1 pty_country_9 +2014102010 14102010 product_2 pty_country_12 +2014102010 14102010 product_4 pty_country_20 +2014102010 14102010 product_4 pty_country_16 +2014102010 14102010 product_5 pty_country_28 +2014102010 14102010 product_2 pty_country_46 +2014102010 14102010 product_4 pty_country_15 +2014102011 14102011 product_5 pty_country_12 +2014102011 14102011 product_1 pty_country_35 +2014102011 14102011 product_1 pty_country_43 +2014102011 14102011 product_1 pty_country_15 +2014102011 14102011 product_4 pty_country_2 +2014102011 14102011 product_4 pty_country_11 +2014102011 14102011 product_1 pty_country_50 +2014102011 14102011 product_5 pty_country_18 +2014102011 14102011 product_5 pty_country_22 +2014102011 14102011 product_3 pty_country_20 +2014102012 14102012 product_2 pty_country_14 +2014102012 14102012 product_3 pty_country_37 +2014102012 14102012 product_1 pty_country_37 +2014102012 14102012 product_3 pty_country_17 +2014102012 14102012 product_4 pty_country_31 +2014102012 14102012 product_3 pty_country_22 +2014102012 14102012 product_3 pty_country_29 +2014102012 14102012 product_3 pty_country_8 +2014102012 14102012 product_5 pty_country_17 +2014102012 14102012 product_2 pty_country_20 +2014102013 14102013 product_2 pty_country_33 +2014102013 14102013 product_3 pty_country_46 +2014102013 14102013 product_5 pty_country_44 +2014102013 14102013 product_1 pty_country_25 +2014102013 14102013 product_1 pty_country_8 +2014102013 14102013 product_2 pty_country_23 +2014102013 14102013 product_2 pty_country_45 +2014102013 14102013 product_4 pty_country_8 +2014102013 14102013 product_3 pty_country_5 +2014102013 14102013 product_2 pty_country_45 +2014102014 14102014 product_3 pty_country_36 +2014102014 14102014 product_5 pty_country_14 +2014102014 14102014 product_5 pty_country_38 +2014102014 14102014 product_4 pty_country_20 +2014102014 14102014 product_3 pty_country_28 +2014102014 14102014 product_5 pty_country_25 +2014102014 14102014 product_2 pty_country_30 +2014102014 14102014 product_4 pty_country_49 +2014102014 14102014 product_3 pty_country_10 +2014102014 14102014 product_4 pty_country_50 +2014102015 14102015 product_3 pty_country_29 +2014102015 14102015 product_3 pty_country_10 +2014102015 14102015 product_1 pty_country_34 +2014102015 14102015 product_1 pty_country_47 +2014102015 14102015 product_4 pty_country_16 +2014102015 14102015 product_1 pty_country_12 +2014102015 14102015 product_5 pty_country_19 +2014102015 14102015 product_5 pty_country_31 +2014102015 14102015 product_1 pty_country_25 +2014102015 14102015 product_1 pty_country_3 +2014102016 14102016 product_3 pty_country_6 +2014102016 14102016 product_2 pty_country_32 +2014102016 14102016 product_3 pty_country_12 +2014102016 14102016 product_4 pty_country_41 +2014102016 14102016 product_3 pty_country_29 +2014102016 14102016 product_1 pty_country_19 +2014102016 14102016 product_4 pty_country_30 +2014102016 14102016 product_4 pty_country_41 +2014102016 14102016 product_4 pty_country_47 +2014102016 14102016 product_1 pty_country_24 +2014102017 14102017 product_2 pty_country_47 +2014102017 14102017 product_3 pty_country_32 +2014102017 14102017 product_3 pty_country_44 +2014102017 14102017 product_2 pty_country_38 +2014102017 14102017 product_4 pty_country_2 +2014102017 14102017 product_5 pty_country_9 +2014102017 14102017 product_3 pty_country_19 +2014102017 14102017 product_5 pty_country_10 +2014102017 14102017 product_3 pty_country_14 +2014102017 14102017 product_2 pty_country_34 +2014102018 14102018 product_2 pty_country_26 +2014102018 14102018 product_5 pty_country_40 +2014102018 14102018 product_5 pty_country_31 +2014102018 14102018 product_5 pty_country_34 +2014102018 14102018 product_4 pty_country_8 +2014102018 14102018 product_1 pty_country_5 +2014102018 14102018 product_1 pty_country_9 +2014102018 14102018 product_2 pty_country_26 +2014102018 14102018 product_1 pty_country_31 +2014102018 14102018 product_1 pty_country_17 +2014102019 14102019 product_3 pty_country_43 +2014102019 14102019 product_5 pty_country_25 +2014102019 14102019 product_5 pty_country_16 +2014102019 14102019 product_3 pty_country_32 +2014102019 14102019 product_4 pty_country_7 +2014102019 14102019 product_2 pty_country_2 +2014102019 14102019 product_1 pty_country_35 +2014102019 14102019 product_5 pty_country_40 +2014102019 14102019 product_1 pty_country_39 +2014102019 14102019 product_4 pty_country_14 +2014102020 14102020 product_2 pty_country_37 +2014102020 14102020 product_1 pty_country_13 +2014102020 14102020 product_2 pty_country_40 +2014102020 14102020 product_2 pty_country_1 +2014102020 14102020 product_5 pty_country_9 +2014102020 14102020 product_4 pty_country_39 +2014102020 14102020 product_2 pty_country_34 +2014102020 14102020 product_5 pty_country_28 +2014102020 14102020 product_4 pty_country_3 +2014102020 14102020 product_5 pty_country_27 +2014102021 14102021 product_3 pty_country_1 +2014102021 14102021 product_3 pty_country_25 +2014102021 14102021 product_3 pty_country_26 +2014102021 14102021 product_1 pty_country_25 +2014102021 14102021 product_5 pty_country_13 +2014102021 14102021 product_5 pty_country_50 +2014102021 14102021 product_5 pty_country_27 +2014102021 14102021 product_1 pty_country_34 +2014102021 14102021 product_3 pty_country_41 +2014102021 14102021 product_4 pty_country_42 +2014102022 14102022 product_2 pty_country_37 +2014102022 14102022 product_3 pty_country_50 +2014102022 14102022 product_2 pty_country_22 +2014102022 14102022 product_3 pty_country_17 +2014102022 14102022 product_3 pty_country_33 +2014102022 14102022 product_5 pty_country_38 +2014102022 14102022 product_3 pty_country_4 +2014102022 14102022 product_4 pty_country_39 +2014102022 14102022 product_2 pty_country_33 +2014102022 14102022 product_5 pty_country_18 +2014102023 14102023 product_1 pty_country_43 +2014102023 14102023 product_1 pty_country_20 +2014102023 14102023 product_5 pty_country_13 +2014102023 14102023 product_2 pty_country_20 +2014102023 14102023 product_3 pty_country_32 +2014102023 14102023 product_1 pty_country_19 +2014102023 14102023 product_4 pty_country_22 +2014102023 14102023 product_5 pty_country_38 +2014102023 14102023 product_4 pty_country_20 +2014102023 14102023 product_3 pty_country_15 +2014102100 14102100 product_5 pty_country_5 +2014102100 14102100 product_4 pty_country_44 +2014102100 14102100 product_4 pty_country_45 +2014102100 14102100 product_4 pty_country_5 +2014102100 14102100 product_2 pty_country_2 +2014102100 14102100 product_5 pty_country_49 +2014102100 14102100 product_4 pty_country_24 +2014102100 14102100 product_3 pty_country_44 +2014102100 14102100 product_1 pty_country_2 +2014102100 14102100 product_3 pty_country_31 +2014102101 14102101 product_4 pty_country_50 +2014102101 14102101 product_3 pty_country_5 +2014102101 14102101 product_3 pty_country_34 +2014102101 14102101 product_4 pty_country_24 +2014102101 14102101 product_4 pty_country_34 +2014102101 14102101 product_4 pty_country_17 +2014102101 14102101 product_2 pty_country_8 +2014102101 14102101 product_4 pty_country_39 +2014102101 14102101 product_3 pty_country_9 +2014102101 14102101 product_4 pty_country_28 +2014102102 14102102 product_5 pty_country_16 +2014102102 14102102 product_2 pty_country_37 +2014102102 14102102 product_2 pty_country_1 +2014102102 14102102 product_3 pty_country_49 +2014102102 14102102 product_3 pty_country_47 +2014102102 14102102 product_1 pty_country_29 +2014102102 14102102 product_4 pty_country_24 +2014102102 14102102 product_1 pty_country_27 +2014102102 14102102 product_2 pty_country_47 +2014102102 14102102 product_2 pty_country_43 +2014102103 14102103 product_5 pty_country_46 +2014102103 14102103 product_5 pty_country_6 +2014102103 14102103 product_3 pty_country_8 +2014102103 14102103 product_4 pty_country_1 +2014102103 14102103 product_5 pty_country_17 +2014102103 14102103 product_4 pty_country_31 +2014102103 14102103 product_5 pty_country_34 +2014102103 14102103 product_4 pty_country_20 +2014102103 14102103 product_1 pty_country_36 +2014102103 14102103 product_3 pty_country_5 +2014102104 14102104 product_2 pty_country_15 +2014102104 14102104 product_2 pty_country_20 +2014102104 14102104 product_5 pty_country_43 +2014102104 14102104 product_2 pty_country_23 +2014102104 14102104 product_1 pty_country_30 +2014102104 14102104 product_1 pty_country_50 +2014102104 14102104 product_1 pty_country_3 +2014102104 14102104 product_5 pty_country_4 +2014102104 14102104 product_3 pty_country_1 +2014102104 14102104 product_4 pty_country_45 +2014102105 14102105 product_1 pty_country_42 +2014102105 14102105 product_2 pty_country_49 +2014102105 14102105 product_3 pty_country_43 +2014102105 14102105 product_1 pty_country_12 +2014102105 14102105 product_4 pty_country_37 +2014102105 14102105 product_3 pty_country_6 +2014102105 14102105 product_2 pty_country_6 +2014102105 14102105 product_3 pty_country_36 +2014102105 14102105 product_3 pty_country_17 +2014102105 14102105 product_5 pty_country_44 +2014102106 14102106 product_5 pty_country_1 +2014102106 14102106 product_1 pty_country_11 +2014102106 14102106 product_4 pty_country_32 +2014102106 14102106 product_5 pty_country_43 +2014102106 14102106 product_1 pty_country_45 +2014102106 14102106 product_2 pty_country_34 +2014102106 14102106 product_3 pty_country_1 +2014102106 14102106 product_5 pty_country_37 +2014102106 14102106 product_4 pty_country_44 +2014102106 14102106 product_4 pty_country_18 +2014102107 14102107 product_2 pty_country_29 +2014102107 14102107 product_2 pty_country_20 +2014102107 14102107 product_2 pty_country_22 +2014102107 14102107 product_4 pty_country_46 +2014102107 14102107 product_4 pty_country_2 +2014102107 14102107 product_5 pty_country_16 +2014102107 14102107 product_5 pty_country_4 +2014102107 14102107 product_2 pty_country_45 +2014102107 14102107 product_4 pty_country_11 +2014102107 14102107 product_4 pty_country_27 +2014102108 14102108 product_2 pty_country_18 +2014102108 14102108 product_3 pty_country_23 +2014102108 14102108 product_5 pty_country_5 +2014102108 14102108 product_2 pty_country_5 +2014102108 14102108 product_1 pty_country_43 +2014102108 14102108 product_5 pty_country_19 +2014102108 14102108 product_2 pty_country_8 +2014102108 14102108 product_1 pty_country_3 +2014102108 14102108 product_3 pty_country_34 +2014102108 14102108 product_3 pty_country_23 +2014102109 14102109 product_4 pty_country_8 +2014102109 14102109 product_4 pty_country_43 +2014102109 14102109 product_1 pty_country_10 +2014102109 14102109 product_2 pty_country_33 +2014102109 14102109 product_1 pty_country_41 +2014102109 14102109 product_1 pty_country_39 +2014102109 14102109 product_3 pty_country_31 +2014102109 14102109 product_5 pty_country_23 +2014102109 14102109 product_5 pty_country_38 +2014102109 14102109 product_2 pty_country_32 +2014102110 14102110 product_2 pty_country_44 +2014102110 14102110 product_1 pty_country_2 +2014102110 14102110 product_3 pty_country_14 +2014102110 14102110 product_4 pty_country_3 +2014102110 14102110 product_5 pty_country_5 +2014102110 14102110 product_5 pty_country_28 +2014102110 14102110 product_4 pty_country_44 +2014102110 14102110 product_3 pty_country_26 +2014102110 14102110 product_5 pty_country_40 +2014102110 14102110 product_5 pty_country_26 +2014102111 14102111 product_5 pty_country_48 +2014102111 14102111 product_3 pty_country_23 +2014102111 14102111 product_3 pty_country_28 +2014102111 14102111 product_4 pty_country_40 +2014102111 14102111 product_5 pty_country_43 +2014102111 14102111 product_4 pty_country_12 +2014102111 14102111 product_2 pty_country_16 +2014102111 14102111 product_4 pty_country_11 +2014102111 14102111 product_2 pty_country_35 +2014102111 14102111 product_2 pty_country_43 +2014102112 14102112 product_2 pty_country_22 +2014102112 14102112 product_4 pty_country_10 +2014102112 14102112 product_1 pty_country_41 +2014102112 14102112 product_1 pty_country_28 +2014102112 14102112 product_1 pty_country_2 +2014102112 14102112 product_2 pty_country_49 +2014102112 14102112 product_5 pty_country_8 +2014102112 14102112 product_5 pty_country_18 +2014102112 14102112 product_2 pty_country_16 +2014102112 14102112 product_4 pty_country_29 +2014102113 14102113 product_2 pty_country_35 +2014102113 14102113 product_4 pty_country_45 +2014102113 14102113 product_3 pty_country_2 +2014102113 14102113 product_4 pty_country_49 +2014102113 14102113 product_1 pty_country_50 +2014102113 14102113 product_2 pty_country_49 +2014102113 14102113 product_2 pty_country_17 +2014102113 14102113 product_1 pty_country_17 +2014102113 14102113 product_4 pty_country_8 +2014102113 14102113 product_3 pty_country_44 +2014102114 14102114 product_1 pty_country_33 +2014102114 14102114 product_5 pty_country_7 +2014102114 14102114 product_4 pty_country_40 +2014102114 14102114 product_2 pty_country_13 +2014102114 14102114 product_2 pty_country_21 +2014102114 14102114 product_4 pty_country_12 +2014102114 14102114 product_4 pty_country_7 +2014102114 14102114 product_5 pty_country_47 +2014102114 14102114 product_3 pty_country_25 +2014102114 14102114 product_4 pty_country_34 +2014102115 14102115 product_1 pty_country_8 +2014102115 14102115 product_3 pty_country_29 +2014102115 14102115 product_1 pty_country_22 +2014102115 14102115 product_1 pty_country_44 +2014102115 14102115 product_2 pty_country_26 +2014102115 14102115 product_4 pty_country_1 +2014102115 14102115 product_5 pty_country_32 +2014102115 14102115 product_4 pty_country_39 +2014102115 14102115 product_2 pty_country_38 +2014102115 14102115 product_5 pty_country_28 +2014102116 14102116 product_2 pty_country_50 +2014102116 14102116 product_2 pty_country_47 +2014102116 14102116 product_3 pty_country_34 +2014102116 14102116 product_5 pty_country_47 +2014102116 14102116 product_2 pty_country_8 +2014102116 14102116 product_3 pty_country_4 +2014102116 14102116 product_5 pty_country_28 +2014102116 14102116 product_4 pty_country_29 +2014102116 14102116 product_3 pty_country_20 +2014102116 14102116 product_1 pty_country_12 +2014102117 14102117 product_3 pty_country_25 +2014102117 14102117 product_4 pty_country_44 +2014102117 14102117 product_1 pty_country_13 +2014102117 14102117 product_2 pty_country_43 +2014102117 14102117 product_3 pty_country_13 +2014102117 14102117 product_2 pty_country_16 +2014102117 14102117 product_5 pty_country_14 +2014102117 14102117 product_1 pty_country_38 +2014102117 14102117 product_1 pty_country_21 +2014102117 14102117 product_1 pty_country_1 +2014102118 14102118 product_2 pty_country_49 +2014102118 14102118 product_2 pty_country_21 +2014102118 14102118 product_1 pty_country_21 +2014102118 14102118 product_5 pty_country_41 +2014102118 14102118 product_4 pty_country_31 +2014102118 14102118 product_5 pty_country_49 +2014102118 14102118 product_3 pty_country_30 +2014102118 14102118 product_2 pty_country_4 +2014102118 14102118 product_1 pty_country_33 +2014102118 14102118 product_4 pty_country_5 +2014102119 14102119 product_4 pty_country_48 +2014102119 14102119 product_3 pty_country_22 +2014102119 14102119 product_3 pty_country_35 +2014102119 14102119 product_2 pty_country_8 +2014102119 14102119 product_3 pty_country_12 +2014102119 14102119 product_1 pty_country_34 +2014102119 14102119 product_4 pty_country_27 +2014102119 14102119 product_1 pty_country_24 +2014102119 14102119 product_3 pty_country_14 +2014102119 14102119 product_2 pty_country_38 +2014102120 14102120 product_5 pty_country_10 +2014102120 14102120 product_4 pty_country_37 +2014102120 14102120 product_4 pty_country_50 +2014102120 14102120 product_3 pty_country_11 +2014102120 14102120 product_1 pty_country_40 +2014102120 14102120 product_5 pty_country_43 +2014102120 14102120 product_5 pty_country_26 +2014102120 14102120 product_3 pty_country_26 +2014102120 14102120 product_5 pty_country_33 +2014102120 14102120 product_2 pty_country_32 +2014102121 14102121 product_2 pty_country_16 +2014102121 14102121 product_4 pty_country_35 +2014102121 14102121 product_3 pty_country_12 +2014102121 14102121 product_4 pty_country_3 +2014102121 14102121 product_4 pty_country_40 +2014102121 14102121 product_2 pty_country_30 +2014102121 14102121 product_5 pty_country_11 +2014102121 14102121 product_2 pty_country_41 +2014102121 14102121 product_2 pty_country_37 +2014102121 14102121 product_4 pty_country_33 +2014102122 14102122 product_3 pty_country_34 +2014102122 14102122 product_1 pty_country_10 +2014102122 14102122 product_4 pty_country_10 +2014102122 14102122 product_4 pty_country_10 +2014102122 14102122 product_2 pty_country_11 +2014102122 14102122 product_3 pty_country_25 +2014102122 14102122 product_2 pty_country_25 +2014102122 14102122 product_5 pty_country_16 +2014102122 14102122 product_1 pty_country_33 +2014102122 14102122 product_4 pty_country_27 +2014102123 14102123 product_5 pty_country_16 +2014102123 14102123 product_3 pty_country_48 +2014102123 14102123 product_5 pty_country_34 +2014102123 14102123 product_2 pty_country_16 +2014102123 14102123 product_2 pty_country_41 +2014102123 14102123 product_5 pty_country_26 +2014102123 14102123 product_2 pty_country_43 +2014102123 14102123 product_5 pty_country_35 +2014102123 14102123 product_2 pty_country_11 +2014102123 14102123 product_5 pty_country_38 +2014102200 14102200 product_2 pty_country_1 +2014102200 14102200 product_2 pty_country_24 +2014102200 14102200 product_2 pty_country_27 +2014102200 14102200 product_1 pty_country_16 +2014102200 14102200 product_2 pty_country_42 +2014102200 14102200 product_3 pty_country_18 +2014102200 14102200 product_3 pty_country_32 +2014102200 14102200 product_1 pty_country_25 +2014102200 14102200 product_2 pty_country_49 +2014102200 14102200 product_3 pty_country_14 +2014102201 14102201 product_5 pty_country_16 +2014102201 14102201 product_3 pty_country_2 +2014102201 14102201 product_4 pty_country_27 +2014102201 14102201 product_3 pty_country_6 +2014102201 14102201 product_4 pty_country_5 +2014102201 14102201 product_3 pty_country_34 +2014102201 14102201 product_2 pty_country_50 +2014102201 14102201 product_4 pty_country_46 +2014102201 14102201 product_4 pty_country_23 +2014102201 14102201 product_2 pty_country_23 +2014102202 14102202 product_3 pty_country_40 +2014102202 14102202 product_3 pty_country_13 +2014102202 14102202 product_2 pty_country_9 +2014102202 14102202 product_2 pty_country_15 +2014102202 14102202 product_2 pty_country_42 +2014102202 14102202 product_1 pty_country_17 +2014102202 14102202 product_4 pty_country_24 +2014102202 14102202 product_5 pty_country_50 +2014102202 14102202 product_4 pty_country_1 +2014102202 14102202 product_3 pty_country_27 +2014102203 14102203 product_5 pty_country_11 +2014102203 14102203 product_3 pty_country_19 +2014102203 14102203 product_5 pty_country_8 +2014102203 14102203 product_4 pty_country_18 +2014102203 14102203 product_3 pty_country_11 +2014102203 14102203 product_2 pty_country_29 +2014102203 14102203 product_2 pty_country_48 +2014102203 14102203 product_3 pty_country_23 +2014102203 14102203 product_4 pty_country_6 +2014102203 14102203 product_1 pty_country_9 +2014102204 14102204 product_1 pty_country_2 +2014102204 14102204 product_2 pty_country_8 +2014102204 14102204 product_3 pty_country_33 +2014102204 14102204 product_2 pty_country_25 +2014102204 14102204 product_1 pty_country_16 +2014102204 14102204 product_3 pty_country_4 +2014102204 14102204 product_1 pty_country_24 +2014102204 14102204 product_3 pty_country_46 +2014102204 14102204 product_2 pty_country_30 +2014102204 14102204 product_5 pty_country_2 +2014102205 14102205 product_1 pty_country_49 +2014102205 14102205 product_1 pty_country_41 +2014102205 14102205 product_4 pty_country_9 +2014102205 14102205 product_4 pty_country_5 +2014102205 14102205 product_5 pty_country_31 +2014102205 14102205 product_1 pty_country_20 +2014102205 14102205 product_4 pty_country_11 +2014102205 14102205 product_3 pty_country_41 +2014102205 14102205 product_2 pty_country_15 +2014102205 14102205 product_1 pty_country_49 +2014102206 14102206 product_5 pty_country_31 +2014102206 14102206 product_5 pty_country_13 +2014102206 14102206 product_4 pty_country_28 +2014102206 14102206 product_1 pty_country_48 +2014102206 14102206 product_2 pty_country_31 +2014102206 14102206 product_1 pty_country_40 +2014102206 14102206 product_1 pty_country_12 +2014102206 14102206 product_3 pty_country_16 +2014102206 14102206 product_4 pty_country_22 +2014102206 14102206 product_2 pty_country_44 +2014102207 14102207 product_4 pty_country_1 +2014102207 14102207 product_1 pty_country_36 +2014102207 14102207 product_1 pty_country_41 +2014102207 14102207 product_4 pty_country_16 +2014102207 14102207 product_1 pty_country_37 +2014102207 14102207 product_4 pty_country_34 +2014102207 14102207 product_3 pty_country_42 +2014102207 14102207 product_3 pty_country_34 +2014102207 14102207 product_2 pty_country_14 +2014102207 14102207 product_2 pty_country_18 +2014102208 14102208 product_5 pty_country_10 +2014102208 14102208 product_4 pty_country_20 +2014102208 14102208 product_4 pty_country_32 +2014102208 14102208 product_5 pty_country_24 +2014102208 14102208 product_2 pty_country_48 +2014102208 14102208 product_5 pty_country_36 +2014102208 14102208 product_3 pty_country_33 +2014102208 14102208 product_2 pty_country_40 +2014102208 14102208 product_1 pty_country_40 +2014102208 14102208 product_5 pty_country_7 +2014102209 14102209 product_3 pty_country_8 +2014102209 14102209 product_2 pty_country_10 +2014102209 14102209 product_5 pty_country_29 +2014102209 14102209 product_2 pty_country_18 +2014102209 14102209 product_4 pty_country_31 +2014102209 14102209 product_5 pty_country_6 +2014102209 14102209 product_3 pty_country_25 +2014102209 14102209 product_4 pty_country_2 +2014102209 14102209 product_5 pty_country_17 +2014102209 14102209 product_4 pty_country_16 +2014102210 14102210 product_4 pty_country_8 +2014102210 14102210 product_4 pty_country_46 +2014102210 14102210 product_5 pty_country_46 +2014102210 14102210 product_5 pty_country_38 +2014102210 14102210 product_3 pty_country_34 +2014102210 14102210 product_3 pty_country_29 +2014102210 14102210 product_5 pty_country_28 +2014102210 14102210 product_5 pty_country_45 +2014102210 14102210 product_2 pty_country_5 +2014102210 14102210 product_1 pty_country_48 +2014102211 14102211 product_3 pty_country_45 +2014102211 14102211 product_3 pty_country_26 +2014102211 14102211 product_1 pty_country_15 +2014102211 14102211 product_4 pty_country_50 +2014102211 14102211 product_1 pty_country_38 +2014102211 14102211 product_5 pty_country_29 +2014102211 14102211 product_3 pty_country_39 +2014102211 14102211 product_2 pty_country_25 +2014102211 14102211 product_1 pty_country_25 +2014102211 14102211 product_5 pty_country_16 +2014102212 14102212 product_3 pty_country_13 +2014102212 14102212 product_2 pty_country_48 +2014102212 14102212 product_2 pty_country_23 +2014102212 14102212 product_4 pty_country_41 +2014102212 14102212 product_5 pty_country_27 +2014102212 14102212 product_5 pty_country_38 +2014102212 14102212 product_4 pty_country_13 +2014102212 14102212 product_4 pty_country_40 +2014102212 14102212 product_1 pty_country_50 +2014102212 14102212 product_3 pty_country_8 +2014102213 14102213 product_2 pty_country_30 +2014102213 14102213 product_2 pty_country_6 +2014102213 14102213 product_5 pty_country_44 +2014102213 14102213 product_3 pty_country_35 +2014102213 14102213 product_3 pty_country_15 +2014102213 14102213 product_1 pty_country_26 +2014102213 14102213 product_5 pty_country_33 +2014102213 14102213 product_3 pty_country_2 +2014102213 14102213 product_4 pty_country_17 +2014102213 14102213 product_5 pty_country_15 +2014102214 14102214 product_5 pty_country_41 +2014102214 14102214 product_5 pty_country_12 +2014102214 14102214 product_5 pty_country_27 +2014102214 14102214 product_3 pty_country_30 +2014102214 14102214 product_5 pty_country_8 +2014102214 14102214 product_5 pty_country_43 +2014102214 14102214 product_4 pty_country_50 +2014102214 14102214 product_3 pty_country_50 +2014102214 14102214 product_4 pty_country_11 +2014102214 14102214 product_2 pty_country_44 +2014102215 14102215 product_1 pty_country_40 +2014102215 14102215 product_5 pty_country_2 +2014102215 14102215 product_3 pty_country_46 +2014102215 14102215 product_2 pty_country_48 +2014102215 14102215 product_1 pty_country_34 +2014102215 14102215 product_4 pty_country_41 +2014102215 14102215 product_5 pty_country_21 +2014102215 14102215 product_2 pty_country_13 +2014102215 14102215 product_3 pty_country_19 +2014102215 14102215 product_2 pty_country_3 +2014102216 14102216 product_4 pty_country_42 +2014102216 14102216 product_1 pty_country_11 +2014102216 14102216 product_4 pty_country_35 +2014102216 14102216 product_2 pty_country_21 +2014102216 14102216 product_2 pty_country_21 +2014102216 14102216 product_1 pty_country_32 +2014102216 14102216 product_1 pty_country_27 +2014102216 14102216 product_2 pty_country_7 +2014102216 14102216 product_4 pty_country_34 +2014102216 14102216 product_5 pty_country_7 +2014102217 14102217 product_1 pty_country_42 +2014102217 14102217 product_1 pty_country_32 +2014102217 14102217 product_3 pty_country_1 +2014102217 14102217 product_2 pty_country_24 +2014102217 14102217 product_3 pty_country_41 +2014102217 14102217 product_1 pty_country_9 +2014102217 14102217 product_4 pty_country_13 +2014102217 14102217 product_5 pty_country_1 +2014102217 14102217 product_1 pty_country_41 +2014102217 14102217 product_5 pty_country_49 +2014102218 14102218 product_4 pty_country_12 +2014102218 14102218 product_5 pty_country_26 +2014102218 14102218 product_4 pty_country_47 +2014102218 14102218 product_1 pty_country_25 +2014102218 14102218 product_5 pty_country_50 +2014102218 14102218 product_5 pty_country_16 +2014102218 14102218 product_3 pty_country_49 +2014102218 14102218 product_4 pty_country_14 +2014102218 14102218 product_1 pty_country_30 +2014102218 14102218 product_4 pty_country_36 +2014102219 14102219 product_2 pty_country_23 +2014102219 14102219 product_5 pty_country_9 +2014102219 14102219 product_2 pty_country_20 +2014102219 14102219 product_5 pty_country_45 +2014102219 14102219 product_1 pty_country_2 +2014102219 14102219 product_3 pty_country_47 +2014102219 14102219 product_4 pty_country_42 +2014102219 14102219 product_3 pty_country_41 +2014102219 14102219 product_4 pty_country_19 +2014102219 14102219 product_3 pty_country_13 +2014102220 14102220 product_3 pty_country_41 +2014102220 14102220 product_1 pty_country_39 +2014102220 14102220 product_1 pty_country_40 +2014102220 14102220 product_2 pty_country_38 +2014102220 14102220 product_1 pty_country_43 +2014102220 14102220 product_3 pty_country_12 +2014102220 14102220 product_2 pty_country_43 +2014102220 14102220 product_4 pty_country_13 +2014102220 14102220 product_2 pty_country_2 +2014102220 14102220 product_1 pty_country_2 +2014102221 14102221 product_5 pty_country_38 +2014102221 14102221 product_4 pty_country_1 +2014102221 14102221 product_5 pty_country_16 +2014102221 14102221 product_1 pty_country_47 +2014102221 14102221 product_3 pty_country_20 +2014102221 14102221 product_4 pty_country_7 +2014102221 14102221 product_1 pty_country_19 +2014102221 14102221 product_2 pty_country_45 +2014102221 14102221 product_5 pty_country_24 +2014102221 14102221 product_3 pty_country_28 +2014102222 14102222 product_1 pty_country_46 +2014102222 14102222 product_5 pty_country_31 +2014102222 14102222 product_1 pty_country_30 +2014102222 14102222 product_5 pty_country_46 +2014102222 14102222 product_4 pty_country_34 +2014102222 14102222 product_4 pty_country_23 +2014102222 14102222 product_2 pty_country_23 +2014102222 14102222 product_3 pty_country_17 +2014102222 14102222 product_2 pty_country_17 +2014102222 14102222 product_4 pty_country_21 +2014102223 14102223 product_2 pty_country_12 +2014102223 14102223 product_1 pty_country_46 +2014102223 14102223 product_1 pty_country_18 +2014102223 14102223 product_5 pty_country_35 +2014102223 14102223 product_2 pty_country_2 +2014102223 14102223 product_2 pty_country_14 +2014102223 14102223 product_4 pty_country_44 +2014102223 14102223 product_2 pty_country_9 +2014102223 14102223 product_5 pty_country_1 +2014102223 14102223 product_5 pty_country_48 +2014102300 14102300 product_3 pty_country_33 +2014102300 14102300 product_3 pty_country_13 +2014102300 14102300 product_1 pty_country_19 +2014102300 14102300 product_1 pty_country_16 +2014102300 14102300 product_5 pty_country_11 +2014102300 14102300 product_4 pty_country_42 +2014102300 14102300 product_5 pty_country_5 +2014102300 14102300 product_4 pty_country_5 +2014102300 14102300 product_3 pty_country_2 +2014102300 14102300 product_3 pty_country_2 +2014102301 14102301 product_4 pty_country_24 +2014102301 14102301 product_4 pty_country_6 +2014102301 14102301 product_2 pty_country_42 +2014102301 14102301 product_3 pty_country_7 +2014102301 14102301 product_1 pty_country_1 +2014102301 14102301 product_2 pty_country_18 +2014102301 14102301 product_5 pty_country_29 +2014102301 14102301 product_5 pty_country_32 +2014102301 14102301 product_1 pty_country_8 +2014102301 14102301 product_1 pty_country_44 +2014102302 14102302 product_5 pty_country_39 +2014102302 14102302 product_2 pty_country_8 +2014102302 14102302 product_3 pty_country_47 +2014102302 14102302 product_2 pty_country_8 +2014102302 14102302 product_3 pty_country_27 +2014102302 14102302 product_2 pty_country_27 +2014102302 14102302 product_2 pty_country_22 +2014102302 14102302 product_4 pty_country_18 +2014102302 14102302 product_3 pty_country_34 +2014102302 14102302 product_2 pty_country_17 +2014102303 14102303 product_2 pty_country_20 +2014102303 14102303 product_1 pty_country_7 +2014102303 14102303 product_5 pty_country_23 +2014102303 14102303 product_3 pty_country_8 +2014102303 14102303 product_1 pty_country_19 +2014102303 14102303 product_2 pty_country_46 +2014102303 14102303 product_5 pty_country_16 +2014102303 14102303 product_4 pty_country_5 +2014102303 14102303 product_1 pty_country_35 +2014102303 14102303 product_2 pty_country_13 +2014102304 14102304 product_2 pty_country_15 +2014102304 14102304 product_3 pty_country_34 +2014102304 14102304 product_3 pty_country_19 +2014102304 14102304 product_5 pty_country_45 +2014102304 14102304 product_5 pty_country_44 +2014102304 14102304 product_5 pty_country_4 +2014102304 14102304 product_3 pty_country_48 +2014102304 14102304 product_4 pty_country_46 +2014102304 14102304 product_4 pty_country_37 +2014102304 14102304 product_1 pty_country_1 +2014102305 14102305 product_1 pty_country_46 +2014102305 14102305 product_5 pty_country_27 +2014102305 14102305 product_3 pty_country_35 +2014102305 14102305 product_3 pty_country_37 +2014102305 14102305 product_2 pty_country_26 +2014102305 14102305 product_2 pty_country_41 +2014102305 14102305 product_2 pty_country_44 +2014102305 14102305 product_4 pty_country_11 +2014102305 14102305 product_2 pty_country_9 +2014102305 14102305 product_3 pty_country_19 +2014102306 14102306 product_5 pty_country_8 +2014102306 14102306 product_2 pty_country_25 +2014102306 14102306 product_5 pty_country_12 +2014102306 14102306 product_2 pty_country_27 +2014102306 14102306 product_3 pty_country_2 +2014102306 14102306 product_5 pty_country_47 +2014102306 14102306 product_4 pty_country_22 +2014102306 14102306 product_2 pty_country_31 +2014102306 14102306 product_3 pty_country_40 +2014102306 14102306 product_1 pty_country_32 +2014102307 14102307 product_4 pty_country_44 +2014102307 14102307 product_4 pty_country_50 +2014102307 14102307 product_3 pty_country_20 +2014102307 14102307 product_4 pty_country_11 +2014102307 14102307 product_2 pty_country_41 +2014102307 14102307 product_3 pty_country_38 +2014102307 14102307 product_4 pty_country_2 +2014102307 14102307 product_4 pty_country_21 +2014102307 14102307 product_1 pty_country_34 +2014102307 14102307 product_2 pty_country_14 +2014102308 14102308 product_1 pty_country_41 +2014102308 14102308 product_5 pty_country_41 +2014102308 14102308 product_4 pty_country_10 +2014102308 14102308 product_3 pty_country_17 +2014102308 14102308 product_3 pty_country_16 +2014102308 14102308 product_5 pty_country_31 +2014102308 14102308 product_5 pty_country_8 +2014102308 14102308 product_1 pty_country_24 +2014102308 14102308 product_5 pty_country_37 +2014102308 14102308 product_4 pty_country_7 +2014102309 14102309 product_2 pty_country_21 +2014102309 14102309 product_3 pty_country_16 +2014102309 14102309 product_5 pty_country_38 +2014102309 14102309 product_5 pty_country_35 +2014102309 14102309 product_1 pty_country_7 +2014102309 14102309 product_1 pty_country_8 +2014102309 14102309 product_3 pty_country_26 +2014102309 14102309 product_2 pty_country_26 +2014102309 14102309 product_5 pty_country_30 +2014102309 14102309 product_3 pty_country_27 +2014102310 14102310 product_3 pty_country_9 +2014102310 14102310 product_5 pty_country_29 +2014102310 14102310 product_3 pty_country_39 +2014102310 14102310 product_1 pty_country_4 +2014102310 14102310 product_4 pty_country_47 +2014102310 14102310 product_1 pty_country_41 +2014102310 14102310 product_4 pty_country_20 +2014102310 14102310 product_4 pty_country_16 +2014102310 14102310 product_4 pty_country_49 +2014102310 14102310 product_3 pty_country_38 +2014102311 14102311 product_5 pty_country_43 +2014102311 14102311 product_2 pty_country_1 +2014102311 14102311 product_5 pty_country_31 +2014102311 14102311 product_2 pty_country_5 +2014102311 14102311 product_5 pty_country_12 +2014102311 14102311 product_3 pty_country_33 +2014102311 14102311 product_2 pty_country_9 +2014102311 14102311 product_1 pty_country_38 +2014102311 14102311 product_5 pty_country_17 +2014102311 14102311 product_3 pty_country_7 +2014102312 14102312 product_1 pty_country_44 +2014102312 14102312 product_3 pty_country_28 +2014102312 14102312 product_2 pty_country_28 +2014102312 14102312 product_4 pty_country_36 +2014102312 14102312 product_1 pty_country_35 +2014102312 14102312 product_2 pty_country_42 +2014102312 14102312 product_2 pty_country_40 +2014102312 14102312 product_2 pty_country_23 +2014102312 14102312 product_5 pty_country_24 +2014102312 14102312 product_3 pty_country_8 +2014102313 14102313 product_2 pty_country_7 +2014102313 14102313 product_1 pty_country_45 +2014102313 14102313 product_1 pty_country_48 +2014102313 14102313 product_1 pty_country_28 +2014102313 14102313 product_5 pty_country_28 +2014102313 14102313 product_1 pty_country_20 +2014102313 14102313 product_4 pty_country_44 +2014102313 14102313 product_2 pty_country_45 +2014102313 14102313 product_1 pty_country_22 +2014102313 14102313 product_5 pty_country_33 +2014102314 14102314 product_3 pty_country_9 +2014102314 14102314 product_5 pty_country_27 +2014102314 14102314 product_3 pty_country_48 +2014102314 14102314 product_2 pty_country_23 +2014102314 14102314 product_4 pty_country_38 +2014102314 14102314 product_4 pty_country_29 +2014102314 14102314 product_4 pty_country_30 +2014102314 14102314 product_2 pty_country_38 +2014102314 14102314 product_2 pty_country_26 +2014102314 14102314 product_2 pty_country_41 +2014102315 14102315 product_1 pty_country_19 +2014102315 14102315 product_1 pty_country_5 +2014102315 14102315 product_4 pty_country_15 +2014102315 14102315 product_2 pty_country_32 +2014102315 14102315 product_2 pty_country_44 +2014102315 14102315 product_5 pty_country_9 +2014102315 14102315 product_5 pty_country_27 +2014102315 14102315 product_3 pty_country_46 +2014102315 14102315 product_1 pty_country_18 +2014102315 14102315 product_2 pty_country_7 +2014102316 14102316 product_4 pty_country_10 +2014102316 14102316 product_2 pty_country_3 +2014102316 14102316 product_3 pty_country_26 +2014102316 14102316 product_4 pty_country_25 +2014102316 14102316 product_1 pty_country_45 +2014102316 14102316 product_4 pty_country_5 +2014102316 14102316 product_5 pty_country_29 +2014102316 14102316 product_3 pty_country_46 +2014102316 14102316 product_2 pty_country_17 +2014102316 14102316 product_5 pty_country_30 +2014102317 14102317 product_2 pty_country_31 +2014102317 14102317 product_3 pty_country_34 +2014102317 14102317 product_3 pty_country_16 +2014102317 14102317 product_4 pty_country_22 +2014102317 14102317 product_2 pty_country_14 +2014102317 14102317 product_2 pty_country_2 +2014102317 14102317 product_3 pty_country_16 +2014102317 14102317 product_1 pty_country_43 +2014102317 14102317 product_4 pty_country_9 +2014102317 14102317 product_3 pty_country_26 +2014102318 14102318 product_2 pty_country_30 +2014102318 14102318 product_2 pty_country_49 +2014102318 14102318 product_3 pty_country_21 +2014102318 14102318 product_5 pty_country_47 +2014102318 14102318 product_1 pty_country_4 +2014102318 14102318 product_2 pty_country_19 +2014102318 14102318 product_2 pty_country_30 +2014102318 14102318 product_3 pty_country_37 +2014102318 14102318 product_4 pty_country_12 +2014102318 14102318 product_3 pty_country_12 +2014102319 14102319 product_3 pty_country_9 +2014102319 14102319 product_2 pty_country_37 +2014102319 14102319 product_4 pty_country_43 +2014102319 14102319 product_5 pty_country_12 +2014102319 14102319 product_2 pty_country_20 +2014102319 14102319 product_5 pty_country_4 +2014102319 14102319 product_4 pty_country_22 +2014102319 14102319 product_2 pty_country_32 +2014102319 14102319 product_4 pty_country_33 +2014102319 14102319 product_2 pty_country_11 +2014102320 14102320 product_4 pty_country_29 +2014102320 14102320 product_3 pty_country_8 +2014102320 14102320 product_5 pty_country_27 +2014102320 14102320 product_5 pty_country_40 +2014102320 14102320 product_3 pty_country_7 +2014102320 14102320 product_4 pty_country_49 +2014102320 14102320 product_3 pty_country_38 +2014102320 14102320 product_3 pty_country_35 +2014102320 14102320 product_4 pty_country_38 +2014102320 14102320 product_1 pty_country_18 +2014102321 14102321 product_3 pty_country_1 +2014102321 14102321 product_3 pty_country_1 +2014102321 14102321 product_3 pty_country_30 +2014102321 14102321 product_4 pty_country_21 +2014102321 14102321 product_1 pty_country_25 +2014102321 14102321 product_3 pty_country_20 +2014102321 14102321 product_4 pty_country_2 +2014102321 14102321 product_4 pty_country_3 +2014102321 14102321 product_3 pty_country_5 +2014102321 14102321 product_5 pty_country_28 +2014102322 14102322 product_2 pty_country_29 +2014102322 14102322 product_3 pty_country_29 +2014102322 14102322 product_2 pty_country_40 +2014102322 14102322 product_3 pty_country_14 +2014102322 14102322 product_1 pty_country_26 +2014102322 14102322 product_1 pty_country_10 +2014102322 14102322 product_4 pty_country_19 +2014102322 14102322 product_5 pty_country_20 +2014102322 14102322 product_5 pty_country_44 +2014102322 14102322 product_1 pty_country_34 +2014102323 14102323 product_5 pty_country_21 +2014102323 14102323 product_1 pty_country_7 +2014102323 14102323 product_2 pty_country_40 +2014102323 14102323 product_1 pty_country_22 +2014102323 14102323 product_5 pty_country_45 +2014102323 14102323 product_1 pty_country_24 +2014102323 14102323 product_3 pty_country_49 +2014102323 14102323 product_1 pty_country_9 +2014102323 14102323 product_3 pty_country_18 +2014102323 14102323 product_2 pty_country_41 +2014102400 14102400 product_5 pty_country_41 +2014102400 14102400 product_3 pty_country_19 +2014102400 14102400 product_4 pty_country_48 +2014102400 14102400 product_2 pty_country_33 +2014102400 14102400 product_2 pty_country_27 +2014102400 14102400 product_5 pty_country_29 +2014102400 14102400 product_1 pty_country_44 +2014102400 14102400 product_1 pty_country_24 +2014102400 14102400 product_2 pty_country_18 +2014102400 14102400 product_1 pty_country_34 +2014102401 14102401 product_3 pty_country_32 +2014102401 14102401 product_3 pty_country_15 +2014102401 14102401 product_3 pty_country_17 +2014102401 14102401 product_5 pty_country_12 +2014102401 14102401 product_3 pty_country_17 +2014102401 14102401 product_3 pty_country_32 +2014102401 14102401 product_2 pty_country_21 +2014102401 14102401 product_4 pty_country_12 +2014102401 14102401 product_1 pty_country_2 +2014102401 14102401 product_1 pty_country_17 +2014102402 14102402 product_1 pty_country_47 +2014102402 14102402 product_5 pty_country_26 +2014102402 14102402 product_5 pty_country_35 +2014102402 14102402 product_3 pty_country_43 +2014102402 14102402 product_1 pty_country_5 +2014102402 14102402 product_2 pty_country_30 +2014102402 14102402 product_1 pty_country_36 +2014102402 14102402 product_5 pty_country_5 +2014102402 14102402 product_1 pty_country_9 +2014102402 14102402 product_2 pty_country_48 +2014102403 14102403 product_5 pty_country_30 +2014102403 14102403 product_3 pty_country_28 +2014102403 14102403 product_5 pty_country_15 +2014102403 14102403 product_4 pty_country_7 +2014102403 14102403 product_2 pty_country_42 +2014102403 14102403 product_3 pty_country_46 +2014102403 14102403 product_4 pty_country_7 +2014102403 14102403 product_2 pty_country_18 +2014102403 14102403 product_2 pty_country_11 +2014102403 14102403 product_4 pty_country_35 +2014102404 14102404 product_5 pty_country_46 +2014102404 14102404 product_5 pty_country_40 +2014102404 14102404 product_2 pty_country_23 +2014102404 14102404 product_5 pty_country_49 +2014102404 14102404 product_1 pty_country_48 +2014102404 14102404 product_5 pty_country_6 +2014102404 14102404 product_3 pty_country_31 +2014102404 14102404 product_1 pty_country_21 +2014102404 14102404 product_4 pty_country_34 +2014102404 14102404 product_5 pty_country_9 +2014102405 14102405 product_3 pty_country_45 +2014102405 14102405 product_3 pty_country_39 +2014102405 14102405 product_3 pty_country_7 +2014102405 14102405 product_5 pty_country_12 +2014102405 14102405 product_3 pty_country_18 +2014102405 14102405 product_4 pty_country_11 +2014102405 14102405 product_3 pty_country_38 +2014102405 14102405 product_3 pty_country_48 +2014102405 14102405 product_5 pty_country_46 +2014102405 14102405 product_5 pty_country_9 +2014102406 14102406 product_4 pty_country_3 +2014102406 14102406 product_1 pty_country_27 +2014102406 14102406 product_4 pty_country_33 +2014102406 14102406 product_1 pty_country_27 +2014102406 14102406 product_3 pty_country_4 +2014102406 14102406 product_5 pty_country_15 +2014102406 14102406 product_1 pty_country_25 +2014102406 14102406 product_3 pty_country_12 +2014102406 14102406 product_1 pty_country_11 +2014102406 14102406 product_4 pty_country_22 +2014102407 14102407 product_2 pty_country_12 +2014102407 14102407 product_2 pty_country_12 +2014102407 14102407 product_2 pty_country_39 +2014102407 14102407 product_4 pty_country_28 +2014102407 14102407 product_5 pty_country_45 +2014102407 14102407 product_4 pty_country_24 +2014102407 14102407 product_4 pty_country_16 +2014102407 14102407 product_5 pty_country_40 +2014102407 14102407 product_2 pty_country_9 +2014102407 14102407 product_2 pty_country_37 +2014102408 14102408 product_3 pty_country_40 +2014102408 14102408 product_3 pty_country_13 +2014102408 14102408 product_5 pty_country_17 +2014102408 14102408 product_3 pty_country_40 +2014102408 14102408 product_5 pty_country_45 +2014102408 14102408 product_3 pty_country_37 +2014102408 14102408 product_1 pty_country_38 +2014102408 14102408 product_5 pty_country_40 +2014102408 14102408 product_5 pty_country_24 +2014102408 14102408 product_1 pty_country_23 +2014102409 14102409 product_1 pty_country_15 +2014102409 14102409 product_4 pty_country_8 +2014102409 14102409 product_1 pty_country_49 +2014102409 14102409 product_5 pty_country_11 +2014102409 14102409 product_1 pty_country_37 +2014102409 14102409 product_3 pty_country_27 +2014102409 14102409 product_4 pty_country_17 +2014102409 14102409 product_1 pty_country_47 +2014102409 14102409 product_5 pty_country_37 +2014102409 14102409 product_4 pty_country_34 +2014102410 14102410 product_5 pty_country_46 +2014102410 14102410 product_4 pty_country_40 +2014102410 14102410 product_4 pty_country_20 +2014102410 14102410 product_1 pty_country_42 +2014102410 14102410 product_3 pty_country_9 +2014102410 14102410 product_1 pty_country_20 +2014102410 14102410 product_5 pty_country_22 +2014102410 14102410 product_2 pty_country_30 +2014102410 14102410 product_5 pty_country_2 +2014102410 14102410 product_3 pty_country_8 +2014102411 14102411 product_4 pty_country_11 +2014102411 14102411 product_5 pty_country_10 +2014102411 14102411 product_2 pty_country_3 +2014102411 14102411 product_4 pty_country_4 +2014102411 14102411 product_3 pty_country_41 +2014102411 14102411 product_4 pty_country_8 +2014102411 14102411 product_4 pty_country_30 +2014102411 14102411 product_1 pty_country_2 +2014102411 14102411 product_5 pty_country_5 +2014102411 14102411 product_4 pty_country_7 +2014102412 14102412 product_5 pty_country_47 +2014102412 14102412 product_3 pty_country_34 +2014102412 14102412 product_5 pty_country_3 +2014102412 14102412 product_1 pty_country_11 +2014102412 14102412 product_3 pty_country_16 +2014102412 14102412 product_3 pty_country_26 +2014102412 14102412 product_3 pty_country_34 +2014102412 14102412 product_2 pty_country_16 +2014102412 14102412 product_1 pty_country_23 +2014102412 14102412 product_4 pty_country_48 +2014102413 14102413 product_2 pty_country_25 +2014102413 14102413 product_4 pty_country_13 +2014102413 14102413 product_3 pty_country_5 +2014102413 14102413 product_5 pty_country_26 +2014102413 14102413 product_5 pty_country_4 +2014102413 14102413 product_5 pty_country_7 +2014102413 14102413 product_4 pty_country_21 +2014102413 14102413 product_1 pty_country_25 +2014102413 14102413 product_1 pty_country_47 +2014102413 14102413 product_5 pty_country_50 +2014102414 14102414 product_1 pty_country_47 +2014102414 14102414 product_2 pty_country_19 +2014102414 14102414 product_2 pty_country_9 +2014102414 14102414 product_3 pty_country_12 +2014102414 14102414 product_3 pty_country_10 +2014102414 14102414 product_5 pty_country_15 +2014102414 14102414 product_1 pty_country_3 +2014102414 14102414 product_5 pty_country_2 +2014102414 14102414 product_5 pty_country_11 +2014102414 14102414 product_2 pty_country_25 +2014102415 14102415 product_4 pty_country_31 +2014102415 14102415 product_5 pty_country_21 +2014102415 14102415 product_3 pty_country_28 +2014102415 14102415 product_1 pty_country_41 +2014102415 14102415 product_2 pty_country_17 +2014102415 14102415 product_5 pty_country_23 +2014102415 14102415 product_4 pty_country_18 +2014102415 14102415 product_2 pty_country_3 +2014102415 14102415 product_4 pty_country_20 +2014102415 14102415 product_3 pty_country_16 +2014102416 14102416 product_5 pty_country_16 +2014102416 14102416 product_5 pty_country_19 +2014102416 14102416 product_2 pty_country_40 +2014102416 14102416 product_1 pty_country_13 +2014102416 14102416 product_3 pty_country_33 +2014102416 14102416 product_3 pty_country_29 +2014102416 14102416 product_4 pty_country_46 +2014102416 14102416 product_2 pty_country_12 +2014102416 14102416 product_1 pty_country_39 +2014102416 14102416 product_1 pty_country_16 +2014102417 14102417 product_5 pty_country_21 +2014102417 14102417 product_5 pty_country_39 +2014102417 14102417 product_5 pty_country_28 +2014102417 14102417 product_1 pty_country_29 +2014102417 14102417 product_3 pty_country_49 +2014102417 14102417 product_5 pty_country_19 +2014102417 14102417 product_3 pty_country_10 +2014102417 14102417 product_3 pty_country_8 +2014102417 14102417 product_4 pty_country_15 +2014102417 14102417 product_2 pty_country_42 +2014102418 14102418 product_3 pty_country_30 +2014102418 14102418 product_4 pty_country_23 +2014102418 14102418 product_3 pty_country_50 +2014102418 14102418 product_1 pty_country_6 +2014102418 14102418 product_5 pty_country_45 +2014102418 14102418 product_4 pty_country_10 +2014102418 14102418 product_4 pty_country_48 +2014102418 14102418 product_2 pty_country_33 +2014102418 14102418 product_3 pty_country_30 +2014102418 14102418 product_2 pty_country_13 +2014102419 14102419 product_1 pty_country_50 +2014102419 14102419 product_2 pty_country_12 +2014102419 14102419 product_2 pty_country_38 +2014102419 14102419 product_3 pty_country_22 +2014102419 14102419 product_4 pty_country_17 +2014102419 14102419 product_1 pty_country_19 +2014102419 14102419 product_2 pty_country_2 +2014102419 14102419 product_5 pty_country_23 +2014102419 14102419 product_4 pty_country_32 +2014102419 14102419 product_4 pty_country_15 +2014102420 14102420 product_5 pty_country_4 +2014102420 14102420 product_4 pty_country_9 +2014102420 14102420 product_3 pty_country_25 +2014102420 14102420 product_2 pty_country_4 +2014102420 14102420 product_1 pty_country_46 +2014102420 14102420 product_3 pty_country_9 +2014102420 14102420 product_2 pty_country_32 +2014102420 14102420 product_4 pty_country_1 +2014102420 14102420 product_3 pty_country_27 +2014102420 14102420 product_4 pty_country_11 +2014102421 14102421 product_3 pty_country_3 +2014102421 14102421 product_1 pty_country_25 +2014102421 14102421 product_1 pty_country_45 +2014102421 14102421 product_5 pty_country_3 +2014102421 14102421 product_1 pty_country_1 +2014102421 14102421 product_3 pty_country_40 +2014102421 14102421 product_3 pty_country_13 +2014102421 14102421 product_3 pty_country_13 +2014102421 14102421 product_5 pty_country_13 +2014102421 14102421 product_5 pty_country_28 +2014102422 14102422 product_2 pty_country_23 +2014102422 14102422 product_2 pty_country_9 +2014102422 14102422 product_4 pty_country_31 +2014102422 14102422 product_5 pty_country_43 +2014102422 14102422 product_3 pty_country_30 +2014102422 14102422 product_5 pty_country_6 +2014102422 14102422 product_1 pty_country_10 +2014102422 14102422 product_2 pty_country_21 +2014102422 14102422 product_3 pty_country_37 +2014102422 14102422 product_1 pty_country_46 +2014102423 14102423 product_2 pty_country_40 +2014102423 14102423 product_4 pty_country_30 +2014102423 14102423 product_4 pty_country_8 +2014102423 14102423 product_2 pty_country_3 +2014102423 14102423 product_5 pty_country_23 +2014102423 14102423 product_4 pty_country_4 +2014102423 14102423 product_2 pty_country_50 +2014102423 14102423 product_1 pty_country_9 +2014102423 14102423 product_3 pty_country_7 +2014102423 14102423 product_4 pty_country_41 +2014102500 14102500 product_2 pty_country_34 +2014102500 14102500 product_2 pty_country_43 +2014102500 14102500 product_5 pty_country_49 +2014102500 14102500 product_4 pty_country_46 +2014102500 14102500 product_1 pty_country_34 +2014102500 14102500 product_3 pty_country_29 +2014102500 14102500 product_2 pty_country_39 +2014102500 14102500 product_2 pty_country_25 +2014102500 14102500 product_3 pty_country_49 +2014102500 14102500 product_5 pty_country_38 +2014102501 14102501 product_4 pty_country_25 +2014102501 14102501 product_2 pty_country_8 +2014102501 14102501 product_4 pty_country_8 +2014102501 14102501 product_2 pty_country_37 +2014102501 14102501 product_3 pty_country_2 +2014102501 14102501 product_3 pty_country_9 +2014102501 14102501 product_3 pty_country_40 +2014102501 14102501 product_1 pty_country_49 +2014102501 14102501 product_4 pty_country_40 +2014102501 14102501 product_3 pty_country_45 +2014102502 14102502 product_2 pty_country_35 +2014102502 14102502 product_3 pty_country_12 +2014102502 14102502 product_1 pty_country_2 +2014102502 14102502 product_4 pty_country_42 +2014102502 14102502 product_1 pty_country_42 +2014102502 14102502 product_5 pty_country_2 +2014102502 14102502 product_1 pty_country_46 +2014102502 14102502 product_1 pty_country_19 +2014102502 14102502 product_3 pty_country_33 +2014102502 14102502 product_3 pty_country_6 +2014102503 14102503 product_1 pty_country_29 +2014102503 14102503 product_1 pty_country_48 +2014102503 14102503 product_4 pty_country_36 +2014102503 14102503 product_3 pty_country_7 +2014102503 14102503 product_3 pty_country_13 +2014102503 14102503 product_2 pty_country_42 +2014102503 14102503 product_3 pty_country_10 +2014102503 14102503 product_5 pty_country_40 +2014102503 14102503 product_5 pty_country_7 +2014102503 14102503 product_5 pty_country_31 +2014102504 14102504 product_4 pty_country_47 +2014102504 14102504 product_3 pty_country_8 +2014102504 14102504 product_2 pty_country_39 +2014102504 14102504 product_4 pty_country_2 +2014102504 14102504 product_1 pty_country_41 +2014102504 14102504 product_3 pty_country_44 +2014102504 14102504 product_5 pty_country_38 +2014102504 14102504 product_1 pty_country_7 +2014102504 14102504 product_1 pty_country_3 +2014102504 14102504 product_2 pty_country_23 +2014102505 14102505 product_4 pty_country_8 +2014102505 14102505 product_2 pty_country_24 +2014102505 14102505 product_1 pty_country_30 +2014102505 14102505 product_4 pty_country_43 +2014102505 14102505 product_4 pty_country_11 +2014102505 14102505 product_1 pty_country_11 +2014102505 14102505 product_3 pty_country_48 +2014102505 14102505 product_5 pty_country_26 +2014102505 14102505 product_1 pty_country_32 +2014102505 14102505 product_3 pty_country_1 +2014102506 14102506 product_4 pty_country_28 +2014102506 14102506 product_5 pty_country_21 +2014102506 14102506 product_2 pty_country_22 +2014102506 14102506 product_2 pty_country_28 +2014102506 14102506 product_2 pty_country_41 +2014102506 14102506 product_3 pty_country_31 +2014102506 14102506 product_1 pty_country_48 +2014102506 14102506 product_2 pty_country_49 +2014102506 14102506 product_4 pty_country_34 +2014102506 14102506 product_2 pty_country_23 +2014102507 14102507 product_5 pty_country_32 +2014102507 14102507 product_2 pty_country_31 +2014102507 14102507 product_4 pty_country_12 +2014102507 14102507 product_1 pty_country_45 +2014102507 14102507 product_5 pty_country_36 +2014102507 14102507 product_1 pty_country_44 +2014102507 14102507 product_1 pty_country_18 +2014102507 14102507 product_1 pty_country_37 +2014102507 14102507 product_5 pty_country_32 +2014102507 14102507 product_3 pty_country_37 +2014102508 14102508 product_2 pty_country_20 +2014102508 14102508 product_4 pty_country_42 +2014102508 14102508 product_5 pty_country_49 +2014102508 14102508 product_4 pty_country_38 +2014102508 14102508 product_4 pty_country_39 +2014102508 14102508 product_2 pty_country_10 +2014102508 14102508 product_5 pty_country_4 +2014102508 14102508 product_3 pty_country_9 +2014102508 14102508 product_3 pty_country_40 +2014102508 14102508 product_2 pty_country_48 +2014102509 14102509 product_3 pty_country_41 +2014102509 14102509 product_5 pty_country_17 +2014102509 14102509 product_1 pty_country_50 +2014102509 14102509 product_5 pty_country_38 +2014102509 14102509 product_4 pty_country_31 +2014102509 14102509 product_2 pty_country_8 +2014102509 14102509 product_5 pty_country_10 +2014102509 14102509 product_2 pty_country_5 +2014102509 14102509 product_3 pty_country_40 +2014102509 14102509 product_3 pty_country_39 +2014102510 14102510 product_5 pty_country_48 +2014102510 14102510 product_1 pty_country_37 +2014102510 14102510 product_1 pty_country_20 +2014102510 14102510 product_4 pty_country_40 +2014102510 14102510 product_5 pty_country_25 +2014102510 14102510 product_4 pty_country_47 +2014102510 14102510 product_2 pty_country_4 +2014102510 14102510 product_1 pty_country_27 +2014102510 14102510 product_5 pty_country_18 +2014102510 14102510 product_3 pty_country_39 +2014102511 14102511 product_1 pty_country_22 +2014102511 14102511 product_3 pty_country_2 +2014102511 14102511 product_3 pty_country_39 +2014102511 14102511 product_4 pty_country_33 +2014102511 14102511 product_4 pty_country_4 +2014102511 14102511 product_5 pty_country_27 +2014102511 14102511 product_3 pty_country_25 +2014102511 14102511 product_1 pty_country_20 +2014102511 14102511 product_2 pty_country_13 +2014102511 14102511 product_2 pty_country_30 +2014102512 14102512 product_1 pty_country_18 +2014102512 14102512 product_3 pty_country_1 +2014102512 14102512 product_4 pty_country_38 +2014102512 14102512 product_2 pty_country_19 +2014102512 14102512 product_4 pty_country_41 +2014102512 14102512 product_4 pty_country_47 +2014102512 14102512 product_2 pty_country_5 +2014102512 14102512 product_5 pty_country_21 +2014102512 14102512 product_1 pty_country_35 +2014102512 14102512 product_2 pty_country_3 +2014102513 14102513 product_3 pty_country_8 +2014102513 14102513 product_5 pty_country_33 +2014102513 14102513 product_2 pty_country_18 +2014102513 14102513 product_2 pty_country_27 +2014102513 14102513 product_2 pty_country_31 +2014102513 14102513 product_2 pty_country_48 +2014102513 14102513 product_3 pty_country_22 +2014102513 14102513 product_2 pty_country_44 +2014102513 14102513 product_3 pty_country_8 +2014102513 14102513 product_3 pty_country_5 +2014102514 14102514 product_2 pty_country_3 +2014102514 14102514 product_2 pty_country_41 +2014102514 14102514 product_2 pty_country_11 +2014102514 14102514 product_5 pty_country_17 +2014102514 14102514 product_3 pty_country_17 +2014102514 14102514 product_1 pty_country_46 +2014102514 14102514 product_3 pty_country_9 +2014102514 14102514 product_4 pty_country_1 +2014102514 14102514 product_1 pty_country_32 +2014102514 14102514 product_5 pty_country_40 +2014102515 14102515 product_1 pty_country_38 +2014102515 14102515 product_4 pty_country_44 +2014102515 14102515 product_3 pty_country_35 +2014102515 14102515 product_5 pty_country_31 +2014102515 14102515 product_5 pty_country_34 +2014102515 14102515 product_4 pty_country_1 +2014102515 14102515 product_3 pty_country_37 +2014102515 14102515 product_5 pty_country_2 +2014102515 14102515 product_4 pty_country_29 +2014102515 14102515 product_1 pty_country_20 +2014102516 14102516 product_3 pty_country_24 +2014102516 14102516 product_5 pty_country_47 +2014102516 14102516 product_4 pty_country_12 +2014102516 14102516 product_3 pty_country_36 +2014102516 14102516 product_4 pty_country_13 +2014102516 14102516 product_3 pty_country_45 +2014102516 14102516 product_4 pty_country_14 +2014102516 14102516 product_1 pty_country_5 +2014102516 14102516 product_5 pty_country_23 +2014102516 14102516 product_3 pty_country_45 +2014102517 14102517 product_2 pty_country_2 +2014102517 14102517 product_3 pty_country_21 +2014102517 14102517 product_1 pty_country_3 +2014102517 14102517 product_3 pty_country_1 +2014102517 14102517 product_3 pty_country_13 +2014102517 14102517 product_3 pty_country_21 +2014102517 14102517 product_1 pty_country_14 +2014102517 14102517 product_4 pty_country_7 +2014102517 14102517 product_5 pty_country_8 +2014102517 14102517 product_5 pty_country_48 +2014102518 14102518 product_2 pty_country_50 +2014102518 14102518 product_4 pty_country_20 +2014102518 14102518 product_2 pty_country_39 +2014102518 14102518 product_4 pty_country_33 +2014102518 14102518 product_2 pty_country_26 +2014102518 14102518 product_1 pty_country_47 +2014102518 14102518 product_4 pty_country_32 +2014102518 14102518 product_4 pty_country_41 +2014102518 14102518 product_4 pty_country_2 +2014102518 14102518 product_3 pty_country_48 +2014102519 14102519 product_3 pty_country_12 +2014102519 14102519 product_1 pty_country_18 +2014102519 14102519 product_5 pty_country_36 +2014102519 14102519 product_1 pty_country_32 +2014102519 14102519 product_1 pty_country_50 +2014102519 14102519 product_4 pty_country_19 +2014102519 14102519 product_2 pty_country_15 +2014102519 14102519 product_2 pty_country_42 +2014102519 14102519 product_4 pty_country_49 +2014102519 14102519 product_3 pty_country_12 +2014102520 14102520 product_1 pty_country_8 +2014102520 14102520 product_2 pty_country_27 +2014102520 14102520 product_4 pty_country_2 +2014102520 14102520 product_1 pty_country_15 +2014102520 14102520 product_4 pty_country_7 +2014102520 14102520 product_4 pty_country_6 +2014102520 14102520 product_2 pty_country_38 +2014102520 14102520 product_1 pty_country_29 +2014102520 14102520 product_4 pty_country_24 +2014102520 14102520 product_5 pty_country_2 +2014102521 14102521 product_1 pty_country_8 +2014102521 14102521 product_4 pty_country_39 +2014102521 14102521 product_3 pty_country_50 +2014102521 14102521 product_5 pty_country_25 +2014102521 14102521 product_5 pty_country_16 +2014102521 14102521 product_3 pty_country_39 +2014102521 14102521 product_2 pty_country_11 +2014102521 14102521 product_3 pty_country_33 +2014102521 14102521 product_2 pty_country_7 +2014102521 14102521 product_2 pty_country_25 +2014102522 14102522 product_1 pty_country_48 +2014102522 14102522 product_5 pty_country_9 +2014102522 14102522 product_5 pty_country_15 +2014102522 14102522 product_2 pty_country_28 +2014102522 14102522 product_4 pty_country_45 +2014102522 14102522 product_1 pty_country_17 +2014102522 14102522 product_5 pty_country_5 +2014102522 14102522 product_5 pty_country_9 +2014102522 14102522 product_4 pty_country_40 +2014102522 14102522 product_5 pty_country_37 +2014102523 14102523 product_3 pty_country_32 +2014102523 14102523 product_4 pty_country_30 +2014102523 14102523 product_3 pty_country_7 +2014102523 14102523 product_2 pty_country_5 +2014102523 14102523 product_2 pty_country_23 +2014102523 14102523 product_3 pty_country_7 +2014102523 14102523 product_4 pty_country_27 +2014102523 14102523 product_2 pty_country_8 +2014102523 14102523 product_5 pty_country_4 +2014102523 14102523 product_3 pty_country_12 +2014102600 14102600 product_3 pty_country_22 +2014102600 14102600 product_2 pty_country_10 +2014102600 14102600 product_4 pty_country_20 +2014102600 14102600 product_1 pty_country_28 +2014102600 14102600 product_2 pty_country_13 +2014102600 14102600 product_3 pty_country_26 +2014102600 14102600 product_1 pty_country_47 +2014102600 14102600 product_2 pty_country_1 +2014102600 14102600 product_2 pty_country_23 +2014102600 14102600 product_3 pty_country_46 +2014102601 14102601 product_5 pty_country_12 +2014102601 14102601 product_1 pty_country_47 +2014102601 14102601 product_1 pty_country_47 +2014102601 14102601 product_2 pty_country_27 +2014102601 14102601 product_4 pty_country_3 +2014102601 14102601 product_4 pty_country_27 +2014102601 14102601 product_3 pty_country_34 +2014102601 14102601 product_3 pty_country_19 +2014102601 14102601 product_3 pty_country_7 +2014102601 14102601 product_2 pty_country_40 +2014102602 14102602 product_1 pty_country_21 +2014102602 14102602 product_4 pty_country_42 +2014102602 14102602 product_5 pty_country_22 +2014102602 14102602 product_2 pty_country_13 +2014102602 14102602 product_2 pty_country_39 +2014102602 14102602 product_4 pty_country_30 +2014102602 14102602 product_5 pty_country_29 +2014102602 14102602 product_2 pty_country_18 +2014102602 14102602 product_4 pty_country_23 +2014102602 14102602 product_2 pty_country_49 +2014102603 14102603 product_3 pty_country_27 +2014102603 14102603 product_4 pty_country_2 +2014102603 14102603 product_5 pty_country_32 +2014102603 14102603 product_2 pty_country_8 +2014102603 14102603 product_1 pty_country_16 +2014102603 14102603 product_4 pty_country_39 +2014102603 14102603 product_3 pty_country_35 +2014102603 14102603 product_3 pty_country_36 +2014102603 14102603 product_3 pty_country_15 +2014102603 14102603 product_1 pty_country_32 +2014102604 14102604 product_3 pty_country_28 +2014102604 14102604 product_3 pty_country_36 +2014102604 14102604 product_5 pty_country_16 +2014102604 14102604 product_4 pty_country_15 +2014102604 14102604 product_2 pty_country_30 +2014102604 14102604 product_4 pty_country_40 +2014102604 14102604 product_1 pty_country_19 +2014102604 14102604 product_2 pty_country_41 +2014102604 14102604 product_5 pty_country_42 +2014102604 14102604 product_3 pty_country_34 +2014102605 14102605 product_4 pty_country_41 +2014102605 14102605 product_2 pty_country_29 +2014102605 14102605 product_5 pty_country_10 +2014102605 14102605 product_3 pty_country_41 +2014102605 14102605 product_5 pty_country_5 +2014102605 14102605 product_2 pty_country_41 +2014102605 14102605 product_5 pty_country_41 +2014102605 14102605 product_1 pty_country_25 +2014102605 14102605 product_1 pty_country_20 +2014102605 14102605 product_5 pty_country_41 +2014102606 14102606 product_4 pty_country_13 +2014102606 14102606 product_5 pty_country_20 +2014102606 14102606 product_2 pty_country_6 +2014102606 14102606 product_1 pty_country_13 +2014102606 14102606 product_1 pty_country_24 +2014102606 14102606 product_5 pty_country_3 +2014102606 14102606 product_3 pty_country_48 +2014102606 14102606 product_5 pty_country_17 +2014102606 14102606 product_1 pty_country_12 +2014102606 14102606 product_5 pty_country_7 +2014102607 14102607 product_3 pty_country_2 +2014102607 14102607 product_4 pty_country_2 +2014102607 14102607 product_5 pty_country_15 +2014102607 14102607 product_5 pty_country_48 +2014102607 14102607 product_1 pty_country_11 +2014102607 14102607 product_4 pty_country_5 +2014102607 14102607 product_3 pty_country_5 +2014102607 14102607 product_5 pty_country_25 +2014102607 14102607 product_2 pty_country_38 +2014102607 14102607 product_4 pty_country_25 +2014102608 14102608 product_5 pty_country_27 +2014102608 14102608 product_4 pty_country_2 +2014102608 14102608 product_5 pty_country_31 +2014102608 14102608 product_2 pty_country_8 +2014102608 14102608 product_3 pty_country_1 +2014102608 14102608 product_1 pty_country_49 +2014102608 14102608 product_4 pty_country_39 +2014102608 14102608 product_4 pty_country_2 +2014102608 14102608 product_3 pty_country_6 +2014102608 14102608 product_4 pty_country_8 +2014102609 14102609 product_1 pty_country_7 +2014102609 14102609 product_5 pty_country_18 +2014102609 14102609 product_2 pty_country_27 +2014102609 14102609 product_3 pty_country_39 +2014102609 14102609 product_4 pty_country_11 +2014102609 14102609 product_2 pty_country_9 +2014102609 14102609 product_5 pty_country_28 +2014102609 14102609 product_2 pty_country_18 +2014102609 14102609 product_1 pty_country_40 +2014102609 14102609 product_2 pty_country_10 +2014102610 14102610 product_5 pty_country_9 +2014102610 14102610 product_2 pty_country_10 +2014102610 14102610 product_4 pty_country_13 +2014102610 14102610 product_2 pty_country_35 +2014102610 14102610 product_5 pty_country_12 +2014102610 14102610 product_3 pty_country_50 +2014102610 14102610 product_4 pty_country_37 +2014102610 14102610 product_1 pty_country_50 +2014102610 14102610 product_3 pty_country_49 +2014102610 14102610 product_4 pty_country_44 +2014102611 14102611 product_3 pty_country_23 +2014102611 14102611 product_1 pty_country_7 +2014102611 14102611 product_1 pty_country_44 +2014102611 14102611 product_5 pty_country_21 +2014102611 14102611 product_2 pty_country_15 +2014102611 14102611 product_1 pty_country_8 +2014102611 14102611 product_1 pty_country_29 +2014102611 14102611 product_2 pty_country_5 +2014102611 14102611 product_1 pty_country_48 +2014102611 14102611 product_2 pty_country_10 +2014102612 14102612 product_1 pty_country_11 +2014102612 14102612 product_4 pty_country_10 +2014102612 14102612 product_4 pty_country_16 +2014102612 14102612 product_5 pty_country_14 +2014102612 14102612 product_5 pty_country_17 +2014102612 14102612 product_3 pty_country_1 +2014102612 14102612 product_2 pty_country_15 +2014102612 14102612 product_4 pty_country_6 +2014102612 14102612 product_4 pty_country_2 +2014102612 14102612 product_4 pty_country_23 +2014102613 14102613 product_5 pty_country_47 +2014102613 14102613 product_3 pty_country_33 +2014102613 14102613 product_5 pty_country_37 +2014102613 14102613 product_2 pty_country_38 +2014102613 14102613 product_1 pty_country_48 +2014102613 14102613 product_4 pty_country_5 +2014102613 14102613 product_1 pty_country_45 +2014102613 14102613 product_1 pty_country_12 +2014102613 14102613 product_4 pty_country_13 +2014102613 14102613 product_1 pty_country_44 +2014102614 14102614 product_4 pty_country_35 +2014102614 14102614 product_3 pty_country_30 +2014102614 14102614 product_4 pty_country_30 +2014102614 14102614 product_2 pty_country_22 +2014102614 14102614 product_1 pty_country_9 +2014102614 14102614 product_2 pty_country_48 +2014102614 14102614 product_3 pty_country_34 +2014102614 14102614 product_4 pty_country_39 +2014102614 14102614 product_5 pty_country_9 +2014102614 14102614 product_3 pty_country_22 +2014102615 14102615 product_5 pty_country_34 +2014102615 14102615 product_3 pty_country_20 +2014102615 14102615 product_4 pty_country_34 +2014102615 14102615 product_1 pty_country_19 +2014102615 14102615 product_2 pty_country_1 +2014102615 14102615 product_5 pty_country_24 +2014102615 14102615 product_3 pty_country_27 +2014102615 14102615 product_3 pty_country_23 +2014102615 14102615 product_3 pty_country_39 +2014102615 14102615 product_1 pty_country_47 +2014102616 14102616 product_4 pty_country_6 +2014102616 14102616 product_1 pty_country_35 +2014102616 14102616 product_3 pty_country_30 +2014102616 14102616 product_4 pty_country_42 +2014102616 14102616 product_5 pty_country_45 +2014102616 14102616 product_5 pty_country_33 +2014102616 14102616 product_4 pty_country_49 +2014102616 14102616 product_2 pty_country_7 +2014102616 14102616 product_1 pty_country_41 +2014102616 14102616 product_2 pty_country_48 +2014102617 14102617 product_4 pty_country_46 +2014102617 14102617 product_4 pty_country_25 +2014102617 14102617 product_1 pty_country_49 +2014102617 14102617 product_3 pty_country_32 +2014102617 14102617 product_5 pty_country_27 +2014102617 14102617 product_5 pty_country_40 +2014102617 14102617 product_4 pty_country_16 +2014102617 14102617 product_3 pty_country_48 +2014102617 14102617 product_3 pty_country_40 +2014102617 14102617 product_5 pty_country_39 +2014102618 14102618 product_3 pty_country_8 +2014102618 14102618 product_5 pty_country_7 +2014102618 14102618 product_5 pty_country_6 +2014102618 14102618 product_1 pty_country_30 +2014102618 14102618 product_4 pty_country_30 +2014102618 14102618 product_1 pty_country_42 +2014102618 14102618 product_2 pty_country_43 +2014102618 14102618 product_1 pty_country_16 +2014102618 14102618 product_3 pty_country_4 +2014102618 14102618 product_1 pty_country_2 +2014102619 14102619 product_5 pty_country_39 +2014102619 14102619 product_1 pty_country_22 +2014102619 14102619 product_1 pty_country_39 +2014102619 14102619 product_2 pty_country_50 +2014102619 14102619 product_4 pty_country_28 +2014102619 14102619 product_3 pty_country_3 +2014102619 14102619 product_3 pty_country_39 +2014102619 14102619 product_5 pty_country_4 +2014102619 14102619 product_5 pty_country_4 +2014102619 14102619 product_3 pty_country_4 +2014102620 14102620 product_4 pty_country_31 +2014102620 14102620 product_2 pty_country_12 +2014102620 14102620 product_1 pty_country_19 +2014102620 14102620 product_5 pty_country_4 +2014102620 14102620 product_2 pty_country_21 +2014102620 14102620 product_2 pty_country_47 +2014102620 14102620 product_4 pty_country_46 +2014102620 14102620 product_1 pty_country_17 +2014102620 14102620 product_4 pty_country_43 +2014102620 14102620 product_5 pty_country_12 +2014102621 14102621 product_4 pty_country_31 +2014102621 14102621 product_3 pty_country_30 +2014102621 14102621 product_2 pty_country_39 +2014102621 14102621 product_3 pty_country_44 +2014102621 14102621 product_4 pty_country_24 +2014102621 14102621 product_3 pty_country_11 +2014102621 14102621 product_4 pty_country_12 +2014102621 14102621 product_4 pty_country_47 +2014102621 14102621 product_4 pty_country_19 +2014102621 14102621 product_2 pty_country_23 +2014102622 14102622 product_5 pty_country_37 +2014102622 14102622 product_2 pty_country_13 +2014102622 14102622 product_5 pty_country_26 +2014102622 14102622 product_5 pty_country_12 +2014102622 14102622 product_2 pty_country_16 +2014102622 14102622 product_2 pty_country_34 +2014102622 14102622 product_4 pty_country_41 +2014102622 14102622 product_4 pty_country_25 +2014102622 14102622 product_2 pty_country_9 +2014102622 14102622 product_2 pty_country_44 +2014102623 14102623 product_1 pty_country_14 +2014102623 14102623 product_1 pty_country_9 +2014102623 14102623 product_3 pty_country_47 +2014102623 14102623 product_4 pty_country_36 +2014102623 14102623 product_2 pty_country_47 +2014102623 14102623 product_2 pty_country_3 +2014102623 14102623 product_2 pty_country_23 +2014102623 14102623 product_2 pty_country_10 +2014102623 14102623 product_3 pty_country_49 +2014102623 14102623 product_1 pty_country_18 +2014102700 14102700 product_3 pty_country_34 +2014102700 14102700 product_4 pty_country_50 +2014102700 14102700 product_4 pty_country_15 +2014102700 14102700 product_4 pty_country_13 +2014102700 14102700 product_1 pty_country_17 +2014102700 14102700 product_5 pty_country_42 +2014102700 14102700 product_3 pty_country_24 +2014102700 14102700 product_2 pty_country_25 +2014102700 14102700 product_1 pty_country_24 +2014102700 14102700 product_1 pty_country_5 +2014102701 14102701 product_3 pty_country_18 +2014102701 14102701 product_1 pty_country_33 +2014102701 14102701 product_1 pty_country_9 +2014102701 14102701 product_4 pty_country_12 +2014102701 14102701 product_3 pty_country_19 +2014102701 14102701 product_3 pty_country_38 +2014102701 14102701 product_3 pty_country_48 +2014102701 14102701 product_5 pty_country_6 +2014102701 14102701 product_2 pty_country_42 +2014102701 14102701 product_4 pty_country_41 +2014102702 14102702 product_3 pty_country_12 +2014102702 14102702 product_3 pty_country_30 +2014102702 14102702 product_4 pty_country_28 +2014102702 14102702 product_2 pty_country_21 +2014102702 14102702 product_1 pty_country_23 +2014102702 14102702 product_4 pty_country_35 +2014102702 14102702 product_1 pty_country_7 +2014102702 14102702 product_3 pty_country_1 +2014102702 14102702 product_2 pty_country_33 +2014102702 14102702 product_1 pty_country_25 +2014102703 14102703 product_2 pty_country_4 +2014102703 14102703 product_3 pty_country_27 +2014102703 14102703 product_1 pty_country_2 +2014102703 14102703 product_1 pty_country_43 +2014102703 14102703 product_2 pty_country_4 +2014102703 14102703 product_4 pty_country_4 +2014102703 14102703 product_2 pty_country_32 +2014102703 14102703 product_4 pty_country_49 +2014102703 14102703 product_4 pty_country_13 +2014102703 14102703 product_2 pty_country_3 +2014102704 14102704 product_1 pty_country_36 +2014102704 14102704 product_2 pty_country_12 +2014102704 14102704 product_1 pty_country_35 +2014102704 14102704 product_3 pty_country_25 +2014102704 14102704 product_3 pty_country_33 +2014102704 14102704 product_4 pty_country_37 +2014102704 14102704 product_5 pty_country_8 +2014102704 14102704 product_1 pty_country_25 +2014102704 14102704 product_3 pty_country_38 +2014102704 14102704 product_1 pty_country_3 +2014102705 14102705 product_3 pty_country_8 +2014102705 14102705 product_2 pty_country_46 +2014102705 14102705 product_5 pty_country_24 +2014102705 14102705 product_4 pty_country_16 +2014102705 14102705 product_2 pty_country_13 +2014102705 14102705 product_4 pty_country_12 +2014102705 14102705 product_5 pty_country_38 +2014102705 14102705 product_3 pty_country_9 +2014102705 14102705 product_3 pty_country_27 +2014102705 14102705 product_3 pty_country_28 +2014102706 14102706 product_5 pty_country_35 +2014102706 14102706 product_1 pty_country_2 +2014102706 14102706 product_1 pty_country_7 +2014102706 14102706 product_4 pty_country_10 +2014102706 14102706 product_1 pty_country_40 +2014102706 14102706 product_5 pty_country_15 +2014102706 14102706 product_2 pty_country_44 +2014102706 14102706 product_2 pty_country_36 +2014102706 14102706 product_4 pty_country_21 +2014102706 14102706 product_4 pty_country_29 +2014102707 14102707 product_4 pty_country_25 +2014102707 14102707 product_3 pty_country_17 +2014102707 14102707 product_3 pty_country_39 +2014102707 14102707 product_5 pty_country_21 +2014102707 14102707 product_2 pty_country_50 +2014102707 14102707 product_2 pty_country_8 +2014102707 14102707 product_3 pty_country_2 +2014102707 14102707 product_3 pty_country_41 +2014102707 14102707 product_4 pty_country_16 +2014102707 14102707 product_4 pty_country_17 +2014102708 14102708 product_1 pty_country_1 +2014102708 14102708 product_2 pty_country_33 +2014102708 14102708 product_3 pty_country_37 +2014102708 14102708 product_3 pty_country_50 +2014102708 14102708 product_4 pty_country_20 +2014102708 14102708 product_4 pty_country_34 +2014102708 14102708 product_1 pty_country_13 +2014102708 14102708 product_2 pty_country_23 +2014102708 14102708 product_1 pty_country_42 +2014102708 14102708 product_2 pty_country_35 +2014102709 14102709 product_1 pty_country_48 +2014102709 14102709 product_2 pty_country_11 +2014102709 14102709 product_1 pty_country_17 +2014102709 14102709 product_5 pty_country_41 +2014102709 14102709 product_3 pty_country_8 +2014102709 14102709 product_2 pty_country_41 +2014102709 14102709 product_5 pty_country_13 +2014102709 14102709 product_5 pty_country_22 +2014102709 14102709 product_5 pty_country_5 +2014102709 14102709 product_2 pty_country_47 +2014102710 14102710 product_1 pty_country_16 +2014102710 14102710 product_4 pty_country_21 +2014102710 14102710 product_1 pty_country_20 +2014102710 14102710 product_3 pty_country_42 +2014102710 14102710 product_3 pty_country_9 +2014102710 14102710 product_3 pty_country_47 +2014102710 14102710 product_4 pty_country_2 +2014102710 14102710 product_5 pty_country_33 +2014102710 14102710 product_4 pty_country_32 +2014102710 14102710 product_5 pty_country_49 +2014102711 14102711 product_1 pty_country_37 +2014102711 14102711 product_5 pty_country_10 +2014102711 14102711 product_2 pty_country_16 +2014102711 14102711 product_4 pty_country_45 +2014102711 14102711 product_3 pty_country_11 +2014102711 14102711 product_3 pty_country_33 +2014102711 14102711 product_2 pty_country_33 +2014102711 14102711 product_1 pty_country_18 +2014102711 14102711 product_4 pty_country_27 +2014102711 14102711 product_5 pty_country_31 +2014102712 14102712 product_4 pty_country_44 +2014102712 14102712 product_3 pty_country_40 +2014102712 14102712 product_1 pty_country_21 +2014102712 14102712 product_3 pty_country_40 +2014102712 14102712 product_4 pty_country_4 +2014102712 14102712 product_2 pty_country_31 +2014102712 14102712 product_5 pty_country_27 +2014102712 14102712 product_3 pty_country_38 +2014102712 14102712 product_2 pty_country_1 +2014102712 14102712 product_4 pty_country_19 +2014102713 14102713 product_3 pty_country_46 +2014102713 14102713 product_1 pty_country_11 +2014102713 14102713 product_1 pty_country_20 +2014102713 14102713 product_1 pty_country_8 +2014102713 14102713 product_4 pty_country_45 +2014102713 14102713 product_2 pty_country_46 +2014102713 14102713 product_3 pty_country_8 +2014102713 14102713 product_1 pty_country_49 +2014102713 14102713 product_4 pty_country_18 +2014102713 14102713 product_4 pty_country_42 +2014102714 14102714 product_2 pty_country_12 +2014102714 14102714 product_4 pty_country_13 +2014102714 14102714 product_5 pty_country_27 +2014102714 14102714 product_5 pty_country_2 +2014102714 14102714 product_4 pty_country_20 +2014102714 14102714 product_5 pty_country_10 +2014102714 14102714 product_5 pty_country_49 +2014102714 14102714 product_1 pty_country_39 +2014102714 14102714 product_3 pty_country_2 +2014102714 14102714 product_2 pty_country_36 +2014102715 14102715 product_1 pty_country_37 +2014102715 14102715 product_1 pty_country_37 +2014102715 14102715 product_2 pty_country_16 +2014102715 14102715 product_1 pty_country_39 +2014102715 14102715 product_1 pty_country_34 +2014102715 14102715 product_2 pty_country_25 +2014102715 14102715 product_1 pty_country_8 +2014102715 14102715 product_5 pty_country_50 +2014102715 14102715 product_1 pty_country_39 +2014102715 14102715 product_5 pty_country_27 +2014102716 14102716 product_4 pty_country_5 +2014102716 14102716 product_3 pty_country_31 +2014102716 14102716 product_3 pty_country_36 +2014102716 14102716 product_4 pty_country_26 +2014102716 14102716 product_5 pty_country_31 +2014102716 14102716 product_2 pty_country_18 +2014102716 14102716 product_1 pty_country_22 +2014102716 14102716 product_4 pty_country_4 +2014102716 14102716 product_1 pty_country_43 +2014102716 14102716 product_4 pty_country_18 +2014102717 14102717 product_1 pty_country_12 +2014102717 14102717 product_5 pty_country_24 +2014102717 14102717 product_2 pty_country_28 +2014102717 14102717 product_5 pty_country_45 +2014102717 14102717 product_3 pty_country_31 +2014102717 14102717 product_2 pty_country_19 +2014102717 14102717 product_5 pty_country_20 +2014102717 14102717 product_4 pty_country_41 +2014102717 14102717 product_3 pty_country_12 +2014102717 14102717 product_1 pty_country_41 +2014102718 14102718 product_3 pty_country_3 +2014102718 14102718 product_1 pty_country_25 +2014102718 14102718 product_4 pty_country_21 +2014102718 14102718 product_5 pty_country_2 +2014102718 14102718 product_4 pty_country_7 +2014102718 14102718 product_5 pty_country_11 +2014102718 14102718 product_2 pty_country_12 +2014102718 14102718 product_4 pty_country_24 +2014102718 14102718 product_2 pty_country_25 +2014102718 14102718 product_4 pty_country_42 +2014102719 14102719 product_5 pty_country_45 +2014102719 14102719 product_4 pty_country_44 +2014102719 14102719 product_5 pty_country_32 +2014102719 14102719 product_3 pty_country_31 +2014102719 14102719 product_5 pty_country_27 +2014102719 14102719 product_4 pty_country_24 +2014102719 14102719 product_3 pty_country_48 +2014102719 14102719 product_5 pty_country_6 +2014102719 14102719 product_2 pty_country_14 +2014102719 14102719 product_1 pty_country_2 +2014102720 14102720 product_4 pty_country_49 +2014102720 14102720 product_4 pty_country_4 +2014102720 14102720 product_4 pty_country_29 +2014102720 14102720 product_5 pty_country_35 +2014102720 14102720 product_3 pty_country_47 +2014102720 14102720 product_4 pty_country_16 +2014102720 14102720 product_1 pty_country_18 +2014102720 14102720 product_3 pty_country_28 +2014102720 14102720 product_5 pty_country_11 +2014102720 14102720 product_1 pty_country_26 +2014102721 14102721 product_1 pty_country_34 +2014102721 14102721 product_1 pty_country_9 +2014102721 14102721 product_5 pty_country_10 +2014102721 14102721 product_4 pty_country_21 +2014102721 14102721 product_3 pty_country_12 +2014102721 14102721 product_5 pty_country_36 +2014102721 14102721 product_4 pty_country_43 +2014102721 14102721 product_5 pty_country_34 +2014102721 14102721 product_1 pty_country_8 +2014102721 14102721 product_4 pty_country_24 +2014102722 14102722 product_2 pty_country_26 +2014102722 14102722 product_2 pty_country_20 +2014102722 14102722 product_4 pty_country_39 +2014102722 14102722 product_5 pty_country_39 +2014102722 14102722 product_5 pty_country_4 +2014102722 14102722 product_2 pty_country_12 +2014102722 14102722 product_4 pty_country_13 +2014102722 14102722 product_5 pty_country_41 +2014102722 14102722 product_3 pty_country_15 +2014102722 14102722 product_3 pty_country_4 +2014102723 14102723 product_1 pty_country_25 +2014102723 14102723 product_3 pty_country_4 +2014102723 14102723 product_5 pty_country_4 +2014102723 14102723 product_5 pty_country_36 +2014102723 14102723 product_4 pty_country_11 +2014102723 14102723 product_3 pty_country_31 +2014102723 14102723 product_3 pty_country_47 +2014102723 14102723 product_5 pty_country_39 +2014102723 14102723 product_3 pty_country_7 +2014102723 14102723 product_4 pty_country_37 +2014102800 14102800 product_4 pty_country_28 +2014102800 14102800 product_2 pty_country_45 +2014102800 14102800 product_5 pty_country_28 +2014102800 14102800 product_2 pty_country_44 +2014102800 14102800 product_4 pty_country_39 +2014102800 14102800 product_2 pty_country_20 +2014102800 14102800 product_5 pty_country_41 +2014102800 14102800 product_2 pty_country_43 +2014102800 14102800 product_2 pty_country_45 +2014102800 14102800 product_3 pty_country_30 +2014102801 14102801 product_3 pty_country_19 +2014102801 14102801 product_1 pty_country_5 +2014102801 14102801 product_2 pty_country_15 +2014102801 14102801 product_1 pty_country_15 +2014102801 14102801 product_4 pty_country_25 +2014102801 14102801 product_4 pty_country_17 +2014102801 14102801 product_2 pty_country_13 +2014102801 14102801 product_2 pty_country_22 +2014102801 14102801 product_1 pty_country_46 +2014102801 14102801 product_4 pty_country_2 +2014102802 14102802 product_5 pty_country_13 +2014102802 14102802 product_1 pty_country_8 +2014102802 14102802 product_3 pty_country_44 +2014102802 14102802 product_3 pty_country_34 +2014102802 14102802 product_3 pty_country_8 +2014102802 14102802 product_3 pty_country_30 +2014102802 14102802 product_5 pty_country_8 +2014102802 14102802 product_5 pty_country_42 +2014102802 14102802 product_1 pty_country_38 +2014102802 14102802 product_5 pty_country_28 +2014102803 14102803 product_5 pty_country_23 +2014102803 14102803 product_3 pty_country_7 +2014102803 14102803 product_2 pty_country_33 +2014102803 14102803 product_4 pty_country_48 +2014102803 14102803 product_4 pty_country_47 +2014102803 14102803 product_5 pty_country_41 +2014102803 14102803 product_3 pty_country_40 +2014102803 14102803 product_5 pty_country_32 +2014102803 14102803 product_4 pty_country_26 +2014102803 14102803 product_2 pty_country_43 +2014102804 14102804 product_3 pty_country_49 +2014102804 14102804 product_2 pty_country_39 +2014102804 14102804 product_5 pty_country_48 +2014102804 14102804 product_4 pty_country_49 +2014102804 14102804 product_5 pty_country_8 +2014102804 14102804 product_1 pty_country_3 +2014102804 14102804 product_4 pty_country_26 +2014102804 14102804 product_2 pty_country_30 +2014102804 14102804 product_4 pty_country_32 +2014102804 14102804 product_5 pty_country_12 +2014102805 14102805 product_4 pty_country_12 +2014102805 14102805 product_2 pty_country_15 +2014102805 14102805 product_5 pty_country_7 +2014102805 14102805 product_3 pty_country_32 +2014102805 14102805 product_1 pty_country_7 +2014102805 14102805 product_5 pty_country_36 +2014102805 14102805 product_2 pty_country_25 +2014102805 14102805 product_1 pty_country_29 +2014102805 14102805 product_4 pty_country_33 +2014102805 14102805 product_3 pty_country_40 +2014102806 14102806 product_5 pty_country_11 +2014102806 14102806 product_3 pty_country_36 +2014102806 14102806 product_3 pty_country_35 +2014102806 14102806 product_4 pty_country_18 +2014102806 14102806 product_5 pty_country_44 +2014102806 14102806 product_1 pty_country_23 +2014102806 14102806 product_4 pty_country_34 +2014102806 14102806 product_4 pty_country_33 +2014102806 14102806 product_1 pty_country_31 +2014102806 14102806 product_1 pty_country_49 +2014102807 14102807 product_3 pty_country_22 +2014102807 14102807 product_4 pty_country_16 +2014102807 14102807 product_4 pty_country_16 +2014102807 14102807 product_2 pty_country_17 +2014102807 14102807 product_4 pty_country_24 +2014102807 14102807 product_4 pty_country_50 +2014102807 14102807 product_5 pty_country_9 +2014102807 14102807 product_1 pty_country_36 +2014102807 14102807 product_1 pty_country_47 +2014102807 14102807 product_5 pty_country_46 +2014102808 14102808 product_3 pty_country_2 +2014102808 14102808 product_4 pty_country_50 +2014102808 14102808 product_5 pty_country_40 +2014102808 14102808 product_1 pty_country_21 +2014102808 14102808 product_4 pty_country_7 +2014102808 14102808 product_4 pty_country_25 +2014102808 14102808 product_5 pty_country_1 +2014102808 14102808 product_4 pty_country_36 +2014102808 14102808 product_4 pty_country_3 +2014102808 14102808 product_5 pty_country_25 +2014102809 14102809 product_4 pty_country_34 +2014102809 14102809 product_4 pty_country_48 +2014102809 14102809 product_5 pty_country_6 +2014102809 14102809 product_3 pty_country_49 +2014102809 14102809 product_3 pty_country_1 +2014102809 14102809 product_5 pty_country_3 +2014102809 14102809 product_2 pty_country_7 +2014102809 14102809 product_5 pty_country_49 +2014102809 14102809 product_3 pty_country_12 +2014102809 14102809 product_5 pty_country_50 +2014102810 14102810 product_1 pty_country_1 +2014102810 14102810 product_1 pty_country_5 +2014102810 14102810 product_1 pty_country_15 +2014102810 14102810 product_1 pty_country_15 +2014102810 14102810 product_4 pty_country_30 +2014102810 14102810 product_1 pty_country_3 +2014102810 14102810 product_1 pty_country_45 +2014102810 14102810 product_2 pty_country_42 +2014102810 14102810 product_5 pty_country_30 +2014102810 14102810 product_5 pty_country_1 +2014102811 14102811 product_2 pty_country_25 +2014102811 14102811 product_4 pty_country_38 +2014102811 14102811 product_1 pty_country_7 +2014102811 14102811 product_3 pty_country_45 +2014102811 14102811 product_2 pty_country_25 +2014102811 14102811 product_3 pty_country_46 +2014102811 14102811 product_3 pty_country_18 +2014102811 14102811 product_3 pty_country_18 +2014102811 14102811 product_4 pty_country_24 +2014102811 14102811 product_5 pty_country_31 +2014102812 14102812 product_1 pty_country_39 +2014102812 14102812 product_1 pty_country_28 +2014102812 14102812 product_1 pty_country_14 +2014102812 14102812 product_4 pty_country_22 +2014102812 14102812 product_5 pty_country_37 +2014102812 14102812 product_4 pty_country_48 +2014102812 14102812 product_3 pty_country_26 +2014102812 14102812 product_1 pty_country_50 +2014102812 14102812 product_5 pty_country_22 +2014102812 14102812 product_4 pty_country_50 +2014102813 14102813 product_1 pty_country_34 +2014102813 14102813 product_3 pty_country_19 +2014102813 14102813 product_4 pty_country_26 +2014102813 14102813 product_3 pty_country_23 +2014102813 14102813 product_1 pty_country_17 +2014102813 14102813 product_1 pty_country_37 +2014102813 14102813 product_4 pty_country_22 +2014102813 14102813 product_1 pty_country_39 +2014102813 14102813 product_5 pty_country_43 +2014102813 14102813 product_2 pty_country_15 +2014102814 14102814 product_5 pty_country_28 +2014102814 14102814 product_3 pty_country_19 +2014102814 14102814 product_4 pty_country_34 +2014102814 14102814 product_3 pty_country_36 +2014102814 14102814 product_2 pty_country_14 +2014102814 14102814 product_2 pty_country_33 +2014102814 14102814 product_1 pty_country_6 +2014102814 14102814 product_1 pty_country_12 +2014102814 14102814 product_5 pty_country_15 +2014102814 14102814 product_2 pty_country_25 +2014102815 14102815 product_2 pty_country_8 +2014102815 14102815 product_4 pty_country_45 +2014102815 14102815 product_4 pty_country_30 +2014102815 14102815 product_3 pty_country_39 +2014102815 14102815 product_2 pty_country_6 +2014102815 14102815 product_4 pty_country_34 +2014102815 14102815 product_2 pty_country_35 +2014102815 14102815 product_5 pty_country_36 +2014102815 14102815 product_1 pty_country_36 +2014102815 14102815 product_5 pty_country_40 +2014102816 14102816 product_2 pty_country_26 +2014102816 14102816 product_2 pty_country_45 +2014102816 14102816 product_3 pty_country_26 +2014102816 14102816 product_1 pty_country_19 +2014102816 14102816 product_4 pty_country_7 +2014102816 14102816 product_4 pty_country_9 +2014102816 14102816 product_4 pty_country_9 +2014102816 14102816 product_3 pty_country_31 +2014102816 14102816 product_5 pty_country_27 +2014102816 14102816 product_2 pty_country_36 +2014102817 14102817 product_5 pty_country_46 +2014102817 14102817 product_1 pty_country_39 +2014102817 14102817 product_3 pty_country_31 +2014102817 14102817 product_1 pty_country_38 +2014102817 14102817 product_5 pty_country_25 +2014102817 14102817 product_4 pty_country_37 +2014102817 14102817 product_1 pty_country_20 +2014102817 14102817 product_2 pty_country_1 +2014102817 14102817 product_1 pty_country_17 +2014102817 14102817 product_1 pty_country_43 +2014102818 14102818 product_2 pty_country_44 +2014102818 14102818 product_4 pty_country_33 +2014102818 14102818 product_2 pty_country_28 +2014102818 14102818 product_2 pty_country_32 +2014102818 14102818 product_4 pty_country_37 +2014102818 14102818 product_2 pty_country_18 +2014102818 14102818 product_1 pty_country_8 +2014102818 14102818 product_5 pty_country_45 +2014102818 14102818 product_3 pty_country_3 +2014102818 14102818 product_4 pty_country_9 +2014102819 14102819 product_1 pty_country_34 +2014102819 14102819 product_1 pty_country_37 +2014102819 14102819 product_3 pty_country_29 +2014102819 14102819 product_4 pty_country_36 +2014102819 14102819 product_3 pty_country_46 +2014102819 14102819 product_1 pty_country_16 +2014102819 14102819 product_1 pty_country_19 +2014102819 14102819 product_4 pty_country_12 +2014102819 14102819 product_1 pty_country_4 +2014102819 14102819 product_1 pty_country_36 +2014102820 14102820 product_2 pty_country_19 +2014102820 14102820 product_5 pty_country_26 +2014102820 14102820 product_4 pty_country_16 +2014102820 14102820 product_4 pty_country_5 +2014102820 14102820 product_3 pty_country_36 +2014102820 14102820 product_5 pty_country_5 +2014102820 14102820 product_5 pty_country_3 +2014102820 14102820 product_2 pty_country_42 +2014102820 14102820 product_5 pty_country_50 +2014102820 14102820 product_1 pty_country_44 +2014102821 14102821 product_3 pty_country_24 +2014102821 14102821 product_4 pty_country_14 +2014102821 14102821 product_5 pty_country_8 +2014102821 14102821 product_4 pty_country_49 +2014102821 14102821 product_2 pty_country_26 +2014102821 14102821 product_5 pty_country_31 +2014102821 14102821 product_5 pty_country_28 +2014102821 14102821 product_5 pty_country_18 +2014102821 14102821 product_4 pty_country_14 +2014102821 14102821 product_2 pty_country_30 +2014102822 14102822 product_4 pty_country_43 +2014102822 14102822 product_4 pty_country_7 +2014102822 14102822 product_1 pty_country_22 +2014102822 14102822 product_1 pty_country_8 +2014102822 14102822 product_3 pty_country_7 +2014102822 14102822 product_2 pty_country_38 +2014102822 14102822 product_5 pty_country_36 +2014102822 14102822 product_3 pty_country_49 +2014102822 14102822 product_1 pty_country_5 +2014102822 14102822 product_1 pty_country_48 +2014102823 14102823 product_2 pty_country_11 +2014102823 14102823 product_4 pty_country_20 +2014102823 14102823 product_1 pty_country_31 +2014102823 14102823 product_2 pty_country_8 +2014102823 14102823 product_2 pty_country_45 +2014102823 14102823 product_1 pty_country_41 +2014102823 14102823 product_2 pty_country_32 +2014102823 14102823 product_5 pty_country_6 +2014102823 14102823 product_5 pty_country_47 +2014102823 14102823 product_4 pty_country_18 +2014102900 14102900 product_1 pty_country_32 +2014102900 14102900 product_2 pty_country_26 +2014102900 14102900 product_2 pty_country_30 +2014102900 14102900 product_2 pty_country_22 +2014102900 14102900 product_1 pty_country_32 +2014102900 14102900 product_3 pty_country_44 +2014102900 14102900 product_3 pty_country_44 +2014102900 14102900 product_1 pty_country_37 +2014102900 14102900 product_3 pty_country_34 +2014102900 14102900 product_4 pty_country_3 +2014102901 14102901 product_2 pty_country_26 +2014102901 14102901 product_5 pty_country_29 +2014102901 14102901 product_1 pty_country_45 +2014102901 14102901 product_2 pty_country_25 +2014102901 14102901 product_1 pty_country_20 +2014102901 14102901 product_3 pty_country_38 +2014102901 14102901 product_2 pty_country_17 +2014102901 14102901 product_2 pty_country_19 +2014102901 14102901 product_1 pty_country_3 +2014102901 14102901 product_2 pty_country_39 +2014102902 14102902 product_1 pty_country_16 +2014102902 14102902 product_1 pty_country_13 +2014102902 14102902 product_2 pty_country_47 +2014102902 14102902 product_5 pty_country_40 +2014102902 14102902 product_4 pty_country_46 +2014102902 14102902 product_2 pty_country_30 +2014102902 14102902 product_3 pty_country_13 +2014102902 14102902 product_4 pty_country_48 +2014102902 14102902 product_4 pty_country_47 +2014102902 14102902 product_4 pty_country_23 +2014102903 14102903 product_4 pty_country_46 +2014102903 14102903 product_1 pty_country_2 +2014102903 14102903 product_3 pty_country_22 +2014102903 14102903 product_4 pty_country_9 +2014102903 14102903 product_2 pty_country_25 +2014102903 14102903 product_5 pty_country_43 +2014102903 14102903 product_5 pty_country_33 +2014102903 14102903 product_2 pty_country_39 +2014102903 14102903 product_4 pty_country_13 +2014102903 14102903 product_1 pty_country_24 +2014102904 14102904 product_3 pty_country_49 +2014102904 14102904 product_1 pty_country_17 +2014102904 14102904 product_1 pty_country_26 +2014102904 14102904 product_2 pty_country_9 +2014102904 14102904 product_5 pty_country_45 +2014102904 14102904 product_1 pty_country_30 +2014102904 14102904 product_1 pty_country_8 +2014102904 14102904 product_3 pty_country_19 +2014102904 14102904 product_1 pty_country_36 +2014102904 14102904 product_2 pty_country_17 +2014102905 14102905 product_5 pty_country_45 +2014102905 14102905 product_3 pty_country_14 +2014102905 14102905 product_3 pty_country_35 +2014102905 14102905 product_3 pty_country_8 +2014102905 14102905 product_5 pty_country_42 +2014102905 14102905 product_2 pty_country_7 +2014102905 14102905 product_3 pty_country_15 +2014102905 14102905 product_3 pty_country_44 +2014102905 14102905 product_4 pty_country_3 +2014102905 14102905 product_3 pty_country_49 +2014102906 14102906 product_5 pty_country_25 +2014102906 14102906 product_4 pty_country_34 +2014102906 14102906 product_1 pty_country_42 +2014102906 14102906 product_4 pty_country_42 +2014102906 14102906 product_2 pty_country_18 +2014102906 14102906 product_3 pty_country_27 +2014102906 14102906 product_5 pty_country_27 +2014102906 14102906 product_2 pty_country_45 +2014102906 14102906 product_4 pty_country_45 +2014102906 14102906 product_4 pty_country_16 +2014102907 14102907 product_2 pty_country_37 +2014102907 14102907 product_2 pty_country_48 +2014102907 14102907 product_1 pty_country_8 +2014102907 14102907 product_5 pty_country_48 +2014102907 14102907 product_4 pty_country_49 +2014102907 14102907 product_2 pty_country_1 +2014102907 14102907 product_1 pty_country_23 +2014102907 14102907 product_2 pty_country_46 +2014102907 14102907 product_5 pty_country_11 +2014102907 14102907 product_3 pty_country_4 +2014102908 14102908 product_2 pty_country_1 +2014102908 14102908 product_5 pty_country_9 +2014102908 14102908 product_2 pty_country_1 +2014102908 14102908 product_5 pty_country_9 +2014102908 14102908 product_4 pty_country_40 +2014102908 14102908 product_3 pty_country_41 +2014102908 14102908 product_3 pty_country_15 +2014102908 14102908 product_5 pty_country_45 +2014102908 14102908 product_3 pty_country_3 +2014102908 14102908 product_4 pty_country_48 +2014102909 14102909 product_5 pty_country_37 +2014102909 14102909 product_5 pty_country_30 +2014102909 14102909 product_4 pty_country_50 +2014102909 14102909 product_3 pty_country_46 +2014102909 14102909 product_2 pty_country_35 +2014102909 14102909 product_1 pty_country_28 +2014102909 14102909 product_4 pty_country_1 +2014102909 14102909 product_2 pty_country_10 +2014102909 14102909 product_4 pty_country_2 +2014102909 14102909 product_4 pty_country_11 +2014102910 14102910 product_2 pty_country_10 +2014102910 14102910 product_3 pty_country_39 +2014102910 14102910 product_3 pty_country_32 +2014102910 14102910 product_3 pty_country_30 +2014102910 14102910 product_1 pty_country_6 +2014102910 14102910 product_1 pty_country_41 +2014102910 14102910 product_5 pty_country_48 +2014102910 14102910 product_5 pty_country_2 +2014102910 14102910 product_1 pty_country_11 +2014102910 14102910 product_5 pty_country_24 +2014102911 14102911 product_3 pty_country_9 +2014102911 14102911 product_1 pty_country_20 +2014102911 14102911 product_1 pty_country_41 +2014102911 14102911 product_3 pty_country_46 +2014102911 14102911 product_2 pty_country_49 +2014102911 14102911 product_4 pty_country_40 +2014102911 14102911 product_4 pty_country_29 +2014102911 14102911 product_1 pty_country_42 +2014102911 14102911 product_5 pty_country_37 +2014102911 14102911 product_3 pty_country_39 +2014102912 14102912 product_5 pty_country_50 +2014102912 14102912 product_4 pty_country_49 +2014102912 14102912 product_1 pty_country_2 +2014102912 14102912 product_3 pty_country_33 +2014102912 14102912 product_5 pty_country_28 +2014102912 14102912 product_2 pty_country_10 +2014102912 14102912 product_5 pty_country_7 +2014102912 14102912 product_2 pty_country_1 +2014102912 14102912 product_2 pty_country_28 +2014102912 14102912 product_5 pty_country_16 +2014102913 14102913 product_2 pty_country_30 +2014102913 14102913 product_1 pty_country_19 +2014102913 14102913 product_3 pty_country_25 +2014102913 14102913 product_1 pty_country_4 +2014102913 14102913 product_2 pty_country_34 +2014102913 14102913 product_2 pty_country_44 +2014102913 14102913 product_3 pty_country_44 +2014102913 14102913 product_4 pty_country_29 +2014102913 14102913 product_5 pty_country_3 +2014102913 14102913 product_4 pty_country_50 +2014102914 14102914 product_4 pty_country_18 +2014102914 14102914 product_2 pty_country_45 +2014102914 14102914 product_1 pty_country_15 +2014102914 14102914 product_3 pty_country_13 +2014102914 14102914 product_1 pty_country_28 +2014102914 14102914 product_3 pty_country_19 +2014102914 14102914 product_2 pty_country_29 +2014102914 14102914 product_1 pty_country_33 +2014102914 14102914 product_3 pty_country_31 +2014102914 14102914 product_1 pty_country_25 +2014102915 14102915 product_2 pty_country_20 +2014102915 14102915 product_3 pty_country_48 +2014102915 14102915 product_4 pty_country_47 +2014102915 14102915 product_5 pty_country_13 +2014102915 14102915 product_5 pty_country_28 +2014102915 14102915 product_3 pty_country_32 +2014102915 14102915 product_1 pty_country_27 +2014102915 14102915 product_5 pty_country_44 +2014102915 14102915 product_5 pty_country_45 +2014102915 14102915 product_1 pty_country_30 +2014102916 14102916 product_2 pty_country_44 +2014102916 14102916 product_2 pty_country_48 +2014102916 14102916 product_1 pty_country_17 +2014102916 14102916 product_5 pty_country_4 +2014102916 14102916 product_4 pty_country_19 +2014102916 14102916 product_5 pty_country_7 +2014102916 14102916 product_5 pty_country_42 +2014102916 14102916 product_5 pty_country_19 +2014102916 14102916 product_3 pty_country_13 +2014102916 14102916 product_5 pty_country_48 +2014102917 14102917 product_3 pty_country_47 +2014102917 14102917 product_2 pty_country_27 +2014102917 14102917 product_4 pty_country_22 +2014102917 14102917 product_1 pty_country_2 +2014102917 14102917 product_1 pty_country_36 +2014102917 14102917 product_2 pty_country_19 +2014102917 14102917 product_3 pty_country_45 +2014102917 14102917 product_5 pty_country_20 +2014102917 14102917 product_4 pty_country_9 +2014102917 14102917 product_2 pty_country_49 +2014102918 14102918 product_3 pty_country_19 +2014102918 14102918 product_3 pty_country_32 +2014102918 14102918 product_4 pty_country_2 +2014102918 14102918 product_4 pty_country_47 +2014102918 14102918 product_4 pty_country_31 +2014102918 14102918 product_5 pty_country_16 +2014102918 14102918 product_5 pty_country_17 +2014102918 14102918 product_5 pty_country_20 +2014102918 14102918 product_4 pty_country_4 +2014102918 14102918 product_4 pty_country_20 +2014102919 14102919 product_3 pty_country_1 +2014102919 14102919 product_5 pty_country_15 +2014102919 14102919 product_5 pty_country_45 +2014102919 14102919 product_3 pty_country_23 +2014102919 14102919 product_5 pty_country_1 +2014102919 14102919 product_1 pty_country_43 +2014102919 14102919 product_1 pty_country_6 +2014102919 14102919 product_3 pty_country_29 +2014102919 14102919 product_5 pty_country_24 +2014102919 14102919 product_5 pty_country_45 +2014102920 14102920 product_2 pty_country_17 +2014102920 14102920 product_3 pty_country_14 +2014102920 14102920 product_2 pty_country_40 +2014102920 14102920 product_2 pty_country_39 +2014102920 14102920 product_2 pty_country_28 +2014102920 14102920 product_2 pty_country_47 +2014102920 14102920 product_3 pty_country_12 +2014102920 14102920 product_5 pty_country_31 +2014102920 14102920 product_4 pty_country_3 +2014102920 14102920 product_2 pty_country_7 +2014102921 14102921 product_1 pty_country_43 +2014102921 14102921 product_1 pty_country_44 +2014102921 14102921 product_4 pty_country_40 +2014102921 14102921 product_5 pty_country_28 +2014102921 14102921 product_5 pty_country_28 +2014102921 14102921 product_2 pty_country_6 +2014102921 14102921 product_5 pty_country_19 +2014102921 14102921 product_3 pty_country_49 +2014102921 14102921 product_1 pty_country_41 +2014102921 14102921 product_3 pty_country_25 +2014102922 14102922 product_2 pty_country_29 +2014102922 14102922 product_4 pty_country_3 +2014102922 14102922 product_3 pty_country_29 +2014102922 14102922 product_1 pty_country_8 +2014102922 14102922 product_5 pty_country_31 +2014102922 14102922 product_5 pty_country_38 +2014102922 14102922 product_4 pty_country_46 +2014102922 14102922 product_3 pty_country_34 +2014102922 14102922 product_4 pty_country_39 +2014102922 14102922 product_4 pty_country_19 +2014102923 14102923 product_1 pty_country_24 +2014102923 14102923 product_5 pty_country_24 +2014102923 14102923 product_1 pty_country_41 +2014102923 14102923 product_1 pty_country_47 +2014102923 14102923 product_4 pty_country_44 +2014102923 14102923 product_3 pty_country_29 +2014102923 14102923 product_1 pty_country_18 +2014102923 14102923 product_1 pty_country_41 +2014102923 14102923 product_4 pty_country_45 +2014102923 14102923 product_1 pty_country_37 +2014103000 14103000 product_5 pty_country_26 +2014103000 14103000 product_3 pty_country_46 +2014103000 14103000 product_1 pty_country_28 +2014103000 14103000 product_5 pty_country_15 +2014103000 14103000 product_1 pty_country_41 +2014103000 14103000 product_5 pty_country_43 +2014103000 14103000 product_5 pty_country_36 +2014103000 14103000 product_1 pty_country_7 +2014103000 14103000 product_2 pty_country_25 +2014103000 14103000 product_2 pty_country_1 +2014103001 14103001 product_3 pty_country_6 +2014103001 14103001 product_5 pty_country_40 +2014103001 14103001 product_2 pty_country_17 +2014103001 14103001 product_1 pty_country_10 +2014103001 14103001 product_5 pty_country_8 +2014103001 14103001 product_4 pty_country_37 +2014103001 14103001 product_3 pty_country_34 +2014103001 14103001 product_3 pty_country_1 +2014103001 14103001 product_2 pty_country_2 +2014103001 14103001 product_2 pty_country_5 +2014103002 14103002 product_4 pty_country_33 +2014103002 14103002 product_2 pty_country_39 +2014103002 14103002 product_4 pty_country_19 +2014103002 14103002 product_3 pty_country_43 +2014103002 14103002 product_3 pty_country_20 +2014103002 14103002 product_2 pty_country_48 +2014103002 14103002 product_5 pty_country_35 +2014103002 14103002 product_3 pty_country_12 +2014103002 14103002 product_5 pty_country_5 +2014103002 14103002 product_5 pty_country_36 +2014103003 14103003 product_3 pty_country_27 +2014103003 14103003 product_2 pty_country_43 +2014103003 14103003 product_4 pty_country_11 +2014103003 14103003 product_5 pty_country_45 +2014103003 14103003 product_1 pty_country_12 +2014103003 14103003 product_5 pty_country_5 +2014103003 14103003 product_2 pty_country_17 +2014103003 14103003 product_1 pty_country_22 +2014103003 14103003 product_1 pty_country_18 +2014103003 14103003 product_4 pty_country_25 +2014103004 14103004 product_3 pty_country_28 +2014103004 14103004 product_2 pty_country_21 +2014103004 14103004 product_1 pty_country_31 +2014103004 14103004 product_2 pty_country_8 +2014103004 14103004 product_3 pty_country_9 +2014103004 14103004 product_3 pty_country_10 +2014103004 14103004 product_5 pty_country_21 +2014103004 14103004 product_1 pty_country_25 +2014103004 14103004 product_2 pty_country_39 +2014103004 14103004 product_2 pty_country_24 +2014103005 14103005 product_5 pty_country_33 +2014103005 14103005 product_5 pty_country_10 +2014103005 14103005 product_1 pty_country_5 +2014103005 14103005 product_1 pty_country_42 +2014103005 14103005 product_5 pty_country_26 +2014103005 14103005 product_1 pty_country_4 +2014103005 14103005 product_3 pty_country_39 +2014103005 14103005 product_3 pty_country_19 +2014103005 14103005 product_2 pty_country_23 +2014103005 14103005 product_1 pty_country_44 +2014103006 14103006 product_5 pty_country_7 +2014103006 14103006 product_2 pty_country_30 +2014103006 14103006 product_3 pty_country_32 +2014103006 14103006 product_3 pty_country_15 +2014103006 14103006 product_2 pty_country_31 +2014103006 14103006 product_3 pty_country_12 +2014103006 14103006 product_3 pty_country_20 +2014103006 14103006 product_2 pty_country_19 +2014103006 14103006 product_2 pty_country_44 +2014103006 14103006 product_5 pty_country_7 +2014103007 14103007 product_2 pty_country_13 +2014103007 14103007 product_3 pty_country_10 +2014103007 14103007 product_2 pty_country_3 +2014103007 14103007 product_1 pty_country_43 +2014103007 14103007 product_1 pty_country_27 +2014103007 14103007 product_3 pty_country_22 +2014103007 14103007 product_3 pty_country_28 +2014103007 14103007 product_3 pty_country_7 +2014103007 14103007 product_2 pty_country_42 +2014103007 14103007 product_2 pty_country_38 +2014103008 14103008 product_5 pty_country_26 +2014103008 14103008 product_5 pty_country_8 +2014103008 14103008 product_1 pty_country_42 +2014103008 14103008 product_3 pty_country_41 +2014103008 14103008 product_4 pty_country_32 +2014103008 14103008 product_1 pty_country_34 +2014103008 14103008 product_5 pty_country_6 +2014103008 14103008 product_4 pty_country_3 +2014103008 14103008 product_3 pty_country_4 +2014103008 14103008 product_4 pty_country_40 +2014103009 14103009 product_2 pty_country_17 +2014103009 14103009 product_2 pty_country_48 +2014103009 14103009 product_4 pty_country_45 +2014103009 14103009 product_3 pty_country_49 +2014103009 14103009 product_5 pty_country_4 +2014103009 14103009 product_5 pty_country_43 +2014103009 14103009 product_2 pty_country_37 +2014103009 14103009 product_1 pty_country_48 +2014103009 14103009 product_4 pty_country_39 +2014103009 14103009 product_1 pty_country_36 +2014103010 14103010 product_2 pty_country_37 +2014103010 14103010 product_4 pty_country_24 +2014103010 14103010 product_1 pty_country_32 +2014103010 14103010 product_1 pty_country_21 +2014103010 14103010 product_3 pty_country_47 +2014103010 14103010 product_3 pty_country_21 +2014103010 14103010 product_5 pty_country_48 +2014103010 14103010 product_3 pty_country_7 +2014103010 14103010 product_2 pty_country_22 +2014103010 14103010 product_1 pty_country_34 +2014103011 14103011 product_3 pty_country_21 +2014103011 14103011 product_1 pty_country_28 +2014103011 14103011 product_5 pty_country_21 +2014103011 14103011 product_3 pty_country_3 +2014103011 14103011 product_5 pty_country_18 +2014103011 14103011 product_5 pty_country_35 +2014103011 14103011 product_1 pty_country_33 +2014103011 14103011 product_2 pty_country_9 +2014103011 14103011 product_5 pty_country_22 +2014103011 14103011 product_3 pty_country_47 +2014103012 14103012 product_5 pty_country_32 +2014103012 14103012 product_4 pty_country_23 +2014103012 14103012 product_4 pty_country_19 +2014103012 14103012 product_1 pty_country_45 +2014103012 14103012 product_5 pty_country_17 +2014103012 14103012 product_3 pty_country_31 +2014103012 14103012 product_5 pty_country_46 +2014103012 14103012 product_2 pty_country_40 +2014103012 14103012 product_2 pty_country_31 +2014103012 14103012 product_2 pty_country_22 +2014103013 14103013 product_1 pty_country_45 +2014103013 14103013 product_3 pty_country_33 +2014103013 14103013 product_1 pty_country_40 +2014103013 14103013 product_5 pty_country_12 +2014103013 14103013 product_2 pty_country_23 +2014103013 14103013 product_1 pty_country_13 +2014103013 14103013 product_4 pty_country_28 +2014103013 14103013 product_2 pty_country_5 +2014103013 14103013 product_3 pty_country_40 +2014103013 14103013 product_5 pty_country_18 +2014103014 14103014 product_1 pty_country_22 +2014103014 14103014 product_4 pty_country_40 +2014103014 14103014 product_5 pty_country_36 +2014103014 14103014 product_3 pty_country_14 +2014103014 14103014 product_1 pty_country_36 +2014103014 14103014 product_3 pty_country_46 +2014103014 14103014 product_1 pty_country_29 +2014103014 14103014 product_3 pty_country_28 +2014103014 14103014 product_2 pty_country_15 +2014103014 14103014 product_4 pty_country_20 +2014103015 14103015 product_3 pty_country_32 +2014103015 14103015 product_2 pty_country_5 +2014103015 14103015 product_5 pty_country_13 +2014103015 14103015 product_5 pty_country_6 +2014103015 14103015 product_3 pty_country_10 +2014103015 14103015 product_3 pty_country_27 +2014103015 14103015 product_1 pty_country_31 +2014103015 14103015 product_4 pty_country_3 +2014103015 14103015 product_5 pty_country_25 +2014103015 14103015 product_1 pty_country_44 +2014103016 14103016 product_2 pty_country_33 +2014103016 14103016 product_3 pty_country_15 +2014103016 14103016 product_1 pty_country_33 +2014103016 14103016 product_5 pty_country_19 +2014103016 14103016 product_1 pty_country_23 +2014103016 14103016 product_5 pty_country_29 +2014103016 14103016 product_2 pty_country_4 +2014103016 14103016 product_4 pty_country_34 +2014103016 14103016 product_5 pty_country_27 +2014103016 14103016 product_1 pty_country_36 +2014103017 14103017 product_2 pty_country_1 +2014103017 14103017 product_2 pty_country_1 +2014103017 14103017 product_2 pty_country_10 +2014103017 14103017 product_1 pty_country_1 +2014103017 14103017 product_2 pty_country_27 +2014103017 14103017 product_4 pty_country_6 +2014103017 14103017 product_1 pty_country_6 +2014103017 14103017 product_1 pty_country_10 +2014103017 14103017 product_2 pty_country_44 +2014103017 14103017 product_1 pty_country_47 +2014103018 14103018 product_4 pty_country_31 +2014103018 14103018 product_3 pty_country_42 +2014103018 14103018 product_1 pty_country_44 +2014103018 14103018 product_1 pty_country_4 +2014103018 14103018 product_3 pty_country_46 +2014103018 14103018 product_3 pty_country_27 +2014103018 14103018 product_3 pty_country_28 +2014103018 14103018 product_3 pty_country_8 +2014103018 14103018 product_2 pty_country_20 +2014103018 14103018 product_2 pty_country_42 +2014103019 14103019 product_1 pty_country_31 +2014103019 14103019 product_5 pty_country_14 +2014103019 14103019 product_4 pty_country_40 +2014103019 14103019 product_5 pty_country_7 +2014103019 14103019 product_5 pty_country_9 +2014103019 14103019 product_4 pty_country_8 +2014103019 14103019 product_2 pty_country_4 +2014103019 14103019 product_1 pty_country_7 +2014103019 14103019 product_2 pty_country_45 +2014103019 14103019 product_3 pty_country_38 +2014103020 14103020 product_5 pty_country_22 +2014103020 14103020 product_3 pty_country_34 +2014103020 14103020 product_1 pty_country_16 +2014103020 14103020 product_1 pty_country_34 +2014103020 14103020 product_5 pty_country_11 +2014103020 14103020 product_4 pty_country_35 +2014103020 14103020 product_3 pty_country_22 +2014103020 14103020 product_1 pty_country_27 +2014103020 14103020 product_2 pty_country_43 +2014103020 14103020 product_3 pty_country_30 +2014103021 14103021 product_2 pty_country_50 +2014103021 14103021 product_1 pty_country_20 +2014103021 14103021 product_5 pty_country_33 +2014103021 14103021 product_5 pty_country_26 +2014103021 14103021 product_3 pty_country_26 +2014103021 14103021 product_3 pty_country_44 +2014103021 14103021 product_2 pty_country_40 +2014103021 14103021 product_2 pty_country_38 +2014103021 14103021 product_3 pty_country_4 +2014103021 14103021 product_5 pty_country_27 +2014103022 14103022 product_4 pty_country_26 +2014103022 14103022 product_5 pty_country_4 +2014103022 14103022 product_1 pty_country_32 +2014103022 14103022 product_2 pty_country_8 +2014103022 14103022 product_3 pty_country_49 +2014103022 14103022 product_3 pty_country_14 +2014103022 14103022 product_5 pty_country_10 +2014103022 14103022 product_2 pty_country_24 +2014103022 14103022 product_5 pty_country_20 +2014103022 14103022 product_5 pty_country_22 +2014103023 14103023 product_1 pty_country_30 +2014103023 14103023 product_3 pty_country_29 +2014103023 14103023 product_5 pty_country_26 +2014103023 14103023 product_3 pty_country_39 +2014103023 14103023 product_2 pty_country_31 +2014103023 14103023 product_2 pty_country_22 +2014103023 14103023 product_4 pty_country_3 +2014103023 14103023 product_3 pty_country_49 +2014103023 14103023 product_1 pty_country_38 +2014103023 14103023 product_3 pty_country_38 +2014103100 14103100 product_1 pty_country_22 +2014103100 14103100 product_3 pty_country_28 +2014103100 14103100 product_2 pty_country_33 +2014103100 14103100 product_2 pty_country_36 +2014103100 14103100 product_3 pty_country_49 +2014103100 14103100 product_5 pty_country_33 +2014103100 14103100 product_2 pty_country_49 +2014103100 14103100 product_3 pty_country_44 +2014103100 14103100 product_2 pty_country_44 +2014103100 14103100 product_1 pty_country_50 +2014103101 14103101 product_5 pty_country_24 +2014103101 14103101 product_4 pty_country_10 +2014103101 14103101 product_1 pty_country_21 +2014103101 14103101 product_5 pty_country_5 +2014103101 14103101 product_4 pty_country_49 +2014103101 14103101 product_5 pty_country_18 +2014103101 14103101 product_1 pty_country_40 +2014103101 14103101 product_4 pty_country_2 +2014103101 14103101 product_3 pty_country_25 +2014103101 14103101 product_5 pty_country_44 +2014103102 14103102 product_1 pty_country_47 +2014103102 14103102 product_4 pty_country_27 +2014103102 14103102 product_2 pty_country_38 +2014103102 14103102 product_2 pty_country_24 +2014103102 14103102 product_3 pty_country_33 +2014103102 14103102 product_3 pty_country_50 +2014103102 14103102 product_4 pty_country_44 +2014103102 14103102 product_3 pty_country_27 +2014103102 14103102 product_5 pty_country_47 +2014103102 14103102 product_4 pty_country_45 +2014103103 14103103 product_1 pty_country_24 +2014103103 14103103 product_4 pty_country_17 +2014103103 14103103 product_4 pty_country_32 +2014103103 14103103 product_2 pty_country_21 +2014103103 14103103 product_1 pty_country_9 +2014103103 14103103 product_3 pty_country_18 +2014103103 14103103 product_4 pty_country_11 +2014103103 14103103 product_5 pty_country_42 +2014103103 14103103 product_2 pty_country_32 +2014103103 14103103 product_1 pty_country_35 +2014103104 14103104 product_3 pty_country_31 +2014103104 14103104 product_5 pty_country_28 +2014103104 14103104 product_2 pty_country_15 +2014103104 14103104 product_1 pty_country_44 +2014103104 14103104 product_2 pty_country_11 +2014103104 14103104 product_2 pty_country_8 +2014103104 14103104 product_1 pty_country_1 +2014103104 14103104 product_4 pty_country_36 +2014103104 14103104 product_4 pty_country_8 +2014103104 14103104 product_5 pty_country_50 +2014103105 14103105 product_4 pty_country_49 +2014103105 14103105 product_1 pty_country_1 +2014103105 14103105 product_1 pty_country_34 +2014103105 14103105 product_3 pty_country_6 +2014103105 14103105 product_1 pty_country_4 +2014103105 14103105 product_4 pty_country_45 +2014103105 14103105 product_2 pty_country_24 +2014103105 14103105 product_3 pty_country_36 +2014103105 14103105 product_2 pty_country_19 +2014103105 14103105 product_2 pty_country_29 +2014103106 14103106 product_1 pty_country_17 +2014103106 14103106 product_1 pty_country_19 +2014103106 14103106 product_3 pty_country_24 +2014103106 14103106 product_2 pty_country_34 +2014103106 14103106 product_4 pty_country_1 +2014103106 14103106 product_2 pty_country_7 +2014103106 14103106 product_4 pty_country_27 +2014103106 14103106 product_2 pty_country_49 +2014103106 14103106 product_1 pty_country_13 +2014103106 14103106 product_1 pty_country_19 +2014103107 14103107 product_3 pty_country_41 +2014103107 14103107 product_2 pty_country_48 +2014103107 14103107 product_2 pty_country_11 +2014103107 14103107 product_1 pty_country_5 +2014103107 14103107 product_2 pty_country_27 +2014103107 14103107 product_4 pty_country_16 +2014103107 14103107 product_3 pty_country_50 +2014103107 14103107 product_3 pty_country_29 +2014103107 14103107 product_1 pty_country_13 +2014103107 14103107 product_3 pty_country_35 +2014103108 14103108 product_3 pty_country_18 +2014103108 14103108 product_5 pty_country_10 +2014103108 14103108 product_1 pty_country_4 +2014103108 14103108 product_5 pty_country_8 +2014103108 14103108 product_5 pty_country_29 +2014103108 14103108 product_4 pty_country_2 +2014103108 14103108 product_3 pty_country_13 +2014103108 14103108 product_4 pty_country_7 +2014103108 14103108 product_1 pty_country_11 +2014103108 14103108 product_1 pty_country_11 +2014103109 14103109 product_1 pty_country_13 +2014103109 14103109 product_5 pty_country_44 +2014103109 14103109 product_3 pty_country_16 +2014103109 14103109 product_1 pty_country_1 +2014103109 14103109 product_1 pty_country_8 +2014103109 14103109 product_3 pty_country_29 +2014103109 14103109 product_5 pty_country_7 +2014103109 14103109 product_3 pty_country_12 +2014103109 14103109 product_1 pty_country_48 +2014103109 14103109 product_1 pty_country_13 +2014103110 14103110 product_4 pty_country_41 +2014103110 14103110 product_1 pty_country_11 +2014103110 14103110 product_3 pty_country_42 +2014103110 14103110 product_1 pty_country_1 +2014103110 14103110 product_4 pty_country_39 +2014103110 14103110 product_2 pty_country_36 +2014103110 14103110 product_4 pty_country_35 +2014103110 14103110 product_1 pty_country_11 +2014103110 14103110 product_3 pty_country_17 +2014103110 14103110 product_2 pty_country_15 +2014103111 14103111 product_3 pty_country_28 +2014103111 14103111 product_1 pty_country_50 +2014103111 14103111 product_2 pty_country_13 +2014103111 14103111 product_5 pty_country_22 +2014103111 14103111 product_2 pty_country_23 +2014103111 14103111 product_3 pty_country_40 +2014103111 14103111 product_4 pty_country_2 +2014103111 14103111 product_2 pty_country_26 +2014103111 14103111 product_4 pty_country_8 +2014103111 14103111 product_2 pty_country_21 +2014103112 14103112 product_1 pty_country_43 +2014103112 14103112 product_1 pty_country_12 +2014103112 14103112 product_4 pty_country_18 +2014103112 14103112 product_1 pty_country_13 +2014103112 14103112 product_3 pty_country_22 +2014103112 14103112 product_3 pty_country_17 +2014103112 14103112 product_4 pty_country_15 +2014103112 14103112 product_5 pty_country_26 +2014103112 14103112 product_2 pty_country_7 +2014103112 14103112 product_5 pty_country_45 +2014103113 14103113 product_5 pty_country_19 +2014103113 14103113 product_3 pty_country_8 +2014103113 14103113 product_4 pty_country_31 +2014103113 14103113 product_4 pty_country_4 +2014103113 14103113 product_4 pty_country_42 +2014103113 14103113 product_2 pty_country_4 +2014103113 14103113 product_1 pty_country_29 +2014103113 14103113 product_3 pty_country_38 +2014103113 14103113 product_5 pty_country_1 +2014103113 14103113 product_2 pty_country_28 +2014103114 14103114 product_4 pty_country_44 +2014103114 14103114 product_2 pty_country_25 +2014103114 14103114 product_2 pty_country_30 +2014103114 14103114 product_3 pty_country_13 +2014103114 14103114 product_4 pty_country_46 +2014103114 14103114 product_3 pty_country_9 +2014103114 14103114 product_2 pty_country_44 +2014103114 14103114 product_4 pty_country_44 +2014103114 14103114 product_5 pty_country_45 +2014103114 14103114 product_4 pty_country_24 +2014103115 14103115 product_2 pty_country_35 +2014103115 14103115 product_1 pty_country_8 +2014103115 14103115 product_5 pty_country_2 +2014103115 14103115 product_3 pty_country_37 +2014103115 14103115 product_5 pty_country_45 +2014103115 14103115 product_5 pty_country_39 +2014103115 14103115 product_5 pty_country_20 +2014103115 14103115 product_3 pty_country_39 +2014103115 14103115 product_2 pty_country_33 +2014103115 14103115 product_1 pty_country_13 +2014103116 14103116 product_4 pty_country_40 +2014103116 14103116 product_5 pty_country_3 +2014103116 14103116 product_4 pty_country_48 +2014103116 14103116 product_1 pty_country_41 +2014103116 14103116 product_4 pty_country_40 +2014103116 14103116 product_2 pty_country_36 +2014103116 14103116 product_5 pty_country_5 +2014103116 14103116 product_2 pty_country_2 +2014103116 14103116 product_2 pty_country_1 +2014103116 14103116 product_5 pty_country_34 +2014103117 14103117 product_4 pty_country_14 +2014103117 14103117 product_5 pty_country_37 +2014103117 14103117 product_4 pty_country_45 +2014103117 14103117 product_2 pty_country_5 +2014103117 14103117 product_4 pty_country_6 +2014103117 14103117 product_3 pty_country_11 +2014103117 14103117 product_3 pty_country_38 +2014103117 14103117 product_2 pty_country_43 +2014103117 14103117 product_2 pty_country_14 +2014103117 14103117 product_3 pty_country_29 +2014103118 14103118 product_1 pty_country_21 +2014103118 14103118 product_4 pty_country_48 +2014103118 14103118 product_3 pty_country_35 +2014103118 14103118 product_3 pty_country_42 +2014103118 14103118 product_1 pty_country_9 +2014103118 14103118 product_2 pty_country_36 +2014103118 14103118 product_1 pty_country_39 +2014103118 14103118 product_1 pty_country_50 +2014103118 14103118 product_1 pty_country_15 +2014103118 14103118 product_1 pty_country_43 +2014103119 14103119 product_2 pty_country_48 +2014103119 14103119 product_5 pty_country_43 +2014103119 14103119 product_1 pty_country_19 +2014103119 14103119 product_5 pty_country_11 +2014103119 14103119 product_5 pty_country_39 +2014103119 14103119 product_1 pty_country_42 +2014103119 14103119 product_3 pty_country_24 +2014103119 14103119 product_5 pty_country_15 +2014103119 14103119 product_2 pty_country_24 +2014103119 14103119 product_1 pty_country_17 +2014103120 14103120 product_1 pty_country_36 +2014103120 14103120 product_3 pty_country_5 +2014103120 14103120 product_5 pty_country_32 +2014103120 14103120 product_4 pty_country_5 +2014103120 14103120 product_1 pty_country_40 +2014103120 14103120 product_4 pty_country_42 +2014103120 14103120 product_2 pty_country_36 +2014103120 14103120 product_4 pty_country_5 +2014103120 14103120 product_1 pty_country_25 +2014103120 14103120 product_1 pty_country_8 +2014103121 14103121 product_3 pty_country_9 +2014103121 14103121 product_1 pty_country_43 +2014103121 14103121 product_3 pty_country_14 +2014103121 14103121 product_2 pty_country_9 +2014103121 14103121 product_4 pty_country_37 +2014103121 14103121 product_5 pty_country_9 +2014103121 14103121 product_4 pty_country_40 +2014103121 14103121 product_4 pty_country_50 +2014103121 14103121 product_3 pty_country_15 +2014103121 14103121 product_3 pty_country_20 +2014103122 14103122 product_4 pty_country_44 +2014103122 14103122 product_3 pty_country_10 +2014103122 14103122 product_4 pty_country_44 +2014103122 14103122 product_1 pty_country_46 +2014103122 14103122 product_5 pty_country_36 +2014103122 14103122 product_1 pty_country_20 +2014103122 14103122 product_3 pty_country_8 +2014103122 14103122 product_1 pty_country_45 +2014103122 14103122 product_5 pty_country_26 +2014103122 14103122 product_4 pty_country_14 +2014103123 14103123 product_2 pty_country_41 +2014103123 14103123 product_3 pty_country_6 +2014103123 14103123 product_2 pty_country_13 +2014103123 14103123 product_5 pty_country_23 +2014103123 14103123 product_1 pty_country_20 +2014103123 14103123 product_4 pty_country_20 +2014103123 14103123 product_1 pty_country_5 +2014103123 14103123 product_1 pty_country_35 +2014103123 14103123 product_2 pty_country_29 +2014103123 14103123 product_4 pty_country_35 +2014110100 14110100 product_4 pty_country_37 +2014110100 14110100 product_3 pty_country_13 +2014110100 14110100 product_2 pty_country_38 +2014110100 14110100 product_3 pty_country_13 +2014110100 14110100 product_4 pty_country_23 +2014110100 14110100 product_1 pty_country_45 +2014110100 14110100 product_1 pty_country_26 +2014110100 14110100 product_4 pty_country_18 +2014110100 14110100 product_1 pty_country_4 +2014110100 14110100 product_3 pty_country_28 +2014110101 14110101 product_1 pty_country_38 +2014110101 14110101 product_1 pty_country_26 +2014110101 14110101 product_1 pty_country_9 +2014110101 14110101 product_4 pty_country_29 +2014110101 14110101 product_4 pty_country_25 +2014110101 14110101 product_5 pty_country_49 +2014110101 14110101 product_5 pty_country_40 +2014110101 14110101 product_2 pty_country_11 +2014110101 14110101 product_2 pty_country_2 +2014110101 14110101 product_3 pty_country_33 +2014110102 14110102 product_2 pty_country_11 +2014110102 14110102 product_2 pty_country_4 +2014110102 14110102 product_3 pty_country_16 +2014110102 14110102 product_2 pty_country_42 +2014110102 14110102 product_2 pty_country_12 +2014110102 14110102 product_5 pty_country_9 +2014110102 14110102 product_2 pty_country_14 +2014110102 14110102 product_1 pty_country_26 +2014110102 14110102 product_4 pty_country_47 +2014110102 14110102 product_5 pty_country_6 +2014110103 14110103 product_5 pty_country_11 +2014110103 14110103 product_4 pty_country_26 +2014110103 14110103 product_2 pty_country_31 +2014110103 14110103 product_2 pty_country_29 +2014110103 14110103 product_2 pty_country_41 +2014110103 14110103 product_5 pty_country_44 +2014110103 14110103 product_1 pty_country_3 +2014110103 14110103 product_5 pty_country_6 +2014110103 14110103 product_1 pty_country_13 +2014110103 14110103 product_5 pty_country_46 +2014110104 14110104 product_3 pty_country_28 +2014110104 14110104 product_3 pty_country_24 +2014110104 14110104 product_2 pty_country_11 +2014110104 14110104 product_5 pty_country_43 +2014110104 14110104 product_1 pty_country_40 +2014110104 14110104 product_2 pty_country_25 +2014110104 14110104 product_4 pty_country_3 +2014110104 14110104 product_5 pty_country_25 +2014110104 14110104 product_2 pty_country_34 +2014110104 14110104 product_1 pty_country_37 +2014110105 14110105 product_4 pty_country_40 +2014110105 14110105 product_2 pty_country_37 +2014110105 14110105 product_2 pty_country_40 +2014110105 14110105 product_4 pty_country_29 +2014110105 14110105 product_2 pty_country_21 +2014110105 14110105 product_2 pty_country_33 +2014110105 14110105 product_1 pty_country_34 +2014110105 14110105 product_2 pty_country_17 +2014110105 14110105 product_1 pty_country_44 +2014110105 14110105 product_5 pty_country_34 +2014110106 14110106 product_1 pty_country_45 +2014110106 14110106 product_1 pty_country_48 +2014110106 14110106 product_5 pty_country_15 +2014110106 14110106 product_2 pty_country_17 +2014110106 14110106 product_4 pty_country_12 +2014110106 14110106 product_4 pty_country_17 +2014110106 14110106 product_1 pty_country_29 +2014110106 14110106 product_2 pty_country_45 +2014110106 14110106 product_1 pty_country_16 +2014110106 14110106 product_5 pty_country_48 +2014110107 14110107 product_1 pty_country_11 +2014110107 14110107 product_1 pty_country_43 +2014110107 14110107 product_2 pty_country_46 +2014110107 14110107 product_3 pty_country_23 +2014110107 14110107 product_1 pty_country_18 +2014110107 14110107 product_4 pty_country_39 +2014110107 14110107 product_2 pty_country_37 +2014110107 14110107 product_1 pty_country_31 +2014110107 14110107 product_3 pty_country_43 +2014110107 14110107 product_1 pty_country_17 +2014110108 14110108 product_5 pty_country_4 +2014110108 14110108 product_3 pty_country_27 +2014110108 14110108 product_5 pty_country_37 +2014110108 14110108 product_2 pty_country_48 +2014110108 14110108 product_3 pty_country_17 +2014110108 14110108 product_4 pty_country_9 +2014110108 14110108 product_1 pty_country_30 +2014110108 14110108 product_3 pty_country_4 +2014110108 14110108 product_3 pty_country_30 +2014110108 14110108 product_3 pty_country_47 +2014110109 14110109 product_1 pty_country_44 +2014110109 14110109 product_3 pty_country_39 +2014110109 14110109 product_1 pty_country_24 +2014110109 14110109 product_3 pty_country_40 +2014110109 14110109 product_4 pty_country_22 +2014110109 14110109 product_1 pty_country_31 +2014110109 14110109 product_2 pty_country_23 +2014110109 14110109 product_2 pty_country_38 +2014110109 14110109 product_2 pty_country_23 +2014110109 14110109 product_4 pty_country_24 +2014110110 14110110 product_2 pty_country_32 +2014110110 14110110 product_4 pty_country_17 +2014110110 14110110 product_5 pty_country_34 +2014110110 14110110 product_4 pty_country_12 +2014110110 14110110 product_1 pty_country_1 +2014110110 14110110 product_2 pty_country_18 +2014110110 14110110 product_3 pty_country_50 +2014110110 14110110 product_3 pty_country_34 +2014110110 14110110 product_2 pty_country_46 +2014110110 14110110 product_5 pty_country_40 +2014110111 14110111 product_2 pty_country_16 +2014110111 14110111 product_2 pty_country_47 +2014110111 14110111 product_1 pty_country_26 +2014110111 14110111 product_1 pty_country_11 +2014110111 14110111 product_4 pty_country_3 +2014110111 14110111 product_3 pty_country_6 +2014110111 14110111 product_4 pty_country_26 +2014110111 14110111 product_5 pty_country_6 +2014110111 14110111 product_4 pty_country_20 +2014110111 14110111 product_4 pty_country_5 +2014110112 14110112 product_4 pty_country_10 +2014110112 14110112 product_5 pty_country_11 +2014110112 14110112 product_1 pty_country_20 +2014110112 14110112 product_4 pty_country_21 +2014110112 14110112 product_4 pty_country_11 +2014110112 14110112 product_2 pty_country_37 +2014110112 14110112 product_4 pty_country_31 +2014110112 14110112 product_3 pty_country_27 +2014110112 14110112 product_3 pty_country_37 +2014110112 14110112 product_4 pty_country_1 +2014110113 14110113 product_1 pty_country_5 +2014110113 14110113 product_1 pty_country_27 +2014110113 14110113 product_1 pty_country_41 +2014110113 14110113 product_5 pty_country_7 +2014110113 14110113 product_2 pty_country_27 +2014110113 14110113 product_3 pty_country_24 +2014110113 14110113 product_2 pty_country_15 +2014110113 14110113 product_5 pty_country_9 +2014110113 14110113 product_2 pty_country_6 +2014110113 14110113 product_5 pty_country_44 +2014110114 14110114 product_3 pty_country_24 +2014110114 14110114 product_5 pty_country_18 +2014110114 14110114 product_2 pty_country_9 +2014110114 14110114 product_1 pty_country_8 +2014110114 14110114 product_1 pty_country_38 +2014110114 14110114 product_3 pty_country_25 +2014110114 14110114 product_5 pty_country_31 +2014110114 14110114 product_3 pty_country_16 +2014110114 14110114 product_2 pty_country_7 +2014110114 14110114 product_1 pty_country_44 +2014110115 14110115 product_3 pty_country_22 +2014110115 14110115 product_3 pty_country_10 +2014110115 14110115 product_4 pty_country_36 +2014110115 14110115 product_3 pty_country_9 +2014110115 14110115 product_2 pty_country_27 +2014110115 14110115 product_2 pty_country_44 +2014110115 14110115 product_1 pty_country_28 +2014110115 14110115 product_3 pty_country_17 +2014110115 14110115 product_3 pty_country_42 +2014110115 14110115 product_2 pty_country_46 +2014110116 14110116 product_2 pty_country_13 +2014110116 14110116 product_1 pty_country_17 +2014110116 14110116 product_4 pty_country_35 +2014110116 14110116 product_1 pty_country_38 +2014110116 14110116 product_5 pty_country_9 +2014110116 14110116 product_4 pty_country_21 +2014110116 14110116 product_1 pty_country_10 +2014110116 14110116 product_1 pty_country_23 +2014110116 14110116 product_1 pty_country_41 +2014110116 14110116 product_2 pty_country_3 +2014110117 14110117 product_2 pty_country_46 +2014110117 14110117 product_1 pty_country_24 +2014110117 14110117 product_3 pty_country_31 +2014110117 14110117 product_2 pty_country_26 +2014110117 14110117 product_4 pty_country_44 +2014110117 14110117 product_4 pty_country_21 +2014110117 14110117 product_1 pty_country_41 +2014110117 14110117 product_4 pty_country_17 +2014110117 14110117 product_2 pty_country_31 +2014110117 14110117 product_2 pty_country_35 +2014110118 14110118 product_5 pty_country_34 +2014110118 14110118 product_2 pty_country_31 +2014110118 14110118 product_5 pty_country_2 +2014110118 14110118 product_4 pty_country_15 +2014110118 14110118 product_4 pty_country_26 +2014110118 14110118 product_2 pty_country_2 +2014110118 14110118 product_1 pty_country_16 +2014110118 14110118 product_2 pty_country_45 +2014110118 14110118 product_1 pty_country_7 +2014110118 14110118 product_1 pty_country_27 +2014110119 14110119 product_3 pty_country_15 +2014110119 14110119 product_4 pty_country_2 +2014110119 14110119 product_3 pty_country_21 +2014110119 14110119 product_3 pty_country_16 +2014110119 14110119 product_2 pty_country_41 +2014110119 14110119 product_2 pty_country_34 +2014110119 14110119 product_5 pty_country_21 +2014110119 14110119 product_3 pty_country_13 +2014110119 14110119 product_3 pty_country_12 +2014110119 14110119 product_4 pty_country_49 +2014110120 14110120 product_4 pty_country_36 +2014110120 14110120 product_2 pty_country_30 +2014110120 14110120 product_1 pty_country_8 +2014110120 14110120 product_1 pty_country_33 +2014110120 14110120 product_5 pty_country_23 +2014110120 14110120 product_5 pty_country_1 +2014110120 14110120 product_4 pty_country_19 +2014110120 14110120 product_3 pty_country_36 +2014110120 14110120 product_1 pty_country_21 +2014110120 14110120 product_5 pty_country_41 +2014110121 14110121 product_2 pty_country_28 +2014110121 14110121 product_3 pty_country_11 +2014110121 14110121 product_5 pty_country_50 +2014110121 14110121 product_5 pty_country_30 +2014110121 14110121 product_1 pty_country_41 +2014110121 14110121 product_2 pty_country_8 +2014110121 14110121 product_4 pty_country_9 +2014110121 14110121 product_2 pty_country_5 +2014110121 14110121 product_3 pty_country_6 +2014110121 14110121 product_2 pty_country_40 +2014110122 14110122 product_2 pty_country_16 +2014110122 14110122 product_1 pty_country_49 +2014110122 14110122 product_4 pty_country_45 +2014110122 14110122 product_1 pty_country_2 +2014110122 14110122 product_2 pty_country_46 +2014110122 14110122 product_5 pty_country_39 +2014110122 14110122 product_4 pty_country_23 +2014110122 14110122 product_2 pty_country_16 +2014110122 14110122 product_4 pty_country_23 +2014110122 14110122 product_3 pty_country_37 +2014110123 14110123 product_2 pty_country_47 +2014110123 14110123 product_1 pty_country_40 +2014110123 14110123 product_5 pty_country_32 +2014110123 14110123 product_2 pty_country_21 +2014110123 14110123 product_3 pty_country_19 +2014110123 14110123 product_2 pty_country_47 +2014110123 14110123 product_5 pty_country_28 +2014110123 14110123 product_2 pty_country_11 +2014110123 14110123 product_4 pty_country_24 +2014110123 14110123 product_5 pty_country_44 +2014110200 14110200 product_5 pty_country_36 +2014110200 14110200 product_2 pty_country_13 +2014110200 14110200 product_2 pty_country_21 +2014110200 14110200 product_2 pty_country_6 +2014110200 14110200 product_4 pty_country_13 +2014110200 14110200 product_2 pty_country_15 +2014110200 14110200 product_5 pty_country_23 +2014110200 14110200 product_2 pty_country_29 +2014110200 14110200 product_1 pty_country_27 +2014110200 14110200 product_3 pty_country_19 +2014110201 14110201 product_2 pty_country_1 +2014110201 14110201 product_4 pty_country_47 +2014110201 14110201 product_1 pty_country_24 +2014110201 14110201 product_4 pty_country_43 +2014110201 14110201 product_5 pty_country_10 +2014110201 14110201 product_2 pty_country_2 +2014110201 14110201 product_4 pty_country_43 +2014110201 14110201 product_4 pty_country_47 +2014110201 14110201 product_3 pty_country_21 +2014110201 14110201 product_5 pty_country_33 +2014110202 14110202 product_4 pty_country_41 +2014110202 14110202 product_1 pty_country_37 +2014110202 14110202 product_2 pty_country_20 +2014110202 14110202 product_4 pty_country_12 +2014110202 14110202 product_2 pty_country_38 +2014110202 14110202 product_4 pty_country_14 +2014110202 14110202 product_5 pty_country_12 +2014110202 14110202 product_3 pty_country_31 +2014110202 14110202 product_5 pty_country_14 +2014110202 14110202 product_1 pty_country_26 +2014110203 14110203 product_4 pty_country_4 +2014110203 14110203 product_5 pty_country_30 +2014110203 14110203 product_1 pty_country_49 +2014110203 14110203 product_1 pty_country_13 +2014110203 14110203 product_2 pty_country_3 +2014110203 14110203 product_2 pty_country_20 +2014110203 14110203 product_3 pty_country_46 +2014110203 14110203 product_2 pty_country_18 +2014110203 14110203 product_4 pty_country_6 +2014110203 14110203 product_2 pty_country_9 +2014110204 14110204 product_3 pty_country_11 +2014110204 14110204 product_2 pty_country_28 +2014110204 14110204 product_3 pty_country_16 +2014110204 14110204 product_1 pty_country_36 +2014110204 14110204 product_1 pty_country_8 +2014110204 14110204 product_2 pty_country_7 +2014110204 14110204 product_2 pty_country_18 +2014110204 14110204 product_3 pty_country_35 +2014110204 14110204 product_2 pty_country_26 +2014110204 14110204 product_1 pty_country_43 +2014110205 14110205 product_1 pty_country_33 +2014110205 14110205 product_3 pty_country_20 +2014110205 14110205 product_5 pty_country_43 +2014110205 14110205 product_4 pty_country_10 +2014110205 14110205 product_5 pty_country_9 +2014110205 14110205 product_3 pty_country_17 +2014110205 14110205 product_4 pty_country_49 +2014110205 14110205 product_1 pty_country_27 +2014110205 14110205 product_4 pty_country_35 +2014110205 14110205 product_3 pty_country_37 +2014110206 14110206 product_3 pty_country_47 +2014110206 14110206 product_2 pty_country_18 +2014110206 14110206 product_2 pty_country_46 +2014110206 14110206 product_2 pty_country_47 +2014110206 14110206 product_3 pty_country_10 +2014110206 14110206 product_3 pty_country_40 +2014110206 14110206 product_5 pty_country_8 +2014110206 14110206 product_1 pty_country_27 +2014110206 14110206 product_5 pty_country_31 +2014110206 14110206 product_4 pty_country_14 +2014110207 14110207 product_5 pty_country_31 +2014110207 14110207 product_2 pty_country_15 +2014110207 14110207 product_2 pty_country_23 +2014110207 14110207 product_4 pty_country_37 +2014110207 14110207 product_3 pty_country_10 +2014110207 14110207 product_3 pty_country_46 +2014110207 14110207 product_4 pty_country_39 +2014110207 14110207 product_4 pty_country_31 +2014110207 14110207 product_3 pty_country_10 +2014110207 14110207 product_3 pty_country_38 +2014110208 14110208 product_4 pty_country_3 +2014110208 14110208 product_5 pty_country_10 +2014110208 14110208 product_1 pty_country_36 +2014110208 14110208 product_1 pty_country_3 +2014110208 14110208 product_4 pty_country_46 +2014110208 14110208 product_3 pty_country_20 +2014110208 14110208 product_5 pty_country_38 +2014110208 14110208 product_4 pty_country_48 +2014110208 14110208 product_2 pty_country_37 +2014110208 14110208 product_4 pty_country_49 +2014110209 14110209 product_2 pty_country_32 +2014110209 14110209 product_3 pty_country_39 +2014110209 14110209 product_2 pty_country_4 +2014110209 14110209 product_4 pty_country_31 +2014110209 14110209 product_5 pty_country_10 +2014110209 14110209 product_5 pty_country_11 +2014110209 14110209 product_3 pty_country_15 +2014110209 14110209 product_1 pty_country_44 +2014110209 14110209 product_1 pty_country_16 +2014110209 14110209 product_5 pty_country_48 +2014110210 14110210 product_5 pty_country_41 +2014110210 14110210 product_3 pty_country_27 +2014110210 14110210 product_4 pty_country_1 +2014110210 14110210 product_3 pty_country_20 +2014110210 14110210 product_2 pty_country_24 +2014110210 14110210 product_5 pty_country_13 +2014110210 14110210 product_4 pty_country_27 +2014110210 14110210 product_1 pty_country_20 +2014110210 14110210 product_1 pty_country_23 +2014110210 14110210 product_2 pty_country_15 +2014110211 14110211 product_3 pty_country_17 +2014110211 14110211 product_2 pty_country_9 +2014110211 14110211 product_5 pty_country_13 +2014110211 14110211 product_4 pty_country_40 +2014110211 14110211 product_2 pty_country_38 +2014110211 14110211 product_4 pty_country_20 +2014110211 14110211 product_4 pty_country_32 +2014110211 14110211 product_2 pty_country_47 +2014110211 14110211 product_4 pty_country_31 +2014110211 14110211 product_2 pty_country_35 +2014110212 14110212 product_4 pty_country_45 +2014110212 14110212 product_2 pty_country_28 +2014110212 14110212 product_3 pty_country_16 +2014110212 14110212 product_2 pty_country_40 +2014110212 14110212 product_2 pty_country_44 +2014110212 14110212 product_5 pty_country_2 +2014110212 14110212 product_5 pty_country_22 +2014110212 14110212 product_3 pty_country_15 +2014110212 14110212 product_5 pty_country_2 +2014110212 14110212 product_5 pty_country_30 +2014110213 14110213 product_2 pty_country_23 +2014110213 14110213 product_2 pty_country_47 +2014110213 14110213 product_3 pty_country_12 +2014110213 14110213 product_1 pty_country_29 +2014110213 14110213 product_3 pty_country_4 +2014110213 14110213 product_5 pty_country_9 +2014110213 14110213 product_5 pty_country_37 +2014110213 14110213 product_4 pty_country_44 +2014110213 14110213 product_3 pty_country_21 +2014110213 14110213 product_4 pty_country_4 +2014110214 14110214 product_5 pty_country_45 +2014110214 14110214 product_5 pty_country_29 +2014110214 14110214 product_3 pty_country_48 +2014110214 14110214 product_2 pty_country_14 +2014110214 14110214 product_4 pty_country_10 +2014110214 14110214 product_3 pty_country_24 +2014110214 14110214 product_3 pty_country_19 +2014110214 14110214 product_4 pty_country_6 +2014110214 14110214 product_2 pty_country_33 +2014110214 14110214 product_5 pty_country_16 +2014110215 14110215 product_1 pty_country_37 +2014110215 14110215 product_1 pty_country_18 +2014110215 14110215 product_3 pty_country_11 +2014110215 14110215 product_1 pty_country_12 +2014110215 14110215 product_1 pty_country_35 +2014110215 14110215 product_1 pty_country_1 +2014110215 14110215 product_4 pty_country_16 +2014110215 14110215 product_5 pty_country_30 +2014110215 14110215 product_1 pty_country_21 +2014110215 14110215 product_5 pty_country_4 +2014110216 14110216 product_4 pty_country_43 +2014110216 14110216 product_5 pty_country_11 +2014110216 14110216 product_1 pty_country_19 +2014110216 14110216 product_5 pty_country_40 +2014110216 14110216 product_4 pty_country_38 +2014110216 14110216 product_4 pty_country_48 +2014110216 14110216 product_4 pty_country_41 +2014110216 14110216 product_2 pty_country_31 +2014110216 14110216 product_2 pty_country_37 +2014110216 14110216 product_4 pty_country_21 +2014110217 14110217 product_2 pty_country_29 +2014110217 14110217 product_5 pty_country_15 +2014110217 14110217 product_4 pty_country_1 +2014110217 14110217 product_4 pty_country_48 +2014110217 14110217 product_5 pty_country_26 +2014110217 14110217 product_4 pty_country_8 +2014110217 14110217 product_1 pty_country_2 +2014110217 14110217 product_2 pty_country_44 +2014110217 14110217 product_5 pty_country_38 +2014110217 14110217 product_2 pty_country_17 +2014110218 14110218 product_3 pty_country_23 +2014110218 14110218 product_3 pty_country_4 +2014110218 14110218 product_3 pty_country_37 +2014110218 14110218 product_5 pty_country_42 +2014110218 14110218 product_5 pty_country_28 +2014110218 14110218 product_2 pty_country_12 +2014110218 14110218 product_2 pty_country_20 +2014110218 14110218 product_2 pty_country_33 +2014110218 14110218 product_3 pty_country_7 +2014110218 14110218 product_4 pty_country_25 +2014110219 14110219 product_2 pty_country_50 +2014110219 14110219 product_3 pty_country_33 +2014110219 14110219 product_3 pty_country_40 +2014110219 14110219 product_2 pty_country_34 +2014110219 14110219 product_1 pty_country_39 +2014110219 14110219 product_3 pty_country_6 +2014110219 14110219 product_3 pty_country_39 +2014110219 14110219 product_1 pty_country_27 +2014110219 14110219 product_1 pty_country_48 +2014110219 14110219 product_2 pty_country_33 +2014110220 14110220 product_3 pty_country_3 +2014110220 14110220 product_2 pty_country_27 +2014110220 14110220 product_2 pty_country_19 +2014110220 14110220 product_1 pty_country_1 +2014110220 14110220 product_5 pty_country_3 +2014110220 14110220 product_2 pty_country_12 +2014110220 14110220 product_5 pty_country_24 +2014110220 14110220 product_4 pty_country_37 +2014110220 14110220 product_1 pty_country_2 +2014110220 14110220 product_3 pty_country_34 +2014110221 14110221 product_1 pty_country_39 +2014110221 14110221 product_4 pty_country_34 +2014110221 14110221 product_3 pty_country_19 +2014110221 14110221 product_3 pty_country_36 +2014110221 14110221 product_1 pty_country_44 +2014110221 14110221 product_3 pty_country_34 +2014110221 14110221 product_2 pty_country_17 +2014110221 14110221 product_2 pty_country_21 +2014110221 14110221 product_1 pty_country_4 +2014110221 14110221 product_1 pty_country_24 +2014110222 14110222 product_3 pty_country_10 +2014110222 14110222 product_1 pty_country_35 +2014110222 14110222 product_1 pty_country_15 +2014110222 14110222 product_5 pty_country_13 +2014110222 14110222 product_4 pty_country_21 +2014110222 14110222 product_1 pty_country_14 +2014110222 14110222 product_4 pty_country_8 +2014110222 14110222 product_5 pty_country_40 +2014110222 14110222 product_4 pty_country_4 +2014110222 14110222 product_1 pty_country_20 +2014110223 14110223 product_1 pty_country_9 +2014110223 14110223 product_5 pty_country_38 +2014110223 14110223 product_2 pty_country_5 +2014110223 14110223 product_2 pty_country_28 +2014110223 14110223 product_3 pty_country_10 +2014110223 14110223 product_1 pty_country_2 +2014110223 14110223 product_5 pty_country_7 +2014110223 14110223 product_1 pty_country_18 +2014110223 14110223 product_3 pty_country_45 +2014110223 14110223 product_2 pty_country_50 diff --git a/extensions-contrib/accurate-cardinality/src/test/resources/simple_test_data_aggregators.json b/extensions-contrib/accurate-cardinality/src/test/resources/simple_test_data_aggregators.json new file mode 100644 index 000000000000..6a330e6ccd90 --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/test/resources/simple_test_data_aggregators.json @@ -0,0 +1,6 @@ +[ + { + "type": "count", + "name": "count" + } +] diff --git a/extensions-contrib/accurate-cardinality/src/test/resources/simple_test_data_group_by_query.json b/extensions-contrib/accurate-cardinality/src/test/resources/simple_test_data_group_by_query.json new file mode 100644 index 000000000000..c00b0d1f1331 --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/test/resources/simple_test_data_group_by_query.json @@ -0,0 +1,24 @@ +{ + "queryType": "groupBy", + "dataSource": "test_datasource", + "granularity": "ALL", + "dimensions": ["product"], + "aggregations": [ + { + "type": "accurateCardinality", + "name": "uv", + "field": { + "type": "default", + "dimension": "duration", + "outputName": "duration", + "outputType": "LONG" + }, + "collectorFactory": { + "type": "roaring" + } + } + ], + "intervals": [ + "2014-10-19T00:00:00.000Z/2014-11-03T00:00:00.000Z" + ] +} diff --git a/extensions-contrib/accurate-cardinality/src/test/resources/simple_test_data_group_by_query1.json b/extensions-contrib/accurate-cardinality/src/test/resources/simple_test_data_group_by_query1.json new file mode 100644 index 000000000000..8391e9a16781 --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/test/resources/simple_test_data_group_by_query1.json @@ -0,0 +1,16 @@ +{ + "queryType": "groupBy", + "dataSource": "test_datasource", + "granularity": "ALL", + "dimensions": [], + "aggregations": [ + { + "type": "cardinality", + "name": "uv", + "fields": ["duration"] + } + ], + "intervals": [ + "2014-10-19T00:00:00.000Z/2014-10-22T00:00:00.000Z" + ] +} diff --git a/extensions-contrib/accurate-cardinality/src/test/resources/simple_test_data_record_parser.json b/extensions-contrib/accurate-cardinality/src/test/resources/simple_test_data_record_parser.json new file mode 100644 index 000000000000..bdd15b1c18af --- /dev/null +++ b/extensions-contrib/accurate-cardinality/src/test/resources/simple_test_data_record_parser.json @@ -0,0 +1,26 @@ +{ + "type": "string", + "parseSpec": { + "format": "tsv", + "timestampSpec": { + "column": "timestamp", + "format": "yyyyMMddHH" + }, + "dimensionsSpec": { + "dimensions": [ + { + "type": "long", + "name": "duration" + }, + "product" + ], + "dimensionExclusions": [], + "spatialDimensions": [] + }, + "columns": [ + "timestamp", + "duration", + "product" + ] + } +} diff --git a/pom.xml b/pom.xml index 58ef424998d4..73e39e965173 100644 --- a/pom.xml +++ b/pom.xml @@ -174,6 +174,7 @@ extensions-contrib/moving-average-query extensions-contrib/tdigestsketch extensions-contrib/influxdb-emitter + extensions-contrib/accurate-cardinality distribution diff --git a/processing/src/main/java/org/apache/druid/query/aggregation/AggregatorUtil.java b/processing/src/main/java/org/apache/druid/query/aggregation/AggregatorUtil.java index e5e5f51d3a8b..191f61a5a6c0 100644 --- a/processing/src/main/java/org/apache/druid/query/aggregation/AggregatorUtil.java +++ b/processing/src/main/java/org/apache/druid/query/aggregation/AggregatorUtil.java @@ -124,6 +124,10 @@ public class AggregatorUtil public static final byte TDIGEST_MERGE_SKETCH_CACHE_TYPE_ID = 0x39; public static final byte TDIGEST_SKETCH_TO_QUANTILES_CACHE_TYPE_ID = 0x40; + // accurete cardinalty aggregator + public static final byte ACCURATE_CARDINALITY_CACHE_TYPE_ID = 0x41; + public static final byte BITMAP_AGG_CACHE_TYPE_ID = 0x42; + /** * returns the list of dependent postAggregators that should be calculated in order to calculate given postAgg * @@ -193,7 +197,10 @@ static BaseFloatColumnValueSelector makeColumnValueSelectorWithFloatDefault( if (fieldName != null) { return metricFactory.makeColumnValueSelector(fieldName); } else { - final ColumnValueSelector baseSelector = ExpressionSelectors.makeExprEvalSelector(metricFactory, fieldExpression); + final ColumnValueSelector baseSelector = ExpressionSelectors.makeExprEvalSelector( + metricFactory, + fieldExpression + ); class ExpressionFloatColumnSelector implements FloatColumnSelector { @Override @@ -238,7 +245,10 @@ static BaseLongColumnValueSelector makeColumnValueSelectorWithLongDefault( if (fieldName != null) { return metricFactory.makeColumnValueSelector(fieldName); } else { - final ColumnValueSelector baseSelector = ExpressionSelectors.makeExprEvalSelector(metricFactory, fieldExpression); + final ColumnValueSelector baseSelector = ExpressionSelectors.makeExprEvalSelector( + metricFactory, + fieldExpression + ); class ExpressionLongColumnSelector implements LongColumnSelector { @Override @@ -281,7 +291,10 @@ static BaseDoubleColumnValueSelector makeColumnValueSelectorWithDoubleDefault( if (fieldName != null) { return metricFactory.makeColumnValueSelector(fieldName); } else { - final ColumnValueSelector baseSelector = ExpressionSelectors.makeExprEvalSelector(metricFactory, fieldExpression); + final ColumnValueSelector baseSelector = ExpressionSelectors.makeExprEvalSelector( + metricFactory, + fieldExpression + ); class ExpressionDoubleColumnSelector implements DoubleColumnSelector { @Override diff --git a/processing/src/test/java/org/apache/druid/query/aggregation/AggregationTestHelper.java b/processing/src/test/java/org/apache/druid/query/aggregation/AggregationTestHelper.java index 8d134b367417..b516c325c0f4 100644 --- a/processing/src/test/java/org/apache/druid/query/aggregation/AggregationTestHelper.java +++ b/processing/src/test/java/org/apache/druid/query/aggregation/AggregationTestHelper.java @@ -106,15 +106,15 @@ public class AggregationTestHelper implements Closeable { private final ObjectMapper mapper; - private final IndexMerger indexMerger; - private final IndexIO indexIO; + protected final IndexMerger indexMerger; + protected final IndexIO indexIO; private final QueryToolChest toolChest; private final QueryRunnerFactory factory; - private final TemporaryFolder tempFolder; + protected final TemporaryFolder tempFolder; private final Closer resourceCloser; - private AggregationTestHelper( + protected AggregationTestHelper( ObjectMapper mapper, IndexMerger indexMerger, IndexIO indexIO, @@ -497,6 +497,7 @@ public void createIndex( index = new IncrementalIndex.Builder() .setIndexSchema( new IncrementalIndexSchema.Builder() + .withDimensionsSpec(parser) .withMinTimestamp(minTimestamp) .withQueryGranularity(gran) .withMetrics(metrics) @@ -517,6 +518,7 @@ public void createIndex( index = new IncrementalIndex.Builder() .setIndexSchema( new IncrementalIndexSchema.Builder() + .withDimensionsSpec(parser) .withMinTimestamp(minTimestamp) .withQueryGranularity(gran) .withMetrics(metrics)