Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Sorting for search results #121

Merged
merged 3 commits into from
Aug 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
78 changes: 78 additions & 0 deletions client/src/main/java/com/tigrisdata/db/client/FieldSort.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright 2022 Tigris Data, Inc.
*
* Licensed 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 com.tigrisdata.db.client;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/** Represents a collection field and corresponding sort order */
public final class FieldSort implements TigrisSort {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In future we can extend this further

class GeoDistanceSort implements TigrisSort {}
Sort.geoDistance(String fieldName, GeoLocation loc)


private final String fieldName;
private final SortOrder operator;
private Map<String, Object> cachedMap;

FieldSort(String fieldName, SortOrder op) {
this.fieldName = fieldName;
this.operator = op;
}

/** {@inheritDoc} */
@Override
public Map<String, Object> getOrder() {
if (cachedMap == null) {
cachedMap =
Collections.unmodifiableMap(
new HashMap<String, String>() {
{
put(fieldName, operator.getOperator());
}
});
}
return cachedMap;
}

/** {@inheritDoc} */
@Override
public String getFieldName() {
return fieldName;
}

@Override
public boolean equals(Object o) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: in other places in this library. I have been using IDE generated equals and hashCode (Java 7+) with java.util.Objects

if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}

FieldSort fieldSort = (FieldSort) o;

if (!Objects.equals(fieldName, fieldSort.fieldName)) {
return false;
}
return operator == fieldSort.operator;
}

@Override
public int hashCode() {
int result = fieldName != null ? fieldName.hashCode() : 0;
result = 31 * result + (operator != null ? operator.hashCode() : 0);
return result;
}
}
40 changes: 40 additions & 0 deletions client/src/main/java/com/tigrisdata/db/client/Sort.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright 2022 Tigris Data, Inc.
*
* Licensed 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 com.tigrisdata.db.client;

/** Helper class to construct {@link TigrisSort} orders */
public final class Sort {
private Sort() {}

/**
* Creates ascending sort order for given field name
*
* @param fieldName collection field name
* @return constructed {@link FieldSort} of ascending order
*/
public static FieldSort ascending(String fieldName) {
return new FieldSort(fieldName, SortOrder.ASC);
}

/**
* Creates descending sort order for given field name
*
* @param fieldName collection field name
* @return constructed {@link FieldSort} of descending order
*/
public static FieldSort descending(String fieldName) {
return new FieldSort(fieldName, SortOrder.DESC);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,19 @@
* limitations under the License.
*/

package com.tigrisdata.db.client.search;
package com.tigrisdata.db.client;

import com.tigrisdata.db.client.JSONSerializable;
enum SortOrder {
ASC("$asc"),
DESC("$desc");

/** Interface to construct sorting order for search results */
public interface SortOrder extends JSONSerializable {}
private final String operator;

SortOrder(String operator) {
this.operator = operator;
}

public String getOperator() {
return operator;
}
}
35 changes: 35 additions & 0 deletions client/src/main/java/com/tigrisdata/db/client/TigrisSort.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 2022 Tigris Data, Inc.
*
* Licensed 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 com.tigrisdata.db.client;

import java.util.Map;

/** Represents the sorting order */
public interface TigrisSort {

/**
* Sort order representation as Key, Value pairs
*
* @return non-null immutable {@link Map}
*/
Map<String, Object> getOrder();

/**
* Gets collection field name that this order represents
*
* @return {@link String}
*/
String getFieldName();
}
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,8 @@ public static Api.SearchRequest toSearchRequest(
if (Objects.nonNull(req.getFacetQuery())) {
builder.setFacet(ByteString.copyFromUtf8(req.getFacetQuery().toJSON(objectMapper)));
}
if (Objects.nonNull(req.getSortOrders())) {
builder.setSort(ByteString.copyFromUtf8(req.getSortOrders().toJSON(objectMapper)));
if (Objects.nonNull(req.getSortingOrder())) {
builder.setSort(ByteString.copyFromUtf8(req.getSortingOrder().toJSON(objectMapper)));
}
if (Objects.nonNull(options)) {
builder.setPage(options.getPage());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public final class FacetFieldsQuery implements FacetQuery {
private static final FacetFieldsQuery EMPTY = newBuilder().build();

private final Map<String, FacetQueryOptions> internalMap;
private String cachedJSON;

private FacetFieldsQuery(Builder builder) {
this.internalMap = Collections.unmodifiableMap(builder.fieldMap);
Expand Down Expand Up @@ -65,6 +66,9 @@ public boolean isEmpty() {

@Override
public String toJSON(ObjectMapper objectMapper) {
if (Objects.nonNull(cachedJSON)) {
return cachedJSON;
}
Function<FacetQueryOptions, Map<String, String>> toOptionsMap =
o ->
new HashMap<String, String>() {
Expand All @@ -78,7 +82,8 @@ public String toJSON(ObjectMapper objectMapper) {
internalMap.entrySet().stream()
.collect(Collectors.toMap(Entry::getKey, e -> toOptionsMap.apply(e.getValue())));
try {
return objectMapper.writeValueAsString(fieldOptionsMap);
cachedJSON = objectMapper.writeValueAsString(fieldOptionsMap);
return cachedJSON;
} catch (JsonProcessingException ex) {
throw new IllegalArgumentException("Failed to serialize FacetFields to JSON", ex);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package com.tigrisdata.db.client.search;

import com.tigrisdata.db.client.TigrisFilter;
import com.tigrisdata.db.client.TigrisSort;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
Expand All @@ -29,7 +30,7 @@ public final class SearchRequest {
private final SearchFields searchFields;
private final TigrisFilter filter;
private final FacetQuery facetQuery;
private final SortOrder sortOrder;
private final SortingOrder sortOrder;
private final List<String> includeFields;
private final List<String> excludeFields;

Expand Down Expand Up @@ -84,9 +85,9 @@ public FacetQuery getFacetQuery() {
/**
* Gets the Order to sort the search results
*
* @return {@link SortOrder} or null
* @return {@link SortingOrder} or null
*/
public SortOrder getSortOrders() {
public SortingOrder getSortingOrder() {
return sortOrder;
}

Expand Down Expand Up @@ -135,7 +136,7 @@ public static final class Builder {
private TigrisFilter filter;
private SearchFields searchFields;
private FacetQuery facetQuery;
private SortOrder sortOrder;
private SortingOrder sortOrder;
private final Set<String> includeFields;
private final Set<String> excludeFields;

Expand Down Expand Up @@ -214,17 +215,29 @@ public Builder withFacetQuery(FacetQuery facetQuery) {
}

/**
* Optional - Sets the SortOrder to sorts search results according to specified attributes and
* Optional - Sets the SortingOrder to sort search results according to specified attributes and
* indicated order
*
* @param sortOrder {@link SortOrder}
* @param sortOrder {@link SortingOrder}
* @return {@link SearchRequest.Builder}
*/
public Builder withSortOrder(SortOrder sortOrder) {
public Builder withSort(SortingOrder sortOrder) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the use cases when this method is useful vs the following?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add a rpc method test to exercise the full trip?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Allow sharing of SortingOrder between search requests. Or if we introduce sorting for read.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense.

this.sortOrder = sortOrder;
return this;
}

/**
* Optional - Sets the SortingOrder to sort search results according to specified attributes and
* indicated order
*
* @param orders {@link TigrisSort}
* @return {@link SearchRequest.Builder}
*/
public Builder withSort(TigrisSort... orders) {
this.sortOrder = SortingOrder.newBuilder().withOrder(orders).build();
return this;
}

/**
* Optional - Sets collection fields to include in returned documents
*
Expand Down