Based on https://en.wikipedia.org/wiki/Jaro%E2%80%93Winkler_distance Implements Jaro similarity
| 1637 | // Based on https://en.wikipedia.org/wiki/Jaro%E2%80%93Winkler_distance |
| 1638 | // Implements Jaro similarity |
| 1639 | DoubleVal StringFunctions::JaroSimilarity( |
| 1640 | FunctionContext* ctx, const StringVal& s1, const StringVal& s2) { |
| 1641 | |
| 1642 | int s1len = s1.len; |
| 1643 | int s2len = s2.len; |
| 1644 | |
| 1645 | // error if either input exceeds 255 characters |
| 1646 | if (s1len > 255 || s2len > 255) { |
| 1647 | ctx->SetError("jaro argument exceeds maximum length of 255 characters"); |
| 1648 | return DoubleVal(-1.0); |
| 1649 | } |
| 1650 | |
| 1651 | // short cut cases: |
| 1652 | // - null strings |
| 1653 | // - zero length strings |
| 1654 | // - identical length and value strings |
| 1655 | if (s1.is_null || s2.is_null) return DoubleVal::null(); |
| 1656 | if (s1len == 0 && s2len == 0) return DoubleVal(1.0); |
| 1657 | if (s1len == 0 || s2len == 0) return DoubleVal(0.0); |
| 1658 | if (s1len == s2len && memcmp(s1.ptr, s2.ptr, s1len) == 0) return DoubleVal(1.0); |
| 1659 | |
| 1660 | // the window size to search for matches in the other string |
| 1661 | int max_range = std::max(0, std::max(s1len, s2len) / 2 - 1); |
| 1662 | |
| 1663 | int* s1_matching = reinterpret_cast<int*>(ctx->Allocate(sizeof(int) * (s1len))); |
| 1664 | if (UNLIKELY(s1_matching == nullptr)) { |
| 1665 | DCHECK(!ctx->impl()->state()->GetQueryStatus().ok()); |
| 1666 | return DoubleVal::null(); |
| 1667 | } |
| 1668 | |
| 1669 | int* s2_matching = reinterpret_cast<int*>(ctx->Allocate(sizeof(int) * (s2len))); |
| 1670 | if (UNLIKELY(s2_matching == nullptr)) { |
| 1671 | ctx->Free(reinterpret_cast<uint8_t*>(s1_matching)); |
| 1672 | DCHECK(!ctx->impl()->state()->GetQueryStatus().ok()); |
| 1673 | return DoubleVal::null(); |
| 1674 | } |
| 1675 | |
| 1676 | std::fill_n(s1_matching, s1len, -1); |
| 1677 | std::fill_n(s2_matching, s2len, -1); |
| 1678 | |
| 1679 | // calculate matching characters |
| 1680 | int matching_characters = 0; |
| 1681 | for (int i = 0; i < s1len; i++) { |
| 1682 | // matching window |
| 1683 | int min_index = std::max(i - max_range, 0); |
| 1684 | int max_index = std::min(i + max_range + 1, s2len); |
| 1685 | if (min_index >= max_index) break; |
| 1686 | |
| 1687 | for (int j = min_index; j < max_index; j++) { |
| 1688 | if (s2_matching[j] == -1 && s1.ptr[i] == s2.ptr[j]) { |
| 1689 | s1_matching[i] = i; |
| 1690 | s2_matching[j] = j; |
| 1691 | matching_characters++; |
| 1692 | break; |
| 1693 | } |
| 1694 | } |
| 1695 | } |
| 1696 |